MCPInstall

pentestcompanion-workspace

Self-hosted pentest management workspace for tracking engagements, running tools, auto-importing findings, and generating reports

Installs
535
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/reason-machines/security-skills --skill pentestcompanion-workspace
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads pentestcompanion-workspace automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

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.

FAQ

What does the pentestcompanion-workspace skill do?

Self-hosted pentest management workspace for tracking engagements, running tools, auto-importing findings, and generating reports

How do I install pentestcompanion-workspace?

Run npx skills add https://github.com/reason-machines/security-skills --skill pentestcompanion-workspace in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/pentestcompanion-workspace/ (personal) or .claude/skills/pentestcompanion-workspace/ (project).

Which AI agents can use pentestcompanion-workspace?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is pentestcompanion-workspace safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains pentestcompanion-workspace?

It is published by reason-machines in reason-machines/security-skills. It has 535 recorded installs.

More from reason-machines/security-skills

View all →

anthropic-cybersecurity-skills

reason-machines/security-skills

Use 754 structured cybersecurity skills mapped to MITRE ATT&CK, NIST CSF, ATLAS, D3FEND, and NIST AI RMF for AI-driven security operations

AI & Agents1.7K installs

pentest-ai-agents

reason-machines/security-skills

Claude Code subagents for offensive security research, penetration testing planning, recon analysis, exploit research, detection engineering, and security reporting

Security1.4K installs

pentest-agents-bug-bounty-framework

reason-machines/security-skills

Autonomous bug bounty agent framework with 50 agents, hunt loops, exploit chains, MCP servers for platform integration and writeup search

openosint-ai-osint-framework

reason-machines/security-skills

AI-powered OSINT agent with interactive REPL, MCP server, and CLI for email/username/domain/IP/phone investigation using 11 integrated tools

Backend & APIs1.2K installs

vibe-security-skill

reason-machines/security-skills

Agent skill that audits vibe-coded apps for common security vulnerabilities introduced by AI coding assistants

Security1.1K installs

openclaw-security-hardening

reason-machines/security-skills

Deploy and manage security hardening for high-privilege autonomous AI agents (OpenClaw) using zero-trust architecture and automated defense matrices

Security1.1K installs

Popular security skills

View category →

azure-compliance

microsoft/azure-skills

Run Azure compliance and security audits with azqr plus Key Vault expiration checks. Covers best-practice assessment, resource review, policy/compliance validation, and security posture checks. WHEN: compliance scan, security audit, BEFORE running azqr (compliance cli tool), Azure best practices, Key Vault expiration check, expired certificates, expiring secrets, orphaned resources, compliance assessment.

OfficialSecurity567K installs

minimal-run-and-audit

lllllllama/rigorpilot-skills

Rigor Run skill for README-first deep learning repo reproduction. Use when the task is specifically to capture or normalize evidence from the selected smoke test or documented inference or evaluation command and write standardized reprooutputs/ files, including patch notes when repository files changed. Do not use for training execution, initial repo intake, generic environment setup, paper lookup, target selection, hidden scientific-meaning changes, or end-to-end orchestration by itself.

Security477.4K installs

seo-audit

coreyhaines31/marketingskills

When the user wants to audit, review, or diagnose SEO issues on their site. Also use when the user mentions "SEO audit," "technical SEO," "why am I not ranking," "SEO issues," "on-page SEO," "meta tags review," "SEO health check," "my traffic dropped," "lost rankings," "not showing up in Google," "site isn't ranking," "Google update hit me," "page speed," "core web vitals," "crawl errors," "indexing issues," "stuck on page 2," "striking distance keywords," "title tag rewrite," "low CTR," "local SEO," "Google Business Profile," or "map pack." Use this even if the user just says something vague like "my SEO is bad" or "help with SEO" — start with an audit. For building pages at scale to target keywords, see programmatic-seo. For adding structured data, see schema. For AI search optimization, see ai-seo.

Security238.7K installs

firebase-security-rules-auditor

firebase/agent-skills

Audits Firebase (Firestore, Cloud Storage) security rules for vulnerabilities, privilege escalation, role bypasses, create vs update inconsistencies, resource exhaustion, type safety, size limits, and hasOnly ownership checks. Use when auditing/reviewing rules, running red-team rule assessments, or scoring against auditor checklists. Don't use for Firebase CLI (login, deploy), Auth, Crashlytics, Remote Config, or database queries.

OfficialSecurity139.1K installs

audit

pbakaus/impeccable

Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.

Security81.9K installs

competitors

coreyhaines31/marketingskills

When the user wants to create competitor comparison or alternative pages for SEO and buyer-facing use. Also use when the user mentions 'alternative page,' 'vs page,' 'competitor comparison,' 'comparison page,' '[Product] vs [Product],' '[Product] alternative,' 'competitive landing pages,' 'how do we compare to X,' 'competitor teardown,' 'audit our competitor pages,' 'are our comparison pages out of date,' or 'competitive asset audit.' Use this for any content that positions your product against competitors. Covers four formats: singular alternative, plural alternatives, you vs competitor, and competitor vs competitor. For auditing existing claims (not researching competitors from scratch, which is competitor-profiling; not technical SEO on these pages, which is seo-audit), use the asset audit here. For internal battle cards and sales-specific competitor docs, see sales-enablement.

Security79.3K installs