Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
aradotso avatar

Pentestcompanion Workspace

  • 1 installs
  • 10 repo stars
  • Updated August 4, 2026
  • aradotso/security-skills

Pentest Companion is a Claude skill for a self-hosted workspace that manages penetration-testing engagements, tool runs, findings, and reports.

About

Pentest Companion is a self-hosted workspace that consolidates penetration-testing engagement tracking, tool execution, finding management, and reporting in one interface. A tester uses it to run 90+ tools with live output, auto-import findings, score them with CVSS v3.1, and generate DOCX or PDF reports. All data stays on the user's own infrastructure with no cloud dependencies.

  • Self-hosted workspace for tracking pentest engagements and findings
  • 90+ integrated tools with live output and auto-import
  • CVSS v3.1 scoring, 2400+ finding templates, DOCX/PDF reporting

Pentestcompanion Workspace by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #1,834 of 2,203 Security skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

pentestcompanion-workspace capabilities & compatibility

Free and self-hosted; requires setting SECRET_KEY and ADMIN_PASSWORD, optional SMTP/Resend keys for email.

Capabilities
security audit · vulnerability scanning · report generation · orchestration
Works with
slack · teams · postgres
Use cases
security audit · orchestration
Platforms
Linux · macOS
Runs
Runs locally
Pricing
Free
From the docs

What pentestcompanion-workspace says it does

Pentest Companion is a self-hosted workspace for managing penetration testing engagements.
SKILL.md
It consolidates target tracking, tool execution, finding management, CVSS scoring, evidence collection, client portals, and report generation into a single interface.
SKILL.md
npx skills add https://github.com/aradotso/security-skills --skill pentestcompanion-workspace

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars10
Last updatedAugust 4, 2026
Repositoryaradotso/security-skills

What it does

Track pentest engagements, run tools, auto-import findings, and generate reports from one self-hosted workspace.

Who is it for?

Pentesters and consultancies wanting a self-hosted hub for engagements, findings, and client reports.

Skip if: Teams needing a cloud-hosted SaaS; this is self-hosted only.

When should I use this skill?

You need to organize a pentest engagement, run tools with auto-import, or generate a client report.

What you get

A single self-hosted workspace tracks targets, runs tools, scores findings, and produces reports.

By the numbers

  • 90+ integrated tools
  • 2400+ finding templates

Files

SKILL.mdMarkdownGitHub ↗

Pentest Companion Workspace

Skill by ara.so — Security Skills collection.

Pentest Companion is a self-hosted workspace for managing penetration testing engagements. It consolidates target tracking, tool execution, finding management, CVSS scoring, evidence collection, client portals, and report generation into a single interface. All data stays on your infrastructure—no cloud dependencies.

What It Does

  • Engagement Management: Track targets, open ports, credentials, attack paths, PTES checklist phases, and time spent
  • Finding Management: CVSS v3.1 scoring, CVE lookup, evidence uploads, 2400+ templates, bulk operations
  • Tools Hub: 90+ integrated tools (nmap, gobuster, nikto, sqlmap, netexec, impacket suite, etc.) with live output streaming and auto-import
  • Web Scanner: Passive security scanner for TLS, headers, cookies, CORS, exposed files, tech fingerprinting
  • Reporting: DOCX/PDF generation with branded cover pages, executive summaries, and technical findings
  • Workflow Playbooks: Sequential multi-tool scan pipelines (External Recon, Web App, AD/SMB Enum, etc.)
  • Terminal Logging: Pipe command output from your terminal into engagement sessions with ANSI replay
  • Scheduled Scans: Recurring tool runs against targets with auto-import
  • Webhooks: Slack/Discord/Teams notifications on finding creation
  • REST API: Read-only endpoints for engagements and findings

Installation

Docker (Recommended)

git clone https://github.com/Poellie01/PentestCompanion.git
cd PentestCompanion
cp .env.example .env

# Generate SECRET_KEY
python3 -c "import secrets; print(secrets.token_hex(32))" | \
  xargs -I {} sed -i 's/^SECRET_KEY=$/SECRET_KEY={}/' .env

# Edit .env to set ADMIN_PASSWORD, SMTP settings (optional)
nano .env

docker compose up -d
docker compose logs -f app

Access at http://localhost:5000. Default admin credentials are printed on first run.

