AI生成コードはテストを見せれば安全になるか。2,705回のSecTDD実験が示した落とし穴
AIに安全なコードを書かせたいなら、脆弱性を検出するテストを最初から全部見せればよい。直感的には、そう思える。
ところが、実験結果はそれほど素直ではなかった。見える機能テストとセキュリティテストを生成前に渡すと、9つのモデルとベンチマークの組み合わせのうち7つでは、隠しテストまで通る割合が改善した。平均差は19.3ポイントだった。一方、残る2条件では成績が下がった。ある条件は45.5%から41.8%へ、別の条件は89.1%から81.8%へ落ちている。
取り上げるのは、arXiv論文「Security Tests as Executable Specifications for LLM Code Generation: Benefits, Trade-offs, and Coverage Limits」だ。SecTDDという実験枠組みを使い、セキュリティテストを「採点の道具」ではなく「実行できる仕様」として、生成前と修正時のどこに置くべきかを分解している。
結論を先に言えば、テストを見せること自体には大きな価値がある。ただし、見えるテストに合格したことと、安全な実装になったことは同じではない。AIコーディングの運用で必要なのは、テストの全量投入ではなく、見える仕様、限定された修正ループ、独立した隠し評価を分離する設計だ。
問題は、機能合格と安全合格が別々に動くこと
通常のコード生成評価は、期待した出力が返るかを中心に見る。しかし、脆弱性のあるコードも機能テストには通り得る。データベース検索で正しい結果を返していても、入力の扱いが悪ければ不正な操作に弱い。逆に、危険な入力をすべて拒否するだけなら安全そうに見えるが、本来受け付けるべき入力まで壊してしまう。
つまり、評価には少なくとも2つの軸がある。
1つ目は機能だ。要求された振る舞いを満たすか。2つ目はセキュリティだ。攻撃入力や境界条件でも、守るべき性質を維持できるか。片方だけを上げる修正は、実用上の成功とは呼べない。
SecTDDは、この2軸を可視と隠しの2区画に分けた。生成中に使えるのは可視テストだけで、最終評価に使う隠しテストは候補コードを凍結した後に一度だけ実行する。この分離が重要だ。隠し結果を修正プロンプトへ戻してしまうと、モデルが本当に一般化したのか、採点項目を暗記したのか区別できなくなる。
論文が報告する主な指標は Hidden Secure-Pass@1 である。隠し機能テストと隠しセキュリティテストの両方を通った割合を見る。見栄えのよい安全率だけでなく、機能を壊して安全そうにした失敗も落とせる設計だ。
SecTDDは3つの判断を切り分けた
この研究の新しさは、テスト駆動を1つの手法として丸ごと比較しなかったことにある。次の3つを別々の判断として扱った。
- 生成前のプロンプトに、要件だけを置くか、機能テストやセキュリティテストも置くか
- 可視テストに失敗したとき、修正を許すか
- 失敗を全部そのまま返すか、1件だけ返すか、セキュリティ優先で構造化して返すか
基準条件のB0は要件だけを見せ、修正を行わない。B3は要件と可視テストを最初から見せる。B4はB0と同じ初回出力を使い、失敗ログを固定順で全部返す。Mは同じ初回出力から、セキュリティ失敗を優先した構造化フィードバックを返す。B6はB3の初回出力に構造化フィードバックを加える。
修正は最大2回に制限される。B4、B5、Mは、B0が生成したバイト単位で同一の初回候補から始める。この工夫により、初回生成の偶然ではなく、どのフィードバックが同じ失敗候補を修復したかを比較しやすくしている。

