チェーンと南京錠で固定されたスマートフォン
ニュース深掘り

GitHub Copilot Autofixが招くシェル注入とCI/CD設定の点検法

目次を見る

GitHub Actionsのワークフロー定義をCopilotなどのAIツールに任意で修正させている、あるいはAutofix(GitHubが提供する自動脆弱性修復機能)を有効にしているチームに向けた内容です。CI/CD(継続的インテグレーション・デリバリー)のパイプライン設定は、AIによる「きれいなリファクタリング」がそのまま新しい脆弱性になり得る領域だと分かってきました。その仕組みと確認方法を整理します。

何が起きたか

Snowflakeが管理するコネクタ用リポジトリで、GitHub Actionsのワークフロー内にあるシェル実行ブロックから、入力値のサニタイズ処理(危険な文字を無害化する処理)が消えるコミットが発生しました。

この変更を提案したのはGitHub Copilot Autofixです。既存の技術的負債を自動で修正するツールで、冗長に見えるコードを短くまとめる挙動を持っています。

変更前は、ユーザー入力から ;& | などの記号を正規表現で除去してからシェルに渡していました。変更後はその除去処理が消え、変数がそのままシェルに展開される形になりました。

ローカルテストは通ります。正常系の挙動も変わりません。しかし異常系、つまり悪意ある文字列が渡された場合の挙動が根本的に変わってしまいました。

5日後、別のAIエージェントがこの穴を見つけます。Wizが運用する攻撃側の自律型セキュリティエージェントが、通常のスキャン業務の中でこのワークフローの欠陥を検出しました。GitHubのIssueタイトルに細工した文字列を入れ、echo文の中から脱出する形でコマンドを注入し、GitHub Actionsのランナー(ワークフローを実行する仮想マシン)上で任意コマンドを実行しました。最終的にJiraの認証情報が外部にコールバック経由で持ち出されています。人間の判断は一切介在していません。

なぜ起きるのか

原因を分解すると、AIが生成・修正するコードに特有の3つの性質が見えてきます。

1つ目は、差分(diff)が「もっともらしい」ことです。文字数が減り、同じ処理をしているように見えるコードは、人間のレビュアーには「クリーンアップ」に映ります。サニタイズ処理が周辺パターンの中に暗黙に組み込まれていた場合、それを削る変更はリファクタリングにしか見えません。

2つ目は、意図としては正しいことです。Copilot Autofixが提案したコードは、依頼された処理を正しく実行します。ただし「この文字列は決してサニタイズされずにシェルに渡らない」という安全性の性質は、そもそも依頼事項ではありませんでした。これは元のコードが偶発的に持っていた副次的な性質であり、AIの自動修正ツールが最適化の対象にしていない部分です。

3つ目は、影響範囲がローカルでは見えないことです。GitHub Actionsのワークフローは、Issueタイトルやコミットメッセージなど外部から渡る文字列をテンプレート展開で扱うことが多く、単体のコードレビューでは攻撃面が見えにくい構造になっています。

この一連の流れが示すのは、AIがコードを書き、別のAIがそのコードの脆弱性を探すという構図が、すでに現実に動いているという事実です。PRのレビューキューが処理される時間よりも、攻撃側AIが穴を見つける時間の方が短くなり得ます。

自分のプロジェクトが該当するか確認する

まず、リポジトリでCopilot Autofixが有効になっているかを確認します。GitHubリポジトリの Settings > Code security and analysis を開き、「Copilot Autofix」または関連するコードスキャンの自動修正提案が有効になっているかを見てください。

次に、GitHub Actionsのワークフローファイル(.github/workflows/*.yml)の中で、run: ブロックに外部入力が直接展開されていないかを確認します。特に危険なのは以下のようなパターンです。

# 危険なパターン: Issueタイトルやコミットメッセージが直接展開される
- name: Process input
  run: echo "${{ github.event.issue.title }}"

この ${{ }} 構文(GitHub Actionsのコンテキスト展開)が run: ブロックのシェル文字列の中に直接置かれている場合、Issueタイトルなどの外部入力が任意のシェルコマンドとして解釈される余地があります。

リポジトリ内でこのパターンを機械的に洗い出すには、以下のようなgrepが使えます。

grep -rn 'run:.*\${{' .github/workflows/

ヒットした箇所は、外部から改変可能な値(Issueタイトル、PRのブランチ名、コミットメッセージなど)がテンプレート展開に含まれていないか、1件ずつ目視で確認する必要があります。

対策の手順

1つ目の対策は、外部入力を環境変数経由で渡すことです。run: ブロックの中に直接テンプレート展開を書かず、env: に一度落としてからシェル内で参照する形にします。

- name: Process input safely
  env:
    ISSUE_TITLE: ${{ github.event.issue.title }}
  run: |
    echo "$ISSUE_TITLE" | process

環境変数経由にすると、シェルのテンプレート展開の段階でコマンド注入が発生する経路が塞がれます。これはGitHub公式のセキュリティガイドでも推奨されているパターンです。

2つ目は、Copilot AutofixやAIによる自動PRを、通常のPRと同じ扱いにしないことです。AIが提案した差分には「セキュリティ関連の変更を含む可能性がある」というラベルを付け、人間のセキュリティレビューを必須にするルールをブランチ保護設定に加えることを検討してください。

3つ目は、CIパイプライン自体の権限を最小化することです。ワークフローの permissions: キーで、必要最小限のスコープ(例えば contents: read のみ)に絞ります。仮に注入が成功しても、ランナーが持つ権限が小さければ被害範囲を抑えられます。

4つ目は、静的解析ツールでワークフローファイルそのものをスキャンすることです。actionlint などのツールはGitHub Actionsのワークフロー定義を対象にした静的解析を行い、危険なテンプレート展開のパターンを検出できます。CIの中に組み込んでおくと、AIが提案した変更もマージ前に機械的にチェックされます。

5つ目は、AIエージェントによる能動的なスキャンを、攻撃側が使う前に自分たちで使うことです。Wizのような攻撃者視点のセキュリティエージェントが実在する以上、同種のツールを社内のCI/CDに対して定期的に走らせ、公開前に穴を塞ぐ運用が現実的な防御ラインになります。

まとめ

AIが書いたコードは「動く」ことと「安全」であることが別の軸だという前提で扱う必要があります。

確認すべきことは3つです。リポジトリでCopilot Autofixが有効か、.github/workflows/ 内に外部入力の直接展開がないか、ワークフローの権限が最小化されているかです。

特に grep -rn 'run:.*\${{' .github/workflows/ は今すぐ実行できる確認コマンドです。ヒットがあれば、その箇所から着手してください。

AIが生成したコードを「読みやすいから安全」と判断せず、外部からの依存物として扱う姿勢が、この種の機械対機械の攻防に対する最初の防御線になります。

参考

AI-Generated Code Vulnerability Exploited by Autonomous Agent in Snowflake

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。