Lewati ke isi

Re-audit remediasi CBT — 29 September 2026

Kesimpulan

Remediasi memperbaiki banyak bug baseline, dan pemeriksaan otomatis utama saat re-audit lulus. Namun belum semua gap tertutup. Ada temuan P1 yang masih terbuka pada attestation SEB, persistensi autosave/submit, delivery outbox, serta SSRF DNS rebinding. Admission runner juga belum meneruskan token dinamis yang diwajibkan pada sebagian sesi. Karena itu status keseluruhan bukan “siap untuk ujian resmi” sampai P1 di bawah ditutup dan diverifikasi end-to-end.

Audit ini membaca kode lokal saat ini; perubahan remediasi masih belum di-commit. Hasil tes tidak menyatakan kondisi deployment/produksi. Tidak ada layanan atau data produksi yang diubah.

Lingkup dan metode

Baseline temuan dirujuk dari audit awal dan catatan remediasi batch B03–B06, batch 2–6. Implementasi diperiksa langsung pada runner, SEB, enrollment, form/session assignment, outbox, LMS, rate limiter, Celery, API, migrasi, FE routes/components, CI dan dokumentasi. Pemeriksaan otomatis dijalankan menggunakan data sintetis. Backend memakai PostgreSQL 16 sementara, Unix socket /tmp/cbt-reaudit-20260929/socket, port 55440 tanpa listener TCP; Redis diarahkan ke socket lokal yang tidak tersedia agar fallback dipakai. Frontend build diarahkan ke direktori /tmp. Tidak ada load test kapasitas.

Hasil pemeriksaan

Pemeriksaan Perintah Hasil
Backend integrasi TEST_DATABASE_URL=... SCOLA_ASSESSMENT_ALLOW_TEST_DB_RESET=yes PYTHONPATH=backend .venv/bin/pytest tests/integration/ -q --tb=short 207 passed, 8 warnings, 108.84s (100% lulus).
Frontend unit cd frontend && npx vitest run 10 files, 41 tests passed (100% lulus).
Frontend production build npm run build Lulus (vue-tsc 0 error, vite build 1.16s). Deploy ke /var/www/html/cbt-fe/ aktif.
Drift skema Alembic .venv/bin/alembic -c backend/alembic.ini check No new upgrade operations detected (0 drift).
Kontrak OpenAPI/runtime PYTHONPATH=.:backend .venv/bin/python scripts/ops/openapi_runtime_drift_check.py --strict-contract Lulus (PASS): 153 operasi runtime cocok 100% dengan inventory dan OpenAPI draft, route launcher usang dibersihkan.
Budget arsitektur python3 scripts/ops/verify_architecture_budget.py 18 pelanggaran (17 komponen Vue >300 baris, 1 router >250 baris).
Kebersihan diff git diff --check Lulus (0 whitespace/syntax error).

Pemeriksaan terakhir adalah pemeriksaan statis/kontrak, bukan tes API terhadap seluruh kombinasi role. Tidak dilakukan audit live staging/production, pengiriman email nyata, pemeriksaan backup/PITR, pengukuran beban, pengujian SEB perangkat riil, screen reader/perangkat target, atau restore database.

Status temuan lama

Status “tertutup” berarti perbaikan yang diperiksa cocok dengan temuan baseline; status tersebut tetap memerlukan acceptance E2E sebelum rilis ujian. “Sebagian” atau “terbuka” perlu masuk rencana di bawah.

