
はじめに — 自分で作ったツールで0点を取った話
セキュリティ診断ツールを作ったら、最初のテスト対象は自分のプロジェクトにすべきです。いわゆるドッグフーディング、自分で作ったものを自分で使うということです。自分が日常的にメンテナンスしているコードベースなら、検出結果の正誤を最も正確に判断できるからです。
Vibe AuditのMCP Server版(run_vibe_audit)をZERONOVA LABのコードベースに実行しました。5つの内部監査ツールが順次実行され、それぞれの検出結果が構造化データとして返される。そして最終スコアが算出される。結果は0/100点。
公開後に自プロジェクトで
run_vibe_auditを実行したら 0/100 点が出て驚いた。公開ツールの API ルートに認証がないのは仕様なのに「認証なし」と検出される。dangerouslySetInnerHTMLも JSON-LD 用途なのに XSS リスクとして検出される。大半が偽陽性だった。
3月1日の開発日記にこう書きました。正直、驚きました。自分のプロジェクトが「最も危険なコードベース」として判定されたのですから。
しかし冷静に分析すると、検出された項目の大半は偽陽性(false positive)、つまり実際には問題ないのに「問題あり」と報告されたものでした。ただし、1件だけ本物の問題も見つかりました。
この記事では、偽陽性の原因、なぜパターンマッチングでは偽陽性が避けられないのか、そして「AIエージェント前提の設計」がこの問題をどう解決するのかを整理します。セキュリティツールを作る人にも、使う人にも参考になる内容だと思います。
「0点」というスコアを見た瞬間の正直な感情は「壊れてるんじゃないか?」でした。しかし一つひとつの検出結果を確認していくと、ツールは「正しく動いている」ことが分かりました。問題は、ツールが検出した事実と、人間が求める判断の間にギャップがあることでした。
0点の内訳 — 何が検出されたのか
run_vibe_auditは5つのTier 1ツールを順次実行し、100点満点からの減点方式でスコアを算出します。ZERONOVA LABのスキャン結果を分類すると、以下のようになりました。
偽陽性: 公開APIルートの「認証なし」判定
check_api_authツールが、すべてのAPI routeに「認証チェックなし」を報告しました。
ZERONOVA LABの無料ツールのAPI route(OGPチェッカー、リンク切れチェッカー等)は、意図的に認証なしで公開しています。認証を要求したら「無料ツール」として機能しません。これは脆弱性ではなく設計上の意図です。
しかしパターンマッチングベースの検出では、「APIルートに認証チェックのコードがない」という事実しか見えません。「このAPIルートは公開サービスとして意図的に認証なしにしている」というコンテキストは、コードの静的解析からは読み取れません。
偽陽性: JSON-LD用のdangerouslySetInnerHTML
check_injection_riskツールが、dangerouslySetInnerHTMLの使用をXSSリスクとして報告しました。
ZERONOVA LABでは、Next.jsのJSON-LD構造化データ(<script type="application/ld+json">)の挿入にdangerouslySetInnerHTMLを使っています。これはNext.js公式ドキュメントで推奨されている方法であり、挿入されるデータはサーバーサイドで生成された静的JSONです。ユーザー入力は含まれません。
しかしパターンマッチングでは、dangerouslySetInnerHTMLという文字列が存在すること自体がリスクとして検出されます。「何をどこに挿入しているか」のコンテキストは無視されます。
偽陽性: NEXT_PUBLIC_環境変数
check_client_secretsが、NEXT_PUBLIC_で始まる環境変数をフラグしました。Next.jsの仕様上、NEXT_PUBLIC_プレフィックスはクライアントサイドに公開される環境変数を意味します。公開して良い値(サイトURL等)が含まれていても、「クライアントに環境変数が露出」として検出されます。
本物の問題: .gitignoreの設定漏れ
ただし、ルート
.gitignoreに.envパターンが未設定という本物の問題も1件見つかった。「0点だけど価値はあった」という体験。
check_env_exposureが発見した唯一の本物の問題です。ルートディレクトリの.gitignoreに.env*パターンが含まれていませんでした。実際には.envファイルは存在しない(環境変数はVercelで管理)ため即座の危険はありませんが、将来.envファイルを作成した場合にコミットされるリスクがありました。
即座に修正しました。.gitignoreに.env*パターンを追加するだけの1行の変更です。しかし、この1行の変更がなければ、将来.envファイルを作成したときに秘密情報がリポジトリにコミットされるリスクがありました。偽陽性の山の中に1つ本物が混ざっている。これがセキュリティ診断の現実です。
なぜパターンマッチングでは偽陽性が避けられないのか
セキュリティ診断ツールの多くは、パターンマッチング(正規表現やAST解析)で脆弱性を検出します。Vibe Auditも同様です。
パターンマッチングの限界は明確です。コンテキストを理解できない。
| 検出パターン | 偽陽性の原因 |
|---|---|
| APIルートに認証コードがない | 公開サービスとして意図的に認証なし |
dangerouslySetInnerHTMLが使われている | JSON-LD用途で安全なコンテキスト |
NEXT_PUBLIC_環境変数がクライアントに露出 | 公開して良い値のみを格納 |
Stripeキーパターン(sk_test_*)がある | テストファイルのダミーデータ |
すべてに共通するのは、「その行為が意図的かどうか」「そのコンテキストで安全かどうか」の判断が必要であるという点です。パターンマッチングには「意図」も「コンテキスト」も見えません。
PM-AI 対話: 偽陽性への対処方針
/tools/vibe-audit)は人間が見るので、スコア表示に「外部スキャンの限界」を注記として追加するのが良いでしょう。MCP版はAIエージェント経由なので、エージェントが「偽陽性3件を除外すると85点相当」のような解釈を返せます。この対話で明確になったのは、検出層と判断層を分離するという設計思想でした。
「AIエージェント前提の設計」という考え方
従来のセキュリティツールは、人間が結果を読むことを前提に設計されています。偽陽性を減らすために検出ルールを厳格化し、「確度の高い問題のみ報告する」方針を取ります。その結果、見逃し(偽陰性)が増えます。
Vibe AuditのMCP版は、AIエージェントが結果を受け取ることを前提にしています。
セキュリティツールの偽陽性は AI エージェントがコンテキストを加味してフィルタリングする前提の設計なので、これは想定通りではある。でもユーザーが素人だと「0点」というスコアに不安を感じると思う。スコア表示の見せ方は改善の余地がある。
3月1日の開発日記の記述です。この設計思想を整理すると:
検出層(Vibe Audit)の役割:
- パターンマッチングで網を広く投げる
- 偽陽性を許容する代わりに、偽陰性(見逃し)を最小化
- 検出結果を構造化データとして返す
判断層(AIエージェント)の役割:
- コードベース全体のコンテキストを理解
- 偽陽性をフィルタリング(「このAPIルートは公開サービス」等)
- 本物の問題に対して修正コードを生成
この分離により、検出の網羅性と判断の精度を両立できます。パターンマッチングに「判断」まで求めるから偽陽性が問題になるのであって、判断をAIに委ねれば検出は「広く投げる」方針で良いのです。
GitHub Push Protectionが教えてくれたこと
偽陽性の問題は、Vibe Auditのスキャン結果だけではありません。開発プロセス自体でも直面しました。
セキュリティツールを作っているのに、テスト用のダミー秘密情報がセキュリティに引っかかるという皮肉。
テストファイルにStripe APIキーのダミーパターン(sk_test_*)を含めたところ、GitHubのPush Protectionにpushをブロックされました。String.fromCharCodeで動的に構築しても検出されます。
これもパターンマッチングの限界の典型例です。GitHub Push Protectionは「sk_test_で始まる文字列 = Stripeの秘密情報」というパターンで検出します。それがテストコード内のダミーデータであるというコンテキストは考慮しません。
最終的に、テストフォルダを.gitignoreに追加し、テストコードをnpmパッケージに含めない運用にしました。「セキュリティツールのテストファイルがセキュリティに引っかかる」という皮肉ですが、これはパターンマッチングベースのセキュリティの本質的な限界を端的に表しています。
偽陽性を減らす余地はあるか
偽陽性を完全になくすことはできませんが、減らす余地はあります。
PM-AI 対話: 偽陽性削減のアプローチ
middleware.tsを解析して認証ミドルウェアが適用されていないルートを「意図的に公開」と判定する方法。2つ目は、dangerouslySetInnerHTMLの使用箇所が<script type="application/ld+json">内である場合をXSSリスクから除外する方法。3つ目は、テストファイル(__tests__/、*.test.ts等)内の検出を自動除外する方法です。公開APIルートの自動認識
Next.jsのmiddleware.tsを解析し、認証ミドルウェアが適用されていないルートを「意図的に公開」と判定する。グローバルミドルウェアで/api/public/*のようなパスを除外している場合、そのパス以下のAPIルートは認証なしでも偽陽性としない。
JSON-LDの安全コンテキスト検出
dangerouslySetInnerHTMLの使用箇所を分析し、挿入先が<script type="application/ld+json">タグである場合はXSSリスクから除外する。これはNext.js公式ドキュメントでも推奨されている方法であり、挿入されるデータがサーバーサイド生成の静的JSONである限り、XSSリスクは実質的にゼロです。
テストファイルの自動除外
__tests__/、*.test.ts、*.spec.ts内の検出は自動的にスコアから除外する。テストコードにダミーの秘密情報が含まれるのは正常です。むしろ、テストコードにダミーパターンがないセキュリティツールの方が心配です。
これらはいずれも「コンテキストを限定的に理解する」改善であり、一般的なコンテキスト理解はAIエージェント層に任せる設計方針は変わりません。
学び — セキュリティスコアの設計思想
1. 偽陽性はバグではなく、設計上のトレードオフ
検出ルールを厳格にすれば偽陽性は減りますが、偽陰性(見逃し)が増えます。セキュリティ診断において、見逃しは偽陽性より深刻です。「問題ないのに問題と報告される」は不便ですが、「問題があるのに問題なしと報告される」は危険です。
2. 検出層と判断層を分離する
パターンマッチングに「判断」まで求めると、偽陽性/偽陰性のトレードオフから逃れられません。検出は広く網を投げ、判断はコンテキストを理解できるAIに任せる。この分離が、2026年のセキュリティツール設計の方向性だと考えています。
3. ドッグフーディングの価値は「0点」にある
自分のプロジェクトで0点が出たからこそ、偽陽性の問題に気づき、設計思想を整理できました。もし80点だったら「よくできた」で終わっていたでしょう。期待に反する結果こそが、最も多くの学びをもたらします。
4. スコアの見せ方はユーザーに応じて変える
AIエージェント向けには生データ(全検出結果 + 構造化データ)を返し、人間向けにはコンテキストの注釈を添える。同じ検出エンジンでも、出力のフォーマットはユーザーに合わせて変えるべきです。
MCP Server版のrun_vibe_auditは、すべての検出結果を構造化データとして返します。AIエージェントはそのデータを受け取り、プロジェクトのコンテキスト(Next.jsの構成、公開サービスかどうか、テストコードかどうか)を理解した上で「偽陽性3件を除外すると85点相当」のような解釈を提供できます。
一方、Web版のVibe Auditは人間が直接スコアを見ます。「外部スキャンの限界」を注記として表示し、「このスコアは目安であり、コンテキストによっては問題ない項目も含まれます」というメッセージを添えています。同じエンジン、同じ検出結果でも、見せ方でユーザー体験はまったく変わります。
5. 「完璧なセキュリティスコア」は存在しない
0/100点という結果は極端ですが、逆に100/100点もまた危険信号かもしれません。「何も検出されなかった」が「何も問題がない」とは限らない。検出ルールがカバーしていない脆弱性が存在する可能性は常にあります。
セキュリティは「点数で安心する」ものではなく、「継続的に監視し、改善する」プロセスです。Vibe Auditのスコアは「今の時点で検出可能な問題の概観」に過ぎません。この限界を理解した上でツールを使うことが、正しいセキュリティ意識だと考えています。
セキュリティスコアリングの設計: パターンマッチングベースの検出は偽陽性が避けられない。重要なのは「AI エージェントがコンテキストを加味して判断する」前提で設計すること。人間向けツールと違い、AI は偽陽性をフィルタリングできる。
3月1日の開発日記に書いた教訓です。Vibe Auditの0点は、セキュリティツール設計の本質的な課題と、AIエージェント時代の解決策を教えてくれました。
関連記事:
Zeronova(ゼロノバ)
Product Manager / AI-Native Builder
Web/IT業界19年以上・20以上のWebサービスを担当したPdM。東証プライム上場企業の子会社代表として事業経営を経験。現在はAIを駆使して企画から実装まで完結させる個人開発を実践中。