PentestMgr

A self-hosted engagement management platform for penetration testers. Manage findings, generate professional reports in PDF, DOCX and PPTX, validate scope, and automate via REST API.

Python / Flask GPL-3.0 SQLite / Postgres Docker Ready
Overview

Features

Everything you need to run a professional penetration testing engagement from scope definition to client delivery.

Engagement Management

Full CRUD, scope tracking, status workflow, notes, and evidence upload.

Finding Tracker

Severity pipeline (Critical → Info), CVSS scoring, CVE, PoC, remediation status.

PDF / DOCX / PPTX

Professional report exports with cover page, exec summary, CVSS bars, and findings.

Report Branding

Custom colours, font families, company name, and footer — per engagement.

Scope Validator

Real-time validation of IPs, CIDRs, domains, wildcards, and URLs against scope.

15 Finding Templates

SQLi, XSS, SSRF, RCE, IDOR, XXE, LFI, CSRF, and more — one-click apply.

REST API

Full JWT-protected API — push findings from Nmap, Nuclei, or your own scripts.

Light / Dark Mode

Toggle persists in localStorage. Text contrast auto-adapts to background colour.

Overview

Tech Stack

Layer Technology Purpose
Web framework Flask 3.0 Routes, blueprints, Jinja2 templates
Database SQLite / PostgreSQL Via SQLAlchemy ORM + Flask-Migrate
Auth Flask-Login + Flask-JWT-Extended Session auth (UI) + JWT (API)
PDF ReportLab 4.2 Programmatic PDF generation
DOCX python-docx Word document export
PPTX pptxgenjs (Node.js) PowerPoint slide generation
Charts matplotlib Risk matrix PNG
Security Flask-WTF CSRF protection on all forms
Deployment Docker + gunicorn Container + production WSGI
Getting Started

Installation

Requirements: Python 3.10+, Node.js 16+ (for PPTX export), pip.

Clone or unzip the project

bash
git clone https://github.com/saherm0hamed/PentestMgr.git
# or: unzip PentestMgr.zip
cd PentestMgr

Create a virtual environment and install dependencies

bash
python3 -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate
pip install -r requirements.txt
npm install -g pptxgenjs           # for PPTX export

Configure environment variables

bash
cp .env.example .env
# Edit .env and set strong secrets:
SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
JWT_SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")

Seed demo data (optional) and run

bash
python manage.py seed   # admin/admin123 + tester/tester123
python run.py           # → http://localhost:5000

The first account you register automatically becomes admin if no seed was run.

Getting Started

Docker

bash
cp .env.example .env              # set secrets first
docker-compose up -d
docker-compose exec pentest-mgr python manage.py seed

Data persists in the pentest_data Docker volume. The container runs gunicorn with 2 workers on port 5000.

Production tip: Always set FLASK_DEBUG=0 and use strong random values for both secret keys before any public deployment.

Getting Started

Configuration

Variable Required Description
SECRET_KEY Yes Flask session secret — long random hex string
JWT_SECRET_KEY Yes JWT signing key — different from SECRET_KEY
DATABASE_URL No Default: sqlite:///pentest.db. Use postgresql://... for Postgres
FLASK_DEBUG No 1 for development, 0 for production
Getting Started

CLI & manage.py

The manage.py script provides administrative commands for managing the platform.

Command Description
create-admin Interactively create an admin user
create-user Interactively create a tester user
list-users List all registered users with roles
reset-password Reset a user's password by username
reset-db Drop and recreate all tables — destroys all data
seed Seed demo data: 2 users + 1 engagement + 5 findings
routes Print all registered URL routes
stats Print database statistics
Core Modules

Engagements

An engagement represents a single penetration testing project. Each engagement has its own scope, finding list, notes, evidence, and report settings.

Status workflow

Planned Active Completed Archived

Engagement types

Web Application, Network, Mobile, Red Team, Social Engineering, Cloud, Physical, API, Other.

Scope format

Enter one target per line. Supports IPs, CIDR ranges, domains, and wildcard domains. The scope validator checks targets in real-time against these entries.

Core Modules

Findings

Findings are automatically sorted by severity. Each finding tracks the full lifecycle from discovery to remediation.

Severity levels

Critical High Medium Low Informational

Status pipeline

Open In Review Remediated Accepted Risk False Positive

Fields

Field Description
title Short descriptive name
severity Critical / High / Medium / Low / Informational
cvss_score CVSS 3.x base score (0.0–10.0)
cve CVE identifier (e.g. CVE-2025-12345)
description Technical description of the vulnerability
impact Business and technical impact
recommendation Remediation steps
proof_of_concept PoC code, commands, or screenshots
affected_hosts Newline-separated list of affected targets
references Newline-separated URLs (OWASP, NVD, etc.)
Core Modules