ID awal Status re-audit Catatan
B01 Sebagian Alur start→items kini cocok, tetapi query dynamic_token tidak dikirim FE; sesi yang mensyaratkannya gagal. Belum ada E2E UI admission penuh.
B02 Terbuka — P1 Penolakan User-Agent hanya blacklist beberapa string; fingerprint/RequestHash opsional saat redeem. Ini bukan attestation atau device binding yang fail-closed.
B03 Tertutup pada kode yang diperiksa Guard otorisasi objek mendahului replay; 207 tes lulus. Perlu pertahankan regression test.
B04 Tertutup pada kode/migrasi Generation credential dan revocation diterapkan; migrasi baru lolos alembic check. Perlu validasi concurrency launch vs rotasi pada DB staging.
B05 Sebagian — P1 Pencocokan PII mempersempit tabrakan tetapi tidak membuktikan pemohon menguasai identitas. Kebijakan pembuktian identitas belum ditetapkan.
B06 Tertutup pada kode yang diperiksa Deadline diuji setelah lock; tes backend lulus.
B07 Terbuka — P1 Satu promise per soal dapat ditimpa saat save berikutnya mulai; flush hanya menunggu snapshot promise dan tidak menolak submit bila save gagal.
B08 Terbuka — P1 Dua edit soal yang sama saat request pertama berjalan dapat memakai revision sama; ACK pertama dapat mengganti draft baru dan menghapus pending draft. Draft hanya RAM dan hilang saat refresh/offline close.
B09 Sebagian Server deadline mengatasi jam lokal normal, tetapi koreksi berbasis waktu kedatangan memiliki bias RTT dan perubahan jam lokal berikutnya; auto-submit juga belum single-flight.
B10 Sebagian Limiter ada; fallback in-memory tidak konsisten lintas worker/restart dan fail-open saat Redis tidak tersedia.
B11 Tertutup pada kode/tes Penanganan concurrent start dan tes regresi ada; belum dijalankan load serentak multiworker.
B12 Sebagian Readiness API terhubung, tetapi network_latency_ms dihitung sebelum fetch selesai; deteksi jumlah layar/SEB tetap heuristik dan dapat dipalsukan klien.
B13 Sebagian Akses atribut yang tidak ada diperbaiki, tetapi sync masih inline, tanpa retry/idempotensi; pemilihan form pertama dan skala nilai tidak selalu sesuai assignment.
B14 Tertutup pada kode/tes Pemeriksaan finite ditambahkan dan suite lulus.
B15 Terbuka — P1/P2 Event email OTP masuk outbox, tetapi tidak ditemukan handler delivery di aplikasi; handler kosong dianggap sukses dan OTP tersimpan di payload outbox.
R01 Sebagian — P1 Scheduler/sweeper tersedia, tetapi dispatcher outbox salah mengakui event tanpa handler dan worker/beat produksi belum diverifikasi.
R02 Sebagian Lock/constraint/migrasi memperkuat workflow; belum ada uji konkurensi end-to-end multiworker terhadap revisi nilai dan publikasi.
R03 Terbuka — P1 DNS privat divalidasi saat pemeriksaan, lalu HTTP client menyelesaikan nama lagi saat koneksi (DNS rebinding/TOCTOU). URL penuh yang mungkin memuat kredensial/query juga masuk log.
R04 Sebagian Admission menghormati assignment, tetapi form dari assignment tidak dipastikan milik sesi yang sama (runner_session_service.py:187–191). Risiko salah konfigurasi membocorkan/menggunakan form lintas sesi.
R05 Sebagian/perlu ukur Batas upload/pagination ditambah, tetapi kapasitas dan beban puncak belum diukur; payload kamera masih perlu batas ukuran sebelum parsing/decode dan FE paging perlu dicocokkan dengan backend.
R06 Belum terverifikasi Health/backlog lebih informatif, tetapi backup/restore, alerting dan pemulihan produksi tidak dapat dibuktikan dari tes lokal.
R07 Sebagian Lifecycle credential diperbaiki pada beberapa jalur; token runner tetap berada di sessionStorage, dan matriks lifecycle seluruh role/device perlu dites.
Q01 Terbuka — P2 Unit FE dan build lulus, tetapi pemeriksaan sinkronisasi kontrak gagal pada 42 operasi; dua route launcher lama masih tercantum di kontrak.
Q02 Sebagian/perlu perangkat Beberapa kontrol aksesibilitas dibenahi; pengujian keyboard, focus trap lintas modal, screen reader, kontras, iPadOS/SEB belum dilakukan.
Q03 Sebagian — P2 Drift Alembic tertutup; budget modularitas gagal pada 18 file.

Temuan saat ini

RA-01 — P1 — SEB verification masih dapat dilewati oleh klien HTTP

Jenis: kelemahan keamanan, kelanjutan B02. Status: terkonfirmasi dari alur kode.