Python

git clone https://github.com/Poellie01/PentestCompanion.git
cd PentestCompanion
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python app.py

Configuration

.env file controls all configuration:

# Required
SECRET_KEY=<generated-hex-key>
ADMIN_PASSWORD=<your-secure-password>

# Database (defaults to SQLite)
DATABASE_URL=sqlite:///pentest_companion.db
# Or PostgreSQL: postgresql://user:pass@localhost/pentestcompanion

# Email (for invites, password reset, MFA)
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=your-email@gmail.com
SMTP_PASSWORD=<app-password>
SMTP_FROM=noreply@example.com

# Or Resend
RESEND_API_KEY=re_xxxxxxxxxxxx
RESEND_FROM=noreply@yourdomain.com

# Application
HOST=0.0.0.0
PORT=5000
FLASK_ENV=production

# SSRF Protection (block private IPs in webhooks/scanner)
SSRF_BLOCK_PRIVATE=true

Core Workflows

Creating an Engagement

# Via Python API (if extending the app)
from models import Engagement, db

engagement = Engagement(
    name="Acme Corp External Pentest",
    client="Acme Corporation",
    scope="10.0.0.0/24, *.acme.com",
    start_date=datetime(2026, 6, 1),
    end_date=datetime(2026, 6, 15),
    status="in_progress"
)
db.session.add(engagement)
db.session.commit()

Via UI: Engagements → New Engagement → fill form → optionally enable Auto-Scan to run tools on target creation.

Adding Targets

from models import Target

target = Target(
    engagement_id=1,
    ip="10.0.0.50",
    hostname="web01.acme.com",
    os="Linux",
    ports="22,80,443"
)
db.session.add(target)
db.session.commit()

UI: Engagement page → Targets → Add Target

Running Tools

From UI:

1. Navigate to Tools Hub 2. Select tool (e.g., nmap-quick) 3. Choose engagement and target 4. Click Run Tool 5. Watch live output stream 6. Findings auto-import on completion

From terminal with logging:

# Set up pclog helper in ~/.bashrc or ~/.zshrc
PCLOG_TOKEN="pcsk_your_token_here"
PCLOG_BASE="http://localhost:5000"

pclog() {
    local eid=$1; shift
    local name="${*:-$(date +%H:%M:%S)}"
    local sid
    sid=$(curl -sf -X POST "$PCLOG_BASE/api/v1/terminal/start" \
        -H "Authorization: Bearer $PCLOG_TOKEN" \
        -H "Content-Type: application/json" \
        -d "{\"engagement_id\":$eid,\"name\":\"$name\"}" \
        | python3 -c "import sys,json; print(json.load(sys.stdin)['session_id'])")
    while IFS= read -r line; do
        printf '%s\n' "$line"
        printf '%s\n' "$line" | curl -sf -X POST "$PCLOG_BASE/api/v1/terminal/append/$sid" \
            -H "Authorization: Bearer $PCLOG_TOKEN" \
            -H "Content-Type: application/octet-stream" --data-binary @- > /dev/null
    done
    curl -sf -X POST "$PCLOG_BASE/api/v1/terminal/close/$sid" \
        -H "Authorization: Bearer $PCLOG_TOKEN" > /dev/null
}

# Usage
nmap -sV -p- 10.0.0.50 | pclog 1 "nmap full scan"
gobuster dir -u http://10.0.0.50 -w /usr/share/wordlists/common.txt | pclog 1 "gobuster"

Creating Findings

from models import Finding

finding = Finding(
    engagement_id=1,
    title="SQL Injection in Login Form",
    severity="critical",
    cvss_score=9.8,
    cvss_vector="CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
    status="open",
    affected_hosts="web01.acme.com",
    description="The login form is vulnerable to SQL injection via the username parameter.",
    remediation="Use parameterized queries or prepared statements.",
    references="https://owasp.org/www-community/attacks/SQL_Injection"
)
db.session.add(finding)
db.session.commit()

Bulk import from Nessus/Burp:

UI: Findings → Import → Upload `.nessus` or Burp XML → findings auto-created

Using templates:

UI: Findings → New Finding → Use Template → select from 2400+ templates → customize

Web Scanning

# Via Python (custom integration)
import requests