実験はCWEval、SALLM、CodeGuard+の監査済み部分集合を使った。一次証拠は2,705軌跡、31課題、16種類のCWEにまたがる。ローカルのQwen系4構成は7Bから32B、外部APIはDeepSeek-V4-Flashを使い、各条件を5回反復した。
外部ベンチマークには、必要な可視と隠しの動的オラクルが揃っていないものもあった。研究チームは安全な実装、機能を保った脆弱な変異、反復実行、振る舞いの分離、機械による意味レビューを組み合わせて補助層を作り、最後にソフトウェア工学の博士課程3人が凍結済み資料を確認している。丁寧な手順だが、機械支援で作られた評価層であることは限界として残る。
最初に全部見せると平均19.3ポイント改善した
要件だけのB0と、可視機能テストと可視セキュリティテストを最初から見せるB3を比べると、B3は9条件中7条件で良かった。ベンチマークとモデルを同じ重みで平均した差は19.3ポイントである。
改善幅が大きい例もある。CWEvalとQwen3-32Bの組み合わせは22.2%から73.3%へ上昇した。SALLMとQwen3.6-27Bは60.0%から90.9%、SALLMとDeepSeek-V4-Flashは52.7%から81.8%へ伸びた。
自然言語で「不正な検索入力を防ぐ」とだけ書くより、危険な入力に対する期待結果を実行可能な例で示す方が、モデルにとって具体的な仕様になる。ここまでは、テスト駆動の支持材料に見える。
しかし、2条件は悪化した。SALLMとQwen2.5-Coder-7Bでは45.5%から41.8%へ低下した。機能面は上がった一方、安全率が74.5%から56.4%へ落ちている。CodeGuard+とDeepSeek-V4-Flashでは89.1%から81.8%へ低下した。可視テストをすべて通過しながら、隠されたデータ整合性の振る舞いで失敗する例もあった。
テストを見せるほど情報は増える。だが、小さいモデルでは注意が分散し、見える例へ局所適合する可能性がある。大きいモデルでも、可視の攻撃例が脅威空間を覆っていなければ、合格は安全性の証明にならない。
論文の結果から確実に言えるのは、「可視テストは多くの場合に有用」までだ。「全部見せれば常に安全」は言えない。
修正では、きれいな説明より実行失敗が効いた
次に重要なのは、失敗後の修正である。構造化フィードバックMは、セキュリティ失敗を先にし、まだ返していない失敗を優先し、1回に最大2件へ絞る。各項目には機能かセキュリティかの印、実行可能な性質の説明、失敗出力の末尾が含まれる。
このMは80件の失敗候補を修復し、隠し機能と隠しセキュリティの共同成功を失う後退は0件だった。一方、固定順で失敗ログを全部返すB4は83件を修復したが、共同成功を失う後退が3件あった。
数字だけ見れば、構造化方式が圧勝したわけではない。465の対応セルで直接比較すると、構造化方式が勝ったのは6、固定ログ方式が勝ったのも6だった。多くのセルでは結果が同じである。
ここから見えるのは、凝った文章形式より、実行した失敗を返すこと自体の効果だ。構造化は魔法ではない。それでも、セキュリティ失敗を先に扱い、情報量を制限する設計は、機能を壊したり別の脆弱性を入れたりする後退を抑える安全柵として意味がある。