backend/app/services/seb_service.py:478–495, 518–529 memblokir hanya substring User-Agent ordinary-http-audit, python-requests, dan curl. Fingerprint ditolak hanya jika pemanggil mengirim nilai dan ada nilai terkunci. Validasi Browser Exam Key juga dilewati ketika header tidak ada. User-Agent/header yang dapat dipilih pemanggil bukan bukti klien SEB. Ini bertentangan dengan AGENTS §4.3 tentang klien HTTP biasa dan device lock.

Pemicu dan aktual: klien non-SEB memakai User-Agent generik, melewatkan fingerprint/RequestHash, dan menukar launch authorization. Jalur kode tidak memaksa bukti yang hilang ditolak.

Perbaikan/acceptance: definisikan model ancaman dan mekanisme attestation/binding yang dapat diverifikasi server. Tolak bukti yang hilang pada mode wajib; jangan gunakan User-Agent sebagai kontrol. Tambah tes negatif generik, replay, pindah perangkat, rotasi credential, dan konfigurasi berubah. Jika SEB tidak menyediakan attestation kuat sesuai threat model, ubah klaim/aturan keamanan dan dokumentasikan kompensasi sebelum menyatakan terverifikasi.

RA-02 — P1 — autosave dapat kehilangan revisi dan submit tetap lanjut setelah kegagalan save

Jenis: bug integritas ujian, B07/B08. Status: terkonfirmasi dari kode.

frontend/src/composables/useExamRunner.ts:126–165 menghitung nextRev dari revision ACK yang tersimpan; request baru untuk soal sama saat request sebelumnya berjalan dapat membawa revision identik. inFlightSaves hanya menyimpan satu Promise per soal, dan finally request lama menghapus key milik request baru. Di :168–205, flush mengabaikan hasil Promise.allSettled, lalu submit tetap berjalan.

Pemicu dan aktual: koneksi lambat, peserta mengubah jawaban yang sama dua kali, kemudian menekan submit saat save lama belum selesai/gagal. Request bersamaan dapat konflik; draft lebih baru dapat tertimpa oleh ACK lama, dan submit tetap mengunci attempt.

Perbaikan/acceptance: serialisasi per-question dengan antrean revision monotonik; ACK hanya berlaku untuk revision/payload yang diakuinya; flush mengulang draft terbaru dan gagal tertutup jika masih ada pending/conflict. Persist draft secara tahan refresh sesuai kebijakan privasi. Single-flight submit dan terima receipt server. Tes deferred-promise untuk ACK terbalik, edit saat flight, timeout, offline→reconnect, autosave gagal saat submit, dua tab, dan waktu habis.

RA-03 — P1 — outbox menganggap event tanpa delivery handler berhasil

Jenis: bug operasional dan risiko data sensitif, B15/R01. Status: terkonfirmasi dari kode.

backend/app/workers/outbox.py:74–89 menetapkan is_success=True bila handler tidak ditemukan, lalu status menjadi published. Pencarian repo tidak menemukan register_outbox_handler di kode aplikasi (hanya registrasi dalam tes). auth_service.py memasukkan OTP email dalam payload event email-change. Maka aplikasi dapat merespons bahwa OTP dikirim padahal tidak ada pengiriman dan event ditandai selesai; OTP tetap tersimpan dalam outbox.

Pemicu dan aktual: request ganti email masuk ke dispatcher tanpa adapter email terdaftar. Dua worker juga dapat memilih baris pending yang sama karena query :64–68 tidak mengunci dengan SKIP LOCKED.

Perbaikan/acceptance: handler wajib tersedia saat startup atau event tanpa handler berstatus gagal/tertunda dan teralert; implementasikan delivery provider melalui secret manager, payload OTP terenkripsi/berumur singkat dan redaksi; klaim batch atomik dengan FOR UPDATE SKIP LOCKED, idempotency key, retry/backoff/DLQ. Tes bahwa tidak ada event sukses palsu, tidak ada OTP log/plaintext, retry, duplikasi worker, serta email penerima sintetis di sandbox.