Finding Templates

15 pre-built finding templates covering the most common vulnerabilities. Apply any template to an engagement with one click — it pre-fills title, severity, CVSS, description, impact, recommendation, and references. You only need to customise the PoC and affected hosts.

Template Severity CVSS
SQL Injection Critical 9.8
Remote Code Execution (RCE) Critical 10.0
Default / Weak Credentials Critical 9.8
Broken Authentication Critical 9.0
Reflected XSS High 7.4
Stored XSS High 8.0
IDOR High 7.5
SSRF High 8.6
XXE High 8.1
LFI High 7.5
CSRF Medium 6.5
Open Redirect Medium 6.1
Sensitive Data Exposure Medium 5.3
Outdated Software Medium 6.5
Missing Security Headers Low 3.1
Core Modules

Scope Validator

Paste any list of targets and validate them instantly against the engagement's in-scope and out-of-scope lists.

Supported target types

Type Example Notes
Bare IP 10.129.14.129 Exact match
CIDR range 192.168.1.0/24 All IPs in range match
Domain app.target.com Exact match
Wildcard domain *.target.com All subdomains match
URL https://api.target.com/v1 Extracted domain matched

API endpoint

bash
POST /scope/engagement/{id}/check
Content-Type: application/json

{"targets": ["10.10.10.1", "192.168.1.0/24", "api.corp.com"]}
Core Modules

Evidence Upload

Attach evidence files directly to an engagement or to a specific finding. Files are stored in instance/uploads/ with unique names to prevent collisions.

Supported types: PNG, JPG, GIF, WebP, PDF, TXT, XML, JSON, LOG, ZIP, PCAP, HAR · Max 20 MB per file.

Reports

Export Formats

Every engagement can be exported in three professional formats from the engagement detail page or the report preview.

Format Contents Route
PDF Cover page, executive summary, severity table, scope, findings with CVSS bars and PoC GET /reports/engagement/<id>/pdf
DOCX Cover, executive summary, scope, findings, remediation tracker table GET /reports/engagement/<id>/docx
PPTX Cover, exec summary, severity bar chart, scope, per-finding slides, tracker slide GET /reports/engagement/<id>/pptx
JSON Full engagement + findings + notes backup (importable) GET /export/engagement/<id>/json
Reports

Branding & Fonts

Each engagement has independent branding settings saved in instance/branding/<id>.json. Changes apply immediately to all export formats.

Setting Description
Company name Appears on the cover page and in the page header of every PDF page
Accent colour Section headings, HR lines, cover accent bar, severity table divider
Background colour Page background — text colour auto-adapts (WCAG luminance formula)
Footer text Shown at the bottom of every PDF page alongside the page number
Font family Applied to all body text in PDF, DOCX, and PPTX

Auto contrast

Text colour is automatically selected based on background luminance — no manual text colour setting needed. Dark backgrounds receive light text (#E6EDF3), light backgrounds receive dark text (#1F2328).

Available fonts

Helvetica
PDF: Helvetica · DOCX: Arial
Times New Roman
PDF: Times-Roman
Georgia
PDF: Helvetica · DOCX: Georgia
Garamond
PDF: Times-Roman · DOCX: Garamond
Calibri
PDF: Helvetica · DOCX: Calibri
Arial
PDF: Helvetica · DOCX: Arial
Verdana
PDF: Helvetica · DOCX: Verdana
Courier New
PDF: Courier · DOCX: Courier New
Reports

Remediation Tracker

Set a remediation deadline and responsible owner for each finding in an engagement. This data is stored in instance/remediation/<id>.json and automatically included in DOCX and PPTX exports as a tracker table.

Access via Engagement Detail → ↓ Export → ✅ Remediation.

API

Authentication

The REST API uses JWT Bearer tokens. Session cookies are used for the web UI — the API is completely independent.

Get a token
curl -X POST http://localhost:5000/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "yourpassword"}'

# Response
{"access_token": "eyJ...", "user": {"username": "admin", ...}}
Use the token
curl http://localhost:5000/api/v1/engagements \
  -H "Authorization: Bearer eyJ..."
API

Endpoints

Auth

POST/api/v1/auth/tokenGet JWT token

Engagements

GET/api/v1/engagementsList all
POST/api/v1/engagementsCreate
GET/api/v1/engagements/:idGet one
PUT/api/v1/engagements/:idUpdate
DELETE/api/v1/engagements/:idDelete

Findings

GET/api/v1/engagements/:id/findingsList findings
POST/api/v1/engagements/:id/findingsCreate finding
GET/api/v1/findings/:idGet finding
PUT/api/v1/findings/:idUpdate finding
DELETE/api/v1/findings/:idDelete finding

Notes & Evidence

GET/api/v1/engagements/:id/notesList notes
POST/api/v1/engagements/:id/notesCreate note
DELETE/api/v1/notes/:idDelete note
GET/api/v1/engagements/:id/evidenceList evidence

Stats

GET/api/v1/statsSeverity counts + totals
API

Code Examples

Push findings from a Python script

python
import requests

BASE  = "http://localhost:5000/api/v1"
token = requests.post(
    f"{BASE}/auth/token",
    json={"username": "admin", "password": "yourpassword"}
).json()["access_token"]

H = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}

