Injeksi Skrip GitHub Actions: Apa yang Sebenarnya Ditunjukkan Kasus Snowflake

Teks issue yang tidak tepercaya menjadi kode shell dalam alur kerja GitHub Actions Snowflake. Jalur injeksinya konvensional meskipun agen otonom menemukan dan menyesuaikannya.

Bagikan artikel ini

Judul issue GitHub publik menjadi kode shell yang dapat dieksekusi di dalam otomasi sebuah repository Snowflake. Menurut perusahaan keamanan Wiz, Red Agent otonomnya menemukan jalur tersebut, menggunakannya untuk memperoleh kredensial Jira internal, lalu melaporkannya kepada Snowflake pada 23 Juni 2026. Riwayat repository publik mengonfirmasi bahwa alur kerja itu diperbaiki pada hari yang sama; Wiz mengatakan Snowflake juga merotasi kredensial tersebut.

Pelajaran praktisnya bukan bahwa satu AI mengalahkan AI lain. Masalahnya adalah alur kerja CI melewati tiga batas kepercayaan tanpa memperlakukannya sebagai batas: masukan publik masuk ke skrip yang dihasilkan, skrip itu berjalan pada worker berhak istimewa, dan job tersebut memegang kredensial untuk layanan lain.

Ini adalah injeksi skrip GitHub Actions: data yang dikendalikan penyerang dimasukkan ke dalam kode sebelum shell runner dimulai. Memberi tanda kutip pada ekspresi di YAML tidak menyelesaikan masalah. Perbaikan yang andal adalah menjaga nilai yang tidak tepercaya tetap berada di luar kode yang dihasilkan, lalu membatasi apa yang dapat dijangkau jika runner berhasil dikompromikan.

Bagaimana injeksi skrip GitHub Actions terjadi

Snowflake connector PR #1218 mengubah alur kerja yang menyalin issue GitHub publik ke sistem Jira perusahaan. Pull request tersebut digabungkan pada 18 Juni 2026. File jira_issue.yml di dalamnya menangani judul dalam bentuk berikut:

run: |
  TITLE=$(echo '${{ github.event.issue.title }}' | sed ...)

Potongan ini sengaja disingkat. Tidak diperlukan payload eksploit untuk memahami kegagalannya.

GitHub memproses ekspresi ${{ ... }} saat menyiapkan job. Dokumentasi injeksi skripnya menjelaskan bahwa nilai hasilnya disubstitusikan ke dalam skrip sementara sebelum shell mengeksekusinya. Jika judul issue publik berisi sintaks shell yang mengakhiri string bertanda kutip, perintah sed setelahnya tidak pernah mendapat kesempatan untuk membuat nilai tersebut aman. Shell sudah mem-parsing kode yang dikendalikan penyerang.

Pemicu tersebut memperluas paparan. Alur kerja berjalan untuk issue yang baru dibuka, sehingga pengguna GitHub biasa dapat memberikan judulnya. Ekspresi if tampak mengecualikan bot, tetapi Wiz melaporkan bahwa field pull request yang dirujuk bernilai null untuk event issue, sehingga job terbuka bagi setiap pembuat issue.

  1. Masukan publik

    Setiap pengguna GitHub dapat memasukkan judul issue.

    Kontrol: Batasi pemicu.

  2. Ekspansi templat

    GitHub menempatkan judul itu ke dalam skrip yang dihasilkan.

    Kontrol: Gunakan variabel lingkungan.

  3. Eksekusi runner

    Shell menafsirkan teks yang dihasilkan sebagai kode.

    Kontrol: Lint alur kerja dan beri tanda kutip pada data.

  4. Batas berhak istimewa

    Job mengekspos kredensial untuk layanan Jira internal.

    Kontrol: Persempit jangkauan token dan jaringan.

Injeksi membuka batas runner; kredensial job dan layanan yang dapat dijangkau menentukan luas dampaknya. Diagram penjelas orisinal ini didasarkan pada alur kerja publik dan pengungkapan yang diatribusikan kepada Wiz.

Repository publik mengonfirmasi perubahan yang rentan dan perbaikannya. Bagian privat dari insiden ini lebih sulit diamati secara independen. Pengungkapan Wiz menyatakan bahwa agennya menyesuaikan proof-of-concept yang gagal, memperoleh token Jira milik akun QA, dan memvalidasi akses baca di seluruh proyek engineering, compliance, dan bug bounty internal. Wiz juga mengatakan log audit Snowflake tidak menemukan akses pihak ketiga selama jendela paparan lima hari dan bahwa Wiz menghapus data yang diaksesnya. Kesimpulan mengenai dampak dan forensik ini berasal dari Wiz dan Snowflake; saat meneliti artikel ini, saya tidak menemukan laporan insiden independen yang tersedia untuk publik.

Pola yang lebih aman menjaga data tetap di luar sintaks shell

Remediasi Snowflake pada 23 Juni mengembalikan batas variabel lingkungan dan menggunakan jq untuk membangun JSON:

env:
  ISSUE_TITLE: ${{ github.event.issue.title }}
run: |
  PAYLOAD=$(jq -n --arg title "$ISSUE_TITLE" '{summary: $title}')

Di sini, GitHub menulis judul ke dalam variabel lingkungan, bukan ke program shell yang dihasilkan. Skrip shell berisi teks tetap "$ISSUE_TITLE", dan kutipan shell biasa meneruskan nilai tersebut sebagai satu argumen. Kemudian jq --arg melakukan encoding JSON, bukan rangkaian penggantian yang dibuat secara manual.