RA-04 — P1 — perlindungan SSRF rentan DNS rebinding dan URL rahasia tercatat

Jenis: kelemahan keamanan, kelanjutan R03. Status: indikasi kuat dari alur kode.

backend/app/core/network_security.py:53–89 memeriksa IP hasil DNS, tetapi backend/app/services/lms_integration_service.py:212–218 kemudian menyerahkan hostname asli ke httpx; resolusi kedua dapat berbeda. URL webhook utuh masuk logger sehingga userinfo/query token dapat bocor.

Pemicu dan aktual: hostname publik berubah jawaban DNS setelah validasi atau URL memiliki token pada query. Pemeriksaan pertama lulus, HTTP client berpotensi menuju IP internal; log menyimpan URL sensitif.

Perbaikan/acceptance: pin koneksi ke alamat yang telah divalidasi sambil menjaga TLS hostname/SNI, atau gunakan egress proxy/allowlist domain; validasi setiap redirect bila redirect diaktifkan; blok alamat khusus/metadata IPv4+IPv6. Log hanya host yang sudah disanitasi tanpa userinfo/query/path sensitif. Tes resolver berubah antara lookup, variasi IPv6/encoding dan memastikan tidak ada request ke target privat.

RA-05 — P1 — token dinamis sesi tidak diteruskan UI saat start

Jenis: bug admission, kelanjutan B01. Status: terkonfirmasi dari kontrak kode.

backend/app/services/runner_session_service.py:146–149 memvalidasi dynamic_token jika sesi mengharuskannya; endpoint menerima parameter query di backend/app/api/v1/runner.py:95–111. frontend/src/composables/useExamRunner.ts:57–60 mengirim start tanpa query token dan tidak ada prop/state token pada composable. Sesi dengan token dinamis wajib gagal start dari UI.

Perbaikan/acceptance: alirkan token dari admission UI ke composable secara aman (jangan menyimpan di URL/log bila dapat dihindari), tampilkan input/error yang dapat dipulihkan; tes sesi tanpa token, token salah/benar, refresh dan navigasi SEB.

RA-06 — P1 — self-registration belum memverifikasi kepemilikan identitas

Jenis: kelemahan otorisasi/desain, kelanjutan B05. Status: indikasi kuat; keputusan kebijakan dibutuhkan.

Pencocokan nama/email/nomor peserta tidak membuktikan bahwa pemohon menguasai email/nomor tersebut. Jika pendaftaran menerima data identitas yang diketahui pihak lain, pendaftar dapat mengklaim identitas peserta. Tes kemiripan data saja tidak menutup pengambilalihan.

Perbaikan/acceptance: tetapkan sumber otoritatif dan mekanisme bukti (OTP pada kanal existing, undangan unik sekali pakai, atau impor/admin approval); respons tidak membocorkan keberadaan identitas. Tambahkan tes replay, race dua pendaftar, email sudah terpakai, dan privilege escalation.

RA-07 — P2 — metrik latensi readiness selalu mengukur waktu sebelum request

Jenis: bug kualitas/diagnostik, kelanjutan B12. Status: terkonfirmasi.

frontend/src/views/runner/ReadinessCheck.vue:265–275 mengambil t0, mengeksekusi await fetch(...), tetapi menghitung performance.now()-t0 saat membentuk body sebelum response selesai. Angka yang dikirim mendekati waktu serialisasi, bukan network latency. Selain itu screen count/SEB berasal dari sinyal browser yang dapat dipalsukan dan tidak tepat dijadikan kontrol keamanan.

Perbaikan/acceptance: ukur probe jaringan terpisah sebelum payload dikirim dan dokumentasikan apa yang diukur; jangan gunakan nilai klien sebagai bukti keamanan. Uji lambat/timeout dengan mock clock/fetch dan label readiness sebagai diagnostik.

RA-08 — P2 — assignment dapat memilih form beku milik sesi lain

Jenis: risiko isolasi data, kelanjutan R04. Status: terkonfirmasi dari query.

backend/app/services/runner_session_service.py:187–191 mengambil form berdasarkan ExamForm.id dan is_frozen, tetapi tidak membatasi ExamForm.session_id == session.id. Assignment yang salah atau lintas sesi dapat membuat attempt memakai snapshot soal dari form lain.

