
SecureAI-Scan v0.6.0
SecureAI-Scanは、AI搭載アプリに特有のセキュリティ問題(プロンプトインジェクション、MCPツールの悪用、RAGデータポイズニング、エージェント信頼性違反など)を検出するために、TypeScriptおよびJavaScriptコードベースをスキャンするCLIツールです。
SecureAI-Scan
TypeScript、JavaScript、Python を対象に LLM・MCP・Agent Skill・RAG のリスクをスキャンするオフライン CLI — import 解決済みのデータフロー証跡を提供し、デフォルトの誤検知ゼロ、OWASP LLM/ASI/MCP Top 10 に対応。
この分野のほとんどのスキャナはキーワードをパターンマッチして検出結果と呼んでいます。SecureAI-Scan は、実際の import 解決済みコードを通して、ソース → フロー → シンクの経路を追跡します。デフォルトのスキャンでは、証明できたものだけが表示されます。アカウント不要、クラウドへのアップロードなし、データがマシンの外に出ることはありません。
公式の OWASP Top 10 for LLM Applications 2026、Top 10 for Agentic Applications (2026)、および MCP Top 10 をリリース初週からカバーしています。
30秒で始める```bash
npx --yes [email protected] scan .
アカウント不要、クラウドアップロード不要、Pythonインタープリタ不要、設定不要。TypeScript、JavaScript、Python、MCP設定、Agent Skillバンドルは自動的に検出されます。
**測定済み `0.9.0` リリース候補:** 136/136テスト · 88.08%ステートメントカバレッジ · 9つの公開リポジトリにわたる12,676ファイル · レビュー済みベースラインに対する新規デフォルト層フィンガープリント0件。[エビデンス](https://github.com/akanthed/secureai-scan/blob/main/docs/benchmarks/v0.9.0.json) · [方法論と限界](https://github.com/akanthed/secureai-scan/blob/main/docs/ReleaseAssurance.md)```
▌ HIGH AI001 Prompt injection via user input
PROVEN LLM01:2026 Prompt Injection
source src/chat.ts:8 request data `req.body.input`
flow src/chat.ts:13 passed as `systemPrompt`
sink src/chat.ts:10 openai.chat.completions.create — system role (OpenAI)
fix Keep system prompts static; pass user input as a user-role message.
これはあなた向けですか? SecureAI-Scanは、LLM、MCP、RAG/エージェントのリスク(プロンプトインジェクション、ツールポイズニング、安全でない出力処理、ベクターストアのアクセス制御、エージェントスキルのポイズニング)に意図的に範囲を絞っています。これは一般的なSASTやシークレットスキャナーではなく、そうであろうともしていません。LLM型のペイロードを持たない既知の悪意あるパッケージ(例:メールAPI呼び出しにハードコードされた外部送信アドレス)は、パターンルールではなくオフラインのアドバイザリリスト(DEP003)で検出されます。コードベースがLLM、MCPサーバー、ベクターストアと通信する場合、またはAgent Skillsを同梱する場合、これはあなたのために作られています。
新機能: LiteLLM Proxy(config.yaml)の静的設定スキャン — ハードコードされたシークレット、平文のプロバイダーエンドポイント、欠落したガードレール。 ルール(LLC001–LLC003)を参照してください。
目次
- このスキャナーが他と違う理由
- 他との比較
- 30秒で始める
- 動作を確認する
- コマンド
- GitHub Action
- Pre-commitフック
- ルール
- アーキテクチャ
- MCPサーバー(Claudeから使用)
- Claudeスキル
- 信頼性とリリース保証
- 精度契約
- テストとベンチマーク
- ロードマップ
- コントリビューション
このスキャナーが他と違う理由
- ノイズではなくエビデンスの階層。 すべての検出結果は
proven(追跡されたデータフローまたは解析済み設定の事実)、likely(解決されたシンク、ヒューリスティック1ホップ)、またはheuristicです。デフォルトのスキャンではproven + likelyのみが表示されます。 ヒューリスティックは--paranoidでオプトインします。 - インポート解決型の検出。 呼び出しが「LLM呼び出し」と見なされるのは、実際のSDKインポート(
openai、@anthropic-ai/sdk、ai、@google/genai、LangChain、Bedrockなど)に解決される場合のみです。Google Mapsクライアントが再びLLMとしてフラグ付けされることはありません。 - 精度でゲートされ、実際のリポジトリに対してベンチマーク済み。 テストスイートは、すべての脆弱なフィクスチャが発火することおよびすべての安全なフィクスチャがクリーンなままであることを検証します — 安全なコーパスでの誤検知はビルドを失敗させます。さらに、
npm run regressionは実際の公開リポジトリ(OpenAI/Anthropic/Vercel AI SDK、公式MCPサーバー、LlamaIndex)をコミット済みの手動レビュー済みベースラインに対してスキャンし、新しいproven/likelyの検出結果があれば失敗します。実際の前後数値についてはテストとベンチマークを、その背景にあるストーリー(ラベル付き悪意あるスキルコーパスでの6/6検出率、そしてなぜllama_indexを「脆弱」と呼んでいないのか — 正直なライブラリレベルの検出結果について)は実際のリポジトリのスキャンで見つかったものを参照してください。 ディスカッションの記事 → - GitHubコードスキャン用のSARIF。
--output report.sarifにより、プルリクエスト上とSecurityタブに検出結果がインライン表示されます。 - AI-BOM。
secureai-scan bom .は、SDK、モデルID、ベクターストア、エージェントフレームワーク、MCPサーバーの構文ベースのインベントリを構築し、OWASP LLM Top 10 / EU AI Actのドキュメント要件にマッピングします。 - MCP設定スキャン。
.mcp.json、claude_desktop_config.json、.cursor/mcp.jsonを解析: ピン留めされていないnpx -yサーバー、インラインシークレット、平文のHTTPトランスポート。 - MCPツールポイズニング検出。 WhatsApp MCPのラグプルとpostmark-mcpのバックドアの背後にあるパターン — 不可視のUnicode、エージェント向けインジェクションフレーズ、ツール名/説明におけるツール間シャドウイング — を、サーバーを実行する前に静的に検出します。
- MCPコマンドインジェクション検出。 リクエストデータから構築されたMCP stdioトランスポートの
command/argsをフラグ付けします — 2026年のMCP STDIO RCE開示の背後にあるパターンです。 - Agent Skillポイズニング検出。 同じ不可視Unicode、インジェクションフレーズ、シャドウイングチェックを
SKILL.mdファイルに適用します — Agent Skillsはコンテキスト全体に読み込まれるため、ポイズニングされたスキルは別名のポイズニングされたツール説明です。 - 回避耐性のあるスキルスキャン。 スキルバンドルは
SKILL.mdだけでなくディレクトリとしてスキャンされ、すべてのコンテンツチェックはテキストの難読化解除バリアントに対して実行されます。これは、Cloak and Detonate(arXiv:2607.02357)で調査された9つのスキャナーの90%以上を回避した公開技術 — ホモグリフ、ゼロ幅分割、.git/やbuild/にステージングされたペイロード、*.test.tsファイルに隠された外部送信 — を対象としています。回避耐性を参照してください。 - 既知の脆弱性および既知の悪意あるパッケージのアドバイザリ、バージョン対応。 すべての依存関係とMCPが起動するすべてのパッケージを、バンドルされたアドバイザリスナップショット — 文書化された実地バックドアの手動キュレーションリストに加え、LLM/MCP/RAGパッケージのウォッチリストに対するHIGH/CRITICAL OSVアドバイザリ(
scripts/sync-advisories.jsによって再生成)— に対してチェックします。フラグ不要で毎回のスキャンでオフライン実行されます。CVEは、ピン留めされたバージョンが影響範囲内に証明可能な場合にのみ発火します。文書化された悪意あるパッケージは、範囲が曖昧な場合でも発火します。バックドアのインストールは回復不能だからです。 - ローカルファースト。 何もマシンの外に出ません。
他との比較
SecureAI-Scanは、一般的なSASTツールやコンテナ/IaCスキャナーの代替ではありません — それらの代わりではなく、併用してください。これはLLM/MCP/RAGの攻撃面に特化して構築されており、フラットなキーワード検出結果よりもデータフローのエビデンスを重視します。
| SecureAI-Scan | Semgrep(OSSルール) | Trivy | GitHub Advanced Security | |
|---|---|---|---|---|
| プロンプトインジェクション(ソース→シンク追跡) | ✅ インポート解決型データフロー | ⚠️ パターンルールのみ、コミュニティ保守 | ❌ | ⚠️ CodeQLは可能だが、AI固有のルールセットなし |
| MCPツールポイズニング / 設定リスク | ✅ MCP007–010、設定スキャナー | ❌ | ❌ | ❌ |
Agent Skillポイズニング(SKILL.md) | ✅ 回避耐性、バンドル対応 | ❌ | ❌ | ❌ |
| RAG / ベクターストアの設定ミス | ✅ VEC001–004 | ❌ | ❌ | ❌ |
| 既知の悪意あるAIパッケージのアドバイザリ | ✅ DEP003、オフライン、バージョン対応 | ❌ | ⚠️ 一般的なCVEフィード、AI固有ではない | ⚠️ Dependabot、一般的なCVEフィード |
| 一般的なSAST(SQLi、XSS、パストラバーサル) | ❌ 設計上スコープ外 | ✅ | ❌ | ✅ |
| コンテナ / IaCスキャン | ❌ | ❌ | ✅ | ⚠️ CodeQL/Actions経由 |
| エビデンス階層(proven/likely/heuristic) | ✅ | ❌ 検出結果はフラット | ❌ | ⚠️ CodeQLには一部あるが、AIチューニングなし |
| SARIF出力(GitHubコードスキャン) | ✅ | ✅ | ✅ | ネイティブ |
| オフライン実行、アカウント不要 | ✅ | ✅(OSSルール) | ✅ | ❌ GitHubが必要 |
すでにSemgrepやGHASを実行している場合は、それらを維持してください — それらがまったくモデル化していないリスク面のためにSecureAI-Scanを追加してください。
まず質問したいですか? 無料の SecureAI-Scan AI Security Advisor on ChatGPT をお試しください。
GitHubやTwitterで見つけたMCPサーバーを実行しようとしていますか? まずそのツール説明を MCP X-Ray に貼り付けてください — ブラウザ内で、インストール不要で、隠されたUnicode、注入された指示、既知の悪意あるパッケージをチェックします。
動作を確認する
secureai-scan scan .をエンドツーエンドで、実際の(小さく、意図的に脆弱な)ファイルに対する実際の出力 — ソース:
スキャナーがエンドツーエンドで追跡する攻撃の形状:
| MCPツールポイズニングのデータフロー | RAGコンテキストインジェクションのデータフロー |
|---|---|
![]() | ![]() |
コマンド
95%のケースで必要なもの:```bash secureai-scan scan .
必要なものはすべて、必要なときにそこにあります。`secureai-scan scan . --help` を実行すると、これらすべてがターミナルに同じグループ分けで表示されます。
**日常的な使用**
| フラグ | 機能 |
|------|---------------|
| *(なし)* | `proven` + `likely` の検出結果 — デフォルト。フラグは不要 |
| `--paranoid` | `heuristic` レベルの検出結果も含める |
| `-s, --severity <level>` | `low`\|`medium`\|`high`\|`critical` 以上の重大度の検出結果のみ表示 |
| `--output <file>` | 完全なレポートを書き出す — `.sarif`(GitHub code scanning)、`.json`、`.md`、または `.html` |
**実行するルールの範囲**
| フラグ | 機能 |
|------|---------------|
| `-r, --rules <list>` | 指定したルール ID のみ実行(例: `AI001,MCP007`) |
| `--only-ai` / `--only-mcp` / `--only-vec` / `--only-skl` | 1 つのルールカテゴリのみ実行 |
| `--check-dependencies` | `package.json`/`requirements.txt` を npm/PyPI レジストリと照合し、タイポや幻覚パッケージ(`DEP001`/`DEP002`)をチェック。`-r` でこれらのルールを直接選択すると自動的に有効化されるため、両方を渡すのを覚えておく必要はありません。`DEP003`(既知の悪意のあるパッケージ)には不要で、これは常にオフラインで実行されます |
**CI / ワークフロー**
| フラグ | 機能 |
|------|---------------|
| `--fail-on <severity>` | この重大度以上の検出結果がある場合、終了コード `1` を返す |
| `--baseline <file>` | 保存済みのベースラインに対して新規・変更された問題のみを追跡 |
| `--policy <file>` | `.secureai-policy.json` からしきい値、スキップするパス、ブロックするルールを読み込む(存在すれば自動検出 — `secureai-scan init` で作成されます) |
**上級者向け**
| フラグ | 機能 |
|------|---------------|
| `--min-confidence <0-1>` | `--paranoid` より細かい制御: 正確な信頼度スコア(`0.9` proven / `0.65` likely / `0.35` heuristic)未満の検出結果を非表示にする |
| `--limit <n>` | ターミナルに表示するルールグループの最大数(デフォルト `10`)— 完全な詳細は常に `--output` に出力されます |
| `--debug` | スキャンしたすべてのファイルと実行されたルールを出力 |
**インストール前にスキャン — クローンも設定も不要:**```bash
secureai-scan skill anthropics/skills # a GitHub "owner/repo" shorthand
secureai-scan skill https://github.com/… # or a full git URL
secureai-scan skill ./some/local/skill-dir # or a local path
secureai-scan mcp some-mcp-server-package # a bare npm package name
secureai-scan mcp owner/mcp-server-repo # or git, same as `skill`
skill と mcp はターゲットを取得してスキャンし、その後取得したコピーを削除します(代わりに検査する場合は --keep を使用)。取得したものが実行されることは決してありません。npm ターゲットは npm pack でダウンロードされます — ターボールのみで、install もライフサイクルスクリプトもありません — また、git ターゲットは単純な git clone --depth 1 です。これが最も重要な瞬間です。スキルが ~/.claude/skills/ に配置される前、またはサーバーが .mcp.json に配置される前であって、後ではありません。
その他のコマンド:```bash secureai-scan bom . --output AI_BOM.md # AI Bill of Materials secureai-scan explain AI001 # why + exploit + fix example, for any rule secureai-scan threat-model . # THREAT_MODEL.md with the OWASP coverage matrix — example: docs/examples/THREAT_MODEL.example.md secureai-scan init # policy file + CI workflow, one-time setup
コード内のレビュー済みの検出結果を抑制する:```ts
// secureai-ignore AI001: reviewed, input sanitized via allowlist
GitHub Action```yaml
name: SecureAI-Scan on: [pull_request] permissions: contents: read security-events: write jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: akanthed/[email protected] with: scanner-version: 0.10.0 fail-on: high
Findingsは、PR上のインライン注釈とリポジトリのSecurityタブに表示されます。(`secureai-scan init`は、CLIを直接使用して同等のワークフローを生成します。)
スキャンがクリーンですか? 自分のREADMEにバッジを追加してください:```md
[](https://github.com/akanthed/SecureAI-Scan)
Pre-commit フック
検出結果をプッシュする前にキャッチしたいですか?GitHub Action の代わりに、またはそれと併用して、このリポジトリを pre-commit フックソースとして追加してください:```yaml repos:
- repo: https://github.com/akanthed/SecureAI-Scan
rev: v0.10.0
hooks:
- id: secureai-scan
The hook scans the whole project on every commit (not just changed files — a dataflow trace into file A can depend on file B, which a partial scan would miss) and blocks the commit on `high`+ severity findings by default. Override the threshold in your own config:```yaml
- id: secureai-scan
args: ["--fail-on", "critical"]
ルール
42のルール。公式のOWASP Top 10 for LLM Applications(2026年)にマッピングされ、さらに該当する場合はOWASP Top 10 for Agentic Applications(2026年、ASI)、OWASP MCP Top 10(2025年)、およびEU AI Actの条項にも対応しています。バージョン管理された2026年のカバレッジと制限を参照してください。threat-modelは、スキャンされた各プロジェクトのマトリクスをレンダリングします。
| ルール | 検証内容 | OWASP |
|---|---|---|
| AI001 | ユーザー入力がシステム/開発者プロンプトに流れ込む(ソース→シンクを追跡。関数/ファイル境界をまたぐ場合も含む) | LLM01 |
| AI002 | プロンプトの内容またはシークレットがログに書き込まれる(LLM SDKを使用するファイル内) | LLM02 |
| AI003 | 認証チェックなしでリクエストハンドラ内でLLM呼び出しが行われる | LLM06 |
| AI004 | ユーザー/セッションオブジェクト全体がプロンプトにシリアライズされる(フィールドの選択はフラグ対象外) | LLM02 |
| AI005 | LLM出力がeval/exec/SQL/HTMLシンクに到達する | LLM10 |
| AI006 | 影響度の高いツール(削除、支払い、デプロイなど)が承認ゲートなしで公開されている | LLM03 |
| AI007 | 取得したRAGコンテンツが特権プロンプトに補間される | LLM01 |
| AI008 | シークレットがシステムプロンプトのテキストに埋め込まれている | LLM08 |
| AI009 | 無制限のユーザー入力 / トークン制限の欠如 | LLM06 |
| AI010 | 取得した外部コンテンツがプロンプトに流れ込む | LLM01 |
| AI011 | エージェント出力が後続の呼び出しでシステムロールに昇格される | LLM03 |
| AI012 | LLM出力がスキーマ検証なしでパースされる | LLM10 |
| MCP001 | MCPツールのメタデータが検証なしでシステムプロンプトに到達する | LLM01 |
| MCP002 | MCPサーバーのURLがユーザー入力から構築される | LLM04 |
| MCP003 | MCPツールの結果がシステムロールに昇格される | LLM10 |
| MCP004 | MCPサーバーがピン留めされていないnpx -yパッケージとして起動される | LLM04 |
| MCP005 | シークレットがコミットされたMCP設定にインラインで含まれている | LLM02 |
| MCP006 | MCPサーバーが平文HTTP上で動作している | LLM04 |
| MCP007 | 不可視/双方向UnicodeがMCPツール名または説明に隠されている | LLM01 · MCP03 |
| MCP008 | エージェント誘導型のインジェクション文言がMCPツールの説明に含まれている | LLM01 · MCP03 |
| MCP009 | 別のツールへの呼び出しを誘導するツール説明(シャドーイング) | LLM01 · MCP03 |
| MCP010 | MCP stdioサーバーのコマンド/引数がユーザー入力から構築される(RCE) | LLM04 · MCP05 |
| SKL001 | 不可視/双方向UnicodeがAgent Skillバンドル内のどこかに存在する | LLM01 |
| SKL002 | エージェント誘導型のインジェクション文言がスキルの説明または本文に含まれる(難読化を通過してマッチング) | LLM01 |
| SKL003 | スキルのコンテンツが別のスキルの使用タイミング/方法を誘導する(シャドーイング) | LLM01 |
| SKL004 | ステージング/自己展開型ペイロード:不透明なブロブ+それをデコードして実行する指示 | LLM04 · MCP04 |
| SKL005 | バンドル付属ファイル内での資格情報の読み取り+ハードコードされた外部エグレス | LLM02 · MCP04 |
| SKL006 | Claude Codeの動的コンテキストインジェクション構文(!`cmd`/```!)によるロード時コマンド実行。ツール権限ゲートの前 | LLM04 · MCP05 |
| SKL007 | スキルのallowed-toolsフロントマターにおけるスコープなしのBash許可 | LLM03 |
| SKL008 | スキルが外部URLから指示を取得し、エージェントにそれに従うよう指示する(「サーカス・オブ・スキルズ」) | LLM04 |
| SKL009 | スキルが別のコンテキストファイル(MEMORY.md/SOUL.md/AGENTS.md/CLAUDE.md)に書き込むことでバックドアを永続化する | LLM05 |
| SKL010 | スキルのフロントマターまたはバンドルされた設定ファイル内の安全でないYAML/JSONデシリアライゼーションタグ | LLM04 |
| VEC001 | テナント/ユーザーフィルタなしのベクター検索 | LLM09 |
| VEC002 | 無制限またはユーザー制御の検索制限 | LLM06 |
| VEC003 | ユーザーコンテンツが共有ベクターストアに取り込まれる | LLM05 |
| VEC004 | テナント/名前空間タグ付けなしの取り込み | LLM09 |
| DEP001 | レジストリに見つからない依存関係名(オプトインの--check-dependencies) | LLM04 |
| DEP002 | 人気パッケージから1文字違いの依存関係名(オプトイン) | LLM04 |
| DEP003 | 悪意のあるリリースまたは重大なCVEが文書化された依存関係 — スキャンごとにオフラインでチェックされ、バージョン範囲を認識(postmark-mcp、mcp-remote CVE-2025-6514など) | LLM04 · MCP04 |
| LLC001 | LiteLLMプロキシのconfig.yamlにハードコードされたシークレット | LLM02 |
| LLC002 | LiteLLMプロキシのapi_baseが平文HTTPで到達可能 | LLM04 |
| LLC003 | LiteLLMプロキシ設定にguardrails:セクションがない(ヒューリスティック、--paranoidのみ) | LLM03 |
secureai-scan explain <RULE_ID>は、任意のルールについてエクスプロイトのウォークスルーと、修正前/修正後のコード例を示します。
アーキテクチャ
3つの独立したスキャンサーフェスが、1つのマージ・重複排除された検出結果リストに統合されます。``` ┌─────────────────────┐ *.ts / *.js ───▶ │ ts-morph AST rules │───┐ │ (import-resolved │ │ │ sinks + dataflow) │ │ └─────────────────────┘ │ │ ┌─────────────────────┐ │ ┌──────────────┐ ┌─────────────────┐ *.py ───▶ │ tree-sitter AST + │───┼───▶ │ scan.ts │───▶ │ evidence filter │ │ local taint flow │ │ │ merge/dedupe│ │ → confidence │ └─────────────────────┘ │ │ + suppress │ │ → severity │ │ │ (// secure- │ │ → baseline diff │ .mcp.json, ┌─────────────────────┐ │ │ ai-ignore) │ │ → report │ SKILL.md ───▶ │ Config/bundle scan │──┘ └──────────────┘ └─────────────────┘ │ (off-disk, evasion- │ │ │ resistant) │ ▼ └─────────────────────┘ terminal · sarif · json · md · html
package.json, requirements.txt ─▶ dependency-guard.ts (advisories.ts, offline, version-aware)
Every AST rule only calls a function an "LLM call" if it resolves through real imports to a known SDK — never by name-matching alone. See [`docs/Architecture.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Architecture.md) for the full breakdown of each surface, and [`docs/DetectionEngine.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/DetectionEngine.md) for how the evidence-tier contract works.
## MCPサーバー(Claudeから使用)
このパッケージには、`scan_repository`、`explain_rule`、`generate_bom`、`scan_untrusted_target`(ClaudeがスキルやMCPサーバーのインストールを推奨する前に、それを取得してスキャンする — `skill`/`mcp` CLIコマンドと同じ、実行せずに取得する動作)を公開するMCPサーバーが同梱されています:```json
{
"mcpServers": {
"secureai-scan": {
"command": "node",
"args": ["/path/to/secureai-scan/mcp-server/index.js"]
}
}
}
Claude Skill
Claude Code / Claude.aiユーザー向けに、skills/secureai-scan/SKILL.mdは、Claudeにスキャンを実行するタイミング(AI/LLMコードのレビュー、またはMCPサーバー/Agent Skillをインストールする前の確認)と結果の読み方を教えます。上記のMCPサーバーとは異なり、別途プロセスを実行する必要はありません。使用するには、skills/secureai-scan/ディレクトリを.claude/skills/にコピーしてください。
回避耐性
2026年7月、Agent Skillスキャナーが簡単にバイパスされることを示す2件の公開研究が発表されました。
Cloak and Detonate(Ji、Xu、Li、Gao、Wei、Wang & Cheung — HKUST、arXiv:2607.02357)は、9つの公開済みスキルスキャナーに対して、1,613件の実在する悪意のあるスキルを用いた敵対的研究を実施しました。ペイロードを完全に保持する2つの攻撃ファミリーがあります:
- Reify — ルールが一致するバイトを書き換え、エージェントは実行時に元の内容を再組み立てします。ほとんどの静的スキャナーで80%超のバイパス率。
- Disperse / SFSパッキング — ペイロードをルールのスコープ外に移動:スキャナーがスキップするディレクトリ、名前を変更した拡張子の背後、または
.git/にアーカイブ・エンコードし、初回実行時に再構築する無害なカバー用SKILL.mdを配置。テストしたすべてのスキャナーで90%以上のバイパス率、ほとんどのスキャナーで96%以上。あるハイブリッドスキャナーの検出率は約99%から約10%に低下しました。
別途、Gecko Security(VentureBeat)は、*.test.tsファイルにペイロードを仕込みました。公開されているすべてのスキャナーがこれをスキップしましたが、JestとVitestは次のnpm testでこれらのファイルを自動検出して実行します。
SecureAI-Scanは、公開された各手法を処理します。以下の各行には、test/deobfuscate.test.jsに対応するテスト、またはtest-fixtures/vulnerable/skills/に対応するフィクスチャがあります:
| 手法(論文での名称) | 例 | 処理方法 |
|---|---|---|
| ゼロ幅文字挿入 | Ignore all previous… | 不可視文字を除去したバリアントに対してテキストを照合 |
| 紛らわしいグリフ | сurl(キリル文字のс) | 照合前にホモグリフをASCIIに折りたたみ |
| 大文字小文字の反転 | EvAl | すべてのコンテンツパターンは大文字小文字を区別しない |
| 変数の連結 | 'cu' + 'rl' | 照合前に隣接する文字列リテラルを結合 |
| 行ごとの分割 | ev⏎al(x) | 単語内の改行を追加のバリアントとして結合 |
| ファイルごとの抽出 | 参照されるヘルパー内のペイロード | SKILL.mdだけでなく、バンドル全体をスキャン |
| ディレクトリの移動 | scripts/x.sh → build/x.sh | バンドル内にディレクトリスキップリストなし |
| 拡張子の変更 | x.sh → x.txt | ファイルは拡張子ではなくコンテンツでスキャン |
| サイズのパディング | スキャナーの上限を超えてパディング | 過大なファイルは先頭から読み取り、スキップしない |
| SFSパッキング | .git/skillpack.dat内のペイロード | バンドルの.git/配下にある非gitファイルはproven(SKL004) |
| テストファイルのステージング | *.test.ts内のペイロード | バンドルスキャンは意図的にテストパスを降格しない(SKL005) |
これは精度契約を弱めない
難読化解除は通常、精度リスクです — 一致が増え、ノイズも増えます。ここではロジックが逆転しています:難読化解除後にのみ現れる一致は、降格ではなくprovenに昇格します。 通常のドキュメントには「ignore previous instructions」内にゼロ幅ジョイナーや、curl内にキリル文字のсは含まれません。隠蔽自体が、意図の積極的な証拠となります。
比較は生の一致の集合に対して行われ、単に「生のテキストが一致したかどうか」ではありません — そうでなければ、攻撃者は無害なフレーズを1つ平文で残すことでシグナルを隠蔽できてしまいます。
2つの新しいバンドルルールは、キーワード単独ではなく、複合条件でのみ発動します:
- SKL004は、不透明なブロブと、そのブロブを名前で参照する展開ディレクティブの両方を必要とします — 無関係なバイナリアセットの隣に
tar -xに言及するREADMEでは不十分です。実際のアーカイブ(gzip/zip/png/pdf/wasm — 拡張子ではなくマジックバイトでチェック)は、どのように圧縮されていても、そもそも「不透明」ではありません。 - SKL005は、具体的な資格情報シグナル(「token」という単語ではなく
~/.aws/credentialsのようなパス、またはos.environ["API_KEY"]ではなくos.environ.items()のような一括環境変数列挙)と、ハードコードされた非ローカルホストへの外部送信が、同じファイル内で互いに25行以内にあること、または1つ以上のリネームを通じて再割り当てされた後に実行されるリモートフェッチを必要とします。ある関数で~/.npmrcを読み取り、40行後にレジストリを呼び出す公開ヘルパーはクリーンのままであり、API呼び出しのために名前付き環境変数を1つ読むことは決してフラグされません — 両方の形状は安全なフィクスチャとして固定されています。
自作のフィクスチャだけでなく、2つの実世界コーパスで検証済み:anthropics/skillsの18個すべての実スキルバンドルとvercel/aiの14個すべてで検出0件、cisco-ai-defense/skill-scannerのラベル付き評価コーパス(各_expected.json判定を持つ20スキル)で6/6正解、安全とラベル付けされたものに対する誤検出ゼロ。詳細はテストとベンチマークを参照してください。
これが何でないか
正直な限界:論文の結論は、実行時デトネーションが静的解析に勝るというものであり、それは正しいです。これらのルールを知る適応的敵対者は、カバーされていない変換を書くことができます。ここで変わるのは、回避のコストです — 公開され、現在流通している手法はもはや機能せず、それらを打ち負かすために必要な難読化は、今や発見の重大度を引き上げます。静的スキャンはフィルターであり、セキュリティ境界ではありません。 スキャナーが何と言おうと、信頼できないスキルは信頼できないコードとして扱ってください。
信頼とリリース保証
- CIは、サポートされているNodeバージョンでLinux、Windows、macOS上で実行されます。
- CodeQL、本番依存関係監査、OpenSSF Scorecard、Dependabot、およびこのスキャナー自身のブロッキング自己スキャンが独立したチェックを提供します。
- 手動のnpm公開は毎回、
prepublishOnlyを通じてテスト、カバレッジ下限、レビュー済み実リポジトリ回帰ゲート、tarball検査を呼び出します。 - GitHub Actionsはnpmパスワードやトークンを受け取らず、パッケージを公開できません。
- リリース保証、単一メンテナーガバナンス、セキュリティ報告、バージョン管理されたベンチマーク証拠は公開されています。
これは単一メンテナーのプロジェクトであり、契約上のSLAや独立した認証はありません。上記の管理策はリスクを低減しますが、静的スキャンをセキュリティの証明に変えるものではありません。
精度契約
誤検出はスキャナーを殺します。SecureAI-Scanのルールエンジンは3つの厳格なルールに従います:
- シンクはインポートを通じて解決されます。 識別子がLLM SDKではないモジュールに解決される場合、名前が何であれ、それは決定的にLLM呼び出しではありません。
- 証拠はラベル付けされ、決して混在しません。 トレースされたデータフローと単語近接一致は同じものではないため、決して同じ階層を共有しません。
- 安全コーパスがすべてのリリースをゲートします。
test-fixtures/safe/には、以前誤検出を引き起こしたパターン(編集済みPIIペイロード、Google Mapsクライアント、LLMクライアントの隣のenv-var APIキー、通常のレスポンスロギング、OAuthメタデータフィールド、ストリーミングレスポンスのchunks、フィクション/ナラティブプロンプトテキスト)が含まれています。そこでの発見はスイートを失敗させます。
テストとベンチマーク
3つの層。スキャナーの主張を信頼するには1つだけでは不十分だからです — 精度と再現率は異なる障害モードであり、両方がチェックされます。
1. フィクスチャコーパス — 精度+再現率、すべてのビルドで実行。```bash npm test
[`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) と [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe) は一緒にスキャンされます。すべての脆弱なフィクスチャは、`proven`/`likely` のエビデンスで期待されるルールを発火させ(再現率)、すべての安全なフィクスチャは **ゼロ** 件の `proven`/`likely` の検出結果を生成しなければなりません(適合率)。高速かつ決定的ですが、これはスキャナーが、テスト専用に書かれたコードに対してのみ正しく動作することを証明するにすぎません。
**2. 実世界の回帰ベンチマーク — 私たちが書いていない公開リポジトリに対して。**```bash
npm run regression # scan the full curated repo set
npm run regression -- --fresh # re-clone everything first
npm run regression -- openai-node # scan just one repo by name
npm run regression -- --update-baseline # accept the current findings
scripts/regression-scan.jsは、厳選された多様な実在の公開リポジトリ(OpenAI/Anthropic/Vercel AI SDK、公式MCPサーバーとTypeScript SDK、LlamaIndex、さらにスキルバンドル対応としてanthropics/skillsとcisco-ai-defense/skill-scanner — TSとPython、SDK利用者のサンプルコードとSDK作者のソースコードにまたがる)をクローンし、ビルド済みCLIでそれぞれをスキャンします。
test/regression-baseline.jsonにまだ含まれていないproven/likelyの検出結果がある場合、非ゼロで終了します — これは、ソース行と照合済みの検出結果を手動レビューした記録です。フィンガープリントはrepo|rule|fileであり行番号ではないため、通常の上流リポジトリの変更ではノイズが発生しません。新しいフィンガープリントは、スキャナーが正当性を説明しなければならない主張です。それが真の問題でなければルールのバグであり、根本原因で修正され、新しいtest-fixtures/safe/フィクスチャとして固定されます。読んでいない検出結果をベースライン化することは、この仕組み全体を無効化します。
スキルバンドル対応には独自の行があります。なぜならcisco-ai-defense/skill-scannerのevals/コーパスにはラベルが付いており、20個のフィクスチャのそれぞれに_expected.jsonの判定結果が同梱され、文字通りmalicious/またはsafe/という名前のディレクトリ配下に置かれているからです。そのため、これは精度チェックだけでなく再現率チェックとしても機能します: 対象範囲内の悪意のあるフィクスチャ6/6が発火し、safeとラベル付けされたものには0件の検出、さらにanthropics/skillsの18個すべての実バンドルとvercel/aiの14個すべてで0件の検出です。(残りのCiscoカテゴリ — SQLインジェクション、パストラバーサル、リソース枯渇、関数引数の汎用的なeval()、4ファイルに意図的に分割されたペイロード — は、文書化されたLLM/MCP/RAGの対象範囲外であるか、同一ファイル内の連言分析の範囲を超えています。それぞれの具体的な理由については0.6.0チェンジログエントリを参照してください。)
元の精度修正を促した実行からの履歴的なbefore/after(デフォルトのエビデンスレベルでの検出結果、--paranoidなし):
| リポジトリ | Before | After | 何が問題だったか |
|---|---|---|---|
| vercel/ai | 773 | 1 | examples/、トップレベルのtests/、およびハイフン付きのecosystem-tests/形式のディレクトリが低信頼パスとして認識されなかった。chunks(一般的なストリーミング応答変数)が曖昧さのないRAGエビデンスとして扱われた |
| openai/openai-node | 47 | 0 | SDK自身のexamples//ecosystem-tests/に適用された同じパス検出のギャップ |
| anthropics/anthropic-sdk-typescript | 2 | 0 | トップレベルのtests/ディレクトリに対する同じパス検出のギャップ |
| modelcontextprotocol/typescript-sdk | 3 | 0 | token_endpoint/tokenType形式のOAuthメタデータフィールドが漏洩シークレットとしてフラグ付けされた |
| run-llama/llama_index | 18 | 15 | Pythonチェックが、"system prompt"を含むdescription=フィールドを、コンテキストに関係なくprovenのMCPツールポイズニングとしてフラグ付けした。残りの15件は、ライブラリ自身の汎用レトリーバー定義に対するVEC001ヒット — ベクトルDB SDKの自社ソースをスキャンしているのであってアプリケーションコードではなく、チェックするためのフィルタが存在し得ない。正直な本質的な限界であり、バグではない |
現在の実行(2026-08-06) — バージョン付きエビデンスはdocs/benchmarks/v0.9.0.jsonに記録されています:
| リポジトリ | 検出結果 | ルール | ステータス |
|---|---|---|---|
| openai-node、anthropic-sdk-typescript、anthropic-sdk-python、modelcontextprotocol/typescript-sdk、modelcontextprotocol/servers | 0 | — | クリーン |
| anthropics/skills(18個の実スキルバンドル) | 0 | — | クリーン — SKL001–005の純粋な精度チェック |
| vercel/ai(5,691ファイル) | 0 | — | トリアージ前は40件だった(AI001、AI003、AI005、AI010、MCP002) — すべて手動でソースと照合し偽陽性と確認され、3つの独立した根本原因バグに起因(下記参照)、修正され、完全再スキャンでクリーンであることを再確認 |
| run-llama/llama_index | 46 | VEC001 | 本質的な限界でありバグではない — テナントフィルタが存在し得ないライブラリ自身の汎用レトリーバー定義 |
| cisco-ai-defense/skill-scanner | 7 | SKL001、SKL002、SKL005 | すべてmalicious/とラベル付けされたフィクスチャに対するもの — 対象範囲内6/6、safe/とラベル付けされたものには0件 |
vercel/aiのトリアージで、根本原因が特定された3つの実際のバグが見つかりました — いずれもv0.6.0のスキルルールに固有のものではなく、すべて多くのルールで共有されるロジックに存在します:
resolveLlmSinkは、メソッド名に関係なく、LLM SDKモジュールに解決された呼び出しをすべてモデル呼び出しとして扱った —generateTextと並んでaiパッケージがエクスポートする型ガードであるisToolUIPartをLLM呼び出しとしてフラグ付けしました。これだけで5つの検出グループのうち3つ(AI001、AI003、AI010)の原因となりました。- AI005の
DANGEROUS_CALLEESにはSQLインジェクション形式のシンク用に"query"が含まれるが、"query"は正当なLLM/エージェント呼び出し動詞でもある — Claude Agent SDK自身のモデル呼び出しであるclaudeSdk.query({ prompt, options })が、共有メソッド名だけの理由で「LLM出力が危険なシンクに渡された」としてフラグ付けされました。 REQUEST_SOURCES(MCP002、MCP010、VEC003全体で同一に重複)が裸の"params."にマッチした — 必ずしもHTTPリクエストデータではない、慣例的にparamsと名付けられた任意の関数パラメータ。URLスキームバリデータ(assertOpenLinkParams(params: unknown))が「ユーザー入力からのMCPサーバーURL」としてフラグ付けされました。
3つすべてが(特定の呼び出しサイトではなく)根本原因で修正され、test-fixtures/配下の恒久フィクスチャとして固定されています。詳細はCHANGELOG.mdにあります。
3. 脆弱版とパッチ適用版の検証 — 精度だけでなく再現率も証明します。
上記の2つのレイヤーは、スキャナーが安全なコードに対して沈黙を保つことだけをチェックします。DEP003のアドバイザリチェックは逆方向に検証されます: 文書化された脆弱バージョンにパッケージを固定してフラグ付けされることを確認し、次にパッチ適用済みバージョンに固定してフラグ付けされないことを確認します。```bash
node --test test/dependency-guard.test.js
covers: `[email protected]`(CVE-2025-6514、脆弱)がフラグ付き / `[email protected]`(パッチ適用済み)がクリア。`[email protected]`(バックドア前)がクリア / `[email protected]`(後 — 悪意のあるパッケージには正当なパッチは存在しない)が依然としてフラグ付き。`llama-cpp-python==0.2.71`(CVE-2024-34359、OSV生成セット由来)がフラグ付き / `==0.2.72`(パッチ適用済み)がクリア。PyPIの名前正規化(`llama_cpp_python`)でも同様。そして`langchain>=0.1.0`形式の非ピン留め指定子は、デフォルトレポートで**ゼロ**件の検出結果を生む。このテストの構築により実際のギャップを発見した:`DEP003`はこれまでパッケージ名のみでアドバイザリを照合しており、宣言されたバージョンをアドバイザリの影響範囲と実際に比較していなかった — [`src/scanner/semver.ts`](https://github.com/akanthed/secureai-scan/blob/main/src/scanner/semver.ts)で修正済み。
曖昧さの解決方法はアドバイザリの種類ごとに意図的に異なる。**悪意のある**パッケージは、宣言されたバージョンを解決できない場合でも発火する — バックドアのインストールは回復不能なため、フラグ付き側に倒れる。**CVE**は、宣言されたバージョンが影響範囲内にあることが証明可能な正確なピン留めである場合にのみ`proven`で発火する。非ピン留めだが影響を受ける可能性がある場合は`heuristic`(`--paranoid`時のみ)に格下げされる。悪意のある種類のルールを162件のCVEスナップショットに適用すると、`langchain>=0.1.0`を宣言するすべてのリポジトリに重大な検出結果が付くことになる — スケールでは実効性のないノイズとなる。
## ロードマップ
出荷済みの機能と計画中の機能については[`ROADMAP.md`](https://github.com/akanthed/secureai-scan/blob/main/ROADMAP.md)を参照。両方の言語エンジンはASTベース:TypeScript/JavaScriptにはts-morph、PythonにはTree-sitterを使用。Pythonのインポート、呼び出し、代入、デコレータ、スコープ、キーワード引数、辞書フィールド、文字列はすべて構文ノードであり、対象コードがインポートまたは実行されることはなく、Pythonインタープリタも不要。残るPythonのギャップは、関数間・ファイル間の汚染深度が限定されている点のみであり、パース処理ではない。スキャン性能と既知の制限は[`docs/Performance.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Performance.md)に記載されている。
## コントリビューション
コントリビューションは歓迎 — ワークフローは[`CONTRIBUTING.md`](https://github.com/akanthed/secureai-scan/blob/main/CONTRIBUTING.md)、上記の精度基準を満たす検出ルールの追加方法は[`docs/WritingRules.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/WritingRules.md) / [`docs/RuleDevelopment.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/RuleDevelopment.md)を参照。新しいルールにはそれぞれ[`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable)と[`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe)の両方にフィクスチャ、`src/scanner/catalog.ts`へのエントリ、`test/corpus.test.js`のテストケースが必要 — `npm test`がこの3つすべてを強制する。
## ライセンス
MIT © Akshay Kanthed

