Raw capture β raw/vulnhunter-repo.md
url: https://github.com/capitalone/vulnhunter title: VulnHunter author: Capital One date_fetched: 2026-07-18 date_published: 2025
Capital One's open-source agentic AI security tool for source code vulnerability hunting. Built as a suite of Claude Code skills (pure prompt engineering + Python helper packages) that form a closed-loop Hunt β Fix β Verify pipeline for security vulnerabilities.
Five self-contained subdirectories:
| Path | Lines | Language | Purpose |
|---|---|---|---|
vulnhunt/ |
~2,500 (markdown prompts) | Prompt-only | Core scanner skill: SKILL.md + 11 phase files orchestrating a multi-agent hunt |
vulnhunter-fix/ |
~15,000+ (Python + prompts) | Python + Markdown | Fix skill: TDD-driven remediation, git worktree orchestration, PR delivery |
vulnhunt-fix-verify/ |
~1,500 (markdown) | Prompt-only | Read-only verification skill for fix correctness |
vulnhunter-agent/ |
~18,000 (Python, inc. tests) | Python | Headless runtime: Claude Agent SDK integration, GitHub automation, audit stream |
harness/ |
~3,000 (Python) | Python | Developer tooling: batch scanning + benchmarking against ground truth |
The three Claude Code skills are prompt-only with no runtime code β pure instruction design:
/vulnhunt (vulnhunt/SKILL.md + phases/) β The orchestrator skill, 353 lines of orchestration logic + 10 phase files. Uses a strict orchestrator-worker pattern:
- Phase 1 (Recon): one subagent builds input inventory, subgraph partitions, and threat model
- Phase 2 (Hunt): parallel trace agents β 3 per partition (INJ/NAV/LOG class groups) + 1 sink-driven auditor. Minimum agent count = (3 Γ partition_count) + 1. Dispatched in waves of max 6 to prevent 429 API failures
- Phase 2b (Verify): adversarial falsification β a single subagent tries to disprove every candidate
- Phase 3a-c (Reproduce/Test/Fix): exploit PoCs + exploit tests built per confirmed finding
- Phase 3d (Sweep): variant discovery using "sweep patterns" to find sibling instances of confirmed vulns
- Phase 4 (Report): compiled from output files
Key design patterns: - Forward-trace paradigm: Starts at attacker entry points and traces data forward to sinks, unlike traditional SAST which searches backward from sinks - Falsification engine (Phase 2b): "Historically, ~50% of candidate findings are false positives" β the verification phase is mandatory, adversarial, and explicitly tries to disprove each candidate - Subgraph partitioning: Uses union-find on a two-level application call graph to partition code into non-overlapping trace scopes, preventing agent context contamination - Class-group specialization: Three vulnerability class groups (Injection/INJ, Navigation-Auth/NAV, Logic-Crypto/LOG) with separate class-specific instruction files for each trace agent - HARD GATES: Every candidate must pass 5 gates in order before being written up: Gate 0 (designed behavior?), Gate 1 (reachability), Gate 2a (attacker-controlled?), Gate 2b (effective sanitization?), Gate 3 (new capability gained?). Gate evaluation requires empirical source-code verification, not training-knowledge assumptions - The orchestrator stays lean: Explicit instruction to not read result files into context β only verify file existence via Glob. Subagent return messages capped at β€20 words
/vulnhunter-fix (vulnhunter-fix/SKILL.md + references/) β TDD-driven remediation with two modes (in-place and fork):
- Phase-boundary checkpoints: operator-gated approval between phases
- Git worktree isolation per finding cluster
- REDβGREEN evidence pipeline: exploit demo β failing test (with evidence persisted to disk as JSON) β fix β regression check
- Seven mechanical delivery gates before gh pr create: severity mask, body completeness, scope, idempotency, anti-merge math (group_cost β€ 0.6 Γ split_cost), verification table, committed test naming
- Sweep algorithm: two-pass sibling defect detection (graph-anchored symbol pass + regex pattern pass)
- Collaboration loop for interactive in-place mode: pauses for developer input on blocking decisions
- 13 helper scripts (preflight, mode detection, worktree setup, parse, cluster scoring, PR body validation, test naming check, etc.)
- 12 reference files encoding patterns: CWE fix patterns, sweep patterns, repo-type adapters, approved crypto algorithms, remediation rigor rules, etc.
/vulnhunt-fix-verify (vulnhunt-fix-verify/SKILL.md + phases/) β Read-only independent verification:
- Strict tool envelope: Read/Write/Edit/Glob/Grep/Agent only β no Bash, no network
- Four-phase workflow: preflight β extract findings β per-VULN verification with 4 gates β emit verified JSON
- Schema-validated output against verify_disposition.schema.json
- "Fail-closed on schema drift" β code is the source of truth, not developer comments
vulnhunter-agent/agent/ wraps the skills in a headless Python runtime (~3,400 lines of production code):
__main__.py (1,320 lines): CLI with scan/verify modes, exhaustive flag validation, audit stream wiringrunner.py (1,275 lines): Claude Agent SDK integration β builds prompt with pre-resolved metadata, manages SDK sessions with retry/continuation logic, handles cold-start 429 backoff (60s β 120s β 300s), detects stalled subagents (60 consecutive zero-progress continuations = abort)config.py (873 lines): TOML + env-var configuration with 14 frozen dataclass config sections (Anthropic, OAuth, TLS, Sandbox, Telemetry, Scan, GitHub, Publish, Issues, Verify, Logging, Audit, RepoProperties)ApiKeyTokenManager and OAuthTokenManager share a common get_valid_token() interface. Bedrock OAuth mode for enterprise proxy environmentsscan_manifest.json validated against JSON schema, serves as stable integration contract for downstream automationThe hunt skill treats prompts as executable code with precise dispatch rules. The orchestrator SKILL.md specifies exact agent counts (3 per partition + 1 sink-driven), wave dispatch of max 6 agents, and hard synchronisation barriers. This is not LLM autonomy β it's deterministic workflow expressed in natural language, closer to an Airflow DAG than a chatbot.
The verification phase (phase2b) is structurally adversarial: it tries to DISPROVE every candidate. This inverts the typical security scanner approach. The explicit claim that ~50% of candidates are false positives sets a realistic baseline. The consensus-skip shortcut (if all agents for a partition cite the same elimination evidence at the same file:line, skip re-verification) is a pragmatic efficiency hack.
Phase 1 builds a two-level call graph per entry point, catalogs shared infrastructure (modules imported by >50% of entry points), then uses union-find to compute subgraph partitions. Two entry points are in the same partition if they share any non-infrastructure function. This is a genuinely novel approach to context scoping for parallel LLM agents β each trace agent gets a self-contained slice with no cross-contamination.
The runner distinguishes between cold-start and mid-stream transient errors. Consecutive 429/5xx SystemMessages BEFORE the first AssistantMessage trigger session restart with backoff. After the first AssistantMessage arrives, transients are logged but don't restart (the harness's subprocess-level retry handles those). The stall detector (60 consecutive continuations with zero task lifecycle events) is an abort mechanism, not a retry β it catches genuinely hung agents.
When grouping multiple vulnerability fixes into one PR, the fixer applies a mechanical check: group_cost β€ 0.6 Γ split_cost (measured in distinct files touched). This prevents over-aggressive grouping that would make PRs unreviewable. The either/or on source vs test files handles the common "tests mirror source tree" convention cleanly.
The agent uses two distinct GitHub tokens (scan_token for clone+issues, reports_token for publish) rather than one. This is deliberate defense-in-depth: a compromised scan token (which touches arbitrary target repos) can't also push to the results repository. The broker mode allows an external parent process to rotate tokens onto disk, keeping the agent itself stateless.
claude for fine-grained observability.Unlike traditional SAST tools (Semgrep, CodeQL) which pattern-match on syntax, VulnHunter reasons about data flow provenance β whether attacker-controlled data actually reaches a sink. Unlike LLM-based code reviewers ([[OpenCodeReview]], [[Metis β ARM AI Security Code Review]]) which review surface-level changes, VulnHunter performs deep forward-trace analysis from every entry point.
The multi-agent orchestration is philosophically similar to Cloudflare's code review system ([[Orchestrating AI Code Review at Scale]]) but the coordination pattern is inverted: Cloudflare runs specialized agents in parallel then merges via a judge; VulnHunter partitions the codebase first, then dispatches each partition to parallel class-group agents, with a separate adversarial verification pass.
The TDD-driven fix pipeline (exploit β failing test β fix β verify) shares DNA with [[no-mistakes]]'s validation pipeline but is security-specific rather than general-purpose.
The fix verifier's read-only "code is the source of truth" discipline parallels [[Interdict]]'s approach of validating agent actions against real state, though applied to vulnerability fixes rather than database queries.