Jaringan Keamanan Mekanis yang Dirancang untuk Diam
Hook rahasia yang membedakan temuan dari error, CI yang mengumpulkan temuan tanpa merahkan build, dan ZAP mingguan yang skip dengan rapi.
Ringkasan
Hook gitleaks suka bikin commit gagal cuma gara-gara error lingkungan, bukan bocoran beneran. Solusinya: blokir cuma kalau exit 1 plus laporan JSON valid, sisanya cukup peringatan. CI dan ZAP mingguan juga kalem, cuma ngumpulin temuan buat direview, nggak asal bikin build merah tanpa bukti nyata.
Perintah commit berjalan, lalu layar menampilkan pesan error dari alat pemindai rahasia. Bukan karena ada kunci API yang terekspos: alat itu gagal membaca konfigurasi sementara atau binary-nya tidak ada di lingkungan lokal. Kalau hook dikonfigurasi memblokir setiap kode keluar non-nol, pengembang akan cepat terbiasa mematikan pengaman itu agar bisa bekerja. Solusinya bukan menghapus pemindaian, melainkan membangun jaringan pengaman yang hanya bereaksi saat bukti kebocoran nyata ada, dan tetap tenang di semua keadaan lain.
Akar masalahnya ada di cara alat seperti gitleaks melaporkan hasil. Dokumentasinya menetapkan kode keluar 0 berarti tidak ada kebocoran, 126 berarti flag tak dikenal, sementara kode keluar 1 dipakai untuk kebocoran yang terdeteksi maupun error yang ditemui [4]. Angka 1 itu ambigu: kebocoran atau kegagalan pemindaian, hasilnya sama. Menganggap setiap 1 sebagai kebocoran adalah desain yang gagal dua kali: sekali saat memblokir commit karena error lingkungan, sekali lagi saat pengguna memasang kebiasaan menonaktifkan hook.
Semantik Exit yang Dipisah
Hook pada repositori ini menutup ambiguitas itu dengan satu aturan: blokir hanya bila ada bukti, sisanya peringatan. Pemindaian berjalan atas konten staged lewat gitleaks protect, lalu laporan JSON-nya divalidasi. Blokir dengan exit 2 hanya terjadi bila kode keluar pemindai 1 dan laporannya JSON valid berisi temuan. Setiap jalur lain (gagal mengurai input, perintah kosong, akar git tak ditemukan, binary tidak ada di PATH, kode keluar 1 tanpa laporan valid, atau kode keluar aneh lain) mencetak peringatan ke stderr lalu mengizinkan commit.
REPORT_FILE=$(mktemp)
gitleaks protect --staged --redact --no-banner \
--report-format json --report-path "$REPORT_FILE" >/dev/null 2>&1
rc=$?
n="$(jq 'if type == "array" then length else -1 end' "$REPORT_FILE" 2>/dev/null)"
if [ "$rc" -eq 1 ] && [ -n "$n" ] && [ "$n" -gt 0 ] 2>/dev/null; then
echo "BLOCKED (gitleaks): $n potential secret(s) in staged content" >&2
exit 2
fi
if [ "$rc" -eq 1 ]; then
echo "WARN (gitleaks): exit 1 tanpa laporan valid (scan error, bukan temuan)" >&2
fi
exit 0
Validasi jq empty memastikan laporan benar-benar terbaca mesin sebelum dianggap bukti, dan mktemp menyimpan laporan di jalur tak terprediksi. Pengecualian yang sudah dikaji tercatat di satu berkas .gitleaksignore di akar repositori, sehingga temuan yang memang dinyatakan aman tidak membanjiri laporan berulang kali. Sisa celahnya diakui terbuka di komentar hook: alias shell dan perintah yang dipecah baris tidak terdeteksi — backstop-nya adalah pemindaian manual dan CI.
CI yang Mengumpulkan, Tidak Menghentikan
Filosofi yang sama berlanjut di pipeline. Pekerjaan pemindaian keamanan tidak boleh otomatis merahkan build, terutama saat adopsi masih awal. Konfigurasinya: jalan hanya pada tag versi, continue-on-error aktif, lalu tiga pemindai berjalan berturut-turut: Semgrep untuk cacat pola kode, Trivy untuk kerentanan dependensi, dan gitleaks untuk riwayat commit penuh. Masukan exit-code pada trivy-action bawaannya 0, artinya temuan tidak menggagalkan pekerjaan kecuali disetel eksplisit [6].
Hasil pemindaian mendarat sebagai artifact plus ringkasan langkah, bukan sebagai build merah. Disposisinya jalan lewat berkas temuan QA terpusat: temuan dibaca, diklasifikasi, diperbaiki atau dikecualikan dengan alasan. Rantai buktinya utuh tanpa memblokir siapa pun, dan rilis tidak sandung oleh temuan yang justru ditunggu untuk direview.
ZAP Mingguan yang Tahu Dirinya Bisa Gagal
Pemindaian dinamis punya tantangan berbeda: targetnya bisa sedang mati. Jadwal ZAP baseline mingguan (Senin 02:00 UTC atau 09:00 WIB) dimulai dari pemeriksaan keterjangkauan sederhana ke lingkungan staging. Target tidak menjawab dalam batas waktu, workflow mencatat skip yang rapi dan keluar sukses, bukan merah karena pemeliharaan rutin. Perlu dihitung juga bahwa event terjadwal bisa tertunda saat load GitHub Actions sedang tinggi, dan zona waktunya baku UTC [5].
Di dalam aksi ZAP baseline sendiri, kontainer secara bawaan keluar dengan kode gagal begitu ada alert. Kunci perilakunya ada di opsi fail_action pada aksi tersebut: tanpa di-set true, status GitHub Scan tidak ikut merah walau alert ada [7]. Temuan tetap lengkap di laporan dan artifact untuk direview. Jadi baseline yang stabil dengan beberapa WARN yang sudah diketahui tidak menjerit tiap Senin pagi; suaranya keluar saat temuan baru benar-benar muncul.
Net yang bisa dipercaya bekerja dua arah: tidak menelan kebocoran nyata, dan tidak berteriak saat hanya error sementara. Verifikasinya bisa langsung dirasakan. Commit dengan rahasia staged tertahan di exit 2 lengkap dengan baris dan aturannya; commit biasa lewat tanpa sadar; CI mingguan menghasilkan laporan yang orang benar-benar buka.
Sumber
[4] gitleaks README, Exit Codes, diakses 13 Oktober 2026.
[5] GitHub Docs, Events that trigger workflows, diakses 13 Oktober 2026.
[6] aquasecurity/trivy-action README, diakses 13 Oktober 2026.
[7] zaproxy/action-baseline README, diakses 13 Oktober 2026.