response = requests.post(
    "http://localhost:5000/api/v1/scanner/scan",
    headers={"Authorization": f"Bearer {API_TOKEN}"},
    json={
        "url": "https://example.com",
        "deep_scan": True,
        "engagement_id": 1
    }
)
scan_id = response.json()["scan_id"]

# Poll for results
results = requests.get(
    f"http://localhost:5000/api/v1/scanner/results/{scan_id}",
    headers={"Authorization": f"Bearer {API_TOKEN}"}
).json()

UI: Web Scanner → New Scan → Enter URL → Run → optionally promote findings to engagement

Workflow Playbooks

Running a playbook:

# Custom playbook definition (models.py)
from models import Playbook, PlaybookStep

playbook = Playbook(
    name="Custom Web Enumeration",
    description="Multi-stage web application enumeration",
    team_id=1
)
db.session.add(playbook)
db.session.flush()

steps = [
    PlaybookStep(playbook_id=playbook.id, order=1, tool_name="whatweb", args=""),
    PlaybookStep(playbook_id=playbook.id, order=2, tool_name="nikto", args=""),
    PlaybookStep(playbook_id=playbook.id, order=3, tool_name="gobuster-dir", args="-w /usr/share/wordlists/dirb/common.txt"),
    PlaybookStep(playbook_id=playbook.id, order=4, tool_name="sqlmap", args="--batch --crawl=2")
]
db.session.add_all(steps)
db.session.commit()

UI: Playbooks → Select Playbook → Choose Target → Run → watch step-by-step progress

Scheduled Scans

from models import ScheduledScan

scan = ScheduledScan(
    target_id=5,
    tool_name="nmap-quick",
    interval="daily",  # or 'hourly', '6hours', 'weekly'
    enabled=True
)
db.session.add(scan)
db.session.commit()

UI: Target page → Scheduled Scans → Add Schedule

Background daemon runs every 60 seconds and claims due jobs.

Generating Reports

# Via Python (custom script)
import requests

response = requests.post(
    "http://localhost:5000/api/v1/reports/generate",
    headers={"Authorization": f"Bearer {API_TOKEN}"},
    json={
        "engagement_id": 1,
        "format": "docx",  # or 'pdf'
        "include_executive_summary": True,
        "include_technical_report": True,
        "redact_sensitive": False
    }
)

with open("report.docx", "wb") as f:
    f.write(response.content)

UI: Engagement page → Report → Generate Report → customize sections → download DOCX/PDF

Webhooks

from models import Webhook

webhook = Webhook(
    team_id=1,
    name="Slack Critical Findings",
    url="https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXX",
    webhook_type="slack",
    enabled=True,
    trigger_on_manual=True,
    trigger_on_auto_import=True,
    severity_filter=["critical", "high"]
)
db.session.add(webhook)
db.session.commit()

UI: Team Settings → Webhooks → New Webhook → paste URL → test delivery

Slack payload example:

{
  "blocks": [
    {
      "type": "header",
      "text": {
        "type": "plain_text",
        "text": "🔴 New Critical Finding"
      }
    },
    {
      "type": "section",
      "fields": [
        {"type": "mrkdwn", "text": "*Title:*\nSQL Injection in Login"},
        {"type": "mrkdwn", "text": "*Severity:*\nCritical (9.8)"},
        {"type": "mrkdwn", "text": "*Host:*\nweb01.acme.com"},
        {"type": "mrkdwn", "text": "*Engagement:*\nAcme Corp Pentest"}
      ]
    }
  ]
}

REST API

All API requests require a Bearer token (create under Team Settings → API Tokens).

List Engagements

curl -H "Authorization: Bearer pcsk_your_token" \
     http://localhost:5000/api/v1/engagements

Response:

{
  "engagements": [
    {
      "id": 1,
      "name": "Acme Corp External Pentest",
      "client": "Acme Corporation",
      "status": "in_progress",
      "start_date": "2026-06-01",
      "end_date": "2026-06-15"
    }
  ]
}

List Findings

curl -H "Authorization: Bearer pcsk_your_token" \
     "http://localhost:5000/api/v1/engagements/1/findings?severity=critical&status=open&page=1"

Response:

