Verify yang Bertindak: Hard Gate Rahasia di Artikel Agen
Artikel otomatis diolah dari context repo beneran. Sekarang verify fail-closed: ketemu pola secret atau topologi internal, artikel ditarik otomatis.
Pas gue lagi ngubah cmd_verify di tools/articles.py buat commit e262ee2, nambah 48 baris fungsi check keamanan, gue tiba-tiba sadar: artikel di blog ini kan ditulis otomatis oleh agent LLM yang ngolah context repo internal kita: commit, config, log, semuanya beneran. Gate lama cuma ngecek struktur SEO, title, heading. Isinya? Lepas tangan.
Gue sempet mikir cukup kasih warning aja kalo ada secret, kayak alert di banyak sistem. Kan kalo verify nemu pola aneh, bilang aja ke user, terus biarin publish. Toh agentnya pintar, maybe jarang salah.
Yang sebenarnya bahaya
Tapi pas gue ngecek lagi contoh context, gue lihat potensi bocornya gede. Agent bisa aja nemu assignment key dengan nilai panjang di config, terus nulis itu di artikel sebagai contoh kode. Atau nyebut alamat IP privat (bentuk samarnya aja, oktet aslinya nggak gue tulis) di eksplanasi topologi. Itu kan rahasia repo.
OWASP emang nyaranin secret di code/log wajib dihapus dan di-rotate, plus deteksi sedini mungkin di pre-commit atau IDE. RFC 1918 juga jelasin blok IP privat 10/8, 172.16/12, 192.168/16 itu nggak routable, jadi penanda intern. Kalau agent menyelipkan itu ke artikel live, bocornya permanen di mesin pencari.
Gate ini ngecek dua kategori sebelum artikel boleh publish:
- Credential dan secret: pola token dari provider populer, JWT, kunci AWS, kunci privat, sampai assignment variabel yang nilainya string panjang yang mencurigakan.
- Topologi internal: alamat di tiga blok privat RFC 1918 (ditulis samar: 10.x.x.x dan teman-temannya), loopback, hostname internal dengan akhiran domain khusus jaringan dalam, path folder home user di Mac, Linux, atau Windows, sampai alamat MAC.
Fail-closed, bukan sekadar lapor
Gue akhirnya mutusin: bukan sekadar lapor. Gate ini **fail-closed**. Satu fungsi check di cmd_verify: kalo ketemu pola credential (API key, token, password, dsb) atau topologi rahasia (IP privat, hostname internal, path absolut home user), verify exit 1. Terus kalo artikel udah live? Auto-rollback jadi draft.
GitHub push protection blokir push sebelum masuk repo, bukan alert sesudah. Secret scanning mereka cover ratusan pola provider plus pola generik. Gue tiru filosofi itu: cegah di titik masuk.
Buat soal false positive, gue rancang biar murah diproses: kalo gate nembak konten yang sebenarnya aman, perbaikannya biasanya cuma satu kalimat ditulis ulang atau satu contoh kode disamarkan, terus verify ulang. Detik-detik, bukan jam. Bandingin sama skenario tanpa gate: satu secret kepublish, keindeks mesin pencari, dan meskipun artikelnya dihapus, cache dan arsip tetep nyimpen. Rotasi kunci, audit log, panik dini jam 2 pagi. Nggak sebanding.
Detail implementasinya sengaja gue bikin tipis: satu fungsi kecil yang jalan di urutan paling akhir verify, setelah semua gate struktur dan SEO. Urutan itu nggak ngaruh ke performa, tapi psikologisnya penting: check ini harus jadi hal terakhir yang sentuh artikel sebelum statusnya berubah jadi live, biar nggak ada jalur lain yang kelewat.
Ada yang bilang, "False positive bakal bikin artikel gagal publish sia-sia." Gue pribadi pilih fail-closed. Buat konten yang ditulis mesin dari context repo beneran, false positive murah: gue tinggal ngubah sedikit, publish lagi. Tapi satu credential bocor itu mahal. Rotate secret, panic, ada yang bisa dipakai orang buat akses.
Jadi di commit e262ee2 itu, gue ngubah verify jadi bertindak. Nggak cuma ngecek, tapi nutup celah. Kalau ketemu secret, artikel yang udah live pun gue tarik jadi draft. Mending repot dikit daripada kebakaran.
Gate ini juga bagian dari pelajaran lama. Pernah ada masa verify bilang 0 fail tapi bug tetep lolos ke live — ceritanya lengkap di artikel verify bilang 0 fail tapi bug tetap lolos. Bedanya, bug dulu itu soal render; yang ini soal isi yang nggak boleh keluar rumah. Dua-duanya nunjukin hal yang sama: gate cuma berguna sejauh yang dia cek.
Kebetulan, pipeline yang pake gate ini sekarang udah event-driven dari awal sampai akhir — ada tulisan terpisah soal migrasi dari tabel RAMP ke trigger event-driven kalo mau ngikutin alur besarnya.