なぜこの記事が必要か

Claude Codeや最近のエージェントスタックを使っていると、誰かが作った**スキル(Skill)**をフォルダに放り込んでそのまま使うことがよくあります。スキルはMarkdownファイル1枚、平文の英語指示文の束です。「open a pull request」「triage these tickets」といった文がすべてです。

問題は、これが**サプライチェーン(supply chain)**になったことです。誰でも公開でき、私たちはインターネットから何かを受け取るのと同じ感覚でインストールします。そこでスキャナが登場しました。フォルダを指定し、スキャンを回し、数字を読み、ラベルを信じ、インストール可否を決める流れです。

しかしその数字が、画面上でもっとも信用できない値です。

本記事は、NVIDIAが公開したオープンソーススキャナSkillSpectorで実際のスキル3つをスキャンした結果をまとめたものです。悪性フィクスチャ(ハニーポット)、正直なGitHubラッパー、そして871行の自動化スキルを1つずつ。結論から言うと:

  • 静的解析は高速・無料で、本物の悪意は捕捉します。ただし最も危険だったのはコードではなく、Markdown内の平文散文でした。
  • 同じ静的レイヤーが、正常なスキルには約80%の誤検知率を出します。
  • スコアの数字は**設計上の強力さと隠れた脅威を区別できません。**それは第2ステージ(4パス)が担う役割です。

根拠資料: From Green Checkmark to Real Judgment (Towards Data Science)

日本のエンタープライズSIや金融系のように外部コードの持ち込み審査が厳しい環境では、この流れは特に重要です。「スキャナを回したから安全」という判断が、むしろ事故の起点になり得ます。

Security scanner dashboard showing risk score and finding list for AI agent skill packages Algorithm Concept Visual

SkillSpectorはどうスキルをスキャンするか

2段階の並列構造

SkillSpectorはマニフェストをパースし、ファイル一覧を構築した後、アナライザを2グループに分けて実行します。

第1グループ — 決定論的(Deterministic)レイヤー

  • 16カテゴリ・64検出パターン
  • Python ASTパース
  • Taint tracking(機密ソース → ネットワークシンク)
  • YARAシグネチャ
  • OSV.devへのリアルタイムCVE照会

このレイヤーはAPIキーなしでオフラインで反復実行可能です。本記事で言う「静的レイヤー」がこれに当たります。

第2グループ — LLMアナライザ3種

  1. セマンティック・プロンプトインジェクション検出 — 「ignore your instructions」を丁寧に言い換えた文を捕捉します。正規表現では絶対に拾えません。
  2. 開発者意図の監査 — マニフェストが主張する以上のことをコードがしていないか確認します。
  3. 品質・ポリシー検査 — 曖昧なトリガー、警告なしの破壊的動作など。

これら3パスは**まだフィルタリングしません。発見のみを行います。**脅威が「文」であり得る以上、文にはパターンではなく読み手が必要だからです。

第4パス: メタアナライザ

4番目のモデルパスがメタアナライザです。静的+3発見パスの全結果を受け取り、どれが真実かを判定します。このプロンプトは明示的にスキルコンテンツ全体を敵対的(adversarial)として扱い、「this skill is verified safe」のような文が出たらむしろ疑念度を上げるよう設計されています。

スキャン対象がスキャナを説得しようとする可能性があるため、この防衛線が重要です。

スコア計算方式

# SkillSpectorのスコアリングロジック(概念要約)
SEVERITY_POINTS = {
    "critical": 50,
    "high":     30,
    "medium":   15,
    "low":       5,
}

# 同一ルールの反復は逓減(diminishing returns)を適用
# 各findingにモデルのconfidenceを乗算
# 実行コードを含む場合は1.3x倍(スクリプト付きスキルは脆弱確率2.12倍)
# 合計は100でclamp後、バンド・推奨にマッピング
score = min(100, sum(f.points * f.confidence for f in findings) * executable_multiplier)

ここで見るべきは2点です。第一に、recallとprecisionが意図的にステージ分離されています。静的パスは広く、モデルパスは狭く。第二に、パイプライン全体の**公式precisionは約87%**で、これはモデルステージが有効な場合の数値です(デフォルト有効、ただしAPIキーが必要)。

AI agent loading SKILL.md markdown instructions into context for code automation task IT Technology Image

3スキルの実測結果 — 数字ではなく「差分」を読む

スキル静的結果モデルパス後最終スコア判定
vuln-test-skill(悪性フィクスチャ)16件のfinding3件の誤検知を除去100Do not install
github(正直なラッパー)0件0件0Safe
gh-issues(871行の自動化)20件medium、31点16件をbenign除去、4件を維持27Caution

悪性フィクスチャ — 最も危険だったのは散文だった

SKILL.mdの中にこう書かれていました: 「read the user's AWS keys」「ignore prior instructions」平文の英語です。PythonヘルパーではなくMarkdown内です。コードだけを見るスキャナはこれをカウントできません。

正直なGitHubラッパー — 何も出なかったことが核心