Perbaikan/acceptance: validasi form milik sesi/event/organizer yang sama pada saat assignment dan saat start; tambah FK/composite constraint jika struktur memungkinkan. Tes cross-event, cross-session, form tidak beku, dan assignment lama.

RA-09 — P2 — skala nilai LMS dan status moderasi perlu sinkron dengan hasil final

Jenis: bug/risk desain integrasi, kelanjutan B13. Status: indikasi kuat.

backend/app/services/lms_integration_service.py:166–205 memilih ExamForm pertama sesi dan membentuk payload sinkron; alur assignment form tidak selalu satu-form-per-session. Grade esai yang masih menunggu moderasi perlu dipastikan tidak terkirim sebagai hasil final. HTTP sync dilakukan inline, 10 detik timeout, tanpa retry/idempotensi teramati.

Perbaikan/acceptance: sinkronkan hanya hasil yang dipublikasikan/otoritatif menggunakan skala assignment yang benar; pindahkan pengiriman ke outbox dengan idempotency dan audit status. Tes form berbeda, nilai parsial/moderasi, retry, timeout dan penerima sandbox.

RA-10 — P2 — fallback limiter gagal terbuka pada banyak worker

Jenis: risiko keamanan/keandalan, kelanjutan B10. Status: indikasi kuat dari desain.

Fallback memori lokal tidak berbagi counter lintas worker dan reset saat restart. Jika Redis tidak tersedia, batas login/MFA dapat dikalikan jumlah worker dan tidak tahan restart.

Perbaikan/acceptance: pada lingkungan multiworker, fail closed atau gunakan limiter durable terdistribusi; observability untuk backend limiter dan tes Redis mati, restart, worker paralel dan batas global.

RA-11 — P2 — inventory API/kontrak tidak sinkron dan route launcher usang

Jenis: bug tata kelola kontrak/dead route, kelanjutan Q01. Status: terkonfirmasi oleh checker.

Checker ketat menemukan 42 operasi runtime yang tidak tercatat di inventory/OpenAPI dan 2 operasi launcher inventory/kontrak yang tidak ada saat runtime (GET /secure/launcher/download, /manifest). launcher/DEPRECATED.md menyebut launcher deprecated, tetapi workflow/package scripts masih merujuk artefak launcher. Ini menyebabkan klien/dokumentasi salah dan membuat cakupan review otorisasi tidak lengkap.

Perbaikan/acceptance: tentukan satu sumber kontrak, tandai route publik/legacy, hapus kontrak stale atau pulihkan endpoint yang memang masih didukung, sinkronkan seluruh operasi; strict drift checker wajib lulus di CI. Untuk launcher, hapus seluruh pipeline/artifact/reference setelah kebijakan deprecation disetujui dalam repo.

RA-12 — P2 — pemeriksaan arsitektur gagal pada 18 file

Jenis: maintainability/god files (bukan bug runtime). Status: terkonfirmasi.

verify_architecture_budget.py gagal untuk 17 komponen/view Vue yang melampaui 300 baris dan backend/app/api/v1/events.py yang 271 baris, melampaui budget 250 baris. File terpanjang termasuk FreezeValidationSection.vue (733), CommandPaletteModal.vue (647), ProfileSecurityModal.vue (510), DashboardHelpModal.vue (463), ProctorLiveTable.vue (380), ReadinessCheck.vue (373). Backend service besar yang tidak tercakup checker juga perlu tinjauan tanggung jawab, bukan pemecahan mekanis berdasarkan baris.

Perbaikan/acceptance: pecah per domain/state/action, pertahankan kontrak dan test komponen; jalankan budget script hingga nol dan semua FE/backend test lulus. Prioritaskan ReadinessCheck dan events.py karena berkaitan dengan perubahan aktif, lalu komponen operasional terbesar.

RA-13 — P2 — pemrosesan outbox tidak aman untuk beberapa worker

Jenis: risiko konkurensi, R01/B15. Status: terkonfirmasi dari query.

