Ketika Penjaga Tak Bisa Menjawab: Fail-Closed pada Hapus Media
Census pemakaian media error dulu diperlakukan sebagai boleh hapus. Kini ErrCensusUnavailable membatalkan delete: baris dan file tetap aman.
Ringkasan
Dulu di KotaPortal tombol hapus media tetap jalan walau kueri sensus gagal, dianggap "tidak dipakai" alias fail-open. Sekarang dibalik jadi fail-closed: kalau error, delete dibatalkan dan client dapet 503 CENSUS_UNAVAILABLE, detail errornya dicatat di server aja. Uji lamanya dirombak biar ngunci perilaku aman ini, file dan data nggak kehapus.
Saat administrator mengklik tombol hapus pada sebuah item media di proyek KotaPortal, kueri sensus basis data sedang mengalami kegagalan. Alih-alih menghentikan proses, kode lama memperlakukan ketiadaan jawaban ini sebagai "tidak digunakan". Proses penghapusan pun berlanjut pada status yang tidak diketahui.
Kebijakan Fail-Open yang Terkunci
Perilaku ini bukanlah kecelakaan yang luput dari pengujian. Sebuah uji karakterisasi lama bernama TestDeleteProceedsWhenUsageCountFails secara eksplisit mengunci kebijakan fail-open tersebut dengan alasan keputusan pemilik tertunda. Uji ini secara tidak sengaja melegitimasi asumsi bahwa kesalahan pada kueri penghitungan penggunaan berarti berkas aman untuk dihapus.
Kebijakan lama menumpang pada satu kondisi: err == nil && n > 0. Logikanya membaca wajar di layar, tetapi ia menggabungkan dua keadaan yang berbeda. Nilai nol berarti census benar-benar menjawab tidak ada pemakaian. Error berarti pertanyaannya tidak pernah terjawab, dan delusi yang berbahaya adalah memperlakukan jawaban yang hilang sebagai jawaban berbunyi aman.
Mekanisme Fail-Closed dan Pemetaan 503
Perbaikan menuju prinsip fail-closed dimulai pada fungsi Delete di berkas service.go. Kondisi lama yang mengabaikan kesalahan diganti dengan pengembalian ErrCensusUnavailable. Di sisi handler, kesalahan ini dipetakan ke status HTTP 503 dengan kode CENSUS_UNAVAILABLE. Pesan untuk klien bersifat umum — detail kesalahan yang sebenarnya dicatat aman via slog.Error.
Uji yang sebelumnya mengunci perilaku berbahaya tersebut ditulis ulang menjadi TestDeleteBlockedWhenUsageCountFails. Uji baru ini memverifikasi bahwa baris basis data dan berkas pendukung tetap bertahan. Uji ini juga memastikan tidak ada berkas sisa yang tertulis di direktori unggahan saat penghapusan dibatalkan.
Detail di sisi server tidak boleh hilang begitu saja. Setiap census yang gagal kini dicatat slog.Error dengan media_id, URL media, dan error aslinya. Tanpa jejak ini, kebijakan fail-closed berubah menjadi pembungkam: admin hanya melihat 503 tanpa tahu infrastruktur mana yang sedang sakit. Pemisahan ini mengikuti doctrine penanganan error yang sama: respons generik untuk klien, detail untuk investigasi [2].
Landasan Keamanan dan Protokol
Prinsip dasarnya tegas: ketika penjaga tak bisa menjawab, sistem tidak boleh menganggapnya sebagai izin. Mengembalikan status 503 Service Unavailable merupakan representasi yang jujur atas kondisi sistem. Menurut spesifikasi RFC 9110, status 503 menandakan bahwa peladen saat ini tidak dapat menangani permintaan karena kelebihan beban sementara, dan permintaan tersebut dapat dicoba kembali nanti [1].
Menggunakan status 500 akan memberikan sinyal yang salah kepada klien bahwa proses penghapusan itu sendiri yang rusak. Pendekatan ini selaras dengan panduan OWASP Error Handling. Kesalahan yang tidak tertangani dapat memberikan informasi tambahan kepada penyerang. Pada kesalahan yang tidak terduga, sistem harus mengembalikan respons yang umum kepada klien, sementara detail kesalahan dicatat di sisi peladen untuk investigasi lebih lanjut [2].
Pembedaan 503 dan 500 juga menyelamatkan keputusan operasional. Nilai 500 menyiratkan delete yang rusak, padahal datanya utuh dan hanya pemeriksaannya yang gagal. RFC 9110 mendefinisikan 503 sebagai kondisi sementara yang kemungkinan pulih setelah jeda, dan server boleh melampirkan header Retry-After [1]. Pesan di handler menutup lingkaran itu: penghapusan dibatalkan agar file yang masih dipakai tidak ikut terhapus, silakan coba beberapa saat lagi.
Sisi pengujian menutup kemungkinan regresi dari dua arah. Uji service mengunggah berkas sungguhan terlebih dahulu, menjalankan delete dengan census yang selalu gagal, lalu memastikan baris dan berkasnya masih ada. Uji handler menambahkan satu klaim lagi: tidak ada satu pun berkas liar yang tertulis di direktori unggahan selama delete dibatalkan. Sebuah operasi yang batal harus benar-benar batal, bukan setengah jalan meninggalkan artefak. Dokumentasi OpenAPI untuk endpoint delete media menyusul pada commit terpisah, sehingga kontrak 503 CENSUS_UNAVAILABLE tercatat untuk konsumen API, bukan hanya di kode.