powered by TechFeed
表示モード
ハウツー

AIが生成したコードは「動く」が「安全」とは限らない — マージ前に必ず確認すべき7つのチェックリスト

10月19日、Kanwal Mehreen氏が「7 Checks to Run Before Trusting AI-Generated Code」と題した記事を公開した。この記事では、AIが生成したコードをマージする前にエンジニアが実施すべき7つの具体的な検証ステップが紹介されている。

10月19日、Kanwal Mehreen氏が「7 Checks to Run Before Trusting AI-Generated Code」と題した記事を公開した。この記事では、AIが生成したコードをマージする前にエンジニアが実施すべき7つの具体的な検証ステップが紹介されている。


GitHub CopilotやClaude Codeといった AIコーディングアシスタントの普及で、「動いているように見えるが、実は危ないコード」が増えている。問題は、そういったコードがコンパイルを通過し、テストも通り、変数名もきれいである点だ。「見た目で判断する」では不十分であり、チェックリストが必要になる。

なかでも後述するチェック3「パッケージの幻覚」は、単なるバグではなくサプライチェーン攻撃に直結するリスクを抱えており、7つの中でも特に警戒が必要だ。

コードが増えすぎていないか:まずdiffを見る(チェック1)

AIエージェントにリポジトリ全体へのアクセスを与えると、タスクの範囲を超えて変更することがある。「メールのバリデーションを追加して」と指示したのに:

12 files changed, 437 insertions(+), 186 deletions(-)

このような差分が出たら、コードの中身を見る前に「なぜこれほど多くのファイルが変わったのか」を問うべきだ。特に以下を確認する:

  • タスクと無関係なファイルの変更
  • テストの削除やバリデーションの無効化
  • 設定ファイルや依存関係ファイルの変更
  • フィーチャーに紛れ込んだ大規模リファクタリング
git diff --stat
git diff

型チェッカーとリンターに任せる(チェック2)

人間が目視で構文チェックする必要はない。以下のツールに任せる:

# Python
python -m compileall .
ruff check .
mypy .

# TypeScript/JavaScript
npm run lint
npx tsc --noEmit

型チェックが特に有効な理由は、AIが「見た目は合っているが型が合っていない」値を平然と接続するからだ。たとえば dict[str, str] を受け取って int を返すと宣言した関数は、型チェッカーがすぐ検出できる。

最重要:依存パッケージとAPIの幻覚を見抜く(チェック3)

7つの中で最も見落とされやすく、かつ被害が大きいのがパッケージの幻覚(ハルシネーション)だ。AIは存在しないパッケージ名を、もっともらしい名前で提案することがある。たとえば:

pip install fastapi-jwt-auth-plus

この名前は一見、本物のパッケージに見える。しかし実在しない可能性がある。

2025年の研究によると、576,000件のLLM生成コードサンプルを分析した結果、パッケージの幻覚率は商用モデルで5.2%、オープンソースモデルで21.7%に達した。さらに深刻なのが「スロップスクワッティング(slopsquatting)」と呼ばれる攻撃手法だ。AIが頻繁に幻覚するパッケージ名を攻撃者があらかじめ登録しておき、開発者が誤ってインストールするのを待つ。単純なバグではなく、サプライチェーン攻撃として機能しうる点が特に危険だ。

AIが追加した依存関係は必ず以下で独立して検証する:

# Python
pip index versions package-name

# npm
npm view package-name

確認すべき項目は「パッケージが実在するか」「公式メンテナーは誰か」「最終更新日はいつか」「既知の脆弱性はないか」など。

APIも同様だ。client.responses.stream_async(...) のようなメソッドが書かれていても、SDKの他の部分に似ているからといって実在する保証はない。必ず公式ドキュメントで クラス → メソッド → パラメータ → 戻り値 → 対応バージョン を確認する。

ハッピーパスだけでなく、境界値・異常系を叩く(チェック4)

AIが生成するテストは「正常系」に偏りやすい。割引計算の関数に対して discount=10 のテストだけが書かれていたとしても、discount=0、discount=-10、discount=150、price=None などは別途テストする必要がある。

すべての機能に対して最低4カテゴリを検証する:正常入力 / 境界値入力 / 不正入力 / 障害条件。

また「AIがテストを削除したり、アサーションを弱めたりして失敗を隠していないか」も確認ポイントだ。

セキュリティ境界を攻撃者の視点で見る(チェック5)

動作するコードと安全なコードは別物だ。以下は全ユニットテストを通過しうるが:

query = f"SELECT * FROM users WHERE email = '{email}'"
cursor.execute(query)

SQLインジェクションの脆弱性がある。ユーザー入力・認証・ファイルパス・シェルコマンド・外部URLが絡む箇所は必ず攻撃者の視点で確認する。静的解析ツールとして Semgrep や Bandit が使える。

また、APIキーや認証情報がハードコードされていないかも確認が必要だ。GitHubのシークレットスキャニングも活用できる。

失敗時の挙動を意図的に壊して確認する(チェック6)

except Exception:
    return {}

このような「広すぎるエラーハンドリング」は、タイムアウト・認証エラー・不正なJSONなど、あらゆる障害を同じ空のレスポンスに変換してデバッグを困難にする。データベースを止めたり、APIに意図的にHTTP 500を返させたりして、「予測可能な形で失敗するか」を確認する。

コードが存在すべきかを問う(チェック7)

最後のチェックは最も省略されやすい。AIはラッパー関数・設定レイヤー・抽象化・重複ユーティリティを無用に生成する傾向がある。

# AI生成
def transform_items(items):
    output = []
    for item in items:
        transformed_item = transform_single_item(item)
        output.append(transformed_item)
    return output

# 本来これで十分
[item["name"] for item in items]

目指すのは「行数の最小化」ではなく「新たな複雑性の最小化」だ。最重要な生成関数について、「何が入力か」「何が出力か」「何の状態が変わるか」「何が失敗しうるか」「なぜこのアプローチか」をAIなしで説明できなければ、本番環境に持ち込む準備ができていない。


AIコーディングツールはコード生成のコストを劇的に下げる。しかしコードの正しさを証明するコストは下げない。適切なワークフローは「生成して信頼する」ではなく、「生成して検証する」だ。

詳細は7 Checks to Run Before Trusting AI-Generated Codeを参照していただきたい。