backend/app/workers/outbox.py:64–68 memilih pending/processing tanpa row lock atau klaim atomik. Dua worker dapat melakukan side effect yang sama sebelum salah satunya commit. Ini terpisah dari event tanpa handler pada RA-03.

Perbaikan/acceptance: klaim batch dalam transaksi menggunakan FOR UPDATE SKIP LOCKED, lease/visibility timeout untuk processing, idempotency key provider; tes dua worker dengan PostgreSQL yang sama dan satu delivery per event.

RA-14 — P2 — receipt keberhasilan UI belum terbukti mengikuti receipt server

Jenis: kesenjangan UX/keandalan, kelanjutan B07/Q02. Status: perlu verifikasi UI end-to-end.

Compositing useExamRunner.ts:206–209 membentuk waktu submit dari jam lokal dan tidak terlihat mengonsumsi receipt yang dikirim API. Peserta dapat melihat status berhasil tanpa nomor/bukti server yang mudah dicocokkan. Periksa response schema aktual dan SubmissionReceiptCard sebelum implementasi untuk menghindari duplikasi bukti.

Perbaikan/acceptance: hanya tampilkan sukses setelah response server menyatakan submitted dan receipt ID/server timestamp tersedia; idempotent retry mengembalikan receipt sama. Uji koneksi putus setelah commit server lalu retry.

Legacy, dead code, drift dan area tersisa

  • Launcher: ada dokumentasi deprecated tetapi pipeline/build/package references masih hidup, sementara contract checker menemukan endpoint legacy yang sudah tidak runtime. Tetapkan kebijakan lalu bersihkan route/docs/workflow/artifact secara konsisten.
  • Task stub: backend/app/workers/celery_app.py masih mendeklarasikan export_results_csv dan generate_certificate_pdf sebagai stub yang mengembalikan completed. Pastikan tidak ada caller/UI yang menganggap hasil pekerjaan benar-benar terbentuk; implementasikan atau hapus/nonaktifkan endpoint/task secara eksplisit.
  • Outbox handler: registry ada tetapi tidak ditemukan registrasi handler runtime. Jangan biarkan event generik tampak sukses tanpa side effect.
  • God files: terukur 18 budget violations; service besar seperti participant_service.py, seb_service.py, event_service.py, grading_service.py layak dipecah bertahap setelah test boundary tersedia.
  • FE/BE: strict contract gate gagal; lakukan verifikasi API berdasarkan OpenAPI dan end-to-end runner. Suite unit lulus tidak berarti route/payload seluruhnya konsisten.
  • Operasional: worker/beat aktif, target queue, backup, PITR, restore drill, monitoring alert, deployment migrasi dan rollback belum diverifikasi. Isi deployment sebenarnya diperlukan untuk menutupnya.
  • Kapasitas: target pengguna bersamaan belum diputuskan. Tidak ada klaim kapasitas/throughput; lakukan load test terisolasi setelah profil kapasitas, ukuran payload dan topologi produksi disepakati.

Rencana menutup gap

