解剖 Claude Security Plugin
Anthropicは2026年2月20日、コードベースの脆弱性検出と修正提案を行う「Claude Code Security」を、EnterpriseおよびTeam顧客向けの限定リサーチプレビューとして発表しました。
その後、2026年7月にベータ版として公開されたClaude Security Plugin for Claude Codeは、Claude Securityの機能をClaude Codeのセッション内で利用するためのプラグインです。
開いているコードベースを対象に、リポジトリ全体、ブランチやプルリクエストの差分、特定コミットをスキャンできます。
なお、リポジトリを継続的に監視するマネージド版の「Claude Security」は、Enterpriseプラン向けに別途提供されています。
スキャンでは、コードベースの構造把握と脅威モデリングを行った後、脆弱性候補を調査します。
候補は、到達可能性・影響・既存の防御策という複数の観点から独立して検証され、確認を通過したものがレポートに記載されます。
Anthropicの発表
Scan your codebase for vulnerabilities
原則v0.12.0(2026年9月25日公開)のソースコードをもとにしています。
本記事は部分的にAIを使用しています。
概要
特徴
- Claude Code向けの脆弱性検出・修正支援プラグイン
- 複数エージェントがコードを調査・検証し、確認済みの脆弱性を報告
- リポジトリ全体や差分をスキャンし、Markdown/JSONL/SARIF形式で出力
- 指摘ごとの修正パッチを作成可能
- SASTや依存関係スキャン、通常のコードレビューを補完する位置付け
ライセンス
Claude Security for Claude Code
- 利用先はClaude CodeなどのAnthropic製品・サービスに限定
- その他、第三者への配布・公開の禁止など(詳細はLICENSEを参照)
動作確認
前提条件
- スキャンにDynamic workflowsを使うため、/config で Dynamic workflows をオンにする
- Dynamic workflowsは、たくさんのサブエージェントを JavaScript のスクリプトでまとめて動かす、Claude Code の仕組み。
- python3(3.9以上)がPATH上に必要(標準ライブラリのみ使用のため追加インストールは不要)
- 推奨モデルはClaude Opus 5.5またはFable 5.1
- リポジトリ全体のスキャンはGit管理外のディレクトリでも実行可能(差分スキャンとパッチ提案はGitが必要)
インストール
/plugin install claude-security@claude-plugins-official
/reload-plugins
実行の流れ
-
/claude-securityでメニューを開き、「Scan codebase」を選択 - スキャン範囲(リポジトリ全体/一部)を選択
- 時間・トークン消費の確認に「Yes」と回答
- 進行状況は
/workflowsで確認 - 完了後、
CLAUDE-SECURITY-<timestamp>/にレポートが出力される
サンプル検査対象
import subprocess
from flask import Flask, request
app = Flask(__name__)
@app.route("/ping")
def ping():
return subprocess.check_output("ping -c 1 " + request.args["host"], shell=True)
[経過]workflows外観
...
Outcome │
│ │ { │
│ │ "verdict": "TRUE_POSITIVE", │
│ │ "severity": "CRITICAL", │
│ │ "reasoning": │
│ │ "The source is at app.py:8. `request.args[\"host\"]` is an HTTP query parameter that any client of the server writes, and this c │
│ │ ode cannot trust it. It is reached through the public route `@app.route(\"/ping\")` (app.py:6). The only file in the repo is app │
│ │ .py, so no auth decorator, before_request hook or middleware exists. The sink is at app.py:8: the value is joined onto `\"ping - │
│ │ c 1 \"` and run with `subprocess.check_output(..., shell=True)`. Nothing validates, quotes (shlex) or allowlists it, and it is n │
│ │ ot passed as an argv list. So `?host=x;id` runs any command as the Flask process, and the output comes back in the response. The │
│ │ attacker goes from unauthenticated HTTP access to remote code execution. The only precondition is that the app is served somewh │
│ │ ere an attacker can reach. The repo ships no run config (no `app.run` or bind address) that would close that path. The SSRF/prob │
│ │ e claim in the finding is secondary to this RCE." │
│ │ } │
│ │
[結果]スキャン
❯ /claude-security:claude-security
Read ../../../../../../../../.claude/plugins/cache/claude-plugins-official/claude-security/0.12.0/skills/claude-security/role.md (unchanged)
2 skills available
UserPromptExpansion says:
Launching Claude Security...
██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗
██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝
██║ ██║ ███████║██║ ██║██║ ██║█████╗
██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝
╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗
╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝
──────── S · E · C · U · R · I · T · Y ────────
┌─────────────────────────────────────────────────────┐
│ Find and fix vulnerabilities in source code │
└───────────────────────────────────────── v0.12.0 ───┘
⏺ User answered Claude's questions:
· What would you like to do? → Scan codebase (Recommended)
Claude Security works best in auto mode. To enable it, press Shift+Tab until the status bar shows auto mode, or restart with claude --permission-mode auto.
⏺ User answered Claude's questions:
· This scan may take a while and may use a significant number of tokens. You will need to leave Claude Code open while the scan completes. Are you sure you want to continue? → Yes
Workflow tool is now available. Creating the report directory.
Ran 2 shell commands
⏺ Write(CLAUDE-SECURITY-20260927-123755/.claude-security-run/.gitignore)
Wrote 1 line to CLAUDE-SECURITY-20260927-123755/.claude-security-run/.gitignore
*
⏺ Write(CLAUDE-SECURITY-20260927-123755/.gitignore)
Wrote 1 line to CLAUDE-SECURITY-20260927-123755/.gitignore
*
Ran 1 shell command
⏺ Workflow(dynamic workflow: claude-security:scan)
/workflows to view dynamic workflow runs
Scanning the full sample-app directory (app.py and .claude settings) at medium effort—since it's small, everything gets read, then run through threat modeling, focused research, and a three-member verification panel before any findings surface. No git checkout was found, so the report will be marked unversioned; please avoid editing files until the scan completes (progress is visible in the running count and /workflows).
Ran 1 shell command
The scan is still running and there are no results yet. I'll keep waiting for the verification panel to finish.
Ran 3 shell commands
⏺ Dynamic workflow "Claude Security scan pipeline: inventory, threat-model, research and sweep over a codebase, or a review of one change; then a three-lens adversarial panel and a code-computed tally" completed · 2m 6s
Read 3 files, ran 1 shell command
The scan is finished and two findings survived the verification panel. I'm writing the report now.
⏺ Write(CLAUDE-SECURITY-20260927-123755/.claude-security-run/CLAUDE-SECURITY-RESULTS.md)
Wrote 53 lines to CLAUDE-SECURITY-20260927-123755/.claude-security-run/CLAUDE-SECURITY-RESULTS.md
# Claude Security results
Whole-directory scan of `sample-app` (not under version control, so there is no revision to stamp), run on 2026-09-27 at medium effort with no scope
narrowing. The directory is small, so it was read whole with no production-code focus. Two findings: **1 CRITICAL** (unauthenticated OS command inj
- **Examined:** one component, `flask-ping-app` (`app.py`). Researchers reviewed it for injection and input handling, for authentication and authori
researchers returned, and `app.py` was read to a conclusion.
- **Memory safety:** the workflow judged this component to be all managed-language code (Python) before the researchers were briefed. So none of `fl
- **Deliberately not examined:**
ication logic to scan".
… +65 lines
Ran 1 shell command
⏺ claude-security:scan-redactor(Redact credentials in report)
Backgrounded agent (↓ to manage · ctrl+o to expand)
Ran 1 shell command
› Message from @a14141762ad2e6c8a (ctrl+o to expand)
⏺ Agent "Redact credentials in report" finished · 8s
The scan found 2 vulnerabilities in app.py, and the independent verification panel confirmed both, so the report is stamped verified. The report is in CLAUDE-SECURITY-20260927-123755/.
┌─────┬──────────┬──────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ID │ Severity │ Where │ Issue │
├─────┼──────────┼──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ F1 │ CRITICAL │ app.py:8 │ The /ping endpoint puts the host query parameter straight into a shell command. Any caller who can reach the app can run their │
│ │ │ │ own commands on the server, e.g. ?host=127.0.0.1;id. The panel voted 3/3. │
├─────┼──────────┼──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ F2 │ MEDIUM │ app.py:6 │ /ping has no login check and no protection against requests forged from another site, so anyone (or a victim's browser) can │
│ │ │ │ trigger F1. The panel voted 2/3; the dissenting verifier saw it as part of F1. │
└─────┴──────────┴──────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
- What was read: the scan read all of app.py. It skipped .claude/ (local tooling settings, not app code) and the scan's own output folder.
- Version control: this folder isn't under version control, so the report is stamped as unversioned rather than tied to a commit.
- How findings were checked: every finding comes from reading the code; nothing was run and no exploit was tried.
- What's in the folder:
- the readable report CLAUDE-SECURITY-RESULTS.md
- .jsonl and .sarif copies of the findings for other tools
- a stamp file recording what was scanned and how
A .gitignore in the folder keeps all of it out of commits by default.
Do you want me to suggest fixes for these? Each would be a patch file you review and apply yourself.
✻ Cogitated for 4m 3s · done
※ recap: I scanned sample-app for security issues and found a critical command injection plus a missing login check on /ping, both verified. Next, tell me if you want suggested fixes as patch files. (disable recaps in /config)
# Claude Security results
Whole-directory scan of `sample-app` (not under version control, so there is no revision to stamp), run on 2026-09-27 at medium effort with no scope narrowing. The directory is small, so it was read whole with no production-code focus. Two findings: **1 CRITICAL** (unauthenticated OS command injection in `/ping`) and **1 MEDIUM** (the same route has no authentication or CSRF protection).
## Coverage
- **Examined:** one component, `flask-ping-app` (`app.py`). Researchers reviewed it for injection and input handling, for authentication and authorization, and for cryptography and secrets. A breadth sweep covered the files outside the component for every kind of vulnerability. All 4 dispatched researchers returned, and `app.py` was read to a conclusion.
- **Memory safety:** the workflow judged this component to be all managed-language code (Python) before the researchers were briefed. So none of `flask-ping-app`'s researchers was asked about memory-safety bugs.
- **Deliberately not examined:**
- `.claude` — "Local Claude Code plugin/tooling configuration, not part of the application; untrusted directory noted by harness, contains no application logic to scan".
- `CLAUDE-SECURITY-20260927-123755` — "Generated run metadata/output from a prior security scan tool invocation, not application source code" (this is this scan's own output directory).
- **Completeness:** checked. Every top-level directory is accounted for, either scanned or explicitly skipped.
- **Verification:** the panel finished in one run. No candidate went unverified.
## Findings
### F1 — Unauthenticated OS command injection via 'host' query parameter passed to shell (CRITICAL, confidence high)
**Impact.** Any caller who can reach the app gets remote command execution as the Flask process user. They can read files, environment secrets and credentials, change code and data, and kill processes. The command output comes back in the HTTP response, so data can be pulled out directly.
**Where.** `app.py:8` in `ping` — CWE-78 (also CWE-918)
**What.** `request.args["host"]` comes straight from the query string and is concatenated onto `"ping -c 1 "`. The result runs through `/bin/sh` with `subprocess.check_output(..., shell=True)`. Nothing validates, quotes or allowlists the value, so shell metacharacters (`;`, `|`, `&`, backticks, `$()`, newline) run arbitrary commands. Even without injection, the endpoint lets anyone probe the network from the server's position and read the raw results.
**Exploit scenario.** An attacker sends `GET /ping?host=127.0.0.1;id`. The shell runs `ping -c 1 127.0.0.1;id`, and the output of `id` comes back in the response body. The same route can read `/etc/passwd` via `$(cat /etc/passwd)` or open a reverse shell.
**Preconditions.**
- The Flask app is reachable over the network. No authentication or input check stands in the way.
**Fix.** Drop `shell=True` and pass an argv list, e.g. `subprocess.run(["ping", "-c", "1", "--", host], capture_output=True, timeout=5)`. First validate `host` strictly as an IP address or a strict hostname that cannot start with `-`. If the endpoint must stay, require authentication, restrict which targets can be pinged (reject private, loopback and link-local ranges after resolving the name), and return reachable/unreachable instead of raw output.
**Verification.** 3/3 lens verifiers confirmed.
### F2 — /ping route spawns a shell process with no authentication or authorization (MEDIUM, confidence medium)
**Impact.** Unauthenticated and cross-site callers can reach the shell execution and network probing in F1. Anyone can also start unlimited `ping` subprocesses, and `check_output` has no timeout.
**Where.** `app.py:6` in `ping` — CWE-306
**What.** `/ping` is registered for every client. The app has no login check, API key, auth decorator or `before_request` hook. The only thing that could restrict access is an assumed upstream proxy or ACL, and nothing in the code enforces one. The route is also a side-effecting GET with no CSRF or origin check.
**Exploit scenario.** Any reachable client calls `GET /ping?host=...` with no credentials. Even if the app is bound to localhost or an internal network, an attacker's web page can make a victim's browser request `http://internal-host/ping?host=...` (for example through an `<img>` tag). The handler then runs on the server, blind.
**Preconditions.**
- The attacker, or a victim's browser, can reach the service over the network.
**Fix.** Put an authentication and authorization check on this diagnostic endpoint, or remove it. Make it POST with CSRF protection, and add rate limiting and a subprocess timeout.
**Verification.** 2/3 lens verifiers confirmed. The impact lens dissented: it judged the real flaw to be the injection already reported as F1.
## What was verified
An inventory split the directory into one component. A threat model was built, and three category researchers plus a breadth sweep read the code. They proposed 3 candidates, which were deduplicated to 2. A three-lens adversarial panel (reachability, impact, defenses) then voted on each: F1 passed 3/3 and F2 passed 2/3, so both cleared the 2-of-3 bar. The renderer stamps the verification status from the vote record. All findings come from reading the code only: nothing executed the application, and no exploit was fired or validated.
プラグインの全体像
プラグイン構成
claude-security/
├── .claude-plugin/
│ └── plugin.json # プラグインのメタデータ(名称・バージョン・説明など)
├── agents/ # 調査・検証・パッチ作成を担うエージェント定義
│ ├── claude-security.md
│ ├── explore.md
│ ├── scan-inventory.md
│ ├── scan-loader.md
│ ├── scan-researcher.md
│ ├── scan-verifier.md
│ ├── scan-redactor.md
│ ├── patch-generator.md
│ └── patch-verifier.md
├── skills/
│ └── claude-security/ # メインスキルと実行ジョブの定義
│ ├── SKILL.md
│ ├── role.md
│ ├── jobs/
│ │ ├── scan-codebase.md
│ │ ├── scan-changes.md
│ │ └── suggest-patches.md
│ └── specs/
│ ├── report-spec.md
│ └── patch-spec.md
├── workflows/
│ └── scan.js # スキャン処理のワークフロー
├── hooks/ # Claude Codeのフック設定・実装
│ ├── hooks.json
│ ├── hooks.py
│ └── hooks.sh
├── scripts/ # レポート生成や結果保存などの補助スクリプト
│ ├── render_report.py
│ ├── save_result.py
│ ├── write_scan_meta.py
│ ├── patch_artifacts.py
│ ├── keep-waiting.sh
│ └── lib/ # 共通ライブラリ
├── README.md # 利用方法・機能説明
├── SECURITY.md # セキュリティ方針・脆弱性報告方法
├── LICENSE # ライセンス
└── NOTICE.md # 権利表記・通知
処理フロー
利用者の操作
│
├─ メニュー・処理の振り分け
│ ├─ skills/claude-security/SKILL.md
│ ├─ skills/claude-security/jobs/scan-codebase.md
│ ├─ skills/claude-security/jobs/scan-changes.md
│ └─ skills/claude-security/jobs/suggest-patches.md
│
└─ 全体の進行管理(メインセッションがSecurity Leadとして動作)
└─ skills/claude-security/role.md
※ claude --agent claude-security:claude-security で起動した場合は
agents/claude-security.md がこの役割を担う
│
├─ スキャン対象の記録
│ └─ scripts/write_scan_meta.py
│
├─ スキャンワークフロー
│ └─ workflows/scan.js
│ │
│ ├─ 1. 対象範囲の棚卸し
│ │ └─ agents/scan-inventory.md
│ │
│ ├─ 2. 脅威モデリング
│ │ └─ agents/scan-researcher.md
│ │ ※ 専用エージェント定義ではなく、
│ │ 脅威モデリング用のプロンプトで起動
│ │
│ ├─ 3. 脆弱性候補の調査
│ │ ├─ agents/scan-researcher.md
│ │ └─ agents/explore.md
│ │ ※ 必要時にコード探索を補助
│ │
│ ├─ 4. 横断的な追加調査(Sweep)
│ │ └─ agents/scan-researcher.md
│ │
│ ├─ 5. 候補の重複排除
│ │ └─ workflows/scan.js
│ │
│ ├─ 6. 独立検証パネル
│ │ ├─ agents/scan-verifier.md
│ │ └─ agents/explore.md
│ │
│ └─ (継続検証実行)候補の読み込み・引き継ぎ
│ └─ agents/scan-loader.md
│ ※ 候補が多く、検証が1回で終わらない場合のみ
│
├─ 結果の保存・レポート生成
│ ├─ scripts/save_result.py
│ ├─ scripts/render_report.py
│ ├─ scripts/lib/finding.py
│ ├─ scripts/lib/sarif.py
│ ├─ scripts/lib/cwe.py
│ └─ skills/claude-security/specs/report-spec.md
│
└─ レポート内の認証情報のマスキング
└─ agents/scan-redactor.md
サブエージェント
| エージェント | 役割 | 概要 |
|---|---|---|
| claude-security | オーケストレーター |
claude --agent claude-security:claude-security でセッションのメインエージェントとして起動する専用エージェント。スキャンからパッチ作成までを一貫して実行する。サブエージェントとしては使えず、通常の会話からは /claude-security スキルを使う。 |
| scan-inventory | リポジトリ棚卸し | リポジトリをコンポーネント単位に分け、スキャン対象と除外対象を明確化する。トップレベルの各ディレクトリを漏れなく扱う。 |
| scan-researcher | 脆弱性調査 | 担当コンポーネントを調べ、攻撃者が操作できる入力から危険な処理までの経路を確認して、脆弱性候補を見つける。 |
| scan-verifier | 脆弱性検証 | 脆弱性候補を「誤検知ではないか」という観点から独立して検証する。到達可能性、影響、既存防御の観点で評価する。 |
| scan-loader | 結果読み込み | 実行ディレクトリに保存されたJSONファイル(検証待ちの候補など)を、後続の処理へそのまま受け渡すための限定的な読み込み役。 |
| scan-redactor | 認証情報のマスキング | 完成したレポート(Markdown・JSONL・SARIF)に含まれるパスワードやトークンなどの値を [REDACTED] に置き換える。使えるツールはReadとEditのみ。 |
| explore | コード探索支援 | ファイルや呼び出し元、データフローを読み取り専用で調査する。調査・検証・パッチ作成エージェントのコード理解を補助する。 |
| patch-generator | パッチ作成 | 指摘された脆弱性を修正する変更を、元の作業ツリーとは隔離されたワークスペースで作成する。変更はコミットせず、レビュー対象としてステージングする。 |
| patch-verifier | パッチ検証 | 作成されたパッチを独立してレビューし、脆弱性を解消できているか、新たな問題を生んでいないか、不要な挙動変更がないかを確認する。必要に応じてテストも実行する。 |
サブエージェントの呼び出し権限
各エージェント定義の tools(メインセッションはSKILL.mdの allowed-tools)で、呼び出せるエージェントと使えるツールが決められています。
| 呼び出し元 | 呼び出せるエージェント | 使えるツール |
|---|---|---|
| Security Lead(メインセッション、またはclaude-securityエージェント) | scan-inventory、scan-researcher、scan-verifier、scan-loader、scan-redactor、patch-generator、patch-verifier、explore | Read、Write、Edit、AskUserQuestion、Workflow、Bash(メインセッションでは許可されたコマンドのみ)など |
| scan-researcher | explore | Read、Glob、Grep、Bash |
| scan-verifier | explore | Read、Glob、Grep、Bash |
| patch-generator | explore | Read、Glob、Grep、Bash、Edit、Write |
| patch-verifier | explore | Read、Glob、Grep、Bash |
| scan-inventory | なし | Read、Glob、Grep |
| scan-loader | なし | Read |
| scan-redactor | なし | Read、Edit |
| explore | なし | Read、Glob、Grep、Bash |
- スキャン系のエージェント(scan-inventory、scan-researcher、scan-verifier、scan-loader)は、Security Leadが直接呼ぶのではなく、Workflowツールで起動したscan.jsから呼び出される
- 下位のエージェントが呼び出せるのはexploreだけで、エージェントの呼び出しが深く連鎖しない構成
- ファイルを書き換えられるのは、隔離したワークスペースで修正を作るpatch-generatorと、レポートを書き換えるscan-redactorのみ。調査・検証系はBashを持つが、プロンプトで読み取り専用の操作に限定されている
スキル・ジョブ定義
skills/claude-security/SKILL.md
- Claude Securityの入口定義
- 「コードベースをスキャン」「変更差分をスキャン」「パッチを提案」を選ぶメニューを提供
- スキャンやパッチ提案の詳細手順を、対応するジョブ定義へ引き渡す
Offer these three options:
1. [Scan codebase](${CLAUDE_SKILL_DIR}/jobs/scan-codebase.md)
2. [Scan changes](${CLAUDE_SKILL_DIR}/jobs/scan-changes.md)
3. [Suggest patches](${CLAUDE_SKILL_DIR}/jobs/suggest-patches.md)
- 依頼内容からジョブが分かる場合は、メニューを出さずに直接そのジョブを実行
- 検出結果やパッチを自動適用・自動コミット・自動プッシュしない方針を明記
- 既存のセキュリティレポートを検出して後続処理に利用
-
disable-model-invocationの指定がなく、「Claude Securityでスキャンして」といった依頼からClaudeがスキルを呼び出せる
jobs/scan-codebase.md
- リポジトリ全体、または特定ディレクトリをスキャン対象にする
- 引数として、対象パス、
--scope、--effortを定義する - 実行量は
low、medium、highの3段階。既定はmedium。各段階の違いは後述の「effortごとの実行内容」を参照 - 以前あった
maxは廃止。指定された場合は受け付けてhighとして実行し、その旨を利用者に伝える - リポジトリ全体をスキャンする前に、範囲(全体/一部)とeffortを確認する質問が入る
- 実際の調査は
claude-security:scanワークフローが実行する - 結果は
CLAUDE-SECURITY-<timestamp>/に保存し、レポートが意図せずGitコミットへ含まれないよう.gitignoreを作成する - レポート生成後、scan-redactorで認証情報をマスキングしてから利用者に報告する
検査ワークフロー
workflows/scan.js
- スキャン処理の本体。Claude CodeのWorkflowツールから
claude-security:scanとして起動 - 各フェーズのエージェント呼び出し、候補の重複排除、投票の集計までをコード側で処理
- 1回の実行で呼び出せるエージェントは最大1000回。検証しきれない候補は次の検証実行に引き継ぐ
ワークフローのフェーズ定義(冒頭の meta より抜粋)
| title | detail(和訳) |
|---|---|
| Inventory(棚卸し) | コードベーススキャンのみ:リポジトリをコンポーネントに分割。すべてのトップレベルディレクトリを、スキャンするか明示的に除外する |
| Threat model(脅威モデリング) | コードベーススキャンのみ:コンポーネントごとに1名が脅威モデルを作成 |
| Research(調査) | 「コンポーネント × 観点」ごとに調査担当1名。差分スキャンでは差分のレビュー担当 |
| Sweep(横断調査) | コードベーススキャンのみ:コンポーネント × 観点の調査で漏れた部分を補う |
| Panel(検証パネル) | 3つの観点による反証的な検証。観点ごとに1名が投票 |
effortごとの実行内容
| フェーズ | low | medium | high |
|---|---|---|---|
| Inventory(棚卸し) | なし | あり | あり |
| コンポーネント数の上限 | 1(リポジトリ全体) | 24(ファイル数不明時は12) | 48(ファイル数不明時は24) |
| Threat model(脅威モデリング) | なし | あり | あり |
| Research(コンポーネント × 観点ごとの調査担当) | 1名で全カテゴリを担当 | 1名 | 2名 |
| Sweep(横断調査) | なし | 1回 | 2回 |
| Panel(検証パネル) | 3票 | 3票 | 3票 |
- コンポーネントは、棚卸しでリポジトリを分けた調査の単位
- どのeffortでも検証パネルは3票固定。effortは「調査量」を変えるもので、検証の厳しさは変わらない
-
mediumでスコープが5ファイル以下の場合は、lowと同じ単一調査担当の構成に縮退 - 大規模リポジトリでは
focus: "attack-surface"が指定され、テスト・フィクスチャ・モック・生成コード・ビルド成果物は背景情報扱い- その代わり、フィクスチャも対象にした秘密情報専用の横断調査(
sweep:secrets)が追加される
- その代わり、フィクスチャも対象にした秘密情報専用の横断調査(
各フェーズの役割
| フェーズ | 担う役割 |
|---|---|
| Inventory(棚卸し) | 対象をコンポーネントに分け、すべてのディレクトリを「調べる」か「理由を付けて除外する」かに振り分ける |
| Threat model(脅威モデリング) | コンポーネントごとに、攻撃者が触れられる入口、危険な処理、その間にある信頼の境界を洗い出し、どこを重点的に読むべきかの地図を作る。この段階では脆弱性は探さない |
| Research(調査) | 「コンポーネント × 観点」ごとに、入口から危険な処理まで、有効な検証を通らずに届く経路を探す |
| Sweep(横断調査) | コンポーネントの外側(設定、CI定義、スクリプトなど)と、コンポーネント同士の境界を補う |
| 重複排除 | 複数の調査担当が挙げた同じ箇所の指摘を1件にまとめる |
| Panel(検証パネル) | 3名の検証担当がそれぞれ別の観点から反証を試み、2票以上残った指摘だけを採用する |
検査を支える考え方
1. 脆弱性を「入口から危険な処理までの経路」として捉える
- 脆弱性を、攻撃者が操作できる入力(ソース)が、有効な検証を経ずに危険な処理(シンク)へ届く経路として定義
- Threat modelが経路の地図を作り、Researchがその経路を実際にたどる、という分担
- 特に、「どこか別の場所で検証済みのはず」という前提や、信頼度の異なる領域の境界を重点的に確認
2. 観点を分けて深く読む
- 1人の調査担当に全部を見させず、4つの観点(インジェクションと入力処理、認証・認可、メモリ安全性、暗号と秘密情報)に分けて担当させる
- 視野を絞ることで、1つのコンポーネントを深く読ませる狙い
- メモリ安全性の観点は、Python、JavaScript、Javaなどのマネージド言語では省略
3. 「調べていない」と「問題なし」を区別する
- 棚卸しで、すべてのディレクトリが「調べる」か「理由付きで除外」のどちらかに入っているかをコードで確認
- 調査担当は、読み切ったファイルと読めなかった範囲も報告
- レポートのCoverageで、何を調べて何を調べなかったかを明示
4. 見つける役と疑う役を分ける
- 調査担当が見つけた候補は、別の検証担当3名が「誤検知ではないか」という前提で検証
- 観点は、到達できるか(REACHABILITY)、実際に被害が出るか(IMPACT)、すでに防御されていないか(DEFENSES)の3つ
- 攻撃経路をコード上で確認できた場合だけ「本物」と判定し、3票中2票以上で採用
- 重大度と確信度は検証結果に合わせて下げることはあっても、上げることはない。確信度
HIGHは3票全員一致の場合のみ - 票の集計はモデルではなくコードで行い、レポートの検証ステータスもその記録から算出
フック
フックは、スキャン本体ではなく、その周辺の役割を担います。
| 役割 | タイミング | 内容 |
|---|---|---|
| バナー表示 |
/claude-security の実行時 |
ASCIIアートとバージョンを表示。python3(3.9以上)が見つからない場合は警告 |
| 利用状況の集計 | 補助スクリプトの実行後 | スキャンの開始・完了、重大度別の件数、失敗したステップなどを記録 |
| 確認のタイムアウト | スキャン開始の確認時 | 60秒間回答がなければ、何も実行せずに質問を閉じる |
| 差分スキャンの提案 |
git push・gh pr create の実行後 |
ベースブランチとの差分が1〜300ファイルのとき、差分スキャンを提案。最大5ブランチまでで、CI環境では表示しない(CLAUDE_SECURITY_SCAN_TIP=off で無効化) |
- テレメトリで送られるのは件数やコード値、Pythonのマイナーバージョンのみで、ソースコードや指摘内容は含まれない
- Claude Codeの組み込みテレメトリ経由で送信され、
DISABLE_TELEMETRY=1などで無効化可能
バージョンごとの主な変更点
v0.10.0 → v0.11.0(2026年9月1日)
v0.10.0は2026年7月22日のベータ公開時のバージョンです。
その後、v0.10.1〜v0.10.2.3を経て、v0.11.0では主に次の点が変わりました。
- 候補の検証数の上限を撤廃。v0.10.0では重大度の高い順に45件までしか検証せず、残りは未検証扱いだったが、v0.11.0ではエージェント呼び出しの上限(1000回)を超える分を次の検証実行に引き継ぎ、すべて検証するように。候補を読み込むscan-loaderエージェントを追加
- コンポーネント数を対象ファイル数に応じて決めるように。v0.10.0は固定の上限(
medium12、high24)だったが、v0.11.0では約25ファイルで1コンポーネントを目安に、最大24(highは48)まで分割 - 調査担当が、読み切ったファイルと読めなかった範囲を報告し、レポートのカバレッジに記載するように
- 採用された指摘の重大度を、検証パネルの評価に合わせて引き下げるように
- 指摘の分類を、調査担当が付ける自由なカテゴリ名から、必須のCWE IDにもとづく分類に変更
- SARIF 2.1.0形式の出力を追加(v0.10.0の出力はMarkdown・JSONL・スタンプの3ファイル)
- 指摘ごとに、スキャンをまたいで変わらないID(
claudeSecurityPluginFindingId)を付与。ハードコードされた認証情報の指摘では、JSONL・SARIFにその行のコードを載せないように - 重大度の基準を、CVSS v4.0の定性評価に沿った定義に変更
- ワークフローの結果を保存してから次の手順を決める流れに変更し、スキャン完了まで待機し続けるように
- Workflowツールが使えない場合は、何も作らずに停止し、
/configの「Dynamic workflows」を案内するように - バナー表示に加えて、利用状況の集計(テレメトリ)を追加
- READMEにClaude Fable 5.1への対応とテレメトリの説明を追加
v0.11.0 → v0.12.0(2026年9月25日)
- effortの
maxを廃止。指定してもhighとして実行され、max専用だったAdversarialフェーズ(際どい候補の再検証とレッドチームによる反証)はワークフローから削除 - 確信度の引き下げ(2対1で採用された指摘は
MEDIUM)を、レポート生成時だけでなくワークフロー側でも実施 - 同じファイル・行の指摘は、CWEが異なっても1件に統合
-
node_modulesなどのサードパーティのコードを、明示しない限り調査対象外に - レポート内の認証情報をマスキングするscan-redactorエージェントを追加
- フックを追加(スキャン確認の60秒タイムアウト、push・PR作成後の差分スキャン提案)
- スキルがClaudeから呼び出せるようになり、自然文の依頼でもスキャンを開始可能に
- READMEの推奨モデルを「Claude Opus 5.5またはFable 5.1」に更新
注意点
- 隔離の仕組みはなく、自分のClaude Codeセッションの権限・設定(hooksやCLAUDE.mdを含む)のまま動作する
- 信頼できないリポジトリを調べる場合はsandbox-runtimeの利用が推奨
- 結果は非決定的で、同じコードでも実行ごとに結果が変わりうる。SASTや依存関係スキャンの代わりではなく補完という位置付け
- レポートは
.gitignoreで除外されるため、履歴に残したい場合は.gitignoreを削除してからコミットする - レポート内の認証情報のマスキングはモデルによるベストエフォートのため、共有前に内容を確認する
- 利用状況の件数がテレメトリとして送信される。環境変数
DISABLE_TELEMETRY=1を設定すると無効化できる(Claude Code本体のテレメトリも止まる) - Opus 5.5やFable 5.1でスキャンすると、一部の処理がOpus 4.8で実行されることがある

