Claude Media
Claude Codeでcargo-denyとcargo auditの指摘を最小更新で直す

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 auditcargo-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 check

cargo 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. 1

    検査を実行する

    cargo deny checkとcargo auditを実行し、指摘をクレート単位で一覧にします。

  2. 2

    指摘を3種類に分ける

    修正版がある脆弱性、修正版が無い脆弱性、ライセンス・重複の指摘に分けます。

  3. 3

    差分を先に見る

    cargo update -pに--dry-runを付けて、動くクレートの一覧を確認します。

  4. 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点が揃って初めて「保留」として扱えます。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/permissions
  2. github.com/EmbarkStudios/cargo-deny
  3. github.com/rustsec/rustsec/blob/main/cargo-audit/README.md
  4. doc.rust-lang.org/cargo/commands/cargo-update.html
この記事を共有:XはてブLinkedIn