mcpField
C
c-review

c-review

github.com/trailofbitsセキュリティ7.9k3.8k 回インストール今日更新
リポジトリ

c-review について

ネイティブなユーザー空間 C/C++ コードを対象とするセキュリティレビュー用スキルで、メモリ安全性、整数オーバーフロー、競合、型混同を扱います。マルチエージェントのワークフローを実行して結果とカバレッジ上の制約をまとめ、カーネルやマネージド言語、libc のないベアメタルコードは対象外です。

主な機能

  1. 01レビュー範囲パラメーターの収集
  2. 02マルチエージェントレビューの実行
  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++ セキュリティレビュー

4 つのパラメーターを解決し、Workflow を 1 回呼び出して、レポートを返します。並行処理、再試行、結果の収集はワークフローが担います。

対象: ネイティブ C/C++ ユーザー空間 — メモリー安全性、整数オーバーフロー、競合状態、型の混同、Linux/macOS デーモン、Windows サービス。

対象外: カーネルドライバーまたはモジュール、マネージド言語(Java、C#、Python、Go、Rust)、libc を使用しない組み込みコードまたはベアメタルコード。

Phase 0 — パラメーター

呼び出し行の自由記述(flamenco only、high severity only、use haiku)を解析し、そこから推測できる値を事前に設定します。その後、未解決の項目について AskUserQuestion を 1 回だけ呼び出します。必須パラメーターを暗黙にデフォルト設定してはいけません。

パラメーター値呼び出しからの推測方法
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/」→ 該当するサブディレクトリ。トップレベルディレクトリとあいまい一致させます。指定なし → .。あいまいな場合は質問します

実行全体を通して、2 つのスコープを分けて扱います。

  • finding_scope_root = scope_subpath(デフォルトは .)— 検出事項はこの範囲内に存在する必要があり、ユニット一覧もこのツリーから生成されます。
  • context_roots = . — 呼び出し元、ビルドフラグ、到達可能性を確認するため、自由に読み取ります。ユーザーが明示的に範囲外の読み取りを禁じた場合に限り、finding_scope_root のみに絞ります。その場合は、到達可能性の確度が下がることを伝えます。

Phase 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"

どちらのパスも解決できない場合は、空のパスで続行せずに停止し、解決したパスを伝えてください。これにより、インストール済みプラグイン以外のコピーが、8 つのエージェントで処理される前に判別できます。

# 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 — ワークフローを実行する

このスキルを呼び出すことは、マルチエージェントオーケストレーションに同意したことを意味します。再確認せずに Workflow を呼び出してください。実際のコードベースのレビューでは、デフォルトのワークフローサイズのガイドラインも超えます。このガイドラインはあくまで参考であり、このケースは例外です。ガイドラインに合わせて並列処理数を減らしたり、手動で生成した Agent 呼び出しに置き換えたりしないでください。

Workflow の呼び出しは 1 回だけです。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 として登録された同じバグと統合されなくなります。

以下の 6 つの追加引数は省略可能です。省略すると、それぞれデフォルト値が使われます。誤った TYPE を渡すと、デフォルト値に置き換えられず、フィールド名を含むエラーがスローされます。ユーザーから指定を求められた場合、または評価を実行する場合にのみ渡してください。

引数デフォルト値用途
maxUnitLines150レビューユニットの上限です。より大きな関数は構文上の区切りで分割されます。値を大きくすると、この上限によって防いでいる飽和状態が再発します
linesPerAgent1500レビューエージェント 1 つあたりのソース行数です。小規模なツリーでは効果がありません — --agent-min(デフォルトは 4)が算出数の下限になるため、大きく異なる値を指定しても割り当てが同じになる場合があります。並列処理数を固定するには reviewAgents を使ってください
reviewAgents算出値レビューの並列処理数を固定します。算出数と同じ下限が適用され、どちらも 4~14 の範囲に制限されます。明示的に 14 を超える値を指定すると、上限はその値に引き上げられます。4 未満の値は 4 に引き上げられ、エージェントを割り当てるには小さすぎる末尾のスライスは隣のスライスにまとめられるため、最終的な数が指定より 1 つ少なくなる場合があります
invariantAuditfalseスイープに共有状態の不変条件監査を追加します。エージェントが 1 つ追加されます。状態機械が中心となる対象では有効にしてください
exclude[]ユニット列挙でスキップする glob または部分文字列の配列です(それぞれ繰り返し --exclude として指定されます)。列挙が所有できないパス(スコープルート外に解決されるシンボリックリンク、読み取りできないディレクトリなど)を理由に中断した場合に使ってください。除外されたパスは黙って無視されず、列挙結果の合計にカバレッジの欠落として明示されます
benchmarkModefalse評価専用です。 レビュープロンプトに外部ソースの宣言を追加し、スキーマに 2 つのフィールドを追加します。指摘内容は変わりません。実際の監査ではオフのままにしてください

ワークフローは引数を検証し、必須の引数がない場合はフィールド名を含むエラーをスローします。次の 5 つのフェーズを実行します。

フェーズエージェント数処理内容
検出1ユニットリストのために enumerate_units.py を実行します。実際の API 使用状況に基づいてプラットフォームフラグを検出します。共有状態の構造体を特定し、バグクラスごとに候補箇所が存在するかを判定します
レビュー4~14ユニットリストの連続したスライスごとにエージェントを 1 つ割り当てます。それぞれが深刻度付きの指摘と、(ユニット × 質問)ごとの台帳行を返します
スイープ0~2クラス軸です。検出フェーズで該当箇所がないと判断されていないバグクラスすべてを、1 つのエージェントが調査します。対象がなければ、このフェーズはスキップされます。invariantAudit: true の場合は、構造体フィールドの監査も追加されます
重複排除0~1アセンブラーで決定的に統合できない重複がある場合にのみ実行されます。通常はスキップされます
集約1assemble_findings.py を実行します。台帳ゲート、決定的な統合、findings.json、REPORT.md、REPORT.sarif を処理します

中規模の対象では、エージェント数は約 8~10 です。

フェーズ 3 — レポートを返す

<output_dir>/REPORT.md を読み、返します。

調査結果の横に、次のことを一度だけ明確に記載してください。誤検知レビューは実施されていません。重大度はすべてレビュー担当者自身が設定したもの(severity_source: "reviewer")で、judgeRan は常に false であり、何も却下されていません。表示される内容には、誤りや対象範囲外のものが含まれている可能性があります。一覧を確定済みの判定として提示せず、その旨を明記してください。また、自分でフィルタリングしないでください。

次に、ワークフローの結果に実行が部分的だったことを示すものがあれば、調査結果とは分けて目立つように提示してください。

フィールド意味と対応
artifactsWritten: falseREPORT.md と REPORT.sarif はありません。parts/ 内のパートファイルは完全な状態です。以下の手順で手作業で再アセンブルしてください。ツールの結果から再構築してはいけません
artifactsWritten: nullアセンブルエージェントから何も返されなかったため、アーティファクトが存在するかは不明です。コマンドは完了しており、構造化された回答だけが失敗した可能性があります。何よりも先に出力ディレクトリを一覧表示してください
gateAccepted: false(アーティファクトは書き込み済み)成功のように見える失敗です。アーティファクトは完全ですが、カバレッジゲートを実行できなかったか、台帳が却下されたため、レビューはアセンブル済みですが未検証です。アセンブラーを再実行しないでください。ledger-gate.json を読み、問題点を報告してください。理由は必ず artifactError に記載されています
coverage: nullカバレッジは未測定であり、完全ではありません。このような実行を完全にカバーされたものとして報告してはいけません。ledger-gate.json を参照してください
coveragechecksSatisfied / checksRequired を報告し、checksCompleted は「回答済み」としてのみ報告してください。「レビュー済みの機能」としてはいけません。checksSatisfied が checksCompleted を下回る場合、ゲートが行を除外しています。これはカバレッジの整合性に関する失敗です。violations を列挙してください
groupsFailed、agentFailures、notesその範囲はカバーされていません。レポートが問題なしと示唆することのないようにしてください
unrecognisedPartsエージェントの出力全体がどのアーティファクトにも含まれていません。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 が記録されます。
  • 6 つのフラグは一切復元できません(--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/ ディレクトリです。

却下すべき合理化

  • 「実行はおおむね成功したので、レポートだけ提示します。」 失敗したエージェントは、誤差として扱うのではなく、未調査の領域として報告してください。調査結果と併せて記載します。
  • 「カバレッジは80%なので、ほぼ完了です。」 未対応の20%は、ledger-gate.json に正確な(ユニット、質問)の組み合わせとして記載されています。具体的に挙げてください。
  • 「ワークフローを実行せず、自分で調査結果を書きます。」 手作業でのオーケストレーションは、再現率が下がるうえ、はるかに手間がかかります。必ず Workflow を呼び出してください。
  • 「成果物の生成に失敗したので、ツールの結果からレポートを再構成します。」 部分ファイルはディスク上にあり、アセンブラーは決定論的です。再実行してください。
  • 「調査結果はゼロなので、報告することはありません。」 調査結果がゼロでも、実行によって両方の成果物が生成されます。また、実際のCコードで調査結果がゼロだったこと自体、明示する価値があります。
  • 「ワークフローから調査結果が返されたので、REPORT.mdを読まなくてもかまいません。」 ツールの結果には上限があり、含まれるのは件数であって調査結果そのものではありません。レポートが成果物です。
  • 「判定者が実行されなかったので、自分で調査結果を絞り込みます。」 いいえ。調査結果を黙って削除すると、判定者の厳密さなしにそのコストだけを再現することになり、成果物と報告内容も食い違います。パイプラインが生成した内容を、未判定であることを明記して報告してください。
  • 「判定者が実行されなかったので、重大度を確定したものとして提示します。」 それも違います。重大度は、あるレビュアーの意見にすぎません。
  • 「エージェントをクラスごとに分割して実行すれば、もっと多く見つかるはずです。」 分割単位は意図的に場所としています。クラスカタログは、そのうえで実施する、対象を限定した網羅性チェックです。追加しないでください。

設計の根拠、カバレッジゲートの脅威モデル、既知の制限については、AGENTS.md を参照してください。プロンプトまたはゲートのルールを変更する前に、必ず読んでください。

コメント