This repository was archived by the owner on Aug 11, 2026. It is now read-only.
fix(cache): invalidasi edge cache tidak pernah benar-benar terjadi (#359) - #361
Merged
Merged
Conversation
) Klien purge memakai metode HTTP kustom `BAN` — idiom Varnish yang lazim, dan yang saya kirim di #353 tanpa pernah menjalankannya lewat jalur aplikasi sungguhan. `fetch` milik Bun DIAM-DIAM MENULIS ULANG metode yang tidak dikenal menjadi `GET`. Diverifikasi pada Bun 1.3.14 terhadap Varnish sungguhan: permintaan tiba sebagai `ReqMethod GET`, tidak pernah menyentuh cabang ban di VCL, dilayani sebagai halaman biasa, dan membalas 200. Karena 200 itu, klien melaporkan purge BERHASIL padahal cache tidak pernah tersentuh — invalidasi yang sepenuhnya mati namun tampak sehat, baik dari kode maupun dari `bun run edge-cache:health` (yang memakai metode kustom yang sama). Dua perubahan menutup kelas kegagalan ini, bukan hanya gejalanya: 1. Transport menjadi `POST /__awcms-edge-cache/ban`. Tidak lagi bergantung pada metode kustom yang harus selamat melewati setiap klien HTTP di rantai — sesuatu yang lapisan ini tidak bisa verifikasi saat runtime. 2. Respons ban membawa penanda `X-Edge-Cache-Ban: ok` yang WAJIB ada. Sebuah 200 tanpa penanda kini dilaporkan GAGAL, bukan sukses: artinya yang menjawab bukan handler ban cache, melainkan sesuatu yang lain (biasanya aplikasi itu sendiri). Inilah yang membuat "sukses palsu" tidak mungkin terulang. Diverifikasi pada Varnish sungguhan: MISS -> HIT -> POST ban (200 + penanda) -> MISS; GET ke path ban 405; POST tanpa token 403. Unit test tidak bisa menangkap ini karena mereka men-stub `fetch`; dua test baru menutupnya dari sisi kontrak — satu menegaskan permintaan benar-benar POST ke path yang dituju, satu menegaskan 200 tanpa penanda dibaca sebagai gagal. Refs #359 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This was referenced Jul 25, 2026
ahliweb
added a commit
that referenced
this pull request
Jul 25, 2026
…CI (#363) Tindak lanjut post-mortem #359/#361. Invalidasi sempat mati total selama dua rilis sementara empat pengaman melaporkan sehat — dan keempatnya ternyata satu asumsi yang sama: bahwa permintaan yang ditulis adalah permintaan yang terkirim. Unit test men-stub `fetch` (persis lapisan yang rusak), `edge-cache:health` memakai klien yang sama dengan yang diperiksanya, klien itu menyimpulkan sukses dari status code, dan metrik ikut mencatat "purged" untuk non-purge. Dua penutup yang berdiri di luar asumsi itu: - tests/integration/edge-cache-varnish.integration.test.ts menyalakan varnish:7.7.3 sungguhan dari deploy/varnish/default.vcl yang dikirim (hanya alamat backend yang ditukar, substitusi yang sama dengan skrip repoint staging), lalu membuktikan purge benar-benar membuang objeknya dan origin kembali dipukul. CI menjalankannya dengan EDGE_CACHE_VARNISH_TEST=1 sehingga ketiadaan Docker menjadi gagal keras, bukan skip diam-diam — suite yang menjaga transport tidak boleh lulus dengan cara tidak dijalankan. - `bun run edge-cache:verify -- --url=<url>` memanaskan URL sampai HIT, mem-purge, lalu mewajibkan MISS. Sebuah MISS saja tidak membuktikan apa pun (kedaluwarsa TTL identik), jadi HIT beberapa detik sebelumnya itulah yang membuat urutan ini sahih. Suite baru diuji balik dengan mengembalikan transport lama sementara: 4 dari 8 test gagal, termasuk satu yang melaporkan `purged` padahal tokennya salah. bun run check hijau: 3737 pass, 0 fail. Co-authored-by: AWCMS-Micro Security <security@awcms-micro>
This was referenced Jul 25, 2026
Merged
ahliweb
added a commit
that referenced
this pull request
Jul 26, 2026
…369/#370/#371/#372 + bagian in-repo #296) (#374) * refactor(lib): tetapkan batas src/lib dan pindahkan kode presentasi modul ke presentation/ `src/lib/` tumbuh menjadi sistem modul kedua yang tidak dijaga gerbang mana pun: lima namespace (`comments`, `newsletter`, `theming`, `seo`, `search`) menyandang nama modul yang sudah ada dan berisi kode milik modul itu, dengan `seo_distribution` bahkan merujuk ke ATAS ke `src/lib/seo/` lewat jalur yang validator DAG tidak bisa lihat. Penyebabnya: kontrak modul tidak punya tempat bagi kode presentasi/pengiriman, sehingga `src/lib/<nama-modul>/` menjadi satu-satunya rumah yang tersedia. ADR-0038 memutuskan definisinya: `src/lib` hanya infrastruktur teknis yang tidak menyandang nama domain; composition root rute, glue middleware, dan skrip klien browser milik modul tinggal di `src/modules/<m>/presentation/`. Lapisan tidak dienumerasi di kode mana pun (tiga lapisan lain juga tidak), jadi tidak ada mekanisme baru yang ditambahkan ke `module-contract.ts` — yang ditegakkan mesin adalah gerbangnya, bukan penamaan lapisannya. - Sepuluh berkas dipindah dengan `git mv` (riwayat terjaga). Pemindahan MURNI: tidak ada perubahan perilaku, API, migration, event, permission, atau registry (tetap 22 modul; `MODULE_CONTRACT_VERSION` tidak berubah). - `modules:dag:check` diperluas: GAGAL bila sebuah namespace `src/lib/<x>/` bertabrakan nama dengan `moduleKey` — persis, atau lewat alias domain terdaftar (`seo` -> `seo_distribution`, `search` -> `site_search`), tanpanya dua dari lima kasus historis akan lolos. Pesan gagalnya menyebut modul pemilik + tujuan pindah. - `tests/unit/lib-namespace-ownership.test.ts` MENYUNTIKKAN pelanggaran untuk membuktikan gerbangnya benar-benar menolak (termasuk memanggil `main()` skrip di atas pohon fixture dan memeriksa exit code 1) — bukan sekadar hijau setelah diperbaiki. Satu pengecualian tercatat: `src/lib/logging/` (primitif logger bebas database, dipakai ~139 berkas); tesnya membuktikan `logging` TERDETEKSI dan hanya disenyapkan oleh tabel pengecualian, bukan titik buta deteksi. - `src/lib/files/` dan `src/lib/errors/` yang kosong (hanya `.gitkeep`) dihapus. Refs #371 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(api): satu factory defineTenantRoute untuk pembukaan auth/tenant rute API Pembukaan auth/tenant yang disalin ke 201 dari 260 rute (resolveAuthInputs → cek tenant/token → getDatabaseClient → hashSessionToken → withTenant → authorizeInTransaction → short-circuit auth.denied) kini hidup satu kali di src/modules/_shared/tenant-route.ts. - `workClass` WAJIB di tipe factory (tanpa default): 221 rute memakai "interactive" karena tidak ada yang meneruskan argumennya, bukan karena ada yang memutuskannya. Menghilangkannya sekarang gagal kompilasi. - `unavailableBehavior` di-hardcode "response" dan sengaja tidak bisa dioverride — rute adalah pemanggil Response menurut konstruksi; "throw" hanya untuk pemanggil non-Response (#323), dan di sini justru akan mengubah 503 terkendali jadi 500. - Larangan Promise.all atas satu `tx` (#324) didokumentasikan di tipe `tx` yang diterima handler. - `prepare` menampung parsing body/query, cek Idempotency-Key, dan request hashing — tetap sebelum koneksi diambil. Modul data_lifecycle dimigrasi penuh (5 file rute, 6 handler) tanpa perubahan perilaku; tes integrasi lamanya tetap hijau tanpa disunting. Gerbang baru `bun run api:tenant-route:check` menolak rute BARU yang memanggil withTenant langsung, dengan daftar NOT_YET_MIGRATED berisi 235 rute lama yang hanya boleh menyusut (entri basi juga menggagalkan gerbang). Generator work-class kini mengenali defineTenantRoute — tanpa itu rute yang dimigrasi justru HILANG dari registry, dan menolak menulis "default" untuk rute factory tanpa literal. Refs #370 * test(website-platform): axe atas daftar/pencarian/404 publik + pemeriksa tautan operator Menutup dua celah #296 yang masih terbuka di dalam repo. 1. `tests/e2e/public-discovery-a11y.e2e.ts` — axe-core (WCAG 2.2 A/AA, gagal pada critical/serious) atas permukaan di ANTARA beranda dan artikel: halaman daftar `/news` + `/blog/{tenantCode}`, halaman pencarian blog (kosong dan berisi), keempat cabang render `/search` (belum ada query / terlalu pendek / tanpa hasil / ada hasil), dan dokumen 404 publik bersama — dalam EN dan ID, pada viewport desktop dan ponsel, ditambah pemeriksaan pencarian yang dioperasikan hanya dengan keyboard (fokus terlihat + Enter mengirim form). Tiga mekanisme locale berbeda dipakai di permukaan publik; spec ini memakai `default_locale` tenant (tenant kedua ber-locale `id`) dan `?locale=` pada `/search`, bukan cookie yang dipakai spec lain. Status HTTP di-assert SEBELUM axe agar route yang diam-diam 404 tidak dilaporkan bebas pelanggaran. Kedua gate dibuktikan bisa MERAH lewat kontrol negatif (img tanpa alt; outline/box-shadow none). Spec ini menulis ulang singleton `awcms_micro_setup_state`, jadi ia memegang advisory lock lintas-berkas `setup-state-ownership` selama hidupnya; seed unik per-run dan dibersihkan di `afterAll` agar aman terhadap retry Playwright. 2. `scripts/link-check.ts` + `bun run link:check -- --url=<url>` — pemeriksa tautan yang bisa dijalankan operator terhadap URL mana pun. Merayapi graf halaman yang dirender dari URL awal, ditambah direktif `Sitemap:` di `robots.txt` dan sitemap index/anak, lalu memverifikasi setiap anchor internal, `rel=canonical`, `rel=alternate hreflang`, tautan feed, dan pagination benar-benar terselesaikan (<400 setelah redirect). Laporan JSON ke stdout mengikuti idiom `scripts/edge-cache-verify.ts`; exit 0 bersih / 1 tautan rusak / 2 usage error atau URL awal tak terjangkau — crawl yang tidak menjangkau apa pun tidak pernah dilaporkan hijau. Internal-vs-eksternal ditentukan oleh HOST, bukan origin penuh: terukur di app ini `<loc>` sitemap memakai `https://` sementara permalink di halaman yang sama memakai `http://`, sehingga perbandingan origin-ketat akan melewati separuh tautan situs sendiri lalu melaporkan hijau. `--site-origin=` untuk memeriksa deployment lewat alamat lain (host staging, IP internal di balik CDN). Ekstraksi tautan memakai pemindai satu-lintasan yang melewati komentar/CDATA/`<script>`/`<style>` (bukan rantai strip-lalu-rescan yang memicu CodeQL js/incomplete-multi-character-sanitization), dan entitas `&` didekode TERAKHIR (js/double-escaping). Refs #296 * refactor(pages): pecah empat halaman admin raksasa jadi markup + lapisan presentation 51 halaman `.astro` berbanding 16 komponen berarti praktis tidak ada lapisan komponen: tiap halaman membangun ulang tabel, dialog, form, dan penanganan errornya sendiri sebagai teks di dalam satu berkas. Gelombang pertama memecah empat halaman yang sudah punya jaring pengaman Playwright, dengan tiga gerakan mekanis per halaman — bukan penulisan ulang. - Skrip inline `<script>` (1.263 baris) pindah ke `src/modules/<m>/presentation/*-client.ts` (ADR-0038) dan diimpor kembali lewat `<script>` agar tetap di-bundle Astro; `bun run build` membuktikan keempat bundle klien terbentuk. Begitu jadi `.ts` ia masuk `tsc` dan bisa di-unit-test. - Frontmatter pindah ke `presentation/<page>-page-data.ts`: permission gating, `withTenant` read, builder daftar opsi, dan builder blob `clientStrings` — yang terakhir diketik dengan interface milik modul kliennya sendiri, jadi `tsc` gagal kalau kedua sisi melenceng. - Stylesheet pindah ke `presentation/<page>.css`. CSS itu kini global, bukan scoped Astro: tiga selector element-level di `tenant/domains` dijangkarkan ulang ke `.domain-manager` agar tidak merembes ke topbar `AdminLayout`. Komponen diekstrak hanya untuk pola yang DIUKUR muncul di lebih dari satu halaman: `ClientJsonData` (35/51 halaman), `LoadErrorNotice` (38/51), `TextField` (27/51), `SelectField` (31/51), `FieldHint` (18/51), `CheckboxField`/`CheckboxGroup` (16/51). Ketiganya sengaja tanpa CSS — halaman-halaman itu tidak sepakat soal spacing, jadi komponen memiliki markup + nama kelas dan stylesheet halaman tetap memiliki tampilannya. `DataTable` dapat prop `columns`/`dataRole`; `ConfirmDialog` membuat label opsional, yang sekaligus memperbaiki `admin/registrations.astro` — satu-satunya pemanggil yang tidak mengirim keduanya dan merender dua tombol tanpa teks (`bun run typecheck` = `tsc --noEmit`, yang tidak pernah membaca `.astro`). Hasil terukur: access-users 1005->397, analytics 1123->380, security 1069->399, tenant/domains 1045->371. Keempat spesifikasi E2E-nya lulus TANPA disunting (6 spec dijalankan nyata terhadap server lokal + Postgres, plus `admin-a11y-smoke`). 48 unit test baru menutup cabang penanganan error yang selama ini tidak pernah dieksekusi tes apa pun. Dua temuan ikutan saat memindahkan kode: `tenant/domains` memanggil `withTenant` tanpa `unavailableBehavior: "throw"` (kelas insiden PR #323), dan bentuk closure-assignment di frontmatter `security` menyempitkan `policy` jadi `never` bagi setiap referensi template. Gerbang anggaran baris sengaja TIDAK ditambahkan di sini (menyentuh `scripts/` + `package.json`); dicatat sebagai langkah lanjutan di doc 14. Refs #372 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(quality): pasang gerbang analisis statis untuk lapisan .astro Sebelum ini lapisan .astro (51 halaman + 16 komponen, ~19.2k baris skrip browser inline) tidak diperiksa alat apa pun: `tsc` melewati ekstensi tak dikenal secara diam-diam, `bun run lint` hanya `prettier --check`, dan `@astrojs/check` belum terpasang. Yang ditambahkan: - `bun run typecheck:astro` = `astro check --minimumSeverity error` (satu-satunya gerbang yang mengetik-periksa frontmatter .astro DAN isi `<script>` inline). Masuk `bun run check` + langkah CI tersendiri. - ESLint flat config (`eslint.config.mjs`) di dalam `bun run lint`: `no-floating-promises` + `no-misused-promises` (type-aware, .ts), `no-floating-promises` untuk frontmatter .astro, dan aturan sintaks (`js.configs.recommended` + `@typescript-eslint/no-unused-vars`) di dalam blok `<script>` yang diekstrak processor. - `"DOM"` + `"DOM.Iterable"` di `compilerOptions.lib`. Angka terukur pada 0a8c3ba (v1.1.0): `astro check` 34 error (7 berkas) -> 0; ESLint 49 error (48 no-floating-promises + 1 no-misused-promises) -> 0 dengan SATU pengecualian per-berkas eksplisit. Tidak ada `--max-warnings` longgar, tidak ada `@ts-ignore`/`any`, tidak ada aturan yang dimatikan global. `no-unnecessary-condition` sengaja BELUM dinyalakan: 87 temuan di ~40 berkas (termasuk area yang sedang dikerjakan paralel) — didokumentasikan sebagai hutang terukur di doc 07, bukan dilonggarkan diam-diam. Bug NYATA yang ditemukan gerbang baru ini: 1. /admin/registrations mengirim NAMA peran, bukan UUID-nya. `role.id` tidak ada pada `RoleWithPermissions` (yang benar `role.roleId`); `<option>` tanpa atribut `value` membuat `select.value` mengembalikan teks opsi, sehingga approve-dengan-peran selalu mengirim roleIds yang tidak valid. 2. /admin/media merender badge "null B" untuk objek tanpa ukuran (`formatSize(bytes: number)` diberi `number | null`; `null < 1024` bernilai true). 3. /admin/reporting/projections memanggil `showBanner(..., "warning")` padahal union-nya hanya "success" | "error" DAN tidak ada CSS `[data-variant="warning"]` di mana pun — peringatan ketidakcocokan rekonsiliasi tampil tanpa gaya, tak terbedakan dari pesan netral. 4. Dua `<ConfirmDialog>` dirender tanpa prop wajib `confirmLabel`/ `cancelLabel` (kosmetik: label ditimpa saat dialog dibuka). 5. 48 promise `Bun.serve(...).stop(true)` tanpa `await` di teardown test (kandidat penyebab flakiness port/timing). Selain itu 26 dari 34 error `astro check` berasal dari satu pola: `let x: T | null = null` yang ditulisi DARI DALAM callback `withTenant` — analisis alur TypeScript tidak melihat penulisan itu sehingga `x` menyempit jadi `never` di seluruh template. Diperbaiki dengan mengembalikan nilai sebagai return value callback, yang otomatis menuntut `{ unavailableBehavior: "throw" }` (AGENTS.md #8, kelas bug Perubahan devDependency: `typescript` diturunkan `^7.0.2` -> pin eksak `6.0.3`. TypeScript 7 adalah kompiler native yang tidak lagi mengekspor API programatik, sehingga `astro check` menolak jalan dan `typescript-eslint` (peer `<6.1.0`) tidak bisa dipakai sama sekali. `tsc --noEmit` tetap nol error, hanya 0,8 s -> 6,9 s. Refs #369 * fix(quality): karantina toolchain TS 6 agar root tetap TypeScript 7 Revisi atas commit sebelumnya: menurunkan `typescript` root ke 6.0.3 adalah arah yang salah. `tsc --noEmit` memeriksa 100% pohon `.ts` (seluruh src/lib, src/modules, scripts, tests), sementara `astro check` hanya menyentuh `.astro` — melemahkan gerbang yang lebih luas demi tooling yang lebih sempit tidak sepadan. Root dikembalikan ke `typescript ^7.0.2` (terbukti: `tsc --version` => 7.0.2, `bun run typecheck` 0,8 s, 0 error). Toolchain yang masih terikat API programatik TypeScript dikarantina di `tools/static-analysis/` dengan package.json + lockfile + node_modules sendiri berisi `typescript` 6.0.3 (pin eksak), `@astrojs/check`, `eslint`, `typescript-eslint`, `eslint-plugin-astro`, `eslint-plugin-jsx-a11y`. Bukan anggota workspace Bun, jadi TS 6 tidak ter-hoist ke root (terbukti: `bun run static-analysis:versions` => root 7.0.2, quarantine 6.0.3). `scripts/static-analysis.ts` menjalankan biner karantina dengan cwd = root repo, sehingga masing-masing me-resolve TypeScript dari pohonnya sendiri: - `bun run typecheck:astro` -> astro-check (1370 berkas, 0 error) - `bun run lint:eslint` -> eslint (1372 berkas dipindai, 0 masalah) - `bun run static-analysis:quarantine:check` -> GAGAL begitu `peerDependencies.typescript` toolchain yang terpasang menerima 7.x, jadi tidak ada yang perlu mengingat kapan workaround ini dicabut. Offline, deterministik, ikut `bun run check` + CI. Asersi cakupan, bukan sekadar "scanned > 0". Varian `basePath` yang disarankan literatur diukur me-lint TEPAT 1 berkas lalu exit 0 tanpa pesan apa pun — "> 0" akan lolos. Runner-nya menghitung berkas sumber nyata (1365) dan menolak hasil di bawah 90% darinya. Dibuktikan dengan mutation test: config lumpuh -> "hanya memeriksa 1 berkas, padahal pohon sumber punya 1365 (minimum 1228). Gerbang MATI, bukan hijau", exit 1. Konsekuensi karantina yang jujur dicatat: type-aware ESLint pada frontmatter `.astro` menjadi mustahil, karena `astro-eslint-parser` me-resolve `typescript` dari `process.cwd()` (= root = TS 7) saat membangun program tipe. Nol temuan hilang (aturan itu memang 0 temuan), dan `astro check` tetap mengetik-periksa frontmatter sepenuhnya. Blok `<script>` tetap di-lint (aturan sintaks) — dibuktikan ulang dengan mutation test setelah pemindahan. Dua jebakan tambahan yang ditemukan saat memindahkan konfigurasi: `src/**/*.ts` juga cocok dengan path virtual `x.astro/1_1.ts` sehingga aturan type-aware ikut menempel di sana (butuh `ignores`), dan `parserOptions` flat-config di-merge dangkal sehingga `project: null` saja tidak mematikan `projectService: true` warisan blok sebelumnya. Semua pekerjaan lain dipertahankan apa adanya: 5 bug nyata, daftar pengecualian per-berkas, keputusan tidak mengaktifkan `no-unnecessary-condition`, dan langkah CI. Refs #369 * chore(quality): tegakkan gerbang tenant-route + sinkronkan skill dengan arsitektur baru Gerbang `api:tenant-route:check` (Issue #370) sebelumnya bisa dijalankan tapi tidak ditegakkan — agent yang membuatnya tidak boleh menyunting baris `check` bersama. Sekarang terpasang, sehingga daftar `NOT_YET_MIGRATED` benar-benar hanya bisa menyusut. Tiga skill disinkronkan: `awcms-micro-new-endpoint` (defineTenantRoute wajib untuk rute tenant-scoped baru), `awcms-micro-new-module` (lapisan `presentation/` + batas `src/lib` per ADR-0038), `awcms-micro-ui-screen` (pola halaman `.astro` tipis, angka nyata hasil #372). Sekalian membuang dua klaim basi di `awcms-micro-new-endpoint`: topologi "LAN-first" (dihapus ADR-0034/0036) dan pernyataan bahwa rute publik tenant-scoped belum punya implementasi contoh. Skill tidak tercakup `bun run check`, jadi keduanya bertahan sejak refactor lama. Refs #369 #370 #371 #372 * fix(quality): perbaiki gerbang ESLint yang gagal karena ukuran laporan, lalu tangani 18 temuan yang tersembunyi di baliknya Gerbang `lint:eslint` dari #369 tidak pernah benar-benar melaporkan isinya di pohon terintegrasi. Dua sebab, keduanya membuatnya merah karena alasan yang tidak ada hubungannya dengan kode: 1. `ignores` tidak memuat `.claude/**`, sehingga ESLint memindai seluruh worktree agent paralel BESERTA keluaran `dist/`-nya. Pola flat-config tanpa awalan `**/` bersifat root-anchored, jadi `dist/**` tidak menangkap `dist` bersarang. CI tidak punya worktree, jadi cacat ini hanya menggigit secara lokal — justru karena itu ia harus dipatok. 2. `Bun.spawnSync` MEMOTONG pipa stdout untuk laporan JSON sebesar ini (~350 KB), sehingga parse-nya meledak di tengah token dan gerbang melaporkan "JSON tidak valid". Laporan kini lewat `-o` ke berkas, sehingga ukuran laporan tidak bisa lagi menentukan vonis. Setelah gerbangnya jujur, ia menemukan 18 `no-misused-promises` — semuanya di berkas `presentation/` BARU, yaitu kode yang baru terlihat oleh ESLint justru karena #372 memindahkannya keluar dari `.astro`. Tesis epik #373 membuktikan dirinya sendiri. Semua 18 adalah `addEventListener("submit", async …)`: DOM menerima promise yang tidak pernah dilihat siapa pun, jadi kegagalan menjadi unhandled rejection dan pengguna tidak melihat apa-apa — kelas kegagalan senyap yang sama dengan #359/#361. Ditangani lewat satu chokepoint `asyncHandler()` di `admin-form-client.ts`; island komentar PUBLIK memakai kembarannya secara lokal agar bundel publik tidak menyeret klien admin. `i18n/messages.pot` diregenerasi (referensi baris bergeser karena kode pindah berkas) dan `api:tenant-route:check` kini bagian dari `bun run check`. Refs #369 #370 #371 #372 * fix(security): tangani temuan ronde review PR #374 Reviewer + security auditor berjalan atas PR ini. Nol CRITICAL, nol BLOCKER. Yang diperbaiki di sini adalah temuan yang berada di berkas yang memang sudah dibuka PR ini. XSS laten — `ClientJsonData.astro` (muncul di KEDUA review). `JSON.stringify` tidak meng-escape `<`, sehingga payload berisi `</script>` menutup data island dan sisanya diparse sebagai HTML hidup. Nol call site eksploitabel hari ini (semuanya string i18n milik repo), tapi komponen ini mengiklankan diri menerima "payload JSON apa pun" dan 35 halaman admin adalah kandidat pemakainya. Serializer dipindah ke `src/lib/ui/client-json-data.ts` agar bisa diuji + 3 unit test dengan payload `</script>` sungguhan. Kelas #323 di rute PUBLIK — tiga pemanggil non-`Response` di modul `theming` memanggil `withTenant` tanpa `unavailableBehavior: "throw"`. Pada `/theming/tokens.css` pool jenuh berarti `[object Response]` tersaji sebagai CSS di setiap page load. Kini degradasi ke tema default / preview `null`. Pra-ada, tapi PR ini menyapu kelas yang sama di `admin/tenant/domains` dan melewatkan tiga ini di berkas yang ia sentuh sendiri. Pengerasan gerbang, semuanya kelas "hijau tapi mati" yang PR ini justru melawan: - `validate-module-graph.ts` — `src/lib` tak terbaca kini MELEMPAR, bukan melaporkan pohon kosong yang lolos; lookup tabel pakai `Object.hasOwn`. - `static-analysis.ts` — laporan tmp pakai `mkdtemp` (path lama bisa ditebak dan jadi target symlink lewat `-o` ESLint); pembersihan dipindah ke sebelum `fail()` karena `process.exit()` melewati `finally`. - `api:tenant-route:check` jadi langkah CI eksplisit — CI menjalankan gerbang satu per satu, jadi memasangnya di `bun run check` saja tidak cukup. - Daftar pengecualian ESLint dikosongkan; entrinya sudah basi di PR yang sama. - `link-check.ts` — filter skema jadi ALLOW-list (deny-list lama melewatkan `file:`), userinfo dibuang dari laporan JSON. Koreksi ADR-0038. Klaim "nol impor relatif lintas-modul di seluruh src/modules" SALAH dan saya yang menulisnya. Pengukuran ulang menemukan ~80 impor semacam itu yang sudah ada jauh sebelum ADR ini. Yang benar: tidak ada SIKLUS antar modul, dan yang dijaga hari ini adalah dependensi yang DIDEKLARASIKAN — bukan impor tingkat berkas. Lubang itu kini tercatat sebagai pekerjaan lanjutan, bukan diklaim selesai. Refs #369 #370 #371 #372 #374 --------- Co-authored-by: AWCMS-Micro Security <security@awcms-micro> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ditemukan saat memverifikasi #360 di staging: saya terbitkan artikel sungguhan, dan cache tetap
HIT.Akar masalahnya
Klien purge memakai metode HTTP kustom
BAN— idiom Varnish yang lazim, dan yang saya kirim di #353 tanpa pernah menjalankannya lewat jalur aplikasi sungguhan.fetchmilik Bun diam-diam menulis ulang metode yang tidak dikenal menjadiGET. Diverifikasi pada Bun 1.3.14 terhadap Varnish sungguhan:Permintaan tidak pernah menyentuh cabang ban di VCL, dilayani sebagai halaman biasa, dan membalas 200. Karena 200 itu, klien melaporkan purge berhasil. Hasilnya: invalidasi yang sepenuhnya mati namun tampak sehat — dari kode, dan dari
bun run edge-cache:health, yang memakai metode kustom yang sama sehingga ikut buta.Perbaikannya menutup kelasnya, bukan gejalanya
POST /__awcms-edge-cache/ban. Tidak lagi bergantung pada metode kustom yang harus selamat melewati setiap klien HTTP di rantai — sesuatu yang lapisan ini tidak bisa verifikasi saat runtime, jadi lebih baik tidak diandalkan.X-Edge-Cache-Ban: ok. Sebuah 200 tanpa penanda kini dilaporkan gagal. Ini bagian yang penting: tanpa penanda, respons 200 dari apa pun — termasuk aplikasi yang kebetulan menjawab — akan terus terbaca sebagai sukses. Sekarang tidak bisa.Verifikasi pada Varnish sungguhan
POSTban dengan tokenX-Edge-Cache-Ban: okGETke path banPOSTtanpa tokenKenapa unit test tidak menangkapnya
Karena mereka men-stub
fetch— persis lapisan yang rusak. Dua test baru menutupnya dari sisi kontrak: satu menegaskan permintaan benar-benar POST ke path yang dituju, satu menegaskan 200 tanpa penanda dibaca sebagai gagal.bun run checkhijau: 3729 pass, 0 fail.🤖 Generated with Claude Code