Gitleaks Crash Pas Commit Multi-File, Lahir Gerbang Supply Chain
Hook pemindai secret crash saat commit multi-file. Ternyata satu flag hilang di definisi hook, dan ujungnya CI diikat checksum.
Ringkasan
Commit multi-file mendadak gagal gara-gara hook gitleaks-system crash karena lupa set pass_filenames false. Bugnya emang ada di file upstream, cuma hook golang sama docker yang bener. Akhirnya gue pindah ke hook lokal yang checksum-nya dicek plus CI cuma pake checkout yang di-pin SHA.
Commit malam itu bawa tiga file sekaligus: workflow CI, daftar hook, dan contoh environment. Pre-commit jalan, lalu berhenti di hook gitleaks-system. Error-nya panjang, intinya satu: exit code 1, commit ditolak. Yang bikin kesal: commit sebelumnya lancar-lancar aja.
Tebakan pertama saya: binary-nya yang rusak. Versi 8.30.1 kan baru dipasang, jadi wajar curiga regresi versi. Saya bahkan udah buka tab baru buat cari cara downgrade.
Ternyata salah total. Di issue tracker resmi gitleaks ada laporan yang persis: hook system-nya gagal justru karena pass_filenames nggak diset false, dilaporkan sejak Juni 2025 di versi 8.27.2 [2]. Saya buka file .pre-commit-hooks.yaml di tag v8.30.1 dan benar saja: blok gitleaks-system cuma berisi language: system, tanpa baris itu. Dua hook lain di file yang sama, versi golang dan versi docker, punya baris tersebut [1].
Karena itulah crash-nya hanya muncul pas commit multi-file. Pre-commit menambahkan daftar nama file ke argumen hook system, gitleaks membacanya sebagai path repo, bingung, lalu mati. Satu file aman, dua file langsung kena. Perbaikannya sudah diajukan lewat PR #1943, tapi saat saya cek belum masuk ke rilis yang saya pakai [2].
Hook lokal yang checksum-nya saya pegang
Daripada menunggu rilis upstream yang waktunya nggak jelas, saya ganti pendekatan. Alasannya bukan cuma soal bug itu. Sebagian besar commit di repo saya sekarang ditulis agent, bukan manusia. Kalau mau cerdas-cerdasan menilai hasil kerja agent, pintunya justru di rantai pasok: alat yang memeriksa kode harus bisa dipertanggungjawabkan asal-usulnya.
Jadi ada hook lokal bernama gitleaks-local yang menjalankan binary hasil unduhan mandiri, sha256-nya diverifikasi dulu, lalu memindai diff yang sudah di-stage:
- repo: local
hooks:
- id: gitleaks-local
name: Detect hardcoded secrets (checksum-verified install)
entry: gitleaks git --pre-commit --redact --staged --no-banner --verbose
language: system
pass_filenames: false
Flag --staged bikin pemindaian fokus ke perubahan yang mau di-commit doang, dan --redact menyamarkan temuan di output biar rahasia yang ke-slip nggak ikut tercetak ke log. pass_filenames: false satu baris itu, persis baris yang bikin hook upstream crash. Hook upstream versi golang sebenarnya bisa jalan, tapi dia mengunduh binary saat instalasi. Saya nggak mau pipeline keamanan saya bergantung pada unduhan runtime yang tidak dicek.
CI: satu-satunya action adalah checkout, dan dia di-pin
Di sisi CI aturannya lebih keras: nol action pihak ketiga. Satu-satunya uses: adalah actions/checkout yang dipin ke SHA commit penuh. Dokumentasi GitHub bilang pin ke full-length commit SHA adalah satu-satunya cara memakai action sebagai immutable release, karena penyerang harus menghasilkan tabrakan SHA-1 untuk bisa menyisipkan backdoor [3]. Risiko-nya nyata, bukan teoretis: satu action yang dikompromikan dapat mengakses semua secret repo dan menulis pakai token CI [3].
Untuk alat lain, unduhan dikunci di file workflow sendiri:
- name: Install gitleaks (checksum-pinned)
run: |
curl -sSfL -o /tmp/gitleaks.tar.gz \
"https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz"
echo "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb /tmp/gitleaks.tar.gz" | sha256sum -c -
mkdir -p "$HOME/bin" && tar -xzf /tmp/gitleaks.tar.gz -C "$HOME/bin" gitleaks
Pola yang sama dipakai buat actionlint, linter file workflow. Hash-nya tinggal di file workflow, jadi tiap penggantian alat jadi diff yang kelihatan saat review. Workflow itu sendiri cuma dibekali izin baca konten, dan sejak Agustus 2025 admin bahkan bisa menegakkan aturan pin ini lewat kebijakan: workflow yang memakai action tanpa pin langsung gagal jalan [5].
Kalo mau dipasangin teori, SLSA v1.0 merangkumnya rapi: provenance nggak berarti apa-apa tanpa verifikasi, dan verifikasi mencocokkan identitas builder, tanda tangan, serta parameter build dengan ekspektasi kita [4]. Mengunduh alat tanpa cek hash sama aja percaya buta pada semua jaringan di antara kita dan server rilis.
Trade-off yang saya bayar
Pin ke SHA itu mahal di sisi perawatan. Update action dan alat jadi manual, dan checksum yang sama dipegang dua tempat: skrip install lokal dan file CI. Ada godaan nulis skrip auto-update, dan saya tolak. Justru gesekan itulah yang bikin tiap upgrade jadi keputusan yang sadar, bukan kejadian yang cuma lewat.
Sekarang tiap perubahan di repo itu lewat dua pintu: hook lokal di laptop dan scan penuh di CI. Yang bikin tenang bukan cuma hasil scannya, tapi aturan mainnya tertulis di file yang ikut di-review. Kalo pipeline kamu masih mengunduh tool dari URL yang nggak diverifikasi, di situ celahnya. Menutupnya cuma butuh satu baris sha256sum -c.