さらに、CodeGuard+から結果を見ずに選んだ11課題の追加評価では、110個のB0セルのうち修正が発火したのは21セルだけで、修復は4件だった。可視テストをすでに通る候補には、隠し攻撃で失敗していてもフィードバックが発生しない。可視範囲の外側は、修正ループから見えないままだ。
AICompanyの解釈。テストを3つの所有者に分ける
ここからは論文の主張ではなく、AICompanyの運用への翻訳である。
AIコーディングで機能テストとセキュリティテストを1つのフォルダへ集め、すべてをモデルに渡すだけでは不十分だ。テストを役割と所有者で3層に分ける方がよい。
第1層は生成仕様だ。正常系、境界値、代表的な攻撃を少数だけ選び、要件と一緒にモデルへ渡す。ここでは、何を実装すべきかを具体化する。
第2層は修正用フィードバックだ。失敗時に、機能かセキュリティか、守るべき性質は何か、観測された差は何かを短く返す。1回に返す量と修正回数へ上限を置く。
第3層は独立評価だ。別担当が保有する回帰テスト、未公開の攻撃族、静的解析、依存関係検査などを置く。結果は候補を採用するか捨てるかの判断に使い、同じ生成セッションへ際限なく戻さない。
この分割には2つの利点がある。1つは、可視テストへの過剰適合を発見しやすいこと。もう1つは、モデルが失敗ログを追いかけ続けてコードを複雑にする修正ループを止められることだ。
重要なのは、隠すこと自体ではない。作成者、修正者、評価者を分け、同じ証拠を同じ目的で使い回さないことにある。人間の開発でも、実装者が自分のテストだけで本番安全性を保証しないのと同じだ。
小さく試すなら、同じ初回候補を4分岐させる
自社でSecTDDの考え方を試すために、いきなり2,705回の実験は要らない。まず1つの小さな関数と、1種類の脆弱性で差を測れる。
たとえば、ユーザー入力から検索条件を作る関数を対象にする。正常入力と空入力の機能テスト、典型的な攻撃入力の可視セキュリティテスト、形式の違う攻撃や文字コード境界を含む隠しテストを用意する。
次の4条件を、同じモデル設定、同じ予算、複数の乱数種で比較する。
- 要件だけで生成し、修正しない
- 要件と可視テストを最初に渡し、修正しない
- 条件1と同じ初回候補へ、失敗ログを固定順で返して最大2回修正する
- 条件1と同じ初回候補へ、セキュリティ優先で最大2件の失敗を返して最大2回修正する
見るべき数字は、可視テスト合格率だけではない。隠し機能と隠しセキュリティの共同成功率、修正で直った件数、修正で壊れた件数、モデル呼び出し回数を並べる。初回候補を共有すれば、生成の偶然と修正方法の差を切り分けやすい。
この試験で条件2が条件1より良くても、すぐに全テストを投入する方針へ変えない方がよい。異なる脆弱性、入力形式、小さいモデルでも繰り返し、悪化する条件を先に探す。平均改善より、悪化が起きる境界を知る方が運用設計には役立つ。
この研究がまだ証明していないこと
第一に、31課題のコード生成実験であり、大規模な既存リポジトリの変更ではない。依存関係、ビルド時間、複数ファイルの整合性、レビュー履歴がある実務へ、19.3ポイントという差をそのまま移すことはできない。
第二に、モデルはQwen系4構成とDeepSeek-V4-Flashである。ほかのオープンモデルやプロプライエタリなコーディングエージェントで同じ方向になる保証はない。外部APIのサーバー改訂は公開されないため、研究チームも応答、日付、モデル識別子を保存する方法を取っている。
第三に、外部ベンチマーク用の評価層には機械支援が入っている。3人の博士課程研究者が凍結資料を確認しているが、人手だけでゼロから構築したオラクルではない。
第四に、匿名の再現パッケージにはプロトコル、プロンプト、2,705軌跡、分析スクリプトなどが含まれる一方、正式公開は採択後の予定とされている。現在のURLが長期保存されるとは限らない。
第五に、生成コードの実行にはBubblewrapを使うが、論文自身が、非特権サンドボックスは強化VMと同じではないと注意している。未検証コードを実行するなら、ネットワークとファイルシステムを分離した専用環境が必要だ。
最後に、隠しテストも完全な安全証明ではない。未知の攻撃、仕様の欠落、依存ライブラリの脆弱性、権限設計の誤りは、用意したテストの外に残る。SecTDDが改善するのは、定義したオラクルに対する検証手順であり、脅威モデルそのものを自動で完成させる技術ではない。
結論。テストは採点表ではなく、分離して運ぶ仕様になる
SecTDDの価値は、AIへテストを見せるべきかという二択を、実験可能な設計問題へ変えたことにある。
可視の機能テストとセキュリティテストは、多くの条件で隠し共同成功を改善した。実行失敗を使う修正も有効だった。しかし、全テストの先出しは2条件で悪化し、可視範囲の外にある失敗は修正を発火させなかった。
したがって、実務で採るべき形は単純な全量投入ではない。生成前に見せる実行仕様、回数を制限した修正フィードバック、独立所有の隠し評価を分ける。候補を凍結してから最後の評価を行い、失敗なら公開しない。
AI生成コードの安全性は、プロンプトの一文では作れない。どの証拠を、いつ、誰が使うか。その境界を設計して初めて、テストは安全性へ近づくための仕様になる。

