§8 review convergence (todo §8); re-ran the architecture + product reviewers to convergence — 5 rounds, until both returned zero new actionable findings. Fixed across rounds 1-4 (tests-first): bounded every outbound Ory fetch with a timeout (src/fetch-timeout.ts withTimeout + ORY_TIMEOUT_SEC default 5, incl. the http JWKS fetch) so a hung Ory can't park a request handler; anonymous on a permission-gated plugin route now 303→/login (was a dead-end 403; signed-in-without-role still 403); an already-signed-in user is sent home from /login + /registration; the onRequest hook short-circuit now sets the fresh CSRF cookie; admin-users malformed :id → 404 (was 500) via safeDecode; parseJwks validates key element shape (fails loud at load); removed the dead COOKIE_SECRET (loaded + enforced + documented but never read); documented HYDRA_ADMIN_URL; admin recovery shows the code + links to the public /recovery instead of the browser-unreachable admin-API link; reference-plugin breadcrumb-label + pagination/datetime README notes; corrected the contract doc to not over-promise a post-login "retry". Declined: unconditional base-ctx chrome (would build the menu per request, regressing the lazy hot path). Deferred → §9: return_to-preservation for deep-link login. Stability-reviewer on the cumulative diff: APPROVE, no Critical/High (addressed its Low nits). typecheck + 310 units + the full scripts/ci.sh gate (visual 9 · auth 1 · oauth 2 · full 6) green.

This commit is contained in:
2026-06-20 00:42:23 +02:00
parent bd20d00714
commit a20f3507e0
19 changed files with 181 additions and 59 deletions

View File

@@ -123,11 +123,13 @@ test("unknown routes serve the 404 page (a real user-facing flow, covered end-to
});
// The reference plugin (plugins/scheduling) ships discovered in the image. Its nav + routes are
// permission-gated, so an anonymous visitor never sees or reaches them (the authenticated list/form
// flow needs cross-host login infra — deferred to the §8 full E2E, todo line 121). Side-effect-free.
test("the reference plugin is permission-gated: anonymous → 403, hidden from the dashboard nav", async ({ page }) => {
const res = await page.goto("/scheduling/shifts");
expect(res?.status()).toBe(403);
// permission-gated, so an anonymous visitor is bounced to sign in (and never sees it in the nav).
// The authenticated list/form flow is the §8 full E2E (full-flow.spec). Side-effect-free.
test("the reference plugin is permission-gated: anonymous → redirect to /login, hidden from the dashboard nav", async ({ page }) => {
// Don't follow the redirect — this Ory-free suite has no /login handler; assert the gate's 303 itself.
const res = await page.request.get("/scheduling/shifts", { maxRedirects: 0 });
expect(res.status()).toBe(303);
expect(res.headers()["location"]).toBe("/login");
await page.goto("/");
await expect(page.locator(".sidebar")).toContainText("People"); // dashboard nav renders