Audit yang Hanya Membaca: Setengah Gerbang yang Sering Hilang
Gerbang commit menolak pesan tanpa trailer dalam sekejap, tapi konfigurasi di baliknya bisa drift diam-diam. Sapuan audit baca-saja menutup celah itu.
Ringkasan
Gates commit di KotaPortal nolak kode tanpa trailer Security-review, tapi hook-nya bisa lepas diam-diam gara-gara bit executable ilang atau core.hooksPath pindah. Solusinya subkomando audit di review_gate.sh yang nyapu empat jalur, selalu exit 0, dan ngecek registrasi hook biar nggak dianggap pipeline gagal. Gerbang itu pencegah, audit itu detektif; yang ngawasin gerbang ya gerbang kedua ini.
Gerbang commit di repositori KotaPortal menolak commit kode tanpa trailer Security-review: dalam hitungan detik. Pesan ditolak, penulis memperbaiki pesan, hidup berjalan. Lalu sebuah pertanyaan muncul dan tidak punya jawaban: apakah gerbang itu sendiri masih terpasang di semua jalur eksekusi? Hook yang kehilangan bit executable diabaikan Git tanpa suara, dan direktori hook bisa berpindah lewat core.hooksPath [2]. Dugaan awal bahwa pemasangan sekali jadi sudah cukup ternyata keliru.
Jawabannya bukan gerbang baru, melainkan subkomando audit pada review_gate.sh: penyapuan yang hanya membaca dan selalu exit 0. Kerangkanya sudah baku di teori kontrol. Kontrol preventif dirancang menghindari peristiwa yang tidak diinginkan sebelum terjadi; kontrol detektif dirancang menemukan dan mengoreksinya setelah terjadi [4]. Gerbang commit adalah sisi preventif. Sapuan audit adalah sisi detektif yang sering tertinggal.
Empat jalur yang dibaca sapuan
Sapuan berjalan pada empat jalur: commit kode sejak tanggal adopsi, berkas warning yang belum tertutup, hasil gerbang slice per tugas, dan catatan bypass. Untuk bagian commit, penyapuan membatasi diri pada direktori tempat kode produksi hidup: api, frontend, deploy, migrations, dan scripts. Commit dokumentasi di luar jalur itu tidak diperlakukan sebagai pelanggaran karena gerbang commit sendiri tidak menuntut trailer dari mereka. Outputnya terbagi lima bagian: daftar commit tanpa trailer persis satu Security-review:, warning trigger path yang masih terbuka, status gerbang slice tiap tugas, bypass yang tercatat, dan status registrasi hook.
Bagian TRAILER tidak memakai --trailers bawaan Git. Nilai trailer yang ganda menghasilkan baris baru di dalam satu rekaman sehingga parsing kolom menjadi rapuh. Loop per-komit memilih jalur yang lebih stabil:
git log --no-merges --since="$ADOPTION_DATE 00:00" --format=%h -- \
api frontend deploy migrations scripts | while read -r sha; do
n=$(git log -1 --format=%B "$sha" | grep -c '^Security-review:')
[ "$n" = "1" ] || printf ' %s %s trailer=%s\n' "$sha" "$(git log -1 --format=%s "$sha")" "$n"
done%B mengambil badan mentah: subject dan body tanpa pembungkusan [3]. Definisi trailernya sendiri sederhana: pasangan key-value dengan pemisah titik dua, dan blok trailer harus didahului baris kosong [1]. Angka selain 1 berarti invariant pecah, dan commit tersebut masuk daftar.
Registrasi yang bisa lepas diam-diam
Bagian registrasi menjawab pertanyaan pembuka. Pemeriksaan mencakup tiga syarat: shim hook bersifat executable, namanya terdaftar di settings.json, dan setiap berkas agen yang punya tool Bash menyebut hook tersebut. Salah satu saja gugur, hasilnya ditandai. Kondisi kegagalannya bukan teori: pengujian mandiri pada skrip ini punya kasus khusus yang menanam commit kode tanpa trailer ke repositori sementara, menjalankan sapuan, lalu memastikan commit tersebut muncul di daftar. Tanpa kasus itu, perubahan kecil pada loop cukup untuk mematikan deteksi tanpa seorang pun menyadari.
Cara memverifikasinya pada repositori sendiri:
test -x .git/hooks/commit-msg || echo 'hook hilang atau tidak executable'
git config core.hooksPath || echo '(default .git/hooks)'
grep -l 'review-gate' .claude/agents/*.md 2>/dev/nullBaris pertama menangkap hook yang hilang atau kehilangan bit executable. Yang kedua memperlihatkan perpindahan direktori hook. Sisanya mencari agen yang lupa menyebut hook. Pola keluarannya jelas: nama berkas berarti jalur itu terdaftar; output kosong berarti perlu diperiksa.
Desain exit 0 yang disengaja
Audit yang mengembalikan nilai bukan nol akan dibungkus logika retry oleh sistem orkestrasi, atau dinonaktifkan karena dianggap sumber kegagalan pipeline yang berisik. Karena itu subkomando ini selalu exit 0 dan hanya mencetak temuan ke output standar. Prosesnya cukup membosankan untuk dijalankan setiap hari. Ia tidak menghentikan apa pun; ia meninggalkan jejak yang bisa ditinjau dan diperbaiki secara berkala. Gerbang mencegah, audit mencatat. Keduanya berpasangan, dan yang kedua baru terasa penting setelah pertanyaan pertama muncul: siapa yang mengawasi gerbang itu sendiri.