68. Mini project: harden and audit Support Desk
Here is every risk from §64, mapped to the line of Support Desk that handles it and the test that keeps it handled. Use the same table as the security review template for your own apps.
| Risk | Control in Support Desk | Test (§77) |
|---|---|---|
| A01 IDOR | order.owner !== s.userId → 404 |
security.spec.js "Alice cannot refund Bob's order" |
| A01 data isolation | queries scoped by session user | "two users in two contexts see only their own data" |
| A02 headers | SECURITY_HEADERS on every response |
"every response carries the security headers" |
| A02 debug routes | /__test__/* only when NODE_ENV=test |
run the prod build and expect 404 (exercise) |
| A03 supply chain | zero runtime dependencies; npm ci in CI |
npm audit signatures in the CI workflow |
| A04 crypto | scrypt + salt, timingSafeEqual, randomBytes session IDs |
review |
| A05 XSS | textContent only + Trusted Types 'none' |
"model output is rendered as text, never as HTML" |
| A05 component injection | registry + Object.hasOwn + schema |
"the UI refuses components it does not know…" |
| A06 design | refunds need human approval; agent can't execute | "refund needs a human approval…", "declining… changes nothing" |
| A07 auth | rate-limit failed logins, generic errors, new SID per login | "a failed sign-in shows a generic error", "login is rate-limited…" |
| A07 cookies | HttpOnly + SameSite=Lax | "the session cookie is HttpOnly and SameSite" |
| A07 logout | server deletes the session | "sign out ends the session on the server too" |
| A08 CSRF | Fetch Metadata + token header | "…without the CSRF token is refused", "a cross-site request is refused…" |
| A09 logging | JSON security events | review the log output during npm test |
| A10 errors | central handler, generic 500, request id, body limits | "errors never leak stack traces" |
| LLM05 output | text-only rendering, img-src 'self' |
XSS payload test |
| LLM06 agency | propose-only agent + approval card | approval tests |
| LLM10 consumption | chat rate limit, 2,000-char cap, abort on close | "Stop aborts the stream mid-reply" |
🎯 Challenges. (1) Add passkey sign-in to Support Desk with SimpleWebAuthn on the server, and a Playwright test that uses the virtual authenticator from §77. (2) Switch the CSP from
'self'to nonces with'strict-dynamic', and add aReporting-Endpointsheader and a/reportsroute. (3) MakePOST /api/refundsidempotent with anIdempotency-Keyheader, and write the test that double-clicks Approve.