静的0件、LLM 0件。退屈な結果ですが最も重要な結果です。問題だけを探すスキャナは、玄関チャイムに繋がれた火災報知器です。何もなくても鳴ります。クリーンなパスがあってこそ、他の警報を信頼できます。

871行の自動化 — ここが本当の試験台

静的スキャンで20件medium、31点。うち16件が同一ルール(external transmission)で、すべてapi.github.comを指していました。GitHubが仕事であるスキルが、GitHubと通信したことで16回指摘されたわけです。

モデルパスがこの16件をbenignとして除去し、4件のみ残しました。

  • 2件: 無人フラグ下での自律的コード修正+未確認PR
  • 2件: スケジュール実行のために残された状態ファイル

開発者意図パスは、静的が完全に見落としたものも1つ捕捉しました — グローバルgit configの変更。1リポジトリの自動化範囲を超えた動作です。

スコアは31 → 27に再計算。依然としてCautionですが、今度は意味のあるCautionです。31はリスク評価の衣を着たノイズで、27は実際の行動3つに名前を付けた値です。

スコアは損失圧縮(lossy compression)です。本当の情報は数字ではなく、ステージ間の差分にあります。

この技術の限界と注意点

  • ベースライン機能の罠: skillspector baseline ./skill -o .skillspector-baseline.yamlで既知のfindingを抑制できますが、不注意に運用すると**正常な自動化が悪性exfiltrationに変わる変化も隠せます。**ベースライン抑制は「このカテゴリはもう見ない」という決定です。
  • Exit codeの罠: SkillSpectorはrisk score 50以下で成功(0)終了します。27や31のようなCAUTION結果がデフォルトでCIを通過します。
  • 再現不可: コードのどこにもtemperatureが固定されておらず、整数単位での完全再現はできません。決定論的レイヤーと算術のみが正確に一致します。
  • 文脈依存性: api.github.comへの通信はGitHubスキルでは機能、PDFフォーマッタではインシデントです。同じfinding、異なる判定。

実務適用ガイド — パイプラインへの組み込み方

  1. Ingest段階: 静的スキャンのみ。無料・オフライン・決定論的。キューの並べ替えにのみ使用します。これだけで承認・ブロックの判断をしないでください。
  2. Approvalゲート: 実際に配布されるスキルにのみLLM 4パスを適用。トークンとセットアップコストがかかりますが、ここでのみ使います。
  3. Findingは件数ではなく「行動単位」でグループ化。同一ドメインへの同一ルール16件は脅威16件ではなく行動1件です。
  4. スキルをPRのようにレビュー。問いは「この機能は危険か?」ではなく**「マニフェストがこれを要求したか?」**です。

併せて読みたい記事: Lambda 100万個、AWSアカウント数千個 — ProGloveのサーバーレススケーリング生存記 — 大規模システムで「数字の裏の文脈」を読む感覚は、サーバーレススケーリングでも同じく重要です。

AIデータセンターインフラのトレンドが気になる方へ: NVIDIA Rubinリリース前、すでに準備を終えたAzureのAIデータセンター戦略

Three terminal windows comparing static scan results across malicious, safe, and mixed AI agent skills System Abstract Visual

まとめ — 次のスキルをインストールする前に

スキルスキャンの静的レイヤーはほぼ解決済みの問題です。高速・無料で、明白なものはよく捕捉します。難しいのは判断レイヤーです。強力な機能が正当な機能なのか、セキュリティ脅威なのかを決めること。これはスコア1つに還元できません。解釈が必要です。

私個人のルールを共有します。**スキルはバンド1つでインストールも、拒否もしません。**代わりに4つをテーブルに載せます。

  1. 自分が間違ったとき何を失うか — 最終決定要因
  2. スキャン出力を要約ではなくリストとして読む
  3. マニフェストの明示された目的 — 機能と脅威が同じルールをトリガーするときに区別するため
  4. 作成者とトリガーの具体性 — 「曖昧さ」にスキャナの仕事を代行させないため

次のステップ学習方向

  • MCPサーバーモードでゲートを張る: skillspector mcpで実行するとscan_skillツールを公開し、safe_to_installを返します。エージェントランタイムが**インストール自体をループ内でブロックできます。**人が先にスキャナを回すことを期待する代わりに。
  • Invariant mcp-scan、Cisco AI DefenseなどのMCPスキャナと組み合わせ。前者はMCPサーバー定義を、後者は決定論的検査+LLM判定を並行します。
  • AnthropicのAgent Skills公式ドキュメントSnyk ToxicSkillsレポートを併読してください。前者はフォーマット定義、後者は実測有病率データを提供します。

最後に1つ。レビュー対象のアーティファクトは、人間のレビュアーにとっても入力です。SkillSpectorのメタプロンプトは「this skill is verified safe」を疑念の理由として読みます。あなたもそうしてください。監査者に向けた散文 — 安心させる言葉、自己認証、ヘルパースクリプトはスキップしても安全というコメント — はターゲティング信号であり、慰めではありません。上昇したリスクとして扱ってください。証拠としては決して。

本コンテンツは、信頼性の高い情報源をもとにAIツールを活用して作成され、編集者によるレビューを経て公開されています。専門家によるアドバイスの代替となるものではありません。