Perbedaan ini mudah terlewat: variabel lingkungan adalah batas data, bukan sanitizer ajaib. Perintah berikutnya masih dapat menyalahgunakannya. Kutip ekspansi shell, teruskan nilai sebagai argumen terstruktur, dan hindari mengevaluasi string yang dibangun.

Referensi penggunaan aman GitHub merekomendasikan variabel lingkungan perantara ketika action khusus tidak dapat menggantikan skrip inline. Action JavaScript atau action terkompilasi sering kali lebih kuat karena nilai yang tidak tepercaya masuk sebagai argumen dan tidak pernah ikut menghasilkan source shell.

Linter alur kerja menangkap pola minimal

Untuk artikel ini, saya mereproduksi aliran data dalam dua file alur kerja sekali pakai dan menjalankan Zizmor 1.29.0 secara offline. Audit template-injection melaporkan ekspresi langsung ${{ github.event.issue.title }} di dalam run: sebagai temuan injeksi kode dengan keyakinan tinggi. Versi variabel lingkungan tidak menghasilkan temuan yang dilaporkan dengan perintah yang sama.

Hal itu berguna, tetapi bukan jaminan universal. Linter dapat mengenali pola source-to-sink yang diketahui; linter tidak dapat membuktikan bahwa setiap skrip dinamis, action pihak ketiga, cakupan token, atau jalur jaringan aman. Perlakukan analisis statis khusus alur kerja sebagai satu sinyal review yang wajib, bukan putusan akhir.

Pemeriksaan CI praktis dapat dimulai dengan:

zizmor .github/workflows/

Jalankan pada representasi alur kerja final yang telah digabungkan, kendalikan versi tool, dan tinjau suppression. Padukan dengan validasi YAML biasa, pinning dependency, pemindaian secret, dan pengujian yang menjalankan konteks khusus event. Alur kerja pull request dan alur kerja issue tidak menerima payload yang identik.

Kisah AI lebih sempit daripada yang disiratkan judulnya

Riwayat commit publik tidak membuktikan bahwa AI menulis perubahan yang rentan. PR #1218 memuat commit terpisah yang co-authored oleh “Copilot Autofix powered by AI”, tetapi revisi jira_issue.yml yang rentan tampaknya muncul dalam commit lain yang dibuat oleh kontributor manusia. Wiz mengoreksi artikelnya pada 17 Agustus dengan mengatakan bahwa Copilot memeriksa perubahan yang telah digabungkan dan mengembalikan hasil aman, sementara kepengarangan perubahan yang rentan tidak jelas.

Wiz juga menjual Red Agent yang menemukan masalah tersebut. Akunnya merupakan pelaporan primer yang bernilai dari tim riset, tetapi klaim mengenai otonomi agen, akses Jira privat, dan cakupan forensik yang tepat harus tetap diatribusikan sampai dapat direproduksi secara independen atau Snowflake menerbitkan laporan sendiri.

Kesimpulan yang dapat dipertahankan dan lebih berguna daripada “AI buruk versus AI baik” adalah:

  • Kode yang dibantu AI menerima review keamanan yang sama dengan kode buatan manusia.
  • Hasil aman otomatis adalah bukti dari satu tool dan konfigurasi, bukan bukti keamanan.
  • Pengujian agentic dapat menjelajahi rantai kegagalan dengan cepat, tetapi hasilnya tetap membutuhkan verifikasi pada tingkat artefak dan pengungkapan yang bertanggung jawab.
  • Reviewer manusia membutuhkan model ancaman alur kerja: siapa yang mengendalikan setiap field event, di mana field tersebut menjadi dapat dieksekusi, dan apa yang dapat diakses runner.

Panduan kami untuk membaca evaluasi AI menyampaikan poin pengukuran yang sama dalam konteks lain: skor hijau hanya menjawab pertanyaan yang benar-benar diuji oleh evaluasi. Daily Digest 18 Agustus memuat kronologi singkat insiden ini.

Apa yang perlu dipantau berikutnya

Pertanyaan yang belum terjawab bukan apakah AI dapat menemukan bug alur kerja; kasus ini mengatakan bahwa AI dapat melakukannya, setidaknya dalam satu pengujian yang dilaporkan vendor. Pertanyaan yang lebih sulit adalah apakah tim menambahkan analisis alur kerja yang memahami event dan mengurangi hak istimewa runner sebelum masukan publik berikutnya mencapai shell.

Untuk insiden khusus ini, bukti lanjutan yang berguna mencakup postmortem yang ditulis Snowflake, pemeriksaan keamanan dan ruleset GitHub yang tepat yang diterapkan pada revisi PR final, serta laporan tentang proses pengambilan keputusan Red Agent yang dapat direproduksi secara independen. Sampai saat itu, riwayat alur kerja publik mendukung kesimpulan engineering yang kuat, tetapi hanya kesimpulan AI yang memenuhi syarat.

Sumber

  1. Wiz disclosure of the Snowflake workflow vulnerability
  2. Snowflake connector PR 1218 and public workflow history
  3. Snowflake workflow remediation commit
  4. GitHub documentation on script injections
  5. GitHub secure-use reference for Actions
  6. Zizmor template-injection audit documentation