Skip to content

Commit

Permalink
Adding statistics section + first graphic
Browse files Browse the repository at this point in the history
  • Loading branch information
Chapizze committed Dec 7, 2024
1 parent 5f87a83 commit 95c14e1
Show file tree
Hide file tree
Showing 18 changed files with 390 additions and 26 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# PwnDoc1A

PwnDoc1A (Pwndoc fork) is a pentest reporting application making it simple and easy to write your findings and generate a customizable Docx report.
The main goal is to have more time to **Pwn** and less time to **Doc** by mutualizing data like vulnerabilities between users.
<div class="markdown-heading" dir="auto"><h1 align="center" tabindex="-1" class="heading-element" dir="auto">Pwndoc1A</h1><a id="user-content-Pwndoc1A" class="anchor" aria-label="Permalink: Pwndoc1A" href="#Pwndoc1A"></a></div>
<p align="center"> <a href="https://vuejs.org/"><img src="https://img.shields.io/badge/Vue.js-v3.15.4-42b883" alt="Vue.js Badge" style="background-color:#42b883;"></a> <a href="https://quasar.dev/"><img src="https://img.shields.io/badge/Quasar-v2.14-1976d2" alt="Quasar Badge" style="background-color:#1976d2;"></a> <a href="https://nodejs.org/"><img src="https://img.shields.io/badge/Node.js-v18-ffca28" alt="Node.js Badge" style="background-color:#ffca28;"></a> <a href="https://www.mongodb.com/"><img src="https://img.shields.io/badge/MongoDB-v8.0-4DB33D" alt="MongoDB Badge" style="background-color:#4DB33D;"></a> <a href="https://www.docker.com/"><img src="https://img.shields.io/badge/Docker-0db7ed" alt="Docker Badge" style="background-color:#0db7ed;"></a> </p>

**PwnDoc1A** (a fork of Pwndoc) is a **penetration testing reporting application** designed to streamline the process of documenting findings and generating customizable Docx reports. It features easy findings creation, knowledge sharing, statistics, and report generation capabilities to further enhance efficiency.
# Installation Guide

## Prerequisites
Expand Down Expand Up @@ -105,10 +104,13 @@ You can also open an GitHub so that I can help you.
- Multi-User reporting
- Docx Report Generation
- Docx Template customization
- Statistics (WIP)

# Demos

