Claude Media
security-reviewのHaiku廃止で誤検知フィルタが無効化する理由

security-reviewのHaiku廃止で誤検知フィルタが無効化する理由

GitHub Action版のsecurity-reviewは誤検知フィルタの検証にHaiku 3.5を固定使用しており、モデル廃止後はフィルタが気づかれないまま無効化されます。原因と現状の対処法です。

GitHub Actionのanthropics/claude-code-security-review(Claude Code Security Reviewer)は、検出した指摘をClaudeにもう一度判定させて誤検知を除く仕組みを持っています。この仕組みの入口で行うAPI疎通確認が、廃止済みのHaiku 3.5モデル(claude-3-5-haiku-20241022)を固定で呼び出しているため、モデル廃止後は確認が失敗し、Claudeによる判定段階が気づかれないまま丸ごとスキップされます。修正は提案されているものの、mainブランチにはまだ取り込まれていません。

何が壊れているか — validate_api_accessが廃止モデルを呼び続ける

Action版は、PRの差分を解析して指摘を出したあと、findings_filter.pyで2段階の除外処理を通します。Claudeを使うのは後段だけで、前段は正規表現のルールです。後段を初期化する際、claude_api_client.pyのvalidate_api_access()がAPIへの疎通確認を行います。

しくみ

指摘が出力されるまでの流れ

  1. 1

    スキャン

    PRの差分をClaudeで解析し、脆弱性の候補を出します。ここはclaude-model入力で指定したモデルで動き、今回のバグの影響を受けません。

  2. 2

    ハード除外(正規表現)

    指摘のタイトルと説明文に正規表現を当て、DoS・レート制限・リソースリーク・オープンリダイレクトなどを除きます。Claudeを呼ばないので、疎通確認が失敗しても動き続けます。

  3. 3

    Claudeによる判定

    validate_api_access()が成功したときだけ、残った指摘をClaudeが「本当に危険か」判定します。失敗するとこの段階ごとスキップされます。

Claude判定の入口にある疎通確認は、Issue #69が次の抜粋で示しています。

def validate_api_access(self) -> Tuple[bool, str]:
    try:
        self.client.messages.create(
            model="claude-3-5-haiku-20241022",  # 廃止済みモデルを固定指定
            max_tokens=10,
            messages=[{"role": "user", "content": "Hello"}],
            timeout=10
        )
        return True, ""
    except Exception as e:
        error_msg = str(e)
        return False, f"API validation failed: {error_msg}"

呼び出しているモデルIDclaude-3-5-haiku-20241022(Claude Haiku 3.5)は、Anthropicの公式廃止ページによると2025年12月19日に廃止予告が出され、2026年2月19日付けで完全に廃止(Retired)されています。推奨移行先はclaude-haiku-4-5-20251001です。廃止後にこのメソッドを呼ぶと疎通確認が失敗し、呼び出し元のfindings_filter.pyが次の分岐に入ります。

valid, error = self.claude_client.validate_api_access()
if not valid:
    logger.warning(f"Claude API validation failed: {error}")
    self.claude_client = None
    self.use_claude_filtering = False  # フィルタを無効化

以降のスキャンはuse_claude_filtering = Falseのまま進み、ハード除外を通った指摘がjustification: "Claude filtering disabled"・confidence_score: 10.0というメタデータ付きでそのまま出力されます。

READMEが「除外される指摘」として挙げるのは、DoS・レート制限・メモリやCPUの枯渇・実害が証明されない汎用的な入力検証・オープンリダイレクトの5種類です。ソースコードを読むと、このうち正規表現で落としているのは、DoSやリソース枯渇、レート制限の不足、リソースリーク、オープンリダイレクトです。ほかに、C/C++以外のファイルでのメモリ安全性の指摘、正規表現インジェクション、.htmlファイルでのSSRF、.mdファイルへの指摘もハード除外の対象です。

一方、「実害が証明されない汎用的な入力検証」にあたる指摘に、対応するルールはありません。表現がルールの語句に当たらなかった指摘も同様です。フィルタが無効化された状態でPRコメントに流れ込むのは、こうした「理屈の上では脆弱性だが悪用可能かは文脈次第」という判断を要する指摘です。