# Create a finding from your recon script
requests.post(f"{BASE}/engagements/1/findings", headers=H, json={
    "title":           "SQL Injection in /api/search",
    "severity":        "Critical",
    "cvss_score":      9.8,
    "cve":             "CVE-2024-1234",
    "description":    "Unsanitised input allows UNION-based SQLi",
    "impact":          "Full database read/write access",
    "recommendation": "Use parameterised queries",
    "proof_of_concept":"' OR 1=1--",
    "affected_hosts":  "app.target.com",
})

Update finding status

bash
curl -X PUT http://localhost:5000/api/v1/findings/1 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"status": "Remediated"}'
Developer

Test Suite

82 pytest tests covering every module. Run from the project root with the virtual environment active.

bash
pytest tests/test_app.py -v

# Run a specific class
pytest tests/test_app.py::TestJWTApi -v
pytest tests/test_app.py::TestScopeValidator -v
pytest tests/test_app.py::TestBrandingAndFont -v
Test class Tests Covers
TestAuth 5 Login, logout, register, protection
TestJWTApi 21 All REST API endpoints
TestModels 7 ORM models, cascades, to_dict
TestScopeValidator 8 IP, CIDR, domain, wildcard matching
TestPDFGeneration 3 PDF bytes validity + download route
TestExportImport 5 JSON export/import, finding duplicate
TestProfile 5 Profile update, password change
TestUserManagement 4 Role, activate, reset password
TestUIRoutes 12 All web pages return 200/404
TestBrandingAndFont 13 Luminance, contrast, FONT_MAP, all 8 fonts
Developer

Project Structure

tree
PentestMgr/
├── run.py                     ← Flask dev server
├── manage.py                  ← CLI tool
├── pytest.ini
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── .env.example
├── docs/
│   └── index.html             ← GitHub Pages site
│
├── app/
│   ├── __init__.py            ← App factory + error handlers
│   ├── models.py              ← User, Engagement, Finding, Evidence, Note
│   │
│   ├── blueprints/
│   │   ├── auth.py            ← Login / logout / register
│   │   ├── dashboard.py       ← Stats overview
│   │   ├── engagements.py     ← Engagement CRUD + notes
│   │   ├── findings.py        ← Finding CRUD
│   │   ├── findings_list.py   ← Global search + filter
│   │   ├── reports.py         ← PDF / DOCX / PPTX + branding
│   │   ├── api.py             ← JWT REST API v1
│   │   ├── evidence.py        ← File upload / serve
│   │   ├── scope.py           ← Scope validator
│   │   ├── templates_bp.py    ← Finding templates
│   │   ├── export.py          ← JSON export / import / duplicate
│   │   ├── users.py           ← Admin user management
│   │   └── profile.py         ← Self-service profile
│   │
│   ├── templates/
│   │   ├── base.html          ← Dark/light layout + sidebar
│   │   ├── auth/
│   │   ├── dashboard/
│   │   ├── engagements/
│   │   ├── findings/
│   │   ├── reports/           ← preview, branding, remediation
│   │   ├── scope/
│   │   ├── templates/
│   │   ├── users/
│   │   ├── profile/
│   │   └── errors/            ← 403, 404, 500
│   │
│   └── static/
│       └── img/               ← favicon.ico, favicon-32.png, apple-touch-icon.png
│
└── tests/
    ├── conftest.py
    └── test_app.py            
            
Developer

Release Guide

Initialise Git and tag v1.0.0

bash
git init && git add .
git commit -m "feat: initial release v1.0.0"
git tag -a v1.0.0 -m "PentestMgr v1.0.0"

Push to GitHub

bash
git remote add origin https://github.com/saherm0hamed/PentestMgr.git
git push -u origin main --tags

Enable GitHub Pages

Go to Settings → Pages, set source to Deploy from branch, branch main, folder /docs. The site will be live at https://saherm0hamed.github.io/PentestMgr.

Create GitHub Release

bash
gh release create v1.0.0 \
  --title "PentestMgr v1.1.1" \
  --notes "Initial release"

Before any public deployment: rotate both SECRET_KEY and JWT_SECRET_KEY — generate with python3 -c "import secrets; print(secrets.token_hex(32))".