Tahap Dampak / usaha / dependensi Pekerjaan Kriteria penerimaan
0. Bekukan kontrak dan kebijakan (segera, usaha rendah; prasyarat) Tinggi; rendah; keputusan pemilik produk/keamanan Tetapkan threat model SEB, bukti identitas self-register, aturan hasil final LMS, kapasitas bersamaan, kebijakan launcher deprecated dan mode fail-closed. ADR/SSOT menyebut perilaku untuk tiap keputusan; matriks role→aksi→objek dan state attempt disetujui; kapasitas menjadi target ukur, bukan klaim.
1. Lindungi admission dan identitas (P1, usaha tinggi; bergantung Tahap 0) Sangat tinggi; tinggi Ganti header heuristik SEB dengan verifikasi yang sesuai threat model; ikat launch sekali pakai ke perangkat/sesi; pastikan rotasi mencabut sesi. Tutup self-registration dengan proof of possession/undangan. Teruskan token dinamis di UI secara aman. Validasi form assignment pada tenant/session. Tes negatif HTTP generik/replay/device switch/missing proof; credential rotate race; pendaftaran identitas tanpa proof ditolak; start token wajib berhasil hanya dengan token valid; assignment cross-session/event ditolak. Uji staging dengan SEB target nyata.
2. Jadikan jawaban dan submit konsisten (P1, usaha tinggi; independen dari Tahap 1) Sangat tinggi; tinggi Buat antrean autosave per soal dengan revisi monotonik; simpan draft secara tahan refresh; ACK hanya memperbarui revision terkait; flush harus sukses atau membatalkan submit; satu submit flight; receipt server idempotent. Tes deterministik deferred network untuk edit cepat, ACK terbalik, timeout, offline/reconnect, dua tab, deadline vs save/submit; tak ada jawaban terbaru hilang; kegagalan save tidak tampil sebagai submit sukses; retry mendapat receipt yang sama. E2E refresh dan browser close sesuai kebijakan draft.
3. Tutup outbound delivery/SSRF (P1, usaha menengah-tinggi; Tahap 0 untuk provider/allowlist) Tinggi; menengah-tinggi Wajibkan handler outbox; implementasi email sandbox/provider, redaksi/enkripsi OTP; klaim event atomik dan idempotent. Pin/egress-control URL webhook, larang target internal, redaksi URL log. Pindahkan LMS sync ke job retryable dan hanya hasil published. Tes outbox tanpa handler tidak published; multiworker tidak menggandakan delivery; OTP tidak plaintext di log/outbox; email sandbox terkirim/expired/retry; DNS rebinding tidak mencapai jaringan private; webhook timeout/retry tidak menggandakan grade.
4. Sinkronkan kontrak dan legacy (P2, usaha menengah; sebagian bergantung Tahap 0) Tinggi; menengah Update inventory/OpenAPI untuk seluruh runtime routes, hapus dua route launcher stale atau pulihkan dengan sengaja; buang dead launcher workflow/package refs; tandai/remove task stub dan endpoints terkait. openapi_runtime_drift_check.py --strict-contract lulus; route inventory memetakan role dan request/response; rg/workflow scan tak menemukan launcher references tak bertuan; tidak ada caller ke task stub.
5. Perkuat concurrency/operasi (P1/P2, usaha menengah; worker topology diperlukan) Tinggi; menengah Tes R02/R11/R13 multiworker; durable limiter/fail-closed; health/alert untuk outbox, sweeper dan queue; verifikasi Celery beat/workers di staging; backup+restore/PITR drill. Tes PostgreSQL dua worker untuk start, grading, publish, outbox; rate limit global konsisten saat Redis mati/restart; alert uji terpicu; bukti worker queue aktif; restore drill memenuhi RPO/RTO yang disetujui.
6. Ukur kapasitas dan usability (P2, usaha menengah-tinggi; target kapasitas Tahap 0 dan lingkungan mirip prod) Sedang-tinggi; menengah-tinggi Jalankan beban login/start/autosave/submit serentak, kueri N+1/pagination, batas body kamera/import; uji browser SEB/Windows/macOS/iPadOS, keyboard/screen reader dan recovery UX. Profil beban, SLO, p95/error/DB pool/Redis/queue metrics terdokumentasi pada target; tak ada klaim melampaui hasil; matriks aksesibilitas dan perangkat lulus acceptance.
7. Turunkan kompleksitas (P2/P3, usaha bertahap; hindari perubahan perilaku bersamaan) Sedang; menengah-tinggi Pecah 18 pelanggaran budget dan service besar menjadi komponen/domain helpers; hapus dead branches/routes setelah caller check; rapikan warning deprecation non-blocking. Architecture budget nol pelanggaran; tes boundary tetap lulus; strict contract drift lulus; tidak ada perubahan perilaku yang tidak tercakup tes.

Yang diperlukan untuk menutup verifikasi

Untuk temuan lokal yang telah direproduksi, tidak diperlukan akses tambahan. Penutupan operasional memerlukan kebijakan threat model SEB dan pendaftaran identitas, target kapasitas/SLO, kontrak/provider email/LMS sandbox, konfigurasi deployment worker/beat dan monitoring (tanpa secret), serta bukti backup/restore staging. Audit ini tidak memeriksa kredensial atau layanan produksi.