疎通確認のような軽い呼び出しに、本体スキャンより安価なHaikuを割り当てる設計自体は理にかなっています。Claude Codeサブエージェントのモデル配分設計で扱っているような、役割ごとに軽量モデルを割り当てる考え方と同じ方向です。問題は、割り当てたモデルIDを特定のスナップショット名で固定し、モデルの世代交代を追随させる仕組みを持たなかった点にあります。

なぜCI上で気づきにくいか

validate_api_access()が失敗しても、スキャン全体は成功として終了します。警告の行き先は、PRコメントでもジョブサマリーでもありません。action.ymlはPythonの標準エラー出力をclaudecode-error.logに書き出します。ジョブログへその中身を出力するのは、結果ファイルが空のときと、無いときだけです。スキャンが非ゼロで終了した場合も、出るのは警告1行だけです。Issue #69の報告者も、同じ理由で「ジョブログにエラーが出ない」と書いています。

警告を見つけるには、upload-results(既定は有効)でアップロードされるsecurity-review-resultsアーティファクトを開きます。中身はfindings.json・claudecode-results.json・claudecode-error.logの3ファイルで、保持期間は7日です。次のように検索すると、状態が分かります。

# claudecode-error.log に疎通確認の失敗が残っているか
grep "Claude API validation failed" claudecode-error.log
 
# 結果JSONで「フィルタ無効化」のまま出力された指摘を数える
grep -c '"justification": "Claude filtering disabled"' claudecode-results.json

ログに警告が残っていれば、そのスキャンではClaudeによる判定が動いていません。結果JSONに指摘が1件も無いスキャンでは、justificationの行自体が出ないため、フィルタの状態は結果JSONからは見えません。

報告者は、GitHub Actionsの実行でENABLE_CLAUDE_FILTERINGはtrue、ANTHROPIC_API_KEYも設定済みなのに、フィルタは無効化されたままだったと説明しています。設定はすべて正しいのに機能しない点が、このバグの厄介なところです。設定漏れのチェックリストをいくら見直しても、validate_api_access()が呼んでいるモデルIDまでは目が届きません。

同じファイルを後続のステップから読めば、フィルタ無効化をジョブの失敗として扱えます。action.ymlは結果ファイルをワークスペース直下にもコピーするので、アクションの直後に次のようなステップを置く方法があります。

- name: Fail if Claude filtering was skipped
  if: always()
  run: |
    if grep -q '"justification": "Claude filtering disabled"' claudecode-results.json; then
      echo "::error::Claude filtering was disabled"
      exit 1
    fi

影響範囲 — ENABLE_CLAUDE_FILTERINGは全実行で常にtrue

action.ymlは、ユーザーが入力を指定しなくても環境変数ENABLE_CLAUDE_FILTERINGを常に'true'にセットします。

env:
  ANTHROPIC_API_KEY: ${{ inputs.claude-api-key }}
  ENABLE_CLAUDE_FILTERING: 'true'

「フィルタ機能をオンにする設定をした覚えがない」プロジェクトでも、既定でフィルタは有効化されようとし、そのたびに廃止済みモデルへの疎通確認が走って失敗します。claude-model入力でスキャン本体のモデルを変更していても無関係です。validate_api_access()はどのモデルが設定されていてもclaude-3-5-haiku-20241022を固定で呼びます。

claude-modelを空のままにすると、constants.pyの既定値claude-opus-4-1-20250805が使われます。このモデルは公式廃止ページで2026年8月5日にRetiredとなっており、action.ymlの入力説明に例として載っているclaude-sonnet-4-20250514も2026年6月15日に廃止されました。今回の修正案は疎通確認にself.modelを使うものなので、入力が廃止済みのモデルのままなら、確認は同じように失敗します。claude-modelには現役のモデルIDを明示する必要があります。

廃止済みモデルへの参照を避ける設定の考え方は、CLIのenforceAvailableModelsでDefaultモデルの抜け穴を塞ぐでも扱っていますが、このGitHub Actionには同種の防御設定はありません。