![](https://github.com/AmadeusITGroup/pwndoc1A/blob/vue3/demo.gif)

![](https://github.com/AmadeusITGroup/pwndoc1A/blob/vue3/docs/_images/stats.png)
# Donate

If you would like to help me and sponsor this project
Expand Down
1 change: 1 addition & 0 deletions backend/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ require('./routes/image')(app);
require('./routes/settings')(app);
require('./routes/sso')(app);
require('./routes/attachment')(app);
require('./routes/stats')(app);

app.get("*", function(req, res) {
res.status(404).json({"status": "error", "data": "Route undefined"});
Expand Down
41 changes: 41 additions & 0 deletions backend/src/lib/stats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
var Audit = require('../models/audit');
var User = require('../models/user');
var VulnerabilityCategory = require('../models/vulnerability-category');




async function getFindingByCategory(isAllowed, userId) {
const categories = await VulnerabilityCategory.getAll();
const audits = await Audit.getAudits(isAllowed, userId);

const data = [];

for (const smallaudit of audits) {
const audit = await Audit.getAudit(isAllowed, smallaudit.id, userId);
const year = new Date(audit.updatedAt).getFullYear();
for (const finding of audit.findings) {
const category = categories.find(cat => cat.name === finding.category);
if (category) {
const index = data.findIndex(d => d.year === year);
if (index === -1) {
data.push({ year: year, data: [] });
}
const yearData = data.find(d => d.year === year);
const index2 = yearData.data.findIndex(d => d.category === category.name);
if (index2 === -1) {
yearData.data.push({ category: category.name, count: 1 });
} else {
yearData.data[index2].count++;
}
}
}
}
const formattedData = data.map(d => ({
name: d.year,
data: d.data.map(item => item.count)
}))

return formattedData;
}
exports.getFindingByCategory = getFindingByCategory;
55 changes: 37 additions & 18 deletions backend/src/models/audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const fs = require('fs');
var key = fs.readFileSync('/run/secrets/key', 'utf8').trim()
var salt = fs.readFileSync('/run/secrets/salt', 'utf8').trim()
const CVSS31 = require('../lib/cvsscalc31');
const { resolveObjectURL } = require('buffer');
var Schema = mongoose.Schema;

var Paragraph = {
Expand Down Expand Up @@ -120,30 +121,49 @@ AuditSchema.plugin(mongooseFieldEncryption, {

// Get all audits (admin)
AuditSchema.statics.getAudits = (isAdmin, userId, filters) => {
return new Promise((resolve, reject) => {
var query = Audit.find(filters)
if (!isAdmin)
query.or([{creator: userId}, {collaborators: userId}, {reviewers: userId}])
query.populate('creator', 'username')
query.populate('collaborators', 'username')
query.populate('reviewers', 'username firstname lastname')
query.populate('approvals', 'username firstname lastname')
query.populate('company', 'name')
query.select('id name auditType language creator collaborators company createdAt state type parentId')
query.exec()
.then((rows) => {
return new Promise(async (resolve, reject) => {
try {
const rows = await Audit.find()
.populate('creator', 'username')
.populate('collaborators', 'username')
.populate('reviewers', 'username firstname lastname')
.populate('approvals', 'username firstname lastname')
.populate('company', 'name')
.select('id name auditType language creator collaborators company createdAt state type parentId')
.exec();
if (filters) {
const tab = [];
for (const row of rows) {
const audit = await Audit.getAudit(true, row._id, userId)
const lowercaseFilter = filters['findings.filter']?.toLowerCase();
if (
(audit.name && audit.name?.toLowerCase()?.match(lowercaseFilter)) ||
(audit.creator.username && audit.creator.username?.match(filters['findings.filter']))
) {
tab.push(row);
break
}
for (const finding of audit.findings || []) {
if(finding.title && finding.title.toLowerCase()?.match(lowercaseFilter)){
tab.push(row);
break; // Exit the loop if a matching finding is found
}
}
}
resolve(tab);
}
resolve(rows)
})
.catch((err) => {
reject(err)
})
}catch (err) {
console.log('An error occured when fetching audits from DB', err)
return reject(err);
}

})
}

// Get Audit with ID to generate report
AuditSchema.statics.getAudit = (isAdmin, auditId, userId) => {
return new Promise((resolve, reject) => {
console.log('sdd')
var query = Audit.findById(auditId)
if (!isAdmin)
query.or([{creator: userId}, {collaborators: userId}, {reviewers: userId}])
Expand All @@ -166,7 +186,6 @@ AuditSchema.statics.getAudit = (isAdmin, auditId, userId) => {
query.populate('comments.replies.author', 'username firstname lastname')
query.exec()
.then(async(row) => {
console.log(row)
if (!row)
throw({fn: 'NotFound', message: 'Audit not found or Insufficient Privileges'})
else{
Expand Down
18 changes: 18 additions & 0 deletions backend/src/routes/stats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = function (app) {

var Response = require('../lib/httpResponse.js');
var Stats = require('../lib/stats.js');
var acl = require('../lib/auth').acl;


app.get('/api/stats/findingByCategory', acl.hasPermission('audits:read-all'), async function (req, res) {
try {
var data = await Stats.getFindingByCategory(acl.isAllowed(req.decodedToken.role, 'audits:read-all'), req.decodedToken.id)
Response.Ok(res, data)
}
catch (err) {
Response.Internal(res, err)
}

});
}
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: '3'
services:
mongodb:
image: mongo:4.2.15
image: mongo:8.0
container_name: mongo-pwndoc
volumes:
- ./backend/mongo-data:/data/db
Expand Down
Binary file added docs/_images/stats.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
94 changes: 94 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "pwndoc frontend",
"productName": "pwndoc",
"cordovaId": "org.cordova.quasar.app",
"author": "Stanley Hammer",
"author": "Chapizze - Stanley Hammer",
"private": true,
"scripts": {
"build": "quasar build",
Expand Down Expand Up @@ -38,7 +38,8 @@
"vue-lodash": "^2.1.2",
"vue-router": "^4.5.0",
"vue2-dragula": "^2.5.5",
"vue3-draggable": "^2.0.9"
"vue3-draggable": "^2.0.9",
"vue3-apexcharts": "^1.8.0"
},
"devDependencies": {
"@quasar/app-webpack": "^3.15.1",
Expand Down
Loading

0 comments on commit 95c14e1

Please sign in to comment.