{"id":50535479,"url":"https://github.com/arcuru/vuln-scanner","last_synced_at":"2026-06-03T16:01:42.179Z","repository":{"id":359417771,"uuid":"1245971448","full_name":"arcuru/vuln-scanner","owner":"arcuru","description":"LLM-powered vulnerability search","archived":false,"fork":false,"pushed_at":"2026-05-21T23:53:20.000Z","size":153,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T04:25:30.003Z","etag":null,"topics":["llm","vulnerability-scanners"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arcuru.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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":null,"dco":null,"cla":null}},"created_at":"2026-05-21T18:39:28.000Z","updated_at":"2026-05-21T23:53:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/arcuru/vuln-scanner","commit_stats":null,"previous_names":["arcuru/vuln-scanner"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/arcuru/vuln-scanner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arcuru%2Fvuln-scanner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arcuru%2Fvuln-scanner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arcuru%2Fvuln-scanner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arcuru%2Fvuln-scanner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arcuru","download_url":"https://codeload.github.com/arcuru/vuln-scanner/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arcuru%2Fvuln-scanner/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33872298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"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":["llm","vulnerability-scanners"],"created_at":"2026-06-03T16:01:38.632Z","updated_at":"2026-06-03T16:01:42.169Z","avatar_url":"https://github.com/arcuru.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vuln-scanner\n\nLLM-driven vulnerability scanner that builds up an **investigation directory**\nper scan target, accumulating history across runs as the target evolves and\nmodels improve.\n\n```\nrecon → hunt → validate → dedupe → consolidate     (per run)\n```\n\n\u003e **Status:** experimental personal project. Expect false positives and\n\u003e meaningful token spend — every hunt task is an independent agent run. Treat\n\u003e findings as leads to validate manually, not as audit output.\n\n## Prerequisites\n\n- Python 3.12+\n- [`uv`](https://docs.astral.sh/uv/) for dependency and environment management\n- `git` on `$PATH`\n- An agent CLI for the chosen backend, authenticated:\n  - [`claude`](https://github.com/anthropics/claude-code) — default backend\n  - [`pi`](https://github.com/anthropics/oh-my-pi) — alternative backend\n  - Anything else you wire up via a custom `[agent.backends.*]` entry\n\n## Install\n\n```bash\n# Run from a checkout without installing globally\nuv run vuln-scanner --help\n\n# Or install as a uv tool (puts `vuln-scanner` on $PATH)\nuv tool install .\n```\n\n## Quick start\n\nCreate a folder for the investigation, scaffold it against a target, run a\nscan, check status.\n\n```bash\nmkdir cool-project-scan \u0026\u0026 cd cool-project-scan\n\n# Clone the target into ./target/, write vuln-scanner.toml + MANIFEST.toml\nuv run vuln-scanner init https://github.com/user/cool-project\n\n# Run a scan (uses target's current HEAD; pass --sha to pin a commit)\nuv run vuln-scanner run -j 8\n\n# Re-run later (target may have new commits, or a newer model is available);\n# the next recon reads prior runs and proposes net-new investigations\nuv run vuln-scanner run --sha \u003cnewer-sha\u003e\n\n# See the run history\nuv run vuln-scanner status\n```\n\nThe investigation folder is self-contained. Move it, archive it, commit it to\nits own git repo — it stays consistent.\n\n## Scan archive\n\nThis repo also archives scans via a git submodule at [`scans/`](scans/)\n([`arcuru/vuln-scans`](https://github.com/arcuru/vuln-scans)). Each subfolder is one\ninvestigation directory: config + manifest + per-run output. The cloned target\nand ephemeral worktrees are gitignored, so what's committed is the audit trail.\n\nClone with `--recurse-submodules` to pull the scan archive alongside the tool.\n\n| Target | Source |\n|---|---|\n| [`cmprss`](scans/cmprss/) | \u003chttps://github.com/arcuru/cmprss\u003e |\n\nFrom the repo root, scan an existing target or add a new one:\n\n```bash\n# Run a scan against a target (anywhere in the tree)\nvuln-scanner run -C scans/cmprss -j 8\n\n# Add a new target\nmkdir scans/\u003cname\u003e \u0026\u0026 cd scans/\u003cname\u003e\nvuln-scanner init https://github.com/owner/repo\nvuln-scanner run -j 8\n```\n\n## CLI reference\n\n```text\n$ vuln-scanner --help\nusage: vuln-scanner [-h] {init,run,status} ...\n\nMulti-phase LLM vulnerability scanner over a single target investigation.\n\npositional arguments:\n  {init,run,status}\n    init             Initialize an investigation directory in cwd (clones\n                     target, writes config).\n    run              Execute one scan run against target/ in cwd.\n    status           List runs in this investigation.\n\n$ vuln-scanner init --help\nusage: vuln-scanner init [-h] [-c CONFIG] target_url\n\npositional arguments:\n  target_url           Git URL of the target repo to clone\n\noptions:\n  -c, --config CONFIG  Path to a vuln-scanner.toml to copy in (default:\n                       minimal built-in)\n\n$ vuln-scanner run --help\nusage: vuln-scanner run [-h] [-C DIR] [--sha SHA] [-j JOBS] [-v]\n\noptions:\n  -C, --dir DIR    Investigation directory to operate on (default: cwd)\n  --sha SHA        Target commit SHA to pin (default: keep current target\n                   HEAD)\n  -j, --jobs JOBS  Parallel workers (default: 4)\n  -v, --verbose\n\n$ vuln-scanner status --help\nusage: vuln-scanner status [-h] [-C DIR]\n\noptions:\n  -C, --dir DIR  Investigation directory to operate on (default: cwd)\n```\n\n## Investigation directory\n\n`init` scaffolds, `run` produces an immutable per-run directory, `SUMMARY.md`\nat the top always points at the latest run:\n\n```\nmy-investigation/\n  vuln-scanner.toml            # config (committed)\n  MANIFEST.toml                # target URL, latest-run pointer\n  target/                      # cloned scan target (gitignored)\n  worktrees/                   # ephemeral worktrees (gitignored)\n  .vuln-scanner.lock           # concurrency guard\n  runs/\n    2026-05-20T14-30-abc1234/  # ISO timestamp + short target SHA\n      manifest.toml            # tool version, target SHA, status, summary\n      config.toml              # effective config snapshot for this run\n      logs/\u003ctask-id\u003e.log       # agent stdout (or SDK event stream)\n      transcripts/\u003ctask-id\u003e.jsonl  # full Claude transcript per task\n      recon/\n        HUNT_QUEUE.json\n        task.toml              # backend, model, session_id, timings\n      hunt/\u003ctask-id\u003e/\n        FINDING.md\n        task.toml\n      validate/\u003ctask-id\u003e/\n        VERIFICATION.md\n        task.toml\n      dedupe/FINDINGS.md\n      consolidate/\n        SUMMARY.md             # cumulative across all runs\n        task.toml\n  SUMMARY.md  →  runs/\u003clatest\u003e/consolidate/SUMMARY.md\n```\n\nEach run directory is self-describing: `config.toml` is the resolved view of\n`vuln-scanner.toml` at run time, and per-task `task.toml` records the exact\nbackend invocation (argv or SDK options), session UUID, model used, duration,\nand cost. The matching `transcripts/\u003ctask-id\u003e.jsonl` is the full Claude\nsession log copied out of `~/.claude/projects/`.\n\nThe `.gitignore` written by `init` covers `target/`, `worktrees/`, and the\nlockfile — so you can run `git init` in the investigation folder and track\nthe config + runs without dragging the target's full history with you.\n\n## How it works\n\nEach phase runs in its own git worktree off `target/`, isolating agent\nartifacts per task:\n\n1. **recon** — one task. Maps architecture and produces `HUNT_QUEUE.json`. On\n   continuation runs, also reads prior runs' `SUMMARY.md` and the git diff\n   since the prior target SHA, then produces a queue of net-new\n   investigations and worthwhile revisits.\n2. **hunt** — fan-out from the queue. Each entry is one attack class in one\n   scope. Produces `FINDING.md` per task.\n3. **validate** — fan-out from hunt tasks. Adversarial review of each finding.\n   Produces `VERIFICATION.md` per task.\n4. **dedupe** — one task. Groups confirmed findings by root cause, records\n   rejected investigations so future recon can skip them, and records *failed*\n   investigations (tasks where the agent crashed or timed out before\n   producing output — outcome unknown, codepath NOT cleared) so future runs\n   re-attempt them. Produces `FINDINGS.md`.\n5. **consolidate** — one task. Produces the cumulative `SUMMARY.md` with each\n   finding tagged **NEW** / **PERSISTS** / **FIXED** / **REGRESSED** relative\n   to prior runs, and a \"Failed Investigations\" section listing this run's\n   unknown-outcome tasks.\n\nEach run resumes from `.done` sentinels — if interrupted, re-running `run`\n(without `--sha`) picks up where it left off in the same run directory.\nConcurrent `run` invocations in the same investigation folder are refused via\nthe lockfile.\n\nIf recon decides there's nothing new to investigate (continuation run on an\nunchanged target), it writes an empty queue and the pipeline bails early.\n\n## Configuration\n\nTwo layers: a **prompt profile** (Python module with prompt functions, plus\nmarkdown bodies) and a **TOML config** (settings overlay).\n\n### Prompt profile (Python + markdown)\n\nThe built-in profile is `vuln-scan` (in `src/vuln_scanner/configs/vuln_scan.py`).\nThe prompt bodies live alongside it in `src/vuln_scanner/configs/prompts/` —\none `.md` per phase, with `$variable` placeholders substituted at render time:\n\n```\nconfigs/\n  vuln_scan.py          # settings + glue (loads + renders the .md files)\n  prompts/\n    _environment.md     # shared snippet injected into every prompt\n    recon.md            # uses $prior_runs_path for continuation runs\n    hunt.md             # uses $attack_class, $scope, $entry_point, …\n    validate.md\n    dedupe.md\n    consolidate.md      # uses $prior_runs_path\n```\n\nTo tweak what the agents are told, edit the markdown — no Python changes\nneeded. Required prompt functions on the profile module:\n\n- `recon_prompt(*, prior_runs_path: str = \"\") -\u003e str`\n- `hunt_prompt(*, attack_class, scope, function, entry_point, rationale, arch_summary) -\u003e str`\n- `validate_prompt() -\u003e str`\n\nOptional: `dedupe_prompt()`, `consolidate_prompt(output_dir, *, prior_runs_path=\"\")`.\n\nWrite your own profile by copying `vuln_scan.py` (and the `prompts/` directory)\nand pointing `vuln-scanner.toml` at it via `[scan] prompt_profile = \"...\"`.\n\n### Settings (TOML)\n\n`init` writes a minimal `vuln-scanner.toml` into the investigation folder\n(unless you pass `-c \u003cpath\u003e` to copy in your own):\n\n```toml\n[scan]\nprompt_profile = \"vuln-scan\"\n\n[agent]\nbackend = \"claude\"\n# [agent.models]\n# recon    = \"claude-sonnet-4-6\"\n# hunt     = \"claude-sonnet-4-6\"\n# validate = \"claude-opus-4-7\"\n```\n\nSee [`vuln-scanner.example.toml`](vuln-scanner.example.toml) for the full set of options\nwith comments. Key sections:\n\n| Section | Purpose |\n|---|---|\n| `attack_classes` (top-level) | Vulnerability categories to scan for |\n| `[scan]` | Profile, branch prefix, parallelism, timeouts |\n| `[scan.task_timeouts]` | Per-phase timeout overrides (seconds) |\n| `[agent]` | Backend name and flags |\n| `[agent.models]` | Per-phase model names |\n| `[agent.backends.\u003cname\u003e]` | Define custom backends in config |\n| `[output]` | Per-phase output filenames |\n| `[files]` | File extensions and exclude directories |\n\n### Backends\n\nBuilt-in backends:\n\n- `claude` — subprocesses the Claude Code CLI (`claude -p`); default.\n- `claude-sdk` — uses the in-process [`claude-agent-sdk`](https://pypi.org/project/claude-agent-sdk/)\n  Python library. Same model and tools as `claude`, but streams structured\n  events (assistant turns, tool calls, the final `ResultMessage` with token\n  and cost info) into the per-task log file.\n- `pi` — Oh My Pi agent CLI.\n\nSet via `[agent] backend = \"...\"`.\n\nCustom backends can be defined directly in TOML — no Python code needed:\n\n```toml\n[agent]\nbackend = \"gemini\"\n\n[agent.backends.gemini]\nexecutable = \"gemini-cli\"\nmodel_flag = \"--model\"\nprompt_flag = \"--prompt\"\nextra_args = [\"--yes\"]\n```\n\nFields: `executable` (required), `prompt_flag` (required), `model_flag`\n(optional), `extra_args` (optional). The resulting command is:\n\n```\ngemini-cli --yes --model \u003cname\u003e --prompt \u003ctext\u003e\n```\n\nFor backends needing custom logic beyond flags, implement the `Backend`\nprotocol in `src/vuln_scanner/claude.py` and add to the `BACKENDS` registry.\n\n### Timeouts\n\n`task_timeout` sets a global default (0 = no timeout). `task_timeouts`\noverrides per phase:\n\n```toml\n[scan]\ntask_timeout = 0          # global default: no timeout\n\n[scan.task_timeouts]\nhunt = 900                # 15 minutes per hunt task\nvalidate = 600\n```\n\nWhen a timeout is hit, the agent subprocess receives SIGTERM, then SIGKILL\nafter 5 seconds.\n\n## Development\n\n```bash\n# Install dev dependencies\nuv sync --extra dev\n\n# Run tests\nuv run pytest tests/ -v\n\n# Type check\nuv run pyright src/\n```\n\n## Credits\n\nThe multi-phase recon → hunt → validate → dedupe → consolidate architecture\nis adapted from the design Cloudflare describes in\n[\"Cyber frontier models: Claude's strengths in software security\"](https://blog.cloudflare.com/cyber-frontier-models/),\nwhich lays out the agentic vulnerability-research pipeline this project\nre-implements on top of Claude Code (or any other agent CLI). Cloudflare's\nin-process gapfill / hunt2 / validate2 second pass is here realized as\n*running the tool again* — the next run's recon reads prior runs' findings\nand produces a queue informed by them.\n\n## License\n\nAGPL-3.0-or-later. See [`LICENSE.txt`](LICENSE.txt).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farcuru%2Fvuln-scanner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farcuru%2Fvuln-scanner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farcuru%2Fvuln-scanner/lists"}