Memblokir Git Commit Langsung Lewat Hook PreToolUse
Hook PreToolUse membuat git commit langsung dari sesi agen koding mustahil secara teknis, bukan sekadar dilarang di dokumen.
Ringkasan
Jadi gerbang review ini dipasang sebagai hook PreToolUse di Claude Code, bukan hook Git biasa, biar commit langsung dari agen koding bisa diblokir lewat exit 2. Pendaftarannya diselipin di settings pengguna plus sembilan berkas definisi agen, jadi nggak ada celah warisan otomatis. Ujinya udah ditolak dua kali bare, bukti fail-closed-nya works.
Percobaan pertama memakai jalur baru di repositori KotaPortal berakhir dengan pesan penolakan: dispatcher mengembalikan exit 2 untuk perintah git commit biasa. Yang menarik, hook-nya bekerja dengan benar, sementara perintah pembungkus yang seharusnya jadi jalur resmi justru tidak dikenal. Pintu darurat tertutup rapat, pintu utama belum dipasang.
Pengalaman itu berasal dari commit pemasangan hook shim review gate. Sebelum perubahan ini, aturan "setiap commit lewat review gate" hanya hidup sebagai konvensi di dokumen workflow. Konvensi tidak punya cara menolak: agen koding yang mengetik git commit -m "..." langsung tanpa trailer tinjauan tetap berhasil, dan pelanggaran baru ketahuan di audit belakangan.
Mengapa Bukan Hook Git Biasa
Tebakan awal menempatkan penegakan di core.hooksPath, mekanisme bawaan Git. Dokumentasi githooks menjelaskan bahwa hook adalah program di direktori hooks yang dipicu Git pada titik tertentu, dan hook tanpa bit eksekusi diabaikan [2]. Git bahkan memindahkan direktori kerja ke akar worktree sebelum memanggil hook [2]. Lapisan repositori memang bisa menolak commit, tapi aktor yang dituju di sini bukan manusia di terminal, melainkan sesi agen koding yang membawa alat Bash sendiri.
Karena itu titik pemasangannya naik satu lapis, ke harness agen. Referensi resmi hooks menjelaskan bahwa hook dijalankan otomatis pada titik-titik tertentu dalam siklus hidup Claude Code, dan event PreToolUse terjadi "sebelum sebuah tool call dieksekusi" serta "bisa memblokirnya" [1]. Matcher diarahkan ke alat Bash, dan hook mengembalikan exit 2 untuk perintah git commit yang lolos langsung. Perintah tidak pernah sampai di shell.
Pendaftaran di Sembilan Definisi Agen
Hook yang sudah ditulis belum berarti apa-apa tanpa pendaftaran. Commit ini mendaftarkannya di dua tempat: pengaturan pengguna dan sembilan berkas definisi agen proyek, mulai dari backend-engineer, code-reviewer, debugger, frontend-engineer, infra-engineer, release-manager, researcher, security-reviewer, sampai test-engineer. Semua agen yang membawa alat Bash menerima baris hook yang sama. Detail komposisi antara konfigurasi level settings dan level agen memang belum dijelaskan tuntas di dokumentasi, jadi pendaftaran dilakukan di keduanya tanpa mengandalkan asumsi prioritas yang tidak terdokumentasi.
Satu-satunya pengecualian adalah variabel lingkungan bypass yang penggunaannya dicatat di log bypass. Jalur pintas tetap ada untuk keadaan darurat, tapi tidak pernah senyap.
Bukti dari Probe, Bukan dari Keyakinan
Pesan commit mencatat hasil pengujian empiris: commit langsung ditolak dua kali, percobaan bypass nol, penggunaan wrapper nol. Jalur negatif diuji langsung, sedangkan jalur sukses diserahkan ke selftest skrip gerbang itu sendiri. Tinjauan ganda kode dan keamanan menghasilkan putusan approve tanpa critical maupun major, dengan beberapa riders yang diparkir untuk tugas berikutnya.
Cara pandang OWASP Error Handling cocok di sini: penanganan kesalahan adalah bagian dari keamanan aplikasi, dan kesalahan yang tidak tertangani justru membantu pengintaian penyerang [3]. Padanannya dalam kontrol proses: pelanggaran kebijakan yang hasilnya sukses diam-diam sama seperti error path yang tidak pernah dibangunkan. Hook ini memastikan setiap pelanggaran terlihat di momen itu juga, bukan pekan kemudian saat audit menyusuri riwayat commit.
Permukaan pendaftaran menentukan kekuatan gerbang semacam ini. Hook yang hanya terdaftar di settings global akan bolong begitu sebuah agen punya berkas definisi sendiri dengan konfigurasi hook-nya. Karena itu daftar sembilan agen tadi ditulis satu per satu, bukan diserahkan ke warisan otomatis. Biayanya sebelas baris konfigurasi yang berulang; imbangannya, setiap sesi agen yang berjalan lewat definisi mana pun membawa penegakan yang sama.
Probe juga menyisakan pelajaran soal apa yang layak dipercaya. Angka "bare 2, bypass 0, wrapper 0" terlihat sepele, tapi pola ini yang membuat keputusan bisa diaudit: jalur penolakan dieksekusi sungguhan, bukan diasumsikan bekerja dari bacaan kode. Untuk gerbang yang tugasnya menolak, bukti penolakan adalah fitur utamanya.
Yang Belum Selesai Sadar-Sadar
Pintu yang menolak sudah terpasang dan teruji. Pintu yang meneruskan, subcommand commit pada review gate, baru jadi pada commit berikutnya setelah pengguna nyata membenturkannya. Pesan penolakan exit 2 yang membuka cerita ini bukan kegagalan sistem, melainkan bukti bahwa fail-closed bekerja: bagian yang belum ada tidak boleh diam-diam mengizinkan.