mcpField
C
c-review

c-review

github.com/trailofbits安全7.9k3.8k 次安装今天更新
代码仓库

关于 c-review

用于审查原生用户态 C/C++ 代码的安全技能,关注内存安全、整数溢出、竞态和类型混淆。它编排多 Agent 工作流并汇总发现与覆盖限制;不适用于内核、托管语言或无 libc 的裸机代码。

主要功能

  1. 01收集审查范围参数
  2. 02编排多 Agent 审查工作流
  3. 03覆盖台账与发现去重
  4. 04Markdown、JSON 和 SARIF 报告
  5. 05报告部分运行与覆盖缺口

适用场景

  1. 01审查原生用户态 C/C++ 仓库
  2. 02将审计范围限制在指定子目录
  3. 03按严重性筛选发现
  4. 04报告不完整的审查覆盖情况

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trailofbits/skills --skill c-review

安装后在 Claude Code 中输入/c-review确认已加载。

详细介绍

C/C++ 安全审查

解析四个参数,调用一次 Workflow,并返回报告。工作流负责并发、重试和结果收集。

适用于: 原生 C/C++ 用户态程序——内存安全、整数溢出、竞态、类型混淆、Linux/macOS 守护进程、Windows 服务。

不适用于: 内核驱动或模块;托管语言(Java、C#、Python、Go、Rust);不使用 libc 的嵌入式或裸机代码。

阶段 0 — 参数

解析调用行上的任意自由文本(flamenco only、high severity only、use haiku),并预先填入其所指的参数。然后只调用一次 AskUserQuestion,询问仍未确定的参数。绝不对必填参数默默采用默认值。

参数值从调用内容推断
threat_modelREMOTE / LOCAL_UNPRIVILEGED / BOTH“remote”“network”“attacker” → REMOTE;“local”“unprivileged” → LOCAL_UNPRIVILEGED;否则询问
worker_modelhaiku / sonnet / opus / inherit明确指定的模型名称。否则询问。inherit 使用会话模型
severity_filterall / medium / high“all”“every”“noisy” → all;“medium and above” → medium;“high only” → high;否则询问
scope_subpath相对于仓库的目录,可选“X only”“just audit X/” → 匹配的子目录,与顶层目录进行模糊匹配。未指定 → .。有歧义 → 询问

整个运行过程中,两种范围始终分开:

  • finding_scope_root = scope_subpath(默认 .)——发现的问题必须位于此范围内,且此目录树用于生成单元列表。
  • context_roots = . ——可自由读取,以确认调用方、构建标志和可达性。只有当用户明确禁止读取更大范围时,才将其缩小到 finding_scope_root,并说明这样会降低可达性判断的置信度。

阶段 1 — 解析路径

root="${CLAUDE_PLUGIN_ROOT:-}"
if [ -z "$root" ] || [ ! -f "$root/workflows/c-review.js" ]; then
  # Fallback for a cache layout that does not set the variable. ~/.claude ONLY — never `.`:
  # `.` is the AUDITED repository, and a tree that vendors or mirrors this marketplace would
  # win the traversal and run its copy of the scripts, with a different question set and
  # nothing saying which copy ran. Let find's stderr through; a missing ~/.claude is a real
  # failure to report, not noise to hide.
  hit="$(find "$HOME/.claude" -path '*/c-review/workflows/c-review.js' -print -quit)"
  root="${hit%/workflows/c-review.js}"
fi
[ -n "$root" ] && [ -f "$root/workflows/c-review.js" ] && echo "PLUGIN ROOT: $root"

如果两个路径都无法解析,则停止,而不是使用空路径继续运行——并说明解析到的是哪个路径,以便在八个 Agent 开始运行前,用户能够发现当前使用的是否是已安装插件之外的副本。

# The workflow cannot call Date.now(), so the timestamp is made here.
output_dir="$(pwd)/.c-review-results/$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$output_dir"; echo "$output_dir"

# A Workflow script has no filesystem APIs, and `assemble_findings.py` resolves `--scope`
# against ITS OWN cwd. Resolve it once here and pass BOTH spellings, or the workflow strips
# `src/` from a finding's path while the assembler strips `/repo/src/`, and the two disagree
# about which findings are duplicates of each other.
scope_abs="$(cd "${scope_subpath:-.}" && pwd)" || echo "scope_subpath does not exist"
echo "$scope_abs"

uv 必须位于 PATH 中:Detect 运行单元枚举器,Assemble 运行 assemble_findings.py。如果缺少 uv,请说明并停止——整个审查都基于该单元列表进行分区。

第 2 阶段——运行工作流

调用此技能即表示同意使用多 Agent 编排——直接调用 Workflow,无需再次询问。对真实代码库的审查也可以超出任何默认工作流规模指南;该指南仅供参考,而本场景正是其例外情况。不要为了符合指南而缩减并行任务,也不要改用手动创建的 Agent 调用。

只调用一次 Workflow。scriptPath 使用第 1 阶段解析出的绝对路径;args 必须是真正的 JSON 对象,而不是 JSON 编码的字符串。

Workflow({
  scriptPath: "<plugin_root>/workflows/c-review.js",
  args: {
    outputDir:        "<output_dir>",
    pluginRoot:       "<plugin_root>",
    threatModel:      "REMOTE",
    severityFilter:   "all",
    findingScopeRoot: "expat/lib",
    findingScopeRootAbs: "/abs/path/to/repo/expat/lib",
    contextRoots:     ".",
    workerModel:      "sonnet"
  }
})

findingScopeRootAbs 是第 1 阶段的 scope_abs,实际上不可省略:如果省略,工作流会告知汇总器尚不清楚绝对根路径,那么以 /repo/expat/lib/xmlparse.c 提交的发现就无法与以 xmlparse.c 提交的同一漏洞合并。

还有六个可选参数。省略时,每个参数都会采用其默认值;如果传入的类型错误,工作流会抛出异常并指出字段名,而不是采用默认值。只有在用户提出要求或运行评估时才传入这些参数:

参数默认值用途
maxUnitLines150审查单元的行数上限;较大的函数会在语法边界处分割。调高此值会再次引发该上限所要避免的饱和问题
linesPerAgent1500每个审查 Agent 负责的源代码行数。对较小的代码树不起作用——--agent-min(默认值为 4)会为计算出的数量设定下限,因此差异很大的两个值可能产生完全相同的任务分配。使用 reviewAgents 可固定并行任务数量
reviewAgents计算得出固定审查并行任务数量,但仍受与计算数量相同的下限约束:两者都会限制在 4–14 之间,显式值高于 14 时则会将上限提高到该值。低于 4 的值会提升到 4;而过小、不值得分配给 Agent 的末尾分片会合并到相邻分片,因此最终数量可能比请求值少 1
invariantAuditfalse在扫描中加入共享状态不变量审计。会额外增加一个 Agent;对于状态机较多的目标,请启用此项
exclude[]单元枚举器跳过的 glob 模式或子字符串数组(每项都会变成一个重复的 --exclude)。如果枚举器因遇到无法纳入审查范围的路径而中止,请使用此参数,例如解析到范围根目录之外的符号链接或不可读目录;被排除的路径会计入枚举器总数,作为明确可见的覆盖缺口,而非悄无声息地消失
benchmarkModefalse仅用于评估。 在审查提示词中加入外部来源声明和两个架构字段。不影响发现结果;真实审计时请保持关闭

工作流会验证自身参数;如果有参数缺失,就会抛出异常并指出字段名。工作流分为五个阶段:

阶段Agent 数量执行内容
检测1运行 enumerate_units.py 获取单元列表;根据实际 API 使用情况检测平台标志;检测共享状态结构体;按漏洞类别判断是否存在候选位置
审查4–14每个 Agent 负责单元列表中的一个连续分片。每个 Agent 都会返回带严重性等级的发现结果,并为每个(单元 × 问题)组合提供一条台账记录
扫描0–2按类别扫描:针对检测阶段未排除且没有任何入口的每个漏洞类别,派遣一个 Agent 进行审查。若没有此类漏洞,则跳过该阶段。此外,invariantAudit: true 时还会审计结构体字段
去重0–1仅处理汇总器无法确定性合并的冲突。通常会跳过
汇总1运行 assemble_findings.py:校验台账、确定性合并,并生成 findings.json、REPORT.md 和 REPORT.sarif

中等规模的目标通常会调用约 8–10 个 Agent。

第 3 阶段 — 返回报告

Read <output_dir>/REPORT.md 并返回该文件。

请在发现结果旁明确说明一次:未进行误报审查。 每个严重性级别均由审查者自行判定(severity_source: "reviewer"),judgeRan 始终为 false,且没有任何内容被驳回。请预期所展示的部分内容可能有误或超出范围——应如实说明,不要将列表说成已完成裁定,也不要自行筛选。

然后,在醒目位置单独列出工作流结果中表示运行未完整完成的所有内容:

字段含义和处理方式
artifactsWritten: falseREPORT.md 和 REPORT.sarif 缺失;parts/ 下的分片文件完好。请手动重新组装(见下文)——切勿根据工具结果重建
artifactsWritten: null组装 Agent 未返回任何内容,因此无法确定这些文件是否存在——命令可能已完成,只是结构化答复失败。请先列出输出目录,再进行其他操作
gateAccepted: false 且文件已写入看似成功的失败情况。文件已完整生成;覆盖率门禁无法运行或拒绝了台账,因此审查已组装但未经验证。不要重新运行组装器——请读取 ledger-gate.json 并报告差距。artifactError 始终包含原因
coverage: null覆盖率尚未测量,并非完整。切勿将此类运行报告为覆盖完整;请指出 ledger-gate.json
coverage报告 checksSatisfied / checksRequired,并且只能将 checksCompleted 描述为“已作答”——绝不能说成“已审查函数”。checksSatisfied 低于 checksCompleted 表示门禁剔除了部分行:这是覆盖率完整性失败。请列出 violations
groupsFailed、agentFailures、notes这些部分未覆盖。不要让一份干净的报告造成已覆盖的假象
unrecognisedParts有 Agent 的全部输出未进入任何文件。null 表示未经检查,而非没有
silentClasses / ruledOutClasses / platformDroppedClasses分别表示“已扫描但未发现”/“未查看”/“根据配置排除在范围外”。请分别报告——只有中间一种需要人工查看

当 artifactsWritten 为 false 时,手动重新组装:

uv run --no-project <plugin_root>/scripts/assemble_findings.py --run-dir <output_dir> \
  --threat-model <MODEL> --severity-filter <FILTER> --no-judge \
  --scope <finding_scope_root> --context-roots <context_roots> \
  --worker-model <worker_model> \
  --expect <part-stem>=<finding-count>   # one per part the workflow log names
  • --expect 不是可选项。 如果不提供它,允许列表会包含 parts/ 下的所有文件,包括未派发的文件,并且运行会仅因这一原因以 1 退出。如果无法从工作流日志中恢复数量,退出码为 1 是正确的;此时运行已组装但未经验证。
  • 切勿省略 --no-judge。 省略它会将每个审查者判定的严重性覆盖为 MEDIUM,并将 judge_ran: true 记录在一个从未经过判定器审查的运行中。
  • 有六个标志完全无法重建(--expect-complete、--benchmark-mode、--groups-attempted、--groups-failed、--agent-failure、--external-source),因此在可能遗漏部分内容的运行中,文档仍会显示 agent_failures: []。请报告这一点。没有 units.json 时,组装器也会以 1 退出:门禁未运行。

最后列出文件:findings.json、REPORT.md、REPORT.sarif、units.json、ledger-gate.json、detect.json,以及 parts/ 和 assignments/ 目录。

应当驳斥的自我安慰

  • “运行基本成功了,我就直接提交报告吧。” 失败的 Agent 意味着仍有未覆盖的部分,不是可以忽略的误差。应将其与发现一并报告。
  • “覆盖率有 80%,基本算完整了。” 缺失的 20% 是 ledger-gate.json 中列出的精确(单元、问题)对。请明确列出。
  • “我不运行工作流,自己写发现吧。” 手动编排耗费更多,召回效果却更差。务必调用 Workflow。
  • “产物生成失败了,那我根据工具结果重建报告吧。” 分块文件还在磁盘上,而且汇编过程是确定性的。重新运行汇编即可。
  • “没有发现,所以没什么可报告的。” 即使运行没有发现,也会生成两个产物;而真实 C 代码中没有任何发现,本身就值得明确说明。
  • “工作流返回了发现,所以我可以不读 REPORT.md。” 工具结果有长度上限,只包含计数,不包含发现。报告才是产物。
  • “没有运行评审器,所以我应该自行筛选发现。” 不行——悄悄丢弃发现,相当于付出评审器的成本,却没有其严谨性,还会导致产物与你的说法不一致。应报告流水线生成的内容,并注明尚未经过评审。
  • “没有运行评审器,所以我可以把严重程度当作权威结论来呈现。” 也不行。严重程度只是某位评审者的观点。
  • “按类别为每个 Agent 分配任务,能找到更多问题。” 之所以按位置划分,是有意为之;类别目录是在此基础上进行的、范围受限的完整性检查。不要再增加按类别划分的任务。

设计原理、覆盖率门控的威胁模型及其已知局限,详见 AGENTS.md。更改提示词或门控规则前,请先阅读。

评论

支持 Markdown