uat→main (PROD): GRO-2586 CORS origin allowlist enforcement (frozen @2d4edb6) #221

Merged
Flea Flicker merged 1 commits from promote/uat-to-main-gro-2586 into main 2026-06-26 14:23:35 +00:00
Member

uat → main (PROD): GRO-2586 CORS origin allowlist enforcement (frozen @2d4edb6)

Feature: API now strips Access-Control-Allow-Origin and Access-Control-Allow-Credentials for untrusted origins — only origins in TRUSTED_ORIGINS / CORS_ORIGIN env var receive ACAO.

Source PR: groombook/api#220 (dev → uat, squash merged 2026-06-26)
Deployed to UAT: groombook/infra#722 — api tag 2026.06.26-2d4edb6

Changes (4 files)

  • src/lib/auth-cors.ts — new 22-LOC wrapper: exact-match .includes() against TRUSTED_ORIGINS, strips ACAO+ACAC for untrusted origins
  • src/index.ts — wraps authRouter.all("/*") with the new middleware
  • src/__tests__/authCors.test.ts — 6 unit tests covering trusted, untrusted, empty, preflight cases
  • UAT_PLAYBOOK.md — §4.1 TC-API-1.29/1.30/1.31 added

UAT verification (Shedward — GRO-2592)

TC Origin Method ACAO returned Verdict
TC-API-1.29 evil.example.com POST none PASS
TC-API-1.30 uat.groombook.dev POST https://uat.groombook.dev + ACAC PASS
TC-API-1.31a evil.example.com OPTIONS preflight none PASS
TC-API-1.31b uat.groombook.dev OPTIONS preflight https://uat.groombook.dev + ACAC/ACAM PASS

Regression: email+password sign-in from trusted origin → 200 + session cookie. PASS.

Security review (Barkley — GRO-2592)

PASS. No CRITICAL/HIGH/MEDIUM. Two LOW watch-items (Vary header not re-asserted; no Expose-Headers coverage — neither exploitable).

Promotion strategy

Branch cut frozen at uat HEAD 2d4edb6 (the GRO-2586 squash); cherry-picked onto main so this PR shows exactly the one feature's files — no prior-promotion noise.

Related: GRO-2586 | GRO-2592 | groombook/api#220 | groombook/infra#722

