Claude Codeでcargo-denyとcargo auditの指摘を最小更新で直す
deny.tomlのadvisories・licenses・bansを設定し、cargo updateの更新範囲をpermissionsで絞ったうえで、Claude Codeに依存の脆弱性とライセンス違反を直させる手順です。
Rustの依存を検査するときは、脆弱性を見るcargo auditと、ライセンスや重複まで見るcargo-denyの2本立てで考えると整理しやすくなります。Claude Codeに任せる部分は、検査の実行と指摘の読み取り、最小限のcargo updateです。任せない部分は、Cargo.tomlの書き換えと、指摘そのものを黙らせる操作です。この記事では、deny.tomlの書き方から、権限ルールの組み方、指摘の種類ごとの更新方針までを順に組みます。
cargo-denyとcargo auditは何が違うのか
cargo auditは、RustSec Advisory Databaseに報告された脆弱性を、依存クレートに照らして調べるコマンドです。cargo-denyは同じ助言データベースの検査に加えて、ライセンス、重複バージョン、禁止クレート、取得元レジストリを1つの設定ファイルで検査します。
| 観点 | cargo audit | cargo-deny |
|---|---|---|
| 脆弱性(RustSec) | cargo audit検査する | cargo-denyadvisoriesで検査する |
| ライセンス | cargo audit見ない | cargo-denylicensesで許可リスト方式 |
| 重複・禁止クレート | cargo audit見ない | cargo-denybansで検査する |
| 取得元の制限 | cargo audit見ない | cargo-denysourcesで検査する |
| 設定ファイル | cargo auditaudit.toml(任意) | cargo-denydeny.toml |
どちらか一方を選ぶ前提で書かれた記事は多くありますが、役割は重なっているだけで置き換えではありません。脆弱性だけを手早く見たい場面はcargo audit、CIで落とす条件を1ファイルに集約したい場面はcargo-denyが向きます。この記事は両方を同じ手順の中で使います。
cargo-denyは、導入から初回検査までをリポジトリ側のQuickstartで次のように示しています。
cargo install --locked cargo-deny
cargo deny init
cargo deny checkcargo deny checkは、licenses・bans・advisories・sourcesの4つをまとめて走らせます。cargo deny check licensesのように、1種類だけを指定して実行することもできます。
deny.tomlでadvisories・licenses・bansを決める
cargo deny initが出力する雛形には、すべてのセクションと既定値が書かれています。そのままでは何も許可されていないので、licenses.allowを埋めるところから始めます。
[advisories]
yanked = "deny"
unmaintained = "workspace"
ignore = [
# 例: { id = "RUSTSEC-0000-0000", reason = "該当コードパスを使っていない", expiry = "P30D" },
]
[licenses]
allow = [
"MIT",
"Apache-2.0",
"Apache-2.0 WITH LLVM-exception",
]
confidence-threshold = 0.8
[bans]
multiple-versions = "warn"
wildcards = "deny"
skip = []
[sources]
unknown-registry = "deny"
unknown-git = "deny"この例は、雛形の既定値から運用寄りに振った設定の一例です。どの値にするかは、プロジェクトが何を許すかで変わります。
advisoriesの値が意味すること
yankedは、取り下げ(yank)されたバージョンを使っているときの扱いで、既定はwarnです。denyにすると検査が失敗します。unmaintainedは、メンテナンスが止まったクレートの助言をどこまで失敗にするかを決めます。allが既定で、workspaceにすると、自分のワークスペースが直接依存するクレートだけが対象になります。推移的な依存でしか使われていないクレートは、自分では直せないことが多いので、workspaceにすると運用しやすくなります。
ignoreには助言IDを書けます。reasonで理由、expiryで期限(P30Dのような期間表記)を添えられるため、理由と期限を必ず書く運用にしておくと、無期限の黙殺を防げます。unused-ignored-advisoryの既定はwarnで、依存グラフに現れなくなった助言がignoreに残っていると警告が出ます。直した後の掃除を促す仕組みです。
licensesは許可リスト方式
licensesは、allowに書いたライセンス以外をすべて拒否します。かつて存在したdenyやcopyleftなどのキーは削除されていて、書くとエラーになります。特定のクレートだけに別のライセンスを許したいときは、全体のallowを広げずにexceptionsへ書きます。
[licenses]
exceptions = [
{ allow = ["Zlib"], crate = "adler32" },
]GNU系ライセンスの扱いには注意があります。0.18.4以降はGPL-3.0とGPL-3.0-or-laterとGPL-3.0-onlyが別物として厳密に扱われるため、許したい表記を個別に並べる必要があります。開発専用の依存(dev-dependencies)は既定ではライセンス検査の対象外で、ビルド専用の依存は対象です。この既定を知らずにdev依存のGPLを探すと、見つからずに戸惑います。
bansは警告から始める
multiple-versionsは同じクレートの複数バージョンが混在したときの扱いで、既定はwarnです。いきなりdenyにすると、自分では直せない推移的依存の重複で検査が止まります。まずwarnで件数を見て、skipに「理由つきで」書ける分だけを整理するほうが現実的です。denyにクレート名を書けば、特定のクレートを禁止できます。wrappersを使うと、特定のクレートからの直接依存だけを許す書き方もできます。
cargoの更新範囲をpermissionsで絞る
検査を回す部分は広く許し、依存を書き換える部分は狭く許す、という分け方にします。Claude Codeの権限ルールは、deny、ask、allowの順に評価され、最初に当たったものが結果を決めます。広いdenyの内側に、狭いallowで例外を作ることはできません。
{
"permissions": {
"allow": [
"Bash(cargo deny check *)",
"Bash(cargo audit *)",
"Bash(cargo update -p time --dry-run)",
"Bash(cargo update -p time)"
],
"deny": [
"Bash(cargo update)",
"Bash(cargo audit fix *)"
]
}
}.claude/settings.jsonに置く想定の例です。ここでは4つの点を押さえておきます。
*は空白を含む任意の文字列に当たります。末尾が「スペース+*」の規則は、引数なしのコマンドにも当たります。Bash(cargo deny check *)はcargo deny check単体にもcargo deny check advisoriesにも当たりますcargo update -p timeのように*を付けない規則は、そのコマンドそのものにだけ当たります。-p time-macrosや--precise付きの形には当たらず、許可されないため通常どおり確認が出ますBash(cargo update -p time *)と書くと、cargo update -p time -p 別のクレートのように後ろへ追加された引数も許してしまいます。更新を許す対象を絞りたいなら、*なしの完全一致で並べるほうが安全ですBash(cargo audit *)はcargo audit fixにも当たりますが、denyが先に評価されるためcargo audit fixは拒否されます。cargo audit fixはCargo.tomlを直接書き換える実験的な機能です。Claude Codeが意図せず依存要件を書き換える入り口になるので、ここは明示的に止めています
Bash(cargo update)のdenyは、全依存を一括で更新する素のcargo updateを止めるためのものです。cargo updateにスペックを渡さないと、Cargo.lock内のすべての依存が最新へ動きます。指摘の修正に必要な範囲を超えて差分が膨らみます。
もう1つ覚えておきたいのは、Bashのルールはコマンドの文字列に対して当たる点です。/usr/bin/cargoのような別の書き方や、bash -cで包んだ形は、同じルールに当たりません。denyルールは、Claudeが通常書く形を止めるもので、セキュリティ境界ではありません。cargo系のdenyルールが効かないという警告が出たときの読み方は、Claude Codeの警告「matches no tool」の意味とdenyルールの直し方にまとめてあります。
CLAUDE.mdに更新方針を書いておく
権限ルールは「できること」を縛り、CLAUDE.mdは「どう判断するか」を伝えます。CLAUDE.mdは強制力のある設定ではなく、Claudeが読む文脈です。守らせたい境界は権限ルールに置き、判断の順番をCLAUDE.mdに置くと役割が分かれます。
## 依存の検査と更新
- 検査は `cargo deny check` と `cargo audit` の2つを使う
- 指摘への対応は次の順で決める
1. 修正版があり semver 互換なら `cargo update -p <crate> --dry-run` で差分を確認してから `cargo update -p <crate>`
2. 修正版が `Cargo.toml` の要件の外にある場合は、更新せずに候補を報告する
3. 修正版が無い場合は deny.toml の ignore に `reason` と `expiry` を付けて提案する
- `cargo update`(引数なし)と `cargo audit fix` は実行しない
- 更新後は `cargo deny check` と `cargo test` を再実行して結果を報告する3つの分岐は、Cargo.tomlに触れずに済む範囲かどうかで切っています。cargo update -p <crate>はCargo.lockだけを動かし、Cargo.tomlの要件を超える更新はしません。要件を超える更新が必要なら、人がCargo.tomlの変更を判断します。
指摘を最小更新で直す流れ
検査の出力を読み、直せるものだけを狭い範囲で直す反復です。順番は次のとおりです。
指摘から最小更新までの順番
- 1
検査を実行する
cargo deny checkとcargo auditを実行し、指摘をクレート単位で一覧にします。 - 2
指摘を3種類に分ける
修正版がある脆弱性、修正版が無い脆弱性、ライセンス・重複の指摘に分けます。
- 3
差分を先に見る
cargo update -pに--dry-runを付けて、動くクレートの一覧を確認します。 - 4
最小更新を実行する
許可された1クレートだけを更新し、検査とテストを再実行します。
実際の依頼は、たとえば次のように書きます。
cargo deny check advisories を実行し、指摘をクレート別に表にして。
修正版がある指摘は、cargo update -p <crate> --dry-run で
動くクレートを確認してから報告して。まだ更新はしないで。最初の依頼を「報告まで」で止めるのは、--dry-runの出力で、更新が連鎖してほかのクレートまで動かないかを人が見られるようにするためです。cargo updateにクレートを指定した更新は保守的で、指定したクレートだけを動かします。推移的な依存は、指定のクレートが更新できないときにだけ動きます。つまり、--dry-runで動く一覧が1行なら安全に進められ、複数行になるなら理由を確認する価値があります。
人が一覧を見て了承したら、次の指示を出します。
time だけ cargo update -p time で更新して。
その後 cargo deny check と cargo test を再実行して、結果を報告して。コマンドがallowの完全一致に当たるので、確認なしで進みます。許可リストに無いクレートの更新は、通常の確認が出ます。確認が出たときが、そのクレートを許可リストへ足してよいかを判断する場面です。
修正版が無い・直せない指摘の扱い
修正版が出ていない脆弱性や、メンテナンスが止まったクレートは、更新では直せません。選択肢は3つに分かれます。
- 代替クレートへ置き換える(人が判断する)
- 該当コードパスを使っていないと確認したうえで、
ignoreに理由と期限を書く - 当面は
warnで運用し、検査は通す
cargo auditにも--ignoreオプションがあり、audit.tomlでも設定できます。READMEは、脆弱性を直す最善の方法は該当クレートの更新だと明記しています。例外にできるのは、更新が出ておらず、助言が自分のアプリに影響しないと確認できた場合です。Claudeにignoreを書かせるときは、助言IDと理由と期限を必ず含めさせ、差分を人が見るようにします。
ライセンスと重複の指摘の扱い
ライセンス違反は更新で直ることもありますが、多くは許可リストに載せるか、そのクレートを避けるかの判断です。後者は人が決める事項なので、Claudeには「違反しているクレートと、それを引き込んでいる依存を示して、候補を2つ出す」ところまでを頼みます。重複バージョンは、skipにreason付きで書くか、上流の更新を待つかを選びます。
運用でつまずきやすい点
- 取得に時間がかかる、または失敗する:
cargo-denyは既定でRustSecの助言データベースを取得します。オフラインで走らせる場合は--offlineかcheck --disable-fetchを使います。このとき、データベースが90日(P90D)より古いとエラーになります cargo auditがCargo.lockを作る:Cargo.lockが無いプロジェクトで実行すると、cargo update --workspaceで生成します。Cargoはそのプロジェクトの設定やコードを実行する場合があるため、信頼できないプロジェクトには--file path/to/Cargo.lockを渡し、プロジェクトの外のディレクトリから実行します- 許可リストのクレートだけ更新したのに差分が大きい: 指定したクレートが更新できないと、その依存も一緒に動きます。
--dry-runで一覧を見るのは、この連鎖を事前に見つけるためです - dev依存のライセンスを検査したい:
licensesのinclude-devをtrueにします。既定はfalseです - ignoreが残り続ける:
unused-ignored-advisoryをdenyにすると、使われなくなったignoreが検査失敗として現れます
Rustの開発環境を整える手順は、Claude CodeでAxumのRust APIを開発するにあります。依存関係の検査を商用サービスに任せる選択肢は、Sonatype GuideプラグインやEndor Labs Agent Kitの記事で扱っています。いずれも手元のdeny.tomlと併用できます。
まとめ
検査を回す権限は広く、依存を動かす権限は完全一致で狭く渡し、判断の順番はCLAUDE.mdに書く。この分担にすると、cargo updateの影響範囲が1クレートに収まり、変更の理由を差分から追えます。deny.tomlのignoreは、reasonとexpiryの2点が揃って初めて「保留」として扱えます。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。