Copilot Autofixが作った穴でSnowflakeのJiraが陥落 — AI生成コードとGitHub Actionsインジェクション
はじめに
セキュリティ企業 Wiz が公開した調査レポートが、Hacker News で 207 ポイントを集めている。内容を一行で言えば、GitHub Copilot Autofix を co-author とするコミットが作り込んだ脆弱性を経路に、Snowflake の Jira 認証情報が外部へ流出したという事例だ。
この話が刺さるのは、単に大企業がやられたからではない。Autofix は本来「検出された脆弱性を AI が自動修正する」機能であり、セキュリティを高めるために存在する。その機能が脆弱性を新規に作り込んだ。そして発見して実際に突いたのも、Wiz の自律型 AI である「Red Agent」だった。攻守どちらも AI という構図になっている。
本記事では、この攻撃チェーンを一次情報から追ったうえで、GitHub Actions スクリプトインジェクションという古典的な穴がなぜ 2026 年になっても踏まれるのか、そして AI にコードを書かせる前提の開発で本当に効く対策は何かを整理する。
何が起きたか — 攻撃チェーンの再構成
Wiz のレポートによれば、時系列はこうだ。
2026年6月18日、Snowflake のリポジトリに PR #1218 がマージされる。タイトルは SNOW-2069227: Update jira workflows、コミットの co-author は Copilot Autofix powered by AI。対象は jira_issue.yml という、GitHub Issue が立つと Jira にチケットを作る CI ワークフローだった。
問題は変更の中身だ。それ以前のコードは env: でタイトルを環境変数に渡し、jq -n --arg title で JSON を組み立てる、安全な書き方をしていた。この変更は jq を丸ごと取り除き、シェルへの直接文字列展開に置き換えている。結果として残ったのが、次の形のコードだ。
run: TITLE=$(echo '${{ github.event.issue.title }}' | sed ...)
${{ }} は Actions ランナーがシェル実行前にテキスト置換する。つまり Issue のタイトルがそのままシェルスクリプトの一部として貼り付けられる。シングルクォートで囲ってあるように見えるが、タイトル側に ' を含めれば閉じて抜け出せる。
ワークフローのトリガーは issues: opened。誰でも Issue を立てられる公開リポジトリでは、これは実質「未認証の第三者が任意コマンドを送り込める」状態を意味する。
ここに二重のミスが重なる。ガード条件として github.event.pull_request.user.login != 'whitesource-for-github-com[bot]' という判定が置かれていたが、Issue イベントでは pull_request フィールドが常に null になる。つまりこの条件は常に真、誰も弾かない。セキュリティゲートの体裁だけが残っていた。
攻撃者(この場合は Red Agent)は、クォートを抜けて curl を実行し、環境変数を base64 化して外部ドメインへ送るタイトルを投げる。奪われたのは JIRA_API_TOKEN / JIRA_USER_EMAIL / JIRA_BASE_URL。このトークンは qa@snowflake.net として認証され、エンジニアリング、セキュリティコンプライアンス、そしてバグバウンティ管理のプロジェクトまで読める権限を持っていた。
なお Red Agent は最初の試行で bash の構文エラーに遭遇し、自分でペイロードを組み直して成功させている。この「失敗を見て適応する」挙動こそ、従来のスキャナと自律エージェントを分ける点だ。
Snowflake の対応は速い。6月23日に Wiz が報告し、同日中にコミット 1dc7766 で修正。翌24日にトークンをローテーションしている。公開は7月25日の期限を経てのものだ。
なぜ古典的な穴が踏まれたのか
GitHub Actions のスクリプトインジェクションは新種の脆弱性ではない。GitHub 公式ドキュメントも、title / body / head_ref / label / message などで終わるコンテキスト値は攻撃者が制御可能であり、run: に直接展開してはいけないと明記している。正しい書き方も確立している。
- name: print title
env:
TITLE: ${{ github.event.issue.title }}
run: echo "$TITLE"
env: 経由なら、値はシェル変数として渡るためコードとして解釈されない。JSON を組むなら jq -n --arg を使う。メルカリをはじめ複数社が社内ガイドラインとして公開している程度には、既知の作法だ。
そして冒頭で見たとおり、Snowflake は元々この安全な書き方をしていた。知識がなかったのではなく、正解が書かれていたファイルが、あとから正解でない形に書き換えられた。これは「知らずに書いた」タイプの脆弱性とは、防ぎ方がまったく違う。
にもかかわらず今回踏まれた理由は、技術的な難しさではなくレビューの文脈にあると考えている。
第一に、この変更はセキュリティ案件の顔をしていなかった。PR タイトルは Update jira workflows、チケット番号付きの、どこにでもある保守作業だ。AI が関与した痕跡はコミットの co-author 行だけで、差分そのものは「ワークフローの整理」にしか見えない。レビュアーが身構える種類の PR ではない。AI 生成コードのリスクを「AI が書いた PR を警戒する」という運用で防ごうとしても、そもそもそれが AI 由来だと画面上でほとんど主張しないのなら、警戒のしようがない。
第二に、AI は「動く形」に最適化しやすい。jq や env: を介した構造化された受け渡しより、シェルで直接文字列を組むほうが短く、パッと見は読みやすい。可読性の改善に見える変更が、セキュリティ境界を溶かしていた。
第三に、CI ワークフローは多くの組織でレビューの死角にある。アプリケーションコードには静的解析もレビュー基準もあるが、.github/workflows/ 配下は「動けばよい設定ファイル」として扱われがちだ。そこに本番の Secrets が置かれている。
エンジニアへの影響 — 何を変えるべきか
「AI 生成コードのレビューを厳しくする」は正論だが、対策としては弱い。生成量は今後も増える一方で、レビューの厳しさは人間の集中力に依存する。持続しない対策は対策ではない。効くのは構造側の変更だ。
1. 変更の種類に応じてゲートを変える。 .github/workflows/ や IaC への差分は、AI 生成かどうかを問わず必ず人間の承認を要求する。加えて zizmor や Semgrep のような Actions 向け静的解析を CI に組み込み、${{ }} の run: 内直接展開を機械的に落とす。人の注意力ではなくルールで止める。
2. AI に「安全な書き方」を制約として与える。 構造化パーサ(jq -n --arg、env:)を文字列補間に置き換える変更を禁止事項として明文化し、リンタと CLAUDE.md / Copilot instructions の両方に書く。今回のコミットは、まさにこの逆をやった。すでに安全に書かれているコードを、より単純な形に「整理」させないというルールは、AI に渡す制約として過小評価されている。
3. 認証情報を「盗んでも短時間しか使えない」ものにする。 これが本丸だ。今回流出した Jira トークンは長期有効で、権限も広かった。同じ日のダイジェストに並んだ 2 本の記事が、ちょうどこの処方箋になっている。ひとつは RFC 8693 のトークン交換(OBO)を使い、id_token からサービスごとの access_token を都度発行し、要求権限と保有権限の積までスコープを絞り込む設計。Amazon Bedrock AgentCore は 2026年4月から対応済みだ。もうひとつは Claude Code のサンドボックスで、環境変数を sentinel(偽の認証情報)に差し替え、外側のプロキシが SigV4 を再署名することで、エージェントに本物の鍵を一切見せない構成である。
方向性は共通している。エージェントや CI が触れる場所に、長期・広範囲の鍵を置かない。 インジェクションを 100% 防ぐより、成立したときの被害を小さくするほうが現実的だ。
まとめ
- Copilot Autofix を co-author とするコミットが
env:+jq -n --argの安全な実装をシェル文字列展開に置き換え、issues: openedで発火する Actions にインジェクション穴を作った - ガード条件は Issue イベントで常に null になるフィールドを見ており、機能していなかった
- Wiz の自律型 Red Agent が構文エラーから自己修正して成功し、Jira の API トークンを外部へ送出した
- 対策の本命は「AI のレビューを厳しくする」ではなく、ワークフロー変更へのゲート強制と、短命・最小スコープの認証情報
- 発見も攻撃も AI が担う時代に入った以上、防御側も検出を自動化しない限り速度で負ける
AI にコードを書かせること自体は止まらない。問われているのは、AI が間違えた前提で被害を封じ込める設計になっているかどうかだ。まずは自分のリポジトリの .github/workflows/ を ${{ github.event. で検索するところから始めたい。
今日のその他のニュース
A Preview of DuckDB v2.0(HN 365pt) DuckDB のメジャーバージョン 2.0 のハイライトが先行公開。前日には非同期 I/O 実装の記事も上位に入っており、ローカル分析エンジンとしての成熟が続いている。ローカル完結の分析基盤を組むなら、破壊的変更を含む 2.0 の差分は事前に押さえておきたい。
Salesforce 公式 Agent Skills 全 148 個のチートシート(Zenn) Salesforce が実運用向けに公開した Agent Skills 148 個の一覧。Skill をどの粒度で切り、description に何を書くかを大企業の実物カタログとして参照できる。自前の Skill 設計を見直す際の比較対象になる。
Claude Code の hook で作る「書いたら直る」ループ(Zenn) PostToolUse hook でコード編集直後に自動修正を走らせる構成。本記事の文脈で言えば、AI の出力を人間のレビュー前に機械的なルールで矯正する層をもう一枚挟む、という発想に相当する。
ソース
- AI-Generated GitHub Copilot “Autofix” Allowed Compromise of Snowflake’s Jira — Wiz
- Script injections — GitHub Docs
- Secure use reference — GitHub Docs
- How to catch GitHub Actions workflow injections before attackers do — The GitHub Blog
- 社内用GitHub Actionsのセキュリティガイドラインを公開します — メルカリエンジニアリング
- AIエージェントの「認可疲れ」に効く処方箋:理論から実装まで — Zenn
- エージェントに AWS の鍵を渡さず AWS を叩かせる方法 — Qiita