修正は提案済み・未マージ — 現状の選択肢

Issueの報告と同時に、self.model(スキャン本体に設定済みのモデル)を疎通確認にも使う修正が提案されています。修正PRは2本出ており、Issue #69へのコメントは0件です。経過は次のとおりです。

経過

Issue #69から修正PRまで

  1. 2026年2月19日Haiku 3.5が廃止、同日にIssue #69

    廃止当日に、validate_api_access()のハードコードが原因だと報告されました。mainの最新コミットはその8日前の2026年2月11日で、以降mainは動いていません。

  2. 2026年3月2日PR #76を提出

    self.modelを使う差し替えに、モデルをFindingsFilterへ渡す変更とテスト(約200行)を加えたものです。3月21日にクローズされました。

  3. 2026年4月14日PR #92を提出

    差し替えに加え、github_action_audit.pyからFindingsFilterへモデルを渡す1行も入っています。テスト151行を伴い、現在も開いています。

anthropics/claude-code-security-reviewにはタグ付きリリースが無く、ワークフローはuses: anthropics/claude-code-security-review@mainのようにmainを直接参照する構成が基本です。修正済みの過去バージョンにピン留めする逃げ道は使えません。

Issue #69が示す修正例は、疎通確認に使うモデルを、ハードコードされた文字列からインスタンスが保持する設定済みモデルに差し替える内容です。

def validate_api_access(self) -> Tuple[bool, str]:
    try:
        self.client.messages.create(
            model=self.model,  # 設定済みのモデルを使う
            max_tokens=10,
            messages=[{"role": "user", "content": "Hello"}],
            timeout=10
        )
        return True, ""
    except Exception as e:
        return False, f"API validation failed: {str(e)}"

現状でこのプロジェクトを使い続ける場合の選択肢は次のとおりです。

選択肢手間効果
何もしない手間なし効果脆弱性検知自体は継続。判断を要する指摘がPRコメントに混ざる
結果JSONを検査するステップを足す手間小効果無効化をジョブの失敗として拾える。フィルタ自体は戻らない
フォークしてvalidate_api_access()のモデルIDを書き換え、uses:を自分のフォークに向ける手間中効果フィルタが復旧するが、フォークをmainの更新に追従させる保守が要る
PR #92のマージを待つ手間なし効果対応が完了すれば自動で解消するが、時期は未定

いずれの対処も一長一短で、CI運用にどこまで手を入れられるかで選び方が変わります。誤検知が増えること自体は脆弱性の見落としには直結しません。ただしPRコメントのノイズが増えるとレビュー負荷が上がる点は、運用上の実害です。

Claude Code CLIの/security-reviewとの違い

同じ「security-review」という名前でも、Action版とCLIスラッシュコマンド版は実装が別物です。READMEはCLIコマンドを、Actionと同じ分析能力を開発環境へ統合したものと位置付けています。

くらべる

Action版とCLI版のフィルタ

PRごとにCI上で実行

Action版

Pythonの2段フィルタ(正規表現 + Claude判定)を持ち、Claude判定の入口でAPI疎通確認をします。廃止モデルの固定指定により、このバグの対象です。

手元で明示的に実行

CLI版

.claude/commands/security-review.mdのプロンプトで動きます。READMEは、このファイルを自分のプロジェクトへコピーして「false positive filtering instructions」を編集する方法を案内しています。

よくある質問

false-positive-filtering-instructionsを設定していれば影響を受けませんか

影響を受けます。false-positive-filtering-instructionsはClaudeによる判定の内容を変える入力で、カスタム指示が渡されるのは疎通確認に成功してClaudeのクライアントが残ったときだけです。確認が失敗すると、指示ごと使われなくなります。

まとめ

修正PR #92がマージされれば、結果JSONを検査するステップは要らなくなります。一方、claude-modelに現役のモデルIDを明示する作業はマージ後も残ります。修正が疎通確認に使うのは、そのモデルだからです。

この記事を共有:XはてブLinkedIn