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.
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.
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) |
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 |
Installation
Requirements: Python 3.10+, Node.js 16+ (for PPTX export), pip.
Clone or unzip the project
git clone https://github.com/saherm0hamed/PentestMgr.git # or: unzip PentestMgr.zip cd PentestMgr
Create a virtual environment and install dependencies
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
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
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.
Docker
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.
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 |
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 |
Engagements
An engagement represents a single penetration testing project. Each engagement has its own scope, finding list, notes, evidence, and report settings.
Status workflow
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.
Findings
Findings are automatically sorted by severity. Each finding tracks the full lifecycle from discovery to remediation.
Severity levels
Status pipeline
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.) |
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 |
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
POST /scope/engagement/{id}/check Content-Type: application/json {"targets": ["10.10.10.1", "192.168.1.0/24", "api.corp.com"]}
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.
Export Formats
Every engagement can be exported in three professional formats from the engagement detail page or the report preview.
| Format | Contents | Route |
|---|---|---|
| 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 |
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
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.
Authentication
The REST API uses JWT Bearer tokens. Session cookies are used for the web UI — the API is completely independent.
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", ...}}
curl http://localhost:5000/api/v1/engagements \ -H "Authorization: Bearer eyJ..."
Endpoints
Auth
Engagements
Findings
Notes & Evidence
Stats
Code Examples
Push findings from a Python script
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
curl -X PUT http://localhost:5000/api/v1/findings/1 \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{"status": "Remediated"}'
Test Suite
82 pytest tests covering every module. Run from the project root with the virtual environment active.
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 |
Project Structure
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
Release Guide
Initialise Git and tag v1.0.0
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
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
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))".