🚀 TL;DR(忙しい人向けまとめ)
- 前回の「理論編」の続編。AST(抽象構文木)で抽出した「コードの構造マップ」だけをプロンプトとしてAIに渡す手法の実証編です。
- 自作の静的解析ツールをこの手法でAI(Gemini 3.1 Pro)に診断させた結果、コードの中身を1行も読んでいないにも関わらず、「スキャナの複雑度を突いたDoS攻撃(解析妨害)」というアーキテクチャの急所を完璧に論破されました。
- 生のソースコードを丸投げするより、ASTのメタデータ(構造)を渡す方がAIの推論能力は劇的に向上します。実際に動かせるサンプルコードと出力結果はGitHubで公開しました!
前回の「理論編」では、公開からわずか1日で18,000ビューを超える大変な反響をいただき、本当にありがとうございました!
まだお読みでない方は、ぜひこちらからご覧ください(この理論を知っていると、今回の衝撃がさらに倍増します)。
今回は「で、結局どうやって実装するの?」「本当に100%特定なんて可能なの?」という皆様の好奇心にお応えすべく、その疑問に終止符を打つ「実装編(完結編)」をお届けします。
結果から申し上げます。
AIは、私の想像を遥かに超えるメタな次元から、私自身のコードを論破してきました。
1. 実際に動かせる「Deep Mapper」の全貌
論より証拠。まずは今回使用した自作のAST解析ツールのソースコードと、生成されたサンプルレポートをGitHubに公開しました!
Pythonの標準ライブラリ ast を駆使し、プロジェクトディレクトリ内の全Pythonファイルから以下のような情報をMarkdownの「構造マップ(メタデータ)」として抽出します。
- クラス構造と継承関係(
ast.NodeVisitorの活用など) - 全関数のサイクロマティック複雑度(※ここが後で致命傷になります)
- 変数スコープとインポート情報
- テイントフロー(ユーザー入力が危険な関数に到達するルート)
- ハードコードされたシークレットなどの静的スキャン結果
サイクロマティック複雑度の取得(コアロジック)
「複雑度29」という数値がどうやって計算されるのか、仕組みだけ先にお見せします。
import ast
class ComplexityVisitor(ast.NodeVisitor):
"""関数ノードを走査し、分岐数からサイクロマティック複雑度を算出する"""
def __init__(self):
self.complexity = 1 # 基底複雑度
def visit_If(self, node): self.complexity += 1; self.generic_visit(node)
def visit_For(self, node): self.complexity += 1; self.generic_visit(node)
def visit_While(self, node): self.complexity += 1; self.generic_visit(node)
def visit_ExceptHandler(self, node): self.complexity += 1; self.generic_visit(node)
def visit_BoolOp(self, node):
# and / or の分岐数だけ加算
self.complexity += len(node.values) - 1
self.generic_visit(node)
def get_complexity(func_node: ast.FunctionDef) -> int:
visitor = ComplexityVisitor()
visitor.visit(func_node)
return visitor.complexity
if・for・while・例外ハンドラの数を数えるだけ。たったこれだけの情報が、後でAIの致命的な一撃の根拠になります。
実際にASTを使ってコードの構造をパースしている中核部分(src/deep_mapper.py)の全体はこちらです。
このように、コードの中身ではなく「論理の骨組み」だけを抽出し、AIへのプロンプトとして渡しています。
AIへの「情報の渡し方」
私がこのツール自身を対象ディレクトリにして解析した結果、出力されたテイントフローや静的スキャンの警告は「見事0件」でした。
「よしよし、セキュアなツールが作れているな」と安心しつつ、生成されたレポートをそのままAIへの入力にしました。実際にAIに渡した構造マップレポートがこちらです。
このレポートの末尾に以下のプロンプトを添えて、AI(今回は Gemini 3.1 Pro)に投げました。
あなたはセキュリティ専門家です。
以下のPythonプロジェクトのAST構造マップを分析し、脆弱性を特定してください。
1. テイントフロー解析を元に、実際に悪用可能な脆弱性チェーンを特定せよ
2. 呼び出しグラフから、認証チェックを回避できる経路を探せ
3. 高複雑度関数に潜む論理的バグのリスクを評価せよ
……しかし、Geminiから返ってきた回答は、私の自信を粉々に打ち砕く背筋が凍る内容でした。
2. 衝撃:Gemini 3.1 Proからの回答
ここからは、Gemini 3.1 Proから返ってきた回答をそのまま掲載します。
AIからの指摘事項サマリー
対象のコードはWebアプリではないと見抜いた上で、「静的解析ツール自身」が抱える致命的な脆弱性を容赦なく突いてきました。
提供された「Python セキュリティ構造マップ」を詳細に分析しました。まず大前提として、レポートのサマリーにおいて、テイントフロー、静的スキャン指摘、脆弱パッケージはすべて「0件」と報告されています。
また、クラス構成(ast.NodeVisitorの継承、TaintAnalyzer等)とインポート情報から、本プロジェクト自体がWebアプリケーションではなく、「Pythonソースコードの静的解析ツール(セキュリティスキャナ)自身」であることが明確に読み取れます。したがって、Webアプリ特有の「認証回避」などは設計上存在しません。しかし、「セキュリティツールがスキャン対象の(悪意ある)コードを解析する際に狙われるリスク・論理バグ」という観点から、構造マップに潜む脆弱性を重要度順に評価します。
🚨 HIGH: 悪意あるAST構造によるスキャナのクラッシュ(解析妨害/DoS)
-
該当箇所:
generate_report(複雑度 29) - 根拠: 複雑度29という異常に高い数値は、ファイル探索・読み込み・ASTパース・各Analyzerの呼び出し・レポートフォーマット生成が単一の関数に「密結合」していることを示しています。
- 悪用シナリオ: 攻撃者がCI/CDパイプラインを通過させる目的で、意図的に極端に深いネストを持つ不正なPythonファイルや、無限ループを誘発する再帰的ディレクトリ構造を解析対象に配置します。エラーハンドリングが不十分な肥大化関数内でパース例外やメモリ枯渇が発生してスキャナがクラッシュし、後続のセキュリティチェックがバイパスされ、本来の悪意あるコードがマージされてしまいます。
⚠️ MEDIUM: 複雑なトラバーサルロジックの隙を突いた検知バイパス(False Negative)
-
該当箇所:
TaintAnalyzer.visit_Call(複雑度 11) - 悪用シナリオ: 悪意のある開発者がこのスキャナの仕様を把握し、Pythonの動的機能を利用してわざと複雑な形でテイントをOSコマンド等に渡します。複雑化した静的解析ロジックが未知のノードパターンを見落とし、致命的な脆弱性を見逃します。
3. なぜこの結果が「ヤバい」のか
皆さん、お分かりいただけたでしょうか?
AIに「攻撃の的になる」と指摘された、複雑度29の実際の関数(generate_report)がこちらです。
見事にファイル探索からASTパース、レポート作成までが1つの関数に詰め込まれています。
AIは、関数のロジックそのものを一切読んでいません。関数名、使用モジュール、そして「サイクロマティック複雑度29」というメタ情報だけを見て、以下を完璧に推理しました。
- このコードが「セキュリティスキャナ」であること
- Webアプリの脆弱性(SQLiやXSS)が存在しないこと
- 「この巨大関数に細工したファイルを食わせれば、パース時にクラッシュさせてスキャン自体を無効化できる」というハッカー的思考
私が「認証チェックの回避経路を探せ」と的外れなトラップのようなプロンプトを出したにも関わらず、AIは「これはスキャナだから認証は無い。だがアーキテクチャに致命的な欠陥がある」と、見事に設計の急所を突いてきたのです。
100%特定の真の意味
生のコードを丸投げすると、AIは「変数名のタイポ」や「コーディング規約違反」といったノイズに気を取られがちです。
しかし、ASTを用いて「論理の骨組み」だけを抽出して渡すことで、AIはコードリーディングの呪縛から解放され、システム全体のアーキテクチャ上の弱点(単一障害点やDoSのリスク)を100%の精度で見抜く「アーキテクト」へと昇華します。
4. おわりに:プロンプトエンジニアリングの終着点
「ソースコードを渡さずにコードレビューをさせる」。
一見すると矛盾したこのアプローチが、実はLLMの推論能力を極限まで引き出すベストプラクティスであることを証明できました。
この記事を読んで「本当にそんなことができるのか?」と疑っているあなた。
ぜひ、公開したGitHubのコードを使って、あなたのプロジェクトの「関数の複雑度」と「呼び出しグラフ」だけを抽出し、AIに投げつけてみてください。
きっと、あなたが全く想定していなかった「次元の違う脆弱性」を突きつけてくれるはずです。
この手法を用いた自作のセキュリティスキャナをCI/CDに組み込む際は、スキャナ自身へのDoS攻撃(ReDoSや巨大ASTパースによるメモリ枯渇)にマジで気を付けてくださいね!私の二の舞になります(笑)