{"id":51994635,"url":"https://github.com/zebbern/pocmap","last_synced_at":"2026-07-31T04:01:39.689Z","repository":{"id":370717224,"uuid":"1296472142","full_name":"zebbern/pocmap","owner":"zebbern","description":"AI-friendly CVE / PoC / exploit-discovery toolkit - CLI + MCP (19 tools) + Python API","archived":false,"fork":false,"pushed_at":"2026-07-10T19:44:33.000Z","size":556,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-25T11:15:23.433Z","etag":null,"topics":["agents","ai-agents","claude-code","cli-tool","codex","cve","cve-scanning","defense-in-depth","exploit-discovery","mcp","mcp-server","mcp-tools","poc","poc-finder","pocmap","python","security-research","security-tools"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zebbern.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-07-10T12:21:37.000Z","updated_at":"2026-07-11T01:20:31.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/zebbern/pocmap","commit_stats":null,"previous_names":["zebbern/pocmap"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/zebbern/pocmap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zebbern%2Fpocmap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zebbern%2Fpocmap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zebbern%2Fpocmap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zebbern%2Fpocmap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zebbern","download_url":"https://codeload.github.com/zebbern/pocmap/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zebbern%2Fpocmap/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36101256,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-31T02:00:06.731Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","ai-agents","claude-code","cli-tool","codex","cve","cve-scanning","defense-in-depth","exploit-discovery","mcp","mcp-server","mcp-tools","poc","poc-finder","pocmap","python","security-research","security-tools"],"created_at":"2026-07-31T04:01:38.953Z","updated_at":"2026-07-31T04:01:39.658Z","avatar_url":"https://github.com/zebbern.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PocMap\n\n[![Version](https://img.shields.io/badge/version-2.4.2-blue.svg)](https://github.com/zebbern/pocmap)\n[![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n[![Pydantic](https://img.shields.io/badge/pydantic-v2-purple.svg)](https://docs.pydantic.dev/)\n\nAI-agent-optimized CVE exploit discovery toolkit for bug bounty hunters and security professionals. Find Proof-of-Concept exploits, CTF labs, bug bounty reports, and vulnerability intelligence from a single interface.\n\n## Key Features\n\n- **Multi-Source Discovery**: Queries GitHub, Exploit-DB, Metasploit, Nuclei, CTF labs, and bug bounty platforms simultaneously\n- **Structured Pydantic Models**: All data validated and serialized with full type safety and JSON Schema support\n- **MCP Server Integration**: 21 AI-native tools via Model Context Protocol for Claude Desktop, Cursor, and other AI agents\n- **Bug Bounty Toolkit**: Complete hunter toolkit with checklists, workflows, report templates, prioritization engine, and scope management\n- **Rich CLI**: 12 commands with colorized tables, progress bars, and bulk processing\n- **Composable Output**: `table`, `json`, `csv`, `md`, and `sarif` output on read commands, plus a stable [exit-code contract](#output-formats--exit-codes) for scripting and CI\n- **CI Security Gate**: `bulk --fail-on kev|critical|high|epss\u003e=N` fails the build on policy matches and emits SARIF 2.1.0 for GitHub code scanning\n- **Caching \u0026 Offline Mode**: persistent, TTL'd HTTP response cache and a first-class `--offline` mode for air-gapped or repeatable runs\n- **Snapshot Diffs**: `latest`/`discover --diff` show only what changed since the last identical run\n- **Concurrent Processing**: Thread pool-based bulk CVE processing with exponential backoff retry logic\n- **Report Generation**: Self-contained HTML reports (styled cards and tables, inline CSS, no external assets or JS) plus JSON export\n- **Security-Hardened**: SSRF protection, sandboxed Jinja2 templates, path traversal prevention, input validation\n\n## Installation\n\n```bash\n# From PyPI (CLI + library)\npip install pocmap\n\n# With the MCP server (MCP SDK 2.x — required for pocmap-mcp)\npip install \"pocmap[server]\"\n\n# From source (editable)\ngit clone https://github.com/zebbern/pocmap.git\ncd pocmap\npip install -e \".[server,dev]\"\n\n# Verify\npocmap --version\npocmap-mcp --help    # only after installing with the [server] extra\n```\n\nEvery CLI command is also available as `python -m pocmap ...` if the `pocmap` script is\nnot on your `PATH`. The MCP server is also available as `python -m pocmap.mcp_server`.\n\n**Requirements:**\n- Python 3.10+ (developed/verified on 3.12)\n- Core dependencies: pydantic\u003e=2.0, requests\u003e=2.28, urllib3, typer\u003e=0.9, click, rich\u003e=13.0, beautifulsoup4, markdown, jinja2, python-dotenv (see `pyproject.toml` for the full list and version pins)\n\n**Optional:**\n- `GITHUB_API_TOKEN` - GitHub PAT for higher rate limits (recommended)\n- `NVD_API_KEY` - NVD API key for increased rate limits\n- The `[server]` extra (MCP SDK `mcp\u003e=2.0,\u003c3`) is required only for the MCP server / `pocmap-mcp`\n\n## Quick Start\n\n```bash\n# Look up a single CVE\npocmap lookup CVE-2021-44228\n\n# Show full description and references\npocmap lookup CVE-2021-44228 --description\n\n# Filter PoCs by programming language\npocmap lookup CVE-2021-44228 --language Python\n\n# Process multiple CVEs from a file\npocmap bulk cves.txt --output ./reports\n\n# Search CTF labs for hands-on practice\npocmap labs CVE-2021-44228\n\n# Search bug bounty reports\npocmap bugbounty CVE-2021-44228\n\n# Get CPEs (affected products) for a CVE\npocmap cpes CVE-2021-44228\n\n# Convert CPE to CVEs\npocmap cpe2cve \"cpe:2.3:a:apache:log4j:2.0\"\n\n# Export JSON schemas for AI agent integration\npocmap schemas --output ./schemas\n\n# Find recently published CVEs from the last 24 hours\npocmap latest\n\n# Find recent critical CVEs with PoCs from the last 7 days\npocmap latest --since 7d --severity critical --only-with-poc\n\n# Discover CVEs affecting a product by name\npocmap discover \"Apache Struts\"\n\n# Discover CVEs for a specific product version\npocmap discover \"Log4j\" --version 2.x\n\n# Machine-readable output (any read command): table (default), json, csv, md, sarif\npocmap lookup CVE-2021-44228 --format json\npocmap latest --since 7d --format sarif --output out/\n\n# Use pocmap as a CI gate (exit 6 if any CVE is in CISA KEV)\npocmap bulk cves.txt --format sarif --fail-on kev\n\n# Only show what changed since the last identical run\npocmap latest --since 24h --diff\n\n# Run self-diagnostics and inspect/clear the response cache\npocmap doctor\npocmap cache info\npocmap cache clear\n\n# Serve everything from the local cache (no network)\npocmap --offline lookup CVE-2021-44228\n\n# Show help with all options\npocmap --help\n```\n\n### CLI Commands (12)\n\n| Command | Purpose |\n|---------|---------|\n| `lookup` | Look up a single CVE plus discovered PoCs, DB exploits, and labs |\n| `bulk` | Process many CVEs from a file or stdin; JSON/HTML reports and CI gate |\n| `labs` | Find CTF labs and vulnerable environments for a CVE |\n| `bugbounty` | Find bug bounty reports / write-ups for a CVE |\n| `cpes` | List affected CPE identifiers for a CVE |\n| `cpe2cve` | List CVE IDs affecting a CPE identifier |\n| `readme` | Print a GitHub repo's README |\n| `schemas` | Export JSON schemas for all data models |\n| `latest` | Find recently published CVEs with exploit intelligence |\n| `discover` | Discover CVEs affecting a product by name and version |\n| `doctor` | Run self-diagnostics (Python, extras, tokens, cache, connectivity) |\n| `cache` | Inspect (`info`) and clear (`clear`) the persistent HTTP cache |\n\nGlobal options (on `pocmap` itself, before the command): `--format/-f {table,json,csv,md,sarif}`,\n`--offline`, `--quiet/-q`, `--version/-v`. Read commands also accept `--format`/`--quiet` locally,\nwhich override the global value.\n\n## Python API\n\n### CVE Information Lookup\n\n```python\nfrom pocmap.services.cve_service import CVEService\n\ncve_svc = CVEService()\ninfo = cve_svc.get_cve_info(\"CVE-2021-44228\")\n\nprint(info.id)                    # \"CVE-2021-44228\"\nprint(info.description)           # Full vulnerability description\nprint(info.cvss.base_score)       # 10.0\nprint(info.cvss.severity.value)   # \"CRITICAL\"\nprint(info.cvss.vector_string)    # \"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H\"\nprint(info.epss)                  # 97.53 (exploitation probability)\nprint(info.kev_status)            # True (in CISA KEV catalog)\nprint(info.cwes)                  # [\"CWE-77\", \"CWE-94\"]\nprint(info.vendor)                # \"Apache\"\nprint(info.product)               # \"Log4j\"\nprint(info.publication_date)      # \"2021-12-10\"\n```\n\n### Exploit Discovery\n\n```python\nfrom pocmap.services.exploit_service import ExploitService\n\nexploit_svc = ExploitService()\n\n# Find all exploits across all sources\nexploits = exploit_svc.find_exploits(\"CVE-2021-44228\")\nfor ex in exploits:\n    print(f\"[{ex.source.value}] {ex.title}\")\n    print(f\"  URL: {ex.url}\")\n    print(f\"  Language: {ex.language} | Stars: {ex.stars} | Forks: {ex.forks}\")\n\n# Filter by programming language\npython_pocs = exploit_svc.filter_by_language(exploits, \"Python\")\ngo_pocs = exploit_svc.filter_by_language(exploits, \"Go\")\n\n# Get a GitHub repo's README\nreadme = exploit_svc.get_readme(\"https://github.com/example/poc\")\n```\n\n### Lab Environments\n\n```python\nfrom pocmap.services.lab_service import LabService\n\nlab_svc = LabService()\nlabs = lab_svc.find_labs(\"CVE-2021-44228\")\nfor lab in labs:\n    print(f\"[{lab.platform.value}] {lab.name}: {lab.url}\")\n    if lab.setup_instructions:\n        print(f\"  Setup: {lab.setup_instructions}\")\n```\n\n### Bug Bounty Reports\n\n```python\nfrom pocmap.services.bb_service import BugBountyService\n\nbb_svc = BugBountyService()\nreports = bb_svc.find_reports(\"CVE-2021-44228\")\nfor report in reports:\n    print(f\"[{report.source.value}] {report.title}\")\n    print(f\"  URL: {report.url} | PoC included: {report.has_poc}\")\n```\n\n### Report Generation\n\n```python\nfrom pocmap.services.report_service import ReportService\n\nreport_svc = ReportService()\n\n# Single CVE report\nentry = report_svc.generate_report(\"CVE-2021-44228\")\nprint(entry.to_json())\n\n# Bulk report with JSON and HTML output\nmulti = report_svc.generate_bulk_report([\n    \"CVE-2021-44228\",\n    \"CVE-2023-38408\",\n    \"CVE-2024-21413\",\n])\nreport_svc.save_json_report(multi, \"./output\")\nreport_svc.save_html_report(multi, \"./output\")\n```\n\n### Schema Export for AI Agents\n\n```python\nfrom pocmap.models import export_schemas\n\npaths = export_schemas(\"./schemas\")\n# Generates: CVSSScore.json, CVEInfo.json, Exploit.json,\n#            LabEnvironment.json, BugBountyReport.json,\n#            CPEInfo.json, RecentExploitResult.json, ReportEntry.json,\n#            MultiReport.json, VersionConstraint.json,\n#            ProductDiscoveryResult.json\n```\n\n## Bug Bounty Toolkit\n\nThe `pocmap.bugbounty` module provides a comprehensive toolkit for bug bounty hunters:\n\n### Structured Checklists\n\nPhase-based checklists with P0-P4 priority levels, completion tracking, and time estimates:\n\n```python\nfrom pocmap.bugbounty import (\n    ReconnaissanceChecklist,\n    CVEResearchChecklist,\n    ExploitationChecklist,\n    ReportingChecklist,\n)\n\n# Create and track a checklist\nchecklist = ReconnaissanceChecklist()\nchecklist.items[0].complete(notes=\"Subdomain enumeration complete\")\nprint(checklist.completion_status())  # Progress percentage\n```\n\n### Methodology Workflows\n\nStructured, repeatable workflows with entry/exit criteria and difficulty ratings:\n\n```python\nfrom pocmap.bugbounty import (\n    CVEToBountyWorkflow,        # CVE -\u003e bug bounty pipeline\n    ZeroDayHuntingWorkflow,     # Proactive vulnerability discovery\n    PatchGapAnalysisWorkflow,   # Patch timing gap exploitation\n)\n\nworkflow = CVEToBountyWorkflow()\nresult = workflow.execute_phase(\"recon\", context={\"target\": \"example.com\"})\n```\n\n### Report Templates\n\nPlatform-specific report templates for HackerOne, Bugcrowd, and internal assessments:\n\n```python\nfrom pocmap.bugbounty import HackerOneTemplate, BugcrowdTemplate\n\ntemplate = HackerOneTemplate()\nreport = template.render(\n    cve_data=cve_info,\n    impact=\"Remote code execution achieved via crafted JNDI lookup\",\n    steps_to_reproduce=[\n        \"1. Identify Log4j 2.x instance\",\n        \"2. Send crafted payload to vulnerable endpoint\",\n        \"3. Observe DNS callback confirming RCE\",\n    ],\n)\n```\n\n### Prioritization Engine\n\nMulti-strategy CVE prioritization with bounty potential estimation:\n\n```python\nfrom pocmap.bugbounty import prioritize_cves, calculate_bounty_potential\n\n# Sort by composite score (CVSS + EPSS + KEV + exploit availability)\nsorted_cves = prioritize_cves(cve_list, strategy=\"composite\")\n\n# Or prioritize by specific factors\nsorted_cves = prioritize_cves(cve_list, strategy=\"epss\")        # Exploitation probability\nsorted_cves = prioritize_cves(cve_list, strategy=\"kev_first\")   # Known exploited first\nsorted_cves = prioritize_cves(cve_list, strategy=\"bounty_potential\")\n\n# Estimate bounty potential\nfor cve in sorted_cves[:10]:\n    bounty = calculate_bounty_potential(cve)\n    print(f\"{cve['id']}: potential=${bounty['estimated_median']}\")\n```\n\n### Scope Management\n\nParse and manage bug bounty program scope, match CVEs to in-scope assets:\n\n```python\nfrom pocmap.bugbounty import ScopeManager, Asset\n\nscope = ScopeManager()\nscope.add_program(\n    platform=\"hackerone\",\n    program=\"example\",\n    in_scope=[\"*.example.com\", \"api.example.com\"],\n    out_of_scope=[\"*.internal.example.com\"],\n)\n\n# Parse scope from file\nscope.parse_scope_file(\"scope.txt\")\n\n# Find CVEs affecting in-scope assets\nmatches = scope.match_cves_to_scope(cve_list)\n```\n\n### Playbooks\n\nJSON playbooks for structured workflows:\n\n```python\nfrom pocmap.bugbounty.playbooks import load_playbook, list_playbooks\n\n# List available playbooks\nfor pb in list_playbooks():\n    print(f\"{pb['name']}: {pb['description']} ({pb['difficulty']})\")\n\n# Load and execute a playbook\nplaybook = load_playbook(\"cve-assessment\")\nfor phase in playbook[\"phases\"]:\n    print(f\"Phase {phase['phase_id']}: {phase['name']}\")\n    for step in phase[\"steps\"]:\n        print(f\"  [{step['priority']}] {step['description']}\")\n```\n\nAvailable playbooks:\n- **cve-assessment**: Full CVE assessment workflow with risk scoring and remediation\n- **rapid-response**: Emergency response for critical/KEV CVEs with time-bounded actions\n- **bb-submission**: Complete bug bounty submission pipeline from finding to report\n\n## Recent CVE Discovery\n\nMonitor newly published vulnerabilities and filter by severity, exploitability, and time window. Ideal for security briefings, threat intelligence feeds, and proactive vulnerability management.\n\n### `pocmap latest`\n\n```bash\n# Recent CVEs from the last 24 hours (default)\npocmap latest\n\n# Specify a relative time window\npocmap latest --since 7d\npocmap latest --since 30d\npocmap latest --since 1h\n\n# Explicit date range\npocmap latest --from 2024-01-01 --to 2024-01-31\n\n# Only CVEs with known PoCs on GitHub\npocmap latest --only-with-poc\n\n# Only CISA KEV entries\npocmap latest --kev-only\n\n# Minimum EPSS score filter\npocmap latest --min-epss 50.0\n\n# Filter by severity levels (comma-separated)\npocmap latest --severity critical,high\n\n# Sort results\npocmap latest --sort cve_date     # newest first (default)\npocmap latest --sort severity     # highest severity first\npocmap latest --sort epss         # highest EPSS first\n\n# Limit results and save to JSON\npocmap latest --since 7d --severity critical --only-with-poc --limit 10 --output ./report.json\n```\n\n**Options:**\n\n| Option | Description |\n|--------|-------------|\n| `--since` | Relative time window: `1h`, `24h`, `7d`, `30d` |\n| `--from` | Start date in `YYYY-MM-DD` format (overrides `--since`) |\n| `--to` | End date in `YYYY-MM-DD` format |\n| `--only-with-poc` | Only return CVEs with known PoCs on GitHub |\n| `--kev-only` | Only return CISA Known Exploited Vulnerabilities |\n| `--min-epss` | Minimum EPSS score (0-100), e.g., `50.0` for EPSS \u003e= 50% |\n| `--severity` | Comma-separated severities: `critical`, `high`, `medium`, `low` |\n| `--sort` | Sort by: `cve_date`, `severity`, or `epss` |\n| `--limit` | Maximum results (1-100, default: 50) |\n| `--output`, `-o` | Save JSON report to file |\n| `--diff`, `--since-last` | Show only what changed since the last identical run (added/removed/changed) |\n| `--notify \u003curl\u003e` | POST a summary of notable CVEs (critical/high or KEV) to a webhook; with `--diff`, only the delta is sent |\n| `--format`, `-f` | Output format: `table` (default), `json`, `csv`, `md`, `sarif` |\n| `--quiet`, `-q` | Suppress decorative output |\n\n**Output includes:** CVE ID, description, CVSS severity/score, EPSS, KEV status, vendor, product, publication date, PoC availability, and PoC source counts.\n\n## Product Discovery\n\nFind all CVEs affecting a specific product without needing a CVE ID. Product names are\nresolved through the **NVD CPE dictionary** to canonical `vendor:product` identifiers,\nand CVEs are then fetched by CPE applicability match with the version constraint applied\nby NVD itself.\n\n### `pocmap discover`\n\n```bash\n# Discover CVEs for a product by name\npocmap discover \"Apache Struts\"\n\n# With version wildcard\npocmap discover \"Log4j\" --version 2.x\n\n# Exact version\npocmap discover \"nginx\" --version 1.20.1\n\n# With vendor hint\npocmap discover \"struts\" --vendor apache --version 2.x\n\n# Save results to JSON\npocmap discover \"Apache Struts\" --version 2.x --output ./struts-cves.json\n```\n\n**Options:**\n\n| Option | Description |\n|--------|-------------|\n| `--version`, `-v` | Version constraint: `2.x`, `2.14.1`, `\u003e= 2.0` |\n| `--vendor` | Vendor name hint: `apache`, `microsoft`, `google` |\n| `--limit` | Maximum CVEs to analyze (1-100, default: 50) |\n| `--output`, `-o` | Save JSON report to file |\n| `--diff`, `--since-last` | Show only what changed since the last identical run (added/removed/changed) |\n| `--notify \u003curl\u003e` | POST a summary of notable CVEs (critical/high or KEV) to a webhook; with `--diff`, only the delta is sent |\n| `--format`, `-f` | Output format: `table` (default), `json`, `csv`, `md`, `sarif` |\n| `--quiet`, `-q` | Suppress decorative output |\n\n### How a product name is resolved\n\n1. **Alias fast path.** A small curated table maps common shorthands to canonical names\n   (see below). A hit skips the dictionary lookup; a miss costs nothing.\n2. **NVD CPE dictionary.** The product name is resolved to every `vendor:product` pair\n   NVD files CVEs under, ranked by how many CPE entries back each pair. **All** pairs are\n   searched and the results unioned, because products change hands: `nginx` resolves to\n   `igor_sysoev:nginx` (0 CVEs), `nginx:nginx` (2) and `f5:nginx` (41), so taking only the\n   top-ranked pair would find almost nothing. At most 5 pairs are queried (a rate-limit\n   bound, not a precision one — NVD allows 5 requests/30s unauthenticated); any dropped\n   pairs are logged, never silently discarded.\n\n   **Editions and product families are included.** NVD files enterprise software under a\n   separate product per edition, so a query also admits any product in the *same vendor's\n   namespace* whose name extends the target: \"Confluence\" reaches\n   `atlassian:confluence_server` and `atlassian:confluence_data_center`, \"Jira\" reaches\n   `jira_service_desk`, `jira_software_data_center` and `jira_service_management`. A\n   third party's lookalike does not — `redhat:kubernetes-client` is not Kubernetes, and\n   `perforce:gliffy` is not Confluence. The bias is deliberate: an extra CVE in the list\n   is visible and dismissible, a missing one is neither.\n3. **Keyword fallback.** If the product cannot be resolved at all, `discover` falls back\n   to NVD full-text search. This is materially weaker — it matches CVE *descriptions*, so\n   it is both noisy and incomplete. The result reports which path ran:\n\n| `search_sources` | `matched_cpes` | Meaning |\n|------------------|----------------|---------|\n| `nvd_cpe_match` | the resolved CPE prefixes | Authoritative applicability match |\n| `nvd_keyword_search` | empty | Unresolvable product; noisy full-text fallback |\n\n\u003e **Rate limits.** Discovery costs one dictionary lookup plus one query per resolved pair.\n\u003e Unauthenticated NVD allows 5 requests per 30 seconds, so setting `NVD_API_KEY` is\n\u003e considerably more valuable than it used to be. Responses are cached (see\n\u003e [Caching \u0026 Offline Mode](#caching--offline-mode)), so repeat runs are cheap.\n\n### Product Alias System\n\nThe discovery command recognizes common product aliases and abbreviations, so you don't need to know the exact canonical product name:\n\n| Alias Input | Resolved Product |\n|-------------|-----------------|\n| `struts` | Apache Struts |\n| `log4j`, `log4j2` | Log4j |\n| `httpd`, `apache2` | Apache HTTP Server |\n| `k8s`, `kube` | Kubernetes |\n| `postgres`, `pgsql` | PostgreSQL |\n| `es`, `elastic` | Elasticsearch |\n| `nodejs`, `node` | Node.js |\n| `ghe` | GitHub Enterprise |\n| `ror` | Ruby on Rails |\n| `wp` | WordPress |\n| `ie`, `msie` | Internet Explorer |\n\nAliases are matched on the whole name, ignoring separators — `apache struts`,\n`apache_struts` and `Apache-Struts` are equivalent — and a known vendor phrase is peeled\noff first, so `\"Palo Alto PAN-OS\"` becomes vendor=`palo alto` + product=`pan-os`.\nMatching is deliberately not substring-based: anything the table does not recognize goes\nto the CPE dictionary, which covers the full NVD catalogue rather than this short list.\n\n### Version Constraint Format\n\nVersion constraints support multiple formats for flexible version matching:\n\n| Format | Example | Meaning |\n|--------|---------|---------|\n| Wildcard | `2.x` | Any version in major version 2 |\n| Exact | `2.14.1` | Exactly version 2.14.1 |\n| Major.Minor | `2.14` | Version 2.14.x |\n| Range (\u003e=) | `\u003e= 2.0` | Version 2.0 and above |\n| Range (\u003c=) | `\u003c= 1.20` | Version 1.20 and below |\n| Range (\u003e) | `\u003e 1.0` | Above version 1.0 |\n| Range (\u003c) | `\u003c 3.0` | Below version 3.0 |\n| None (omit) | - | Any version |\n\nResults are grouped into three confidence tiers. Matching considers **every**\n`(vendor, product)` pair a CVE is filed under, not just one — a CVE typically names the\nvulnerable component plus every distribution that shipped it, and judging it by a single\npair misclassifies the component the CVE is actually about:\n\n- **Confirmed**: Vendor AND product match AND version constraint is met\n- **Possibly**: Vendor OR product matches but version info is unclear\n- **Not enough data**: CVE has insufficient product/version information\n\nVersion matching is an interval-overlap test that honours NVD's out-of-band range\nattributes (`versionStartIncluding` / `versionEndExcluding`), which is where modern CVE\nrecords express affected ranges — the literal version field in the CPE string is usually\njust `*`.\n\n## Output Formats \u0026 Exit Codes\n\nRead commands emit machine-readable output via `--format/-f`. `--format` and `--quiet/-q`\ncan be set globally (before the command) or per command (the local value wins).\n\n```bash\npocmap lookup CVE-2021-44228 --format json      # structured view model to stdout\npocmap latest --since 7d --format csv           # spreadsheet-ready rows\npocmap discover \"Log4j\" --format md             # Markdown table for tickets/wikis\npocmap latest --since 24h --format sarif        # SARIF 2.1.0 for code scanning\npocmap -f json latest --since 7d                # global form\n```\n\n| Format | Value | Notes |\n|--------|-------|-------|\n| Table | `table` | Default. Rich colorized tables (human-facing). |\n| JSON | `json` | JSON-serializable view model to stdout, nothing else. |\n| CSV | `csv` | One row per record (`csv.DictReader`-friendly). |\n| Markdown | `md` | A Markdown table for tickets/wikis. |\n| SARIF | `sarif` | SARIF 2.1.0 log for CI code scanning. |\n\n**Format support by command:**\n- `lookup`, `doctor`, `cache info`, `cache clear`: `table`, `json`\n- `labs`, `bugbounty`, `cpes`, `cpe2cve`: `table`, `json`, `csv`, `md`\n- `latest`, `discover`: `table`, `json`, `csv`, `md`, `sarif`\n- `bulk`: `table` (writes JSON + HTML files), `json`, `csv`, `sarif`\n\nSARIF results are keyed on CVE IDs, so it is available only on the CVE-list commands\n(`latest`, `discover`, `bulk`). Requesting `--format sarif` on any other command exits\n`4` (invalid input) with a clear message. Severity maps to SARIF levels as\n`critical`/`high` -\u003e `error`, `medium` -\u003e `warning`, `low` -\u003e `note`; EPSS, KEV, exploit\ncount, and CVSS ride along in `result.properties`, and each CVE's NVD page is the rule `helpUri`.\n\n### Exit-Code Contract\n\nEvery command returns a stable, documented exit code (see `src/pocmap/utils/exit_codes.py`)\nso scripts and CI can react to *why* a command stopped, not just whether it succeeded:\n\n| Code | Name | Meaning |\n|------|------|---------|\n| `0` | `OK` | Success — the command ran and produced output. |\n| `1` | `ERROR` | Generic / unclassified error. |\n| `2` | `NO_RESULTS` | Ran fine but found nothing (empty result set). |\n| `3` | `NOT_FOUND` | Requested resource does not exist upstream (e.g. unknown CVE). |\n| `4` | `INVALID_INPUT` | Caller input was malformed (bad CVE ID, unsafe path, bad `--fail-on`). |\n| `5` | `UPSTREAM_ERROR` | An upstream data source failed (network, rate limit, 5xx, offline cache miss). |\n| `6` | `POLICY_FAIL` | A `bulk --fail-on` policy condition matched (the CI gate tripped). |\n\nThese values are a public contract: existing codes are never renumbered.\n\n## Caching \u0026 Offline Mode\n\nPocMap keeps a **persistent, TTL'd HTTP response cache** on disk (default `./.cache`).\nThis turns network-bound calls into sub-second cached ones, dodges GitHub/NVD rate\nlimits, and backs a real offline mode. Non-200 and error responses are never cached.\n\n```bash\n# Warm the cache with a normal (online) run, then work entirely offline\npocmap lookup CVE-2021-44228\npocmap --offline lookup CVE-2021-44228     # served from cache, zero network I/O\n\n# Inspect / clear the cache\npocmap cache info                          # location, entry count, on-disk size\npocmap cache clear                         # delete every cached entry\n```\n\nIn `--offline` mode (or with `POCMAP_OFFLINE=1`) HTTP GETs are served only from the\ncache; a cache miss surfaces a clear offline error and exits `5` (`UPSTREAM_ERROR`)\nrather than masquerading as \"not found\" or \"no results\". An **expired**-but-cached\nentry is served **stale** offline (an air-gapped run cannot refresh it, so stale\ndata beats an error) — only a genuinely absent entry raises. Online runs are\nunaffected: they still honour the TTL and refetch expired entries.\n\n**Cache / offline configuration:**\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `POCMAP_CACHE_ENABLED` | `true` | Enable/disable the persistent HTTP cache. |\n| `POCMAP_CACHE_DIR` | `./.cache` | Directory for cached responses. |\n| `POCMAP_CACHE_TTL` | `3600` | Seconds a cached entry stays fresh. |\n| `POCMAP_CACHE_MAX_MB` | `200` | On-disk cache cap (MB) before LRU eviction. |\n| `POCMAP_OFFLINE` | `false` | Serve only from cache; a miss errors instead of hitting the network. |\n\n## Verifying PoCs (opt-in)\n\nThe CVE indexes list repositories that *mention* a CVE, which is not the same as\nrepositories that exploit it — link lists, course notes and personal repos all show up.\nStar count does not separate them either: a popular repo can be an index, and a genuine\none-file PoC often has zero stars.\n\n`verify_github_pocs` downloads the top PoCs' **source** and reports what each actually\ncontains:\n\n| Verdict | Meaning |\n|---------|---------|\n| `confirmed` | Names the CVE **in code** and ships runnable code. The only tier that claims the repo exploits the CVE. |\n| `likely` | Names the CVE but has no code — a writeup. |\n| `unverified` | Has code, but never names this CVE. Unproven, *not* disproven: a PoC may be named for its target instead. |\n| `unrelated` | No mention, or an index — a link list, notes repo or scan dump. |\n\nIt also derives the language from file extensions, so it needs **zero** GitHub API calls.\n\nThe index test is *how many distinct CVEs the repository cites*, not how many files it\nhas: a PoC or a writeup is about one vulnerability, while a link list cites dozens.\nAcross a 55-repository sample, genuine PoCs cited at most 3 distinct CVEs and the\nindexes cited 10, 22 and 118 — so the boundary sits in a wide empty gap. Citing many\nCVEs only counts against a repo that also ships essentially no code, so a multi-CVE\nexploit toolkit is not mistaken for a list.\n\n```bash\nexport POCMAP_ALLOW_FETCH_POC_SOURCE=1     # CLI / Python API\n```\n\n\u003e **Using the MCP server? `export` will not work.** MCP clients launch the server with a\n\u003e filtered environment — the stdio transport inherits only `HOME`, `LOGNAME`, `PATH`,\n\u003e `SHELL`, `TERM` and `USER` — so **no `POCMAP_*` variable set in your shell reaches the\n\u003e server**, and `verify_github_pocs` will keep returning `not_enabled`. Put it in your\n\u003e client config's `env` block instead (this applies to `GITHUB_API_TOKEN` and\n\u003e `NVD_API_KEY` too):\n\u003e\n\u003e ```json\n\u003e {\n\u003e   \"mcpServers\": {\n\u003e     \"pocmap\": {\n\u003e       \"command\": \"uvx\",\n\u003e       \"args\": [\"--from\", \"pocmap[server]\", \"pocmap-mcp\"],\n\u003e       \"env\": {\n\u003e         \"POCMAP_ALLOW_FETCH_POC_SOURCE\": \"1\",\n\u003e         \"POCMAP_POC_SOURCE_DIR\": \"/home/you/.local/share/pocmap/poc-source\",\n\u003e         \"GITHUB_API_TOKEN\": \"ghp_xxxxxxxxxxxx\"\n\u003e       }\n\u003e     }\n\u003e   }\n\u003e }\n\u003e ```\n\n**This is off by default and must be set deliberately.** It writes third-party exploit\ncode to disk, which endpoint protection will often quarantine — run it on an isolated VM\nor a dedicated research host. pocmap never executes, imports, or evaluates fetched\ncontent; it only reads bytes.\n\nThe fetcher is bounded and hardened: owner/repo names are validated before they reach the\nURL, transfers go through the same SSRF-guarded HTTP client as everything else (per-hop\nredirect re-validation included), downloads and *extracted* sizes are both capped so a\ndecompression bomb cannot fill the disk, and archive members that are absolute, contain\n`..`, or are symlinks/devices are dropped rather than extracted.\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `POCMAP_ALLOW_FETCH_POC_SOURCE` | `false` | Master switch. Nothing is fetched unless this is set. |\n| `POCMAP_POC_SOURCE_DIR` | `\u003ccache_dir\u003e/poc-source` | Extraction root. |\n| `POCMAP_POC_SOURCE_MAX_MB` | `20` | Per-repo cap on download and extracted size. |\n| `POCMAP_POC_SOURCE_TOTAL_MAX_MB` | `500` | Total cap; oldest fetched repos are evicted first. |\n\nOffline mode has nothing to serve here — tarballs deliberately bypass the HTTP response\ncache — so `--offline` reports that plainly instead of failing obscurely.\n\n### Where to point `POCMAP_POC_SOURCE_DIR`\n\nThe default sits under the response cache, which is convenient but is *not* the right\nplace on every machine. Two things decide it: whether an on-access scanner will quarantine\nthe files, and whether the directory is synced anywhere.\n\n**Windows.** Defender scans NTFS on access and will quarantine exploit source, which both\ninterrupts the fetch and silently corrupts any scoring that reads the files back. Either\nput the directory inside WSL2 — its ext4 lives in a VHDX that Defender does not\nreal-time scan the way it does NTFS — or add a Defender exclusion for a dedicated path:\n\n```powershell\n# Option A: run pocmap inside WSL2 and keep the source there\n#   (from your WSL shell)\nexport POCMAP_POC_SOURCE_DIR=\"$HOME/.local/share/pocmap/poc-source\"\n\n# Option B: stay on Windows and exclude one dedicated directory (admin PowerShell)\nNew-Item -ItemType Directory -Force \"$env:LOCALAPPDATA\\pocmap\\poc-source\"\nAdd-MpPreference -ExclusionPath \"$env:LOCALAPPDATA\\pocmap\\poc-source\"\n$env:POCMAP_POC_SOURCE_DIR = \"$env:LOCALAPPDATA\\pocmap\\poc-source\"\n```\n\nExclude the narrowest path that works — one directory used only for this — never your\nhome directory or the whole repo.\n\n**Linux / macOS.** No on-access scanner by default, so a per-user cache path is fine:\n\n```bash\nexport POCMAP_POC_SOURCE_DIR=\"$HOME/.cache/pocmap/poc-source\"\n```\n\n**VM or dedicated research host.** Anywhere. This is the intended environment and needs\nno special handling.\n\n**Do not point it at a synced or shared folder.** OneDrive, Dropbox, iCloud Drive and\nGoogle Drive will upload the exploit source to cloud storage, where the provider's own\nscanner may flag the account and the content may be shared further than intended. This is\neasy to hit by accident on Windows, where `Documents` is frequently redirected to\nOneDrive — so a repo cloned there gets a OneDrive-synced `.cache/` with it. Prefer\n`%LOCALAPPDATA%` (never synced) over anything under `Documents`.\n\nThe same reasoning applies to `POCMAP_CACHE_DIR`, though it holds only API responses\nrather than exploit code, so it is far less sensitive.\n\n## Diagnostics: `doctor` \u0026 `cache`\n\n`pocmap doctor` is the fastest path from \"installed\" to \"working\". It checks the Python\nversion, the optional `[server]` extra, the *format* of your `GITHUB_API_TOKEN` /\n`NVD_API_KEY` (never printing their values), that the cache directory is writable, and\nruns a live connectivity probe against NVD and the GitHub API. It prints a PASS/WARN/FAIL\ntable and **exits nonzero if any check FAILs**.\n\n```bash\npocmap doctor                  # full run with a live connectivity probe\npocmap doctor --offline        # skip the network probe (labelled SKIPPED)\npocmap doctor --format json    # machine-readable check results\n```\n\n`pocmap cache info|clear` reports and clears the response cache (see above).\n\n## PocMap in CI\n\n`bulk` is a composable CI gate. Point it at a CVE list (a file, or `-` to read stdin),\nchoose a machine format, and use `--fail-on` to fail the build on a policy match:\n\n```bash\n# Fail the build (exit 6) if any dependency CVE is in the CISA KEV catalog,\n# and write a SARIF log for GitHub code scanning.\npocmap bulk cves.txt --format sarif --output out/ --fail-on kev\n\n# Pipe CVE IDs straight from another tool\ngrep -oE 'CVE-[0-9]{4}-[0-9]+' sbom.txt | pocmap bulk - --format json --fail-on critical\n```\n\n`--fail-on` accepts `critical`, `high` (HIGH *or worse*), `kev`, or `epss\u003e=N` (e.g.\n`epss\u003e=50` on the 0-100 EPSS scale). A match exits `6` (`POLICY_FAIL`) — distinct from a\ngeneric error — so CI can tell a tripped gate apart from an operational failure; no match\nexits `0`. A malformed `--fail-on` exits `4`. In `table` mode `bulk` preserves its\nhistorical behaviour (writes a JSON **and** an HTML report to `--output`); the machine\nformats (`json`/`csv`/`sarif`) emit a clean stdout summary and write no files, so the\nstream stays parseable.\n\nSee [`examples/ci-github-actions.yml`](examples/ci-github-actions.yml) for a ready-to-use\nGitHub Actions job that runs the gate and uploads the SARIF to code scanning, and the\n[`examples/`](examples/) directory for more runnable scripts.\n\n## AI Agent Integration\n\nPocMap includes a full MCP (Model Context Protocol) server exposing 21 AI-native tools for integration with Claude Desktop, Cursor, and other MCP-compatible clients.\n\n### MCP Server Setup for Claude Desktop\n\nRecommended: [`uv`](https://github.com/astral-sh/uv) on `PATH`, no local clone required.\n`--from pocmap[server]` pulls the package with the MCP SDK and runs the `pocmap-mcp`\nconsole script over STDIO.\n\n**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`\n\n**Windows:** `%APPDATA%/Claude/claude_desktop_config.json`\n\n```json\n{\n  \"mcpServers\": {\n    \"pocmap\": {\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"pocmap[server]\", \"pocmap-mcp\"],\n      \"env\": {\n        \"GITHUB_API_TOKEN\": \"ghp_xxxxxxxxxxxx\",\n        \"NVD_API_KEY\": \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n      }\n    }\n  }\n}\n```\n\nPin a release with `pocmap-mcp@X.Y.Z` as the last arg (that PyPI version must include the\n`pocmap-mcp` entry point). Optional env vars raise GitHub / NVD rate limits.\n\n**Already installed locally** (`pip install \"pocmap[server]\"` or `pip install -e \".[server]\"`):\n\n```json\n{\n  \"mcpServers\": {\n    \"pocmap\": {\n      \"command\": \"pocmap-mcp\",\n      \"args\": [],\n      \"env\": {\n        \"GITHUB_API_TOKEN\": \"ghp_xxxxxxxxxxxx\",\n        \"NVD_API_KEY\": \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n      }\n    }\n  }\n}\n```\n\n### Running the MCP Server\n\nThe MCP server requires the MCP SDK (`[server]` extra). It serves protocol\n`2026-07-28` (stateless core); the SDK still speaks earlier protocol versions to\nclients that have not upgraded:\n\n```bash\npip install \"pocmap[server]\"\n# or from a clone: pip install -e \".[server]\"\n```\n\n```bash\n# STDIO (default — what Claude Desktop / most MCP clients expect)\npocmap-mcp\npython -m pocmap.mcp_server\n\n# Other transports / flags\npocmap-mcp --transport sse\npocmap-mcp --transport http --host 0.0.0.0 --port 9000\npocmap-mcp --debug\n```\n\nRepo-root `python mcp_server.py` is a thin launcher shim to the same module (handy in a\ngit checkout). See also [`examples/mcp-config.json`](examples/mcp-config.json).\n\n### MCP Tools (21 Total)\n\n| Tool | Category | Description |\n|------|----------|-------------|\n| `lookup_cve` | CVE Intel | Full CVE details from NVD, CVE.org, CISA KEV, EPSS |\n| `get_epss_score` | CVE Intel | EPSS exploitation probability score (0.0-1.0) with risk level |\n| `check_kev_status` | CVE Intel | Check CISA Known Exploited Vulnerabilities catalog status |\n| `get_attack_techniques` | CVE Intel | MITRE ATT\u0026CK techniques a CVE maps to — how it's exploited and what follows |\n| `find_github_pocs` | Exploits | GitHub PoC repos with stars, language, and forks |\n| `verify_github_pocs` | Exploits | **Reads PoC source** to score whether a repo really exploits the CVE (opt-in) |\n| `find_metasploit_module` | Exploits | Metasploit module availability and msfconsole command |\n| `find_exploitdb_entry` | Exploits | ExploitDB entry with searchsploit command |\n| `find_nuclei_template` | Exploits | Nuclei scanner template for detection/verification |\n| `find_bug_bounty_reports` | Research | Bug bounty write-ups from HackerOne, PentesterLand |\n| `find_practice_labs` | Labs | CTF labs on Vulhub, HackTheBox, TryHackMe |\n| `find_vulhub_docker` | Labs | Vulhub Docker Compose environment with setup steps |\n| `find_recent_exploits` | Discovery | Recently published CVEs with PoC/KEV/severity filters |\n| `discover_product_cves` | Discovery | Find CVEs by product name with version constraints |\n| `cve_to_cpe` | Conversion | Convert CVE to affected CPE identifiers |\n| `cpe_to_cve` | Conversion | Find all CVEs affecting a given product (CPE) |\n| `generate_json_report` | Reports | **One-shot CVE assessment** — details + all exploits + labs + bug bounty reports for one or many CVEs in a single call |\n| `generate_html_report` | Reports | Self-contained HTML report with styled cards |\n| `get_cve_assessment_playbook` | Playbooks | Full CVE assessment workflow playbook |\n| `get_rapid_response_playbook` | Playbooks | Emergency response playbook for critical CVEs |\n| `get_bug_bounty_playbook` | Playbooks | Bug bounty submission workflow playbook |\n\n### MCP Resources\n\n| Resource | URI Pattern | Content |\n|----------|-------------|---------|\n| CVE Info | `cve://{cve_id}` | Full CVE details as human-readable text |\n| Exploits | `exploits://{cve_id}` | All available exploits and PoCs |\n| Report | `report://{cve_id}` | Generated vulnerability report (JSON) |\n\n### MCP Prompts\n\n| Prompt | Description |\n|--------|-------------|\n| `vulnerability_assessment` | 5-phase structured CVE assessment workflow |\n| `exploit_research` | Deep exploit analysis with detection engineering focus |\n| `bug_bounty_analysis` | Real-world impact analysis from bug bounty write-ups |\n\n### Example Agent Workflow\n\n```\nUser: \"Should I prioritize CVE-2021-44228, CVE-2023-38408, or CVE-2024-21413?\"\n\nAgent:\n1. lookup_cve(\"CVE-2021-44228\")     -\u003e CVSS 10.0 CRITICAL, EPSS 0.9753, KEV=true\n2. lookup_cve(\"CVE-2023-38408\")     -\u003e CVSS 9.8 CRITICAL, EPSS 0.3124, KEV=true\n3. lookup_cve(\"CVE-2024-21413\")     -\u003e CVSS 8.8 HIGH, EPSS 0.8912, KEV=true\n4. get_epss_score for each          -\u003e Confirm exploitation probabilities\n5. find_github_pocs for each        -\u003e Count available exploits\n6. check_kev_status for each        -\u003e Confirm KEV status\n7. Prioritize: Log4j (highest EPSS + most exploits) \u003e CVE-2024-21413 \u003e CVE-2023-38408\n```\n\n## JSON Schemas\n\nExport structured JSON schemas for all data models to integrate with AI agents, validation pipelines, and external tools:\n\n```python\nfrom pocmap.models import export_schemas\n\npaths = export_schemas(\"./schemas\")\nfor p in paths:\n    print(f\"  {p.name}\")\n# CVSSScore.json\n# CVEInfo.json\n# Exploit.json\n# LabEnvironment.json\n# BugBountyReport.json\n# CPEInfo.json\n# RecentExploitResult.json\n# ReportEntry.json\n# MultiReport.json\n# VersionConstraint.json\n# ProductDiscoveryResult.json\n```\n\nUse these schemas for:\n- **AI Agent Context**: Provide schema files to AI agents so they understand data structures\n- **Validation Pipelines**: Validate incoming/outgoing data against schemas\n- **API Documentation**: Auto-generate API docs from schemas\n- **Type Generation**: Generate TypeScript, Go, or Rust types from JSON schemas\n\n## Architecture\n\n```\n+------------------+     +------------------+     +------------------+\n|     CLI Layer    |     |   MCP Server     |     |   Python API     |\n|   (Typer/Rich)   |     |  (MCP SDK / 21   |     |   (Services)     |\n+------------------+     |     Tools)       |     +------------------+\n         |               +------------------+             |\n         |                         |                      |\n         v                         v                      v\n+------------------+     +------------------+     +------------------+\n|   Service Layer  |\u003c---\u003e|   Service Layer  |\u003c---\u003e|   Service Layer  |\n|                  |     |                  |     |                  |\n|  CVEService      |     |  ExploitService  |     |  ReportService   |\n|  BugBountyService|     |  LabService      |     |  + 2 more        |\n+------------------+     +------------------+     +------------------+\n         |                         |                      |\n         v                         v                      v\n+------------------+     +------------------+     +------------------+\n|  Client Layer    |     |  Client Layer    |     |   Models Layer   |\n|                  |     |                  |     |                  |\n|  NVDClient       |     |  GitHubClient    |     |  CVEInfo         |\n|  CPEDictClient   |     |  ExploitClient   |     |  Exploit         |\n|  CVEOrgClient    |     |  + others        |     |  + 11 more       |\n+------------------+     +------------------+     +------------------+\n         |                         |\n         v                         v\n+-------------------------------------------------------------+\n|                    External Data Sources                     |\n|  NVD API  CVE.org  CISA KEV  EPSS  GitHub  ExploitDB  etc.  |\n+-------------------------------------------------------------+\n```\n\n**Layered architecture:**\n1. **Presentation Layer**: CLI (`cli.py`) + MCP Server (`pocmap.mcp_server` / `pocmap-mcp`)\n2. **Service Layer**: Business logic (7 services: CVE, Exploit, Lab, Report, Bug Bounty, Recent, Product Discovery)\n3. **Client Layer**: External API clients (NVD, GitHub, CVE.org, ExploitDB, etc.)\n4. **Model Layer**: 13 Pydantic models with full validation and JSON Schema support (11 exported as standalone JSON schemas)\n5. **Utility Layer**: HTTP client with retries, formatters, validators, config\n6. **Toolkit Layer**: Bug bounty hunter toolkit (checklists, methodology, templates, prioritization, scope, automation)\n\n## Security Features\n\n### SSRF Protection\nAll HTTP requests pass through `is_safe_url()` validation that blocks:\n- Internal hosts: `localhost`, `127.0.0.1`, `0.0.0.0`, `::1` (matched by exact host / dotted-suffix, so public hosts that merely *contain* those strings — e.g. the IPv6 literal `2606:4700:4700::1111` — are not falsely blocked)\n- Cloud metadata endpoints: `169.254.169.254` (AWS), `metadata.google.internal` (GCP), `100.100.100.200` (Alibaba)\n- Private IP ranges, loopback, link-local, and reserved addresses\n- Numeric-encoded IPs (decimal/hex/octal) and IPv4-mapped IPv6 that canonicalize to an internal address\n- Non-HTTP(S) schemes: `file://`, `ftp://`, `gopher://`, `dict://`\n\nRedirects are followed manually and **every hop is re-validated** through the same guard, and credential-bearing headers (`Authorization`, `Cookie`, NVD `apiKey`) are **stripped on a cross-origin redirect** so a token is never replayed to a redirect target.\n\n### CSV Injection Prevention\nCSV export neutralizes spreadsheet **formula injection** (CWE-1236): a string cell\nthat begins with a formula character (`=`, `+`, `-`, `@`, tab, or CR) is prefixed\nwith a single quote so externally-sourced text (CVE descriptions, repo names) cannot\nexecute when the file is opened in Excel / Google Sheets. Genuine numbers are left intact.\n\n### Sandboxed Templates\nJinja2 templates use `SandboxedEnvironment` with `BaseLoader` (no filesystem access) and `select_autoescape` for HTML/XML contexts. Prevents Server-Side Template Injection (SSTI) attacks.\n\n### Path Traversal Protection\nFile operations use `safe_path()` which normalizes paths and validates they stay within the base directory. Raises `ValueError` on traversal attempts.\n\n### Input Validation\n- CVE IDs validated against `^CVE-\\d{4}-\\d+$` regex pattern\n- CPE strings parsed with strict format validation\n- Maximum bulk size limit (100 CVEs) prevents DoS\n- All inputs sanitized before external API calls\n\n### XSS Prevention\n- HTML report generation uses auto-escaped template rendering\n- All user-facing output is properly escaped\n- No inline JavaScript execution in generated HTML reports\n\n## Configuration\n\nConfiguration is loaded from environment variables (prefixed with `POCMAP_`) and optional `.env` file:\n\n```bash\n# Create .env file\ncat \u003e .env \u003c\u003c 'EOF'\nGITHUB_API_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx\nNVD_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\nPOCMAP_HTTP_TIMEOUT=30\nPOCMAP_MAX_RETRIES=3\nPOCMAP_BACKOFF_FACTOR=1.5\nPOCMAP_THREAD_POOL_SIZE=10\nPOCMAP_LOG_LEVEL=INFO\nPOCMAP_CACHE_ENABLED=true\nPOCMAP_CACHE_TTL=3600\nPOCMAP_CACHE_MAX_MB=200\nEOF\n```\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `GITHUB_API_TOKEN` | None | GitHub personal access token for higher rate limits |\n| `NVD_API_KEY` | None | NVD API key for increased rate limits |\n| `POCMAP_HTTP_TIMEOUT` | 30 | HTTP request timeout in seconds |\n| `POCMAP_MAX_RETRIES` | 3 | Maximum retry attempts for failed requests |\n| `POCMAP_BACKOFF_FACTOR` | 1.5 | Exponential backoff multiplier |\n| `POCMAP_THREAD_POOL_SIZE` | 10 | Worker thread count for bulk operations |\n| `POCMAP_LOG_LEVEL` | INFO | Logging verbosity (DEBUG, INFO, WARNING, ERROR) |\n| `POCMAP_CACHE_ENABLED` | true | Enable the persistent HTTP response cache |\n| `POCMAP_CACHE_DIR` | ./.cache | Directory for cached responses |\n| `POCMAP_CACHE_TTL` | 3600 | Seconds a cached entry stays fresh |\n| `POCMAP_CACHE_MAX_MB` | 200 | On-disk cache cap (MB) before LRU eviction |\n| `POCMAP_OFFLINE` | false | Serve HTTP only from cache; a miss errors instead of hitting the network |\n| `POCMAP_ALLOW_FETCH_POC_SOURCE` | false | Opt in to downloading PoC **source code** to disk (see below) |\n| `POCMAP_POC_SOURCE_DIR` | `\u003ccache\u003e/poc-source` | Where fetched PoC source is extracted |\n| `POCMAP_POC_SOURCE_MAX_MB` | 20 | Per-repo cap, applied to download **and** extracted size |\n| `POCMAP_POC_SOURCE_TOTAL_MAX_MB` | 500 | Total on-disk cap for fetched sources |\n\nSee [Caching \u0026 Offline Mode](#caching--offline-mode) and the [exit-code contract](#output-formats--exit-codes)\nfor how these behave at runtime.\n\n## Contributing\n\n### Adding New Exploit Sources\n\nNew exploit sources can be registered via the plugin pattern:\n\n1. Create a new client in `src/pocmap/clients/`:\n```python\n# src/pocmap/clients/my_source_client.py\nfrom pocmap.models import Exploit, ExploitSource\n\nclass MySourceClient:\n    \"\"\"Client for My Exploit Source.\"\"\"\n\n    SOURCE = ExploitSource.OTHER  # or add to enum\n\n    def search(self, cve_id: str) -\u003e list[Exploit]:\n        # Implement search logic\n        return []\n```\n\n2. Integrate into `ExploitService` in `src/pocmap/services/exploit_service.py`:\n```python\nfrom pocmap.clients.my_source_client import MySourceClient\n\nclass ExploitService:\n    def __init__(self):\n        self._my_source = MySourceClient()\n\n    def find_exploits(self, cve_id: str) -\u003e list[Exploit]:\n        exploits = []\n        exploits.extend(self._my_source.search(cve_id))\n        # ... existing sources\n        return exploits\n```\n\n3. Add tests and documentation.\n\n### Third-Party Exploit Sources (plugins — no fork needed)\n\nExternal packages can add exploit sources **without modifying pocmap** by registering an\nentry point in the `pocmap.exploit_sources` group. A source is any object exposing\n`search(cve_id: str) -\u003e list[Exploit]` (the `ExploitSourcePlugin` protocol):\n\n```toml\n# your package's pyproject.toml\n[project.entry-points.\"pocmap.exploit_sources\"]\nmy-source = \"my_pkg.source:MySource\"\n```\n\n```python\n# my_pkg/source.py\nfrom pocmap.models import Exploit, ExploitSource\n\nclass MySource:\n    source = \"my-source\"\n\n    def search(self, cve_id: str) -\u003e list[Exploit]:\n        return [Exploit(source=ExploitSource.OTHER, url=\"https://…\", title=\"…\")]\n```\n\n`pip install` your package and its results automatically appear in `pocmap lookup` and\n`ExploitService.find_exploits`. Plugins are **error-isolated**: a failing plugin degrades\nto a `FetchStatus.ERROR` (visible via `find_exploits_with_status`) without affecting the\nbuilt-in sources. A complete runnable example is in\n[`examples/example-exploit-source/`](examples/example-exploit-source/). Note: entry-point\nplugins execute third-party code you chose to install — pocmap only calls their `search()`\nand aggregates the results with per-source status isolation.\n\n### Development Setup\n\n```bash\ngit clone https://github.com/zebbern/pocmap.git\ncd pocmap\npython -m venv .venv\nsource .venv/bin/activate\npip install -e \".[dev]\"\n\n# Run tests\npytest -v\n\n# Run type checker\nmypy src/pocmap\n\n# Run linter\nruff check src/pocmap\n```\n\n## License\n\nMIT License - See [LICENSE](LICENSE) file for details.\n\n---\n\n*PocMap is not a weapon. It is a research and defensive tool for security professionals and bug bounty hunters. Always operate within the bounds of applicable law and program scope.*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzebbern%2Fpocmap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzebbern%2Fpocmap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzebbern%2Fpocmap/lists"}