{
  "findings": [
    {
      "id": 42,
      "title": "SQL Injection in Login Form",
      "severity": "critical",
      "cvss_score": 9.8,
      "status": "open",
      "affected_hosts": "web01.acme.com",
      "created_at": "2026-06-02T14:30:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 50,
    "total": 1
  }
}

Terminal Logging API

Start session:

curl -X POST http://localhost:5000/api/v1/terminal/start \
  -H "Authorization: Bearer pcsk_your_token" \
  -H "Content-Type: application/json" \
  -d '{"engagement_id":1,"name":"nmap scan"}'

Append output:

echo "Starting Nmap 7.94" | curl -X POST http://localhost:5000/api/v1/terminal/append/SESSION_ID \
  -H "Authorization: Bearer pcsk_your_token" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @-

Close session:

curl -X POST http://localhost:5000/api/v1/terminal/close/SESSION_ID \
  -H "Authorization: Bearer pcsk_your_token"

Tool Integration Examples

Adding a Custom Tool

# tools/custom_tool.py
from tools.base import BaseTool
import subprocess

class MyCustomTool(BaseTool):
    name = "my-custom-tool"
    display_name = "My Custom Scanner"
    category = "Custom"
    description = "Custom vulnerability scanner"
    
    def check_installed(self):
        return subprocess.run(["which", "my-scanner"], 
                            capture_output=True).returncode == 0
    
    def build_command(self, target, extra_args=""):
        return f"my-scanner --target {target.ip} {extra_args}"
    
    def parse_output(self, output):
        # Return list of Finding objects
        findings = []
        if "VULN-001" in output:
            findings.append({
                "title": "Custom Vulnerability Found",
                "severity": "high",
                "affected_hosts": target.ip,
                "description": "Detailed description..."
            })
        return findings

Register in tools/__init__.py:

from tools.custom_tool import MyCustomTool

TOOLS = [
    # ... existing tools
    MyCustomTool(),
]

Auto-Import Hook

# Custom auto-import handler
from models import Finding, db

def process_tool_output(engagement_id, tool_name, output):
    """Called after tool execution completes"""
    tool = get_tool_by_name(tool_name)
    parsed = tool.parse_output(output)
    
    for item in parsed:
        finding = Finding(
            engagement_id=engagement_id,
            title=item["title"],
            severity=item["severity"],
            affected_hosts=item["affected_hosts"],
            description=item["description"],
            auto_imported=True,
            import_source=tool_name
        )
        db.session.add(finding)
    
    db.session.commit()
    
    # Trigger webhooks
    trigger_webhooks(engagement_id, parsed)

Common Patterns

Bulk Finding Updates

from models import Finding, db

# Mark all informational findings as false positive
findings = Finding.query.filter_by(
    engagement_id=1, 
    severity="informational"
).all()

for finding in findings:
    finding.status = "false_positive"
    
db.session.commit()

UI: Select findings → Bulk Actions → Mark False Positive

Export/Import Engagements

Export:

from utils.bundle import export_bundle

bundle_path = export_bundle(engagement_id=1, output_dir="/tmp")
# Creates /tmp/acme-corp-pentest-2026-06-02.pcbundle

UI: Engagement → Export Bundle

Import:

from utils.bundle import import_bundle

new_engagement_id = import_bundle("/path/to/bundle.pcbundle")

UI: Engagements → Import Bundle → upload .pcbundle

Client Portal Sharing

from models import ClientPortal
import secrets

portal = ClientPortal(
    engagement_id=1,
    token=secrets.token_urlsafe(32),
    password_protected=True,
    password_hash=generate_password_hash("client-pass"),
    expires_at=datetime.now() + timedelta(days=30),
    enabled=True
)
db.session.add(portal)
db.session.commit()

# Share URL: http://localhost:5000/portal/{portal.token}

UI: Engagement → Client Portal → Create Portal Link → copy shareable URL

Exam Mode

from models import ExamSession

exam = ExamSession(
    engagement_id=1,
    exam_type="OSCP",
    duration_hours=24,
    points_required=70,
    started_at=datetime.now()
)
db.session.add(exam)
db.session.commit()

UI: Engagement → Exam Mode → Start Exam → live countdown in navbar → points tracker → screenshot slots

Troubleshooting

Tools Not Showing Up

Problem: Tool shows as "Not Installed" even though it's on PATH

Solution:

# Verify tool is accessible
docker exec -it pentestcompanion-app-1 which nmap

# Tools are detected via subprocess.run(["which", "tool"])
# Ensure tool binary is in container PATH
# For custom tools, add to Dockerfile or mount volume

Auto-Import Not Working

Problem: Tool runs successfully but findings don't appear

Solution:

# Check tool has parse_output() method
from tools import get_tool_by_name

tool = get_tool_by_name("nmap-quick")
print(tool.parse_output.__doc__)

# Verify output parsing logic
output = """... tool output ..."""
findings = tool.parse_output(output)
print(findings)  # Should return list of dicts

Enable debug logging in .env:

FLASK_ENV=development
LOG_LEVEL=DEBUG

Webhook Not Firing

Problem: Webhooks configured but no notifications received

Solution:

# Check webhook delivery log in UI
Team Settings → Webhooks → View Deliveries

# Test webhook manually
curl -X POST http://localhost:5000/api/v1/webhooks/test/WEBHOOK_ID \
  -H "Authorization: Bearer pcsk_your_token"

# Verify URL is not blocked by SSRF protection
# Private IPs blocked by default (10.x.x.x, 192.168.x.x, 127.x.x.x)
# To allow: SSRF_BLOCK_PRIVATE=false in .env

Database Migration Issues

Problem: Schema changes not applied

Solution:

# Apply migrations manually
docker exec -it pentestcompanion-app-1 flask db upgrade

# Or recreate database (WARNING: data loss)
docker compose down -v
docker compose up -d

Report Generation Fails

Problem: "Failed to generate report" error

Solution:

# Check pandoc is installed (for PDF conversion)
docker exec -it pentestcompanion-app-1 which pandoc

# Verify template files exist
docker exec -it pentestcompanion-app-1 ls -la templates/report_template.docx

# Check logs for detailed error
docker compose logs app | grep -i report

Performance with Large Engagements

Problem: UI sluggish with 1000+ findings

Solution:

# Enable pagination in queries
findings = Finding.query.filter_by(engagement_id=1)\
    .order_by(Finding.severity.desc())\
    .paginate(page=1, per_page=50)

# Archive old engagements
engagement.status = "archived"
db.session.commit()

UI: Engagement → Archive (hides from main list but preserves data)

Environment Variables Reference

# Core
SECRET_KEY=<required-hex-string>
ADMIN_PASSWORD=<optional-overrides-bootstrap>
DATABASE_URL=sqlite:///pentest_companion.db
FLASK_ENV=production
HOST=0.0.0.0
PORT=5000

# Email
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=<email>
SMTP_PASSWORD=<password>
SMTP_FROM=<from-address>
# Or use Resend
RESEND_API_KEY=<key>
RESEND_FROM=<from-address>

# Security
SSRF_BLOCK_PRIVATE=true
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_HTTPONLY=true
SESSION_COOKIE_SAMESITE=Lax

# Logging
LOG_LEVEL=INFO
LOG_FILE=/var/log/pentestcompanion.log

# Scheduled Scans
SCHEDULER_INTERVAL=60  # seconds

Key Files

  • app.py - Flask application entry point
  • models.py - SQLAlchemy models (Engagement, Finding, Target, etc.)
  • tools/ - Tool integration modules
  • routes/ - Flask blueprints for each feature
  • templates/ - Jinja2 templates for UI
  • static/ - CSS, JS, images
  • utils/bundle.py - .pcbundle export/import
  • utils/scanner.py - Web scanner logic
  • utils/report.py - Report generation (DOCX/PDF)
  • migrations/ - Alembic database migrations

Additional Resources

  • Documentation: docs/ folder in repository
  • Tool Templates: templates/finding_templates/ (2400+ pre-written findings)
  • Example Configs: .env.example, docker-compose.yml
  • API Spec: Built-in Swagger UI at /api/docs (when enabled)

---

This skill enables AI agents to help users set up, configure, and operate Pentest Companion for comprehensive penetration testing engagement management.

Related skills

FAQ

How many tools are integrated?

90+ tools including nmap, gobuster, nikto, sqlmap, netexec, and the impacket suite, with live output and auto-import.

Where is data stored?

Entirely on your own infrastructure; there are no cloud dependencies.

Securityauditappseccompliance

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.