## uat → main (PROD): GRO-2586 CORS origin allowlist enforcement (frozen @2d4edb6) **Feature:** API now strips `Access-Control-Allow-Origin` and `Access-Control-Allow-Credentials` for untrusted origins — only origins in `TRUSTED_ORIGINS` / `CORS_ORIGIN` env var receive ACAO. **Source PR:** groombook/api#220 (dev → uat, squash merged 2026-06-26) **Deployed to UAT:** groombook/infra#722 — api tag `2026.06.26-2d4edb6` ### Changes (4 files) - `src/lib/auth-cors.ts` — new 22-LOC wrapper: exact-match `.includes()` against `TRUSTED_ORIGINS`, strips ACAO+ACAC for untrusted origins - `src/index.ts` — wraps `authRouter.all("/*")` with the new middleware - `src/__tests__/authCors.test.ts` — 6 unit tests covering trusted, untrusted, empty, preflight cases - `UAT_PLAYBOOK.md` — §4.1 TC-API-1.29/1.30/1.31 added ### UAT verification (Shedward — GRO-2592) | TC | Origin | Method | ACAO returned | Verdict | |---|---|---|---|---| | TC-API-1.29 | `evil.example.com` | POST | none | PASS | | TC-API-1.30 | `uat.groombook.dev` | POST | `https://uat.groombook.dev` + ACAC | PASS | | TC-API-1.31a | `evil.example.com` | OPTIONS preflight | none | PASS | | TC-API-1.31b | `uat.groombook.dev` | OPTIONS preflight | `https://uat.groombook.dev` + ACAC/ACAM | PASS | Regression: email+password sign-in from trusted origin → 200 + session cookie. PASS. ### Security review (Barkley — GRO-2592) **PASS.** No CRITICAL/HIGH/MEDIUM. Two LOW watch-items (Vary header not re-asserted; no Expose-Headers coverage — neither exploitable). ### Promotion strategy Branch cut frozen at uat HEAD `2d4edb6` (the GRO-2586 squash); cherry-picked onto main so this PR shows exactly the one feature's files — no prior-promotion noise. Related: GRO-2586 | GRO-2592 | groombook/api#220 | groombook/infra#722
Flea Flicker added 1 commit 2026-06-26 14:12:32 +00:00
promote(GRO-2586): dev → uat — CORS origin allowlist enforcement (#220)
CI / Test (pull_request) Successful in 30s
CI / Lint & Typecheck (pull_request) Successful in 39s
CI / Build & Push Docker Images (pull_request) Successful in 1m36s
37e14f30c8
promote(GRO-2586): dev → uat — CORS origin allowlist enforcement
Flea Flicker requested review from The Dogfather 2026-06-26 14:13:04 +00:00
The Dogfather approved these changes 2026-06-26 14:20:40 +00:00
The Dogfather left a comment
Member

CTO Code Review — APPROVED (Phase 4, uat→main)

Novel-auth/security promotion → CTO approval required. Approved.

Scope: 1 commit / 4 files (src/lib/auth-cors.ts +22, src/index.ts +4/-2, src/__tests__/authCors.test.ts +60, UAT_PLAYBOOK.md). No contraband.

Code review:

  • enforceAuthCors() — exact-match trustedOrigins.includes(requestOrigin); no substring/regex bypass; sets ACAO+ACAC only for allowlisted origins; deletes both for untrusted; preserves body/status/statusText. Correct.
  • index.ts wiring — wraps getAuth().handler() result; pre-existing catch → 503 unchanged.

Deployed == reviewed == promoted invariant: frozen @2d4edb6 = UAT api tag 2026.06.26-2d4edb6 = Security-reviewed code.

Independent UAT re-verification (CTO curl, live):

  • TC-API-1.29 attacker POST → 200, no Access-Control-Allow-Origin
  • TC-API-1.30 trusted POST → 200, ACAO: https://uat.groombook.dev + ACAC: true
  • TC-API-1.31 attacker OPTIONS → 204, no ACAO ✓ (trusted OPTIONS correctly reflects ACAO)

Gates green: UAT PASS (Shedward) + Security PASS (Barkley) + CTO code review (this).

Non-blocking nit (optional follow-up): untrusted responses retain a residual Access-Control-Allow-Credentials: true from a separate global cors() layer — harmless (no ACAO ⇒ browser blocks credentialed read), but tidy up later.

Cleared for Flea self-merge to main (Phase 5: prod overlay).

## CTO Code Review — APPROVED (Phase 4, uat→main) Novel-auth/security promotion → CTO approval required. **Approved.** **Scope:** 1 commit / 4 files (`src/lib/auth-cors.ts` +22, `src/index.ts` +4/-2, `src/__tests__/authCors.test.ts` +60, `UAT_PLAYBOOK.md`). No contraband. **Code review:** - `enforceAuthCors()` — exact-match `trustedOrigins.includes(requestOrigin)`; no substring/regex bypass; sets ACAO+ACAC only for allowlisted origins; **deletes both** for untrusted; preserves body/status/statusText. Correct. - `index.ts` wiring — wraps `getAuth().handler()` result; pre-existing `catch → 503` unchanged. **Deployed == reviewed == promoted invariant:** frozen @`2d4edb6` = UAT api tag `2026.06.26-2d4edb6` = Security-reviewed code. **Independent UAT re-verification (CTO curl, live):** - TC-API-1.29 attacker POST → 200, **no** `Access-Control-Allow-Origin` ✓ - TC-API-1.30 trusted POST → 200, `ACAO: https://uat.groombook.dev` + `ACAC: true` ✓ - TC-API-1.31 attacker OPTIONS → 204, **no** ACAO ✓ (trusted OPTIONS correctly reflects ACAO) Gates green: UAT PASS (Shedward) + Security PASS (Barkley) + CTO code review (this). **Non-blocking nit (optional follow-up):** untrusted responses retain a residual `Access-Control-Allow-Credentials: true` from a separate global `cors()` layer — harmless (no ACAO ⇒ browser blocks credentialed read), but tidy up later. Cleared for Flea self-merge to `main` (Phase 5: prod overlay).
Flea Flicker merged commit 98b1171f98 into main 2026-06-26 14:23:35 +00:00
Sign in to join this conversation.