allsmog/vuln-scan-cannon-harness

GitHub: allsmog/vuln-scan-cannon-harness

cannon 是一个基于 Rust 的 AI 安全扫描编排框架,通过排列组合扫描齐射和对抗性验证机制对代码库进行深度漏洞发现与误报筛选。

Stars: 0 | Forks: 0

# 🜂 cannon — 具备*推理*能力的 AI 安全测试框架 [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/bc241cf167091845.svg)](https://github.com/allsmog/vuln-scan-cannon-harness/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Release](https://img.shields.io/github/v/release/allsmog/vuln-scan-cannon-harness)](https://github.com/allsmog/vuln-scan-cannon-harness/releases) ![Built with Rust](https://img.shields.io/badge/built%20with-Rust-orange) 向单个目标发起**排列组合后的“防御性代码”扫描齐射**,然后 **积累 → 分流 → 攻击链 → 可视化** —— 通过持久化的 漏洞发现账本以及能够在你的终端中原生绘制 威胁模型和攻击链的 **Ratatui 控制台** 进行管理。 一个独立的 **Rust binary** (`cannon`)。实际的安全推理 委托给你本地已经过身份验证的 `claude` CLI —— cannon 负责编排(异步), 积累并去重,运行对抗性验证器,并管理 漏洞发现的生命周期。 在*形态上*分叉自 Anthropic 的 [defending-code-reference-harness](https://github.com/anthropics/defending-code-reference-harness): 模块化阶段,JSON checkpoints + `--resume`,以及承载核心逻辑的对抗性验证器。 刻意**不使用 broker、数据库或 LLM prompt-compiler** —— prompt 是普通的 可编辑文件;排列矩阵即是“变异”面;账本和 磁盘上的 JSON/Markdown 构成了全部状态。 ``` feed context → threat-model → CANNON (salvo of permuted scans) → accumulate → triage → LEDGER → attack-chain → cockpit (code + (THREAT_MODEL.md (focus × variant × model × runs) (union+dedup) (adversarial (VULN_ (over triaged (Ratatui: design docs) + graph + focus) verify+rank) FINDINGS.md) findings) native graphs) ``` **文档:** [排列规划器](PERMUTATION.md) · [对比 Semgrep 基准测试](BENCHMARK.md) · [工作原理(时序图)](docs/how-it-works.html) ## 安装 ``` cargo build --release # → target/release/cannon (one binary, no runtime deps) # 使用你已通过身份验证的本地 `claude` CLI — 无需 API key。 ``` (仅在你想要原始版本时,才在 `reference-python/` 下构建 Python 原型;Rust binary 才是正式产物。) ## 工作流 ``` # 1. Fire: 优先进行 threat-model(播种 focus + graph),salvo,triage,chain,合并至 ledger。 cannon fire canary --threat-model --chain # 2. 管理发现(三种可互换方式,均写入同一个 ledger): cannon tui canary # the cockpit — browse + triage + graphs cannon findings set canary F-006 --status false_positive --note "behind gateway" $EDITOR targets/canary/VULN_FINDINGS.md && cannon findings sync canary # 3. Chain 任何你已 triaged 为真实的内容: cannon chain canary # default scope = confirmed cannon chain canary --scope triaged # widen ``` ## 控制台 (`cannon tui `) 基于同一进程内账本的三个面板 —— 使用 Ratatui 的 `Canvas`/braille **原生**绘制图形(无需浏览器;在没有内联图像 协议的 Warp 中也能工作): ``` ┌ findings · canary (10) ───────────────┬ threat model (Tab→chains) ───────────┐ │▶ F-001 CRITICAL confirmed SQLi … │ (trust-tier columns: untrusted → │ │ F-006 HIGH false_pos binding … │ trusted-core → datastore, drawn │ │ … │ with braille edges) │ ├ detail ────────────────────────────────┼─────────────────────────────────────────┤ │ F-001 CRITICAL confirmed (auto) │ attack chains (Tab) = braille lanes: │ │ app.py:30 · CWE-89 · verifier REAL 1.0 │ start ▸ step ▸ step ▸ 💥 impact │ └────────────────────────────────────────┴─────────────────────────────────────────┘ ↑↓ move · c/f/a/x triage · e note · Tab graph · r regen chains · g open HTML · q quit ``` 按键:**c**onfirm / **f**alse-positive / **a**ccept / fi**x**ed 实时设置状态; **e** 编辑笔记;**Tab** 切换 威胁模型 ↔ 链条;**r** 基于已确认集合重组链条;**g** 打开渲染后的 Mermaid HTML(用于庞大图形的兜底方案);**q** 退出。 ## 账本 (`VULN_FINDINGS.md` + `.cannon/ledger.json`) 持久化、按目标划分、跨运行保留。`ledger.json` 是权威来源;`VULN_FINDINGS.md` 是面向人类的渲染视图,其**状态标识支持双向同步**: ``` ### F-002 · CRITICAL · /ping 中的 OS command injection ← edit by hand → `cannon findings sync` - file: `app.py:50` · cwe: CWE-78 · ×2 rounds · verifier: REAL (0.99) · triaged_by: auto - note: ``` - **状态词汇表:** `new | confirmed | false_positive | accepted | fixed | duplicate`。“Triaged” = 任何非 `new` 的状态。 - **稳定 ID**(`F-001`…),分配一次后永不重新编号。 - **保留人类决策:** 重新运行 `fire`/`ingest` 会刷新证据和佐证,但**绝不覆盖**你手动设置的状态(`triaged_by: human`)。新发现由验证器的判定结果播种(`triaged_by: auto`),因此 `fire --chain` 开箱即用;你拥有最终决定权。 ## 命令 CLI *即*是流程:**aim → fire → triage → manage → prove → chain → fix → measure**。 ``` # 目标 cannon aim # threat model + focus areas (+ Mermaid graph) cannon map # repo trust-graph → reachability oracle for the verifier # 计划 (signal-driven、human-gated、cost-estimated permutation — 参见 PERMUTATION.md) cannon permute [--budget $] [--sources commits,threat-model,threat-intel,evolution] [--research] [--yes] [--plan-only] cannon queue list|run|budget|clear # the proposal queue # FIRE cannon fire [--threat-model] [--repo-map] [--chain] [--dedup] [--diff ] [--detector static_review|secrets|dynamic] [--votes N] [--runs N] [--focus "a;b"] [--variants …] [--models …] [--resume DIR] # TRIAGE cannon triage [--all] [--votes N] # (re)verify the ledger's findings # 管理 cannon manage # the Ratatui cockpit (/ filter, source-snippet pane) cannon findings list|set|sync # CLI ledger ops (set F-NNN --status confirmed …) cannon seed [--format auto|sarif|semgrep|json|csv] [--verify] cannon ingest # merge an existing run's triage.json # 证明 cannon prove # dynamic detector — reproduce by execution (CANNON_ALLOW_EXEC=1) cannon metamorphic [--scope review] [--apply] # perturb the code to prove safe-vs-vulnerable # CHAIN cannon chain [--scope confirmed|accepted|triaged] cannon fleet # cross-service attack chains # 修复 cannon fix [--scope confirmed] [--top N] # draft patches + independent review # 度量 cannon measure [--verify] [--against tool.sarif] [--gate] [--write-baseline] cannon tune --variants a,b,c [--holdout 0.5] # optimize prompts on a train/test split # 输出 cannon report # re-render REPORT.md (+ report.sarif) ``` ### 基于上下文的推理 —— cannon 的优势(详见 [`BENCHMARK.md`](BENCHMARK.md)) 基准测试显示其原始检测能力与 Semgrep 基本持平;cannon 的优势在于*对正确上下文进行语义推理*。四种机制强化了这一点: - **Agentic 跨过程污点解析**(find 阶段):发现器必须**在每个文件中追踪候选项** —— 跟踪一个值进入另一个文件中的辅助函数并读取其返回结果 —— 然后才能进行报告。它会记录解析出的路径(`source → … → sink`)和结果。如果它自身的追踪落在了常量 / 净化器 / 死胡同上,该发现会被**直接丢弃,不予报告**(OWASP `getTheValue()` 返回 `"bar"` 的陷阱就在这里被消灭)。保证召回率:仅丢弃自证伪的发现。 - **仓库级信任图谱预言机**(`cannon map`, `fire --repo-map`):构建每个仓库一次的调用/路由/信任图谱(`repo_map.json`),随后验证器会确定性地向其提问 —— *“这个 sink 是否可从不受信任的入口点触达?”* “无路径”的回答是一个强烈的误报信号,验证器会对其进行权衡(但仍会结合代码进行确认)。威胁模型变成了机器预言机,而不仅仅是一张图片。 - **蜕变验证**(`cannon metamorphic`):通过**扰动**证明安全与漏洞 —— 合成能使代码变得可利用的最小变异;如果代码是安全的但变异后触发了报错,说明安全性是核心支撑 → FALSE_POSITIVE;如果按原样编写就触发报错 → REAL。当设置 `CANNON_ALLOW_EXEC=1` 时,它会实例化变异体并*运行*原始版本与变异体,以获得测量后的判定。`--apply` 会将判定写回(仅限非人类发现的条目)。 - **多投票、视角多样化的验证器**(`--votes`,默认值为 3):每个发现都由独立的质疑者使用*可达性*、*可利用性*和*缓解措施*视角进行评判;少数服从多数。现在它还会将发现器的污点路径和图谱预言机作为输入。它会报告访问级别 + 前置条件,cannon 据此**重新推导严重性**,而不是盲目相信发现器的声明。 ### 其他验证机制 - **语义去重**(`--dedup`):LLM judge 会折叠那些在特征匹配阶段被遗漏的跨位置重复项。 - **SARIF 输出**:每次运行都会写入 `report.sarif`;账本会写入 `findings.sarif` —— 可上传至 GitHub code scanning。 - **Diff 模式**(`--diff `):将扫描齐射范围限定为自某个 git ref 之后更改的文件(用于 PR 审查)。 - **成本明细**:每次运行都会打印 `$cost (in/out tokens)`。 - **Git 历史上下文**:最近的提交会被作为证据输入给 agent。 ## 填充现有的漏洞积压 已经有来自其他扫描器(CodeQL、Semgrep、SARIF 导出文件或 漏洞赏金电子表格)的发现结果?将它们倾倒入账本, 然后让 cannon 的对抗性验证器尽可能将其证伪 —— 这正是参考测试框架中“先输入你的积压”的举措: ``` cannon seed canary scan.sarif results.json findings.csv # auto-detects format cannon verify canary # triage the imported (unverified) findings ``` 填充的发现结果以 `new` / `triaged_by: imported` 状态进入系统,并通过签名与 cannon 已经发现的内容进行去重(匹配成功只会添加来源标签,例如 `sources: [cannon, sarif:CodeQL]`)。随后 `cannon verify` 会对它们运行同样的“无罪推定”(即除非被证明有罪,否则视为误报)验证器,并将每一条结果翻转为 `confirmed` 或 `false_positive`。支持格式:**SARIF** (CodeQL/Semgrep/大多数工具),**Semgrep JSON**, **通用发现数组**,以及 **CSV**。 ## 检测器 检测器将目标转化为发现结果;注册表(`src/detector.rs`)基于 `config.yaml` 的 `detector:` 进行分发: - **`static_review`**(默认) —— LLM agent 读取源代码并报告发现。 - **`secrets`** —— 确定性,**不使用 LLM**:模式规则(AWS/Stripe/GitHub/Slack/Google 密钥,私钥块,凭证分配)+ Shannon 熵启发式算法。免费且快速。 - **`dynamic`** —— **携带证明(proof-carrying)**:agent 构造输入,cannon *执行*目标,只有在可执行的 **witness 复现成功率达到 2-of-3 时**才保留该发现。配置:`run_command`(带有 `{input}`/`{src}`),`witness`(`crash` | `exit_nonzero` | `exit_code:N` | `output_contains:PAT`),可选的 `build_command`。由于它会执行目标代码,因此受到 **`CANNON_ALLOW_EXEC=1`** 的门控 —— 请在 sandbox/VM 中运行。示例:`targets/crasher/`。 ## 基准测试(精确率 / 召回率)—— `cannon measure` 基于带标签的语料库对 cannon 进行评分 —— 每个目标都附带一个包含真值 `{file,line,cwe}` 的 `labels.json`: ``` cannon measure bench # detector-only (free on the secrets corpus) cannon measure bench --verify # full pipeline (find → verify → confirmed) cannon measure bench-owasp --verify # an OWASP-BenchmarkJava slice (real labels + FP-traps) cannon measure bench-owasp --against semgrep.sarif # score ANY tool's SARIF, same scorer cannon measure bench --write-baseline # pin the current F1 as the regression baseline cannon measure bench --gate # CI gate: exit 2 if F1 dropped below the baseline cannon tune bench --variants default,aggressive # optimize prompts on a train/test split ``` **自我调优 + 回归门控**(`cannon tune`, `measure --gate`):该框架的前提是对 prompt 进行排列/变异 —— 现在已被量化。`tune` 评估每个 prompt 变体在**训练集**上的 F1 值,选出最佳方案,并报告其在**留出测试集**上的 F1 值(这样所谓的“胜出”就不仅仅是过拟合)。当 prompt 的编辑导致 F1 值低于锁定基线时,`measure --gate` 会使 CI 失败(exit 2)。“变异”面不再是凭感觉行事。 打印按目标和总体的 **TP/FP/FN、精确率、召回率、F1**,并写入 `bench.json`。由于 `--against` 使用 cannon *完全相同的*评分器对外部工具的 SARIF 进行评分,因此你可以在完全相同且带标签的代码上,获得与 Semgrep / CodeQL **公平的对决** —— 那些误报陷阱案例(真实存在但不存在漏洞的相似代码)正是语义验证应当胜过模式匹配的地方。 **具体的数字详见 [`BENCHMARK.md`](BENCHMARK.md)**(OWASP BenchmarkJava,正面硬刚 Semgrep)。真实结论:在单文件情况下,cannon ≈ Semgrep(精确率 P 0.632 vs 0.600,召回率均为 1.0);但在一项受控实验中,*在给定那个能让陷阱变安全的辅助函数的情况下*,即使文件存在,cannon 的推理也能拒绝 Semgrep 误报的漏洞(2 个误报 → 0)。文档还记录了天真的上下文堆砌在何处**损害了**精确率 —— 上下文范围界定至关重要。 ## 集群与跨服务链 ``` cannon fleet fleet.example.yaml ``` 扫描每个目标,合并它们的 **confirmed** 发现(按服务标记),并组合成**跨服务**的攻击链 —— 比如服务 A 泄露的机密恰好解锁了服务 B 中的注入漏洞。 ## 从你的分流操作中学习 你记录的每一个 `false_positive`(通过 CLI / md 编辑 / TUI)都会作为此仓库的**已知误报模式**输入到验证器的 prompt 中,因此随着时间的推移,它会对你*团队*拒绝的噪声变得更加警惕 —— 同时又不会自动压制真实的 bug。 ## 目标 ``` targets// config.yaml # detector, language, description, focus_areas, engagement_context src/ # the code to scan (or set source_root) context/ # design docs — auto-fed into prompts as evidence (not instructions) prompt_overrides/ # optional *.md shadowing ../../prompts for THIS target VULN_FINDINGS.md # ← the managed ledger (generated; you edit status tokens) THREAT_MODEL.md # ← generated narrative + graph .cannon/ledger.json # ← canonical ledger state ``` ## 排列与 prompt 变异 扫描齐射是 `focus × variant × model × runs` 的交叉组合 —— 同一个目标受到来自多个角度的攻击,结果取并集(即参考框架中的“预期存在差异;跨运行合并”,在此被武器化)。每个阶段的 prompt 按照从高到低的优先级进行解析: `targets//prompt_overrides/.md` → `prompts/variants//.md` → `prompts/.md`。编辑其中任何一个,下一次运行时即刻生效;每一轮都会 记录 prompt 的 `sha256` 以供溯源。 ### 排列规划器 —— `cannon permute`(完整文档:[`PERMUTATION.md`](PERMUTATION.md)) 盲目触发整个交叉组合无异于散弹枪打鸟,可能会烧光你的预算。规划 器使其变得**数据驱动、人工门控并带有成本估算**:四个信号 生成器将*标有定价*的排列组合*提案*放入队列,你可以在 严格的预算上限下逐个批准它们,只有获批的提案才会被触发 —— 并通过 每次运行的实际花费**自我校准**成本估算。 - **信号:** **提交考古学**(commt archaeology,过去的安全修复提交 → *不完整的修复 变体搜索* + 代码高频变动热点),**威胁模型**(threat-model,trust-graph → 不受信任到 sink 的 流程审计,按资产价值排序),**威胁情报**(threat-intel,依赖清单 → 已知的 代码搜索,+ 可选的实时 CVE 研究),以及**进化**(evolution,培育 prompt 变体;适应度 = 此处的确认发现数)。 - **门控:** `cannon permute` 按照收益率从高到低遍历提案 —— `[k]ick / [s]kip / [d]efer / [a]pprove-all / [r]e-permute / [g]o / [q]uit`,或者**输入你的直觉**以播种 定向排列组合。`--budget` 限制支出;`--yes` 为非交互模式;`--plan-only` 仅入队不触发(稍后通过 `cannon queue run` 运行)。 - **实战证明:** 在一个带有*“修复 /search 中的 SQL 注入”*提交的演示仓库中, 不完整修复搜索发现了**补丁从未触及的 `/profile` SQLi** —— 这是 单纯基于 diff 的审查会漏掉的变体。状态保存在 `targets//.cannon/queue.json` 中。 ## 目录布局 ``` src/ # the Rust crate agent.rs # tokio `claude -p` wrapper: stream-json + resume/backoff runner.rs # run_salvo — the single orchestration seam (semaphore + checkpoints + resume) ledger.rs # the persistent findings ledger + VULN_FINDINGS.md round-trip stages/ # find · verify (adversary) · recon · threat_model · chain · report tui/ # app.rs draw/keys · graph.rs native canvas layout prompts/ # editable prompt templates (+ variants/) targets/ # per-target config, source, context, ledger reference-python/ # the original verified Python prototype (kept as the spec) ``` ## 规模化扩展(后续计划) 整个运行管理器只有一个函数,`runner::run_salvo`(异步扇出 + checkpoints)。如果你以后将轮次分配给多台机器,只需将该函数的 函数体替换为生产者/消费者模式 —— 上下的每个阶段都保持 原样不动。除非你有多台机器,否则不要进行此操作。如今 `static_review` 是唯一的 检测器(只读的 `claude` 工具,无执行操作);`dynamic` 沙箱 检测器将在构建/运行沙箱 + PoC 后方实现相同的 `run_round`。
标签:Homebrew安装, Rust, 云安全监控, 人工智能, 可视化界面, 用户模式Hook绕过, 网络流量审计, 通知系统, 防御加固, 静态分析