Skip to content

fix(core): route 504 CORS through allowlist + cap Access-Control-Max-Age at 7200 - #315

Merged
soberm merged 4 commits into
mainfrom
fix/cors-504-reflection-and-maxage
Aug 7, 2026
Merged

fix(core): route 504 CORS through allowlist + cap Access-Control-Max-Age at 7200#315
soberm merged 4 commits into
mainfrom
fix/cors-504-reflection-and-maxage

Conversation

@soberm

@soberm soberm commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

What

The 504 timeout path in createLambdaHandler built its CORS headers by hand: it reflected the request Origin verbatim (falling back to '*') and always sent Access-Control-Allow-Credentials: true. That reflected an unvalidated origin back to the caller with credentials enabled, and '*' + credentials is outright invalid, so a timed-out request either leaked a permissive CORS grant or produced a response the browser rejects. The timeout path now goes through buildCorsHeaders(origin) — the same allowlist-validated, fail-closed helper the normal response path uses — so an origin that is not on the allowlist gets no CORS grant at all.

Also lowers Access-Control-Max-Age from 86400 to 7200 on the OPTIONS preflight response and in the dev server. 86400 is misleading: browsers cap preflight caching well below that (Chromium 7200s, Safari/WebKit lower still), so the advertised value never applied and made the preflight cache lifetime look longer than it really is. 7200 is the largest value actually honoured by the strictest mainstream engine.

Found during the bug bash (P3 report on the CORS preflight Max-Age value); the 504 origin reflection was spotted while confirming the Max-Age code path.

Changes

  • packages/core/src/lambda-handler.ts — 504 path uses buildCorsHeaders(origin); OPTIONS Access-Control-Max-Age 86400 → 7200.
  • packages/core/src/scripts/dev-server.tsAccess-Control-Max-Age 86400 → 7200.
  • packages/core/src/cors.test.ts — preflight Max-Age expectation updated to 7200.
  • packages/core/src/lambda-handler.test.ts — the old test asserting reflective 504 behaviour is replaced by two allowlist-aware tests (allowed origin gets the grant, disallowed origin gets none).

Testing

npx tsx --test src/cors.test.ts src/lambda-handler.test.ts in packages/core: 81/81 pass, 0 fail (21 suites).

The 2 pre-existing hosting.ts build errors in this package are unrelated to this change and are left untouched.

soberm added 2 commits August 6, 2026 11:39
…Age at 7200

The 504 timeout path in createLambdaHandler built its CORS headers by hand:
it reflected the request Origin verbatim (falling back to '*') and always
sent Access-Control-Allow-Credentials: true. That reflected an unvalidated
origin back to the caller with credentials enabled, and the '*' fallback is
outright invalid in combination with credentials, so a timed-out request
either leaked a permissive CORS grant or produced a response the browser
rejects. The timeout path now goes through buildCorsHeaders(origin), the
same allowlist-validated, fail-closed helper the normal response path uses:
an origin that is not on the allowlist gets no CORS grant at all.

Also lowers Access-Control-Max-Age from 86400 to 7200 on the OPTIONS
preflight response and in the dev server. 86400 is misleading — browsers cap
preflight caching well below that (Chromium 7200s, Firefox 86400s but
Safari/WebKit far lower), so the advertised value never applied and made the
preflight cache lifetime look longer than it is. 7200 is the largest value
that is actually honoured by the strictest mainstream engine.

Found during the bug bash (P3 report on the CORS preflight Max-Age value);
the 504 reflection was spotted while confirming the Max-Age code path.

Tests updated for the new Max-Age, and the old test asserting reflective
504 behaviour is replaced by two allowlist-aware tests (allowed origin gets
the grant, disallowed origin gets none). 81/81 unit tests pass.
@soberm
soberm requested a review from a team as a code owner August 6, 2026 11:40
@changeset-bot

changeset-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b22f4c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@aws-blocks/core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

…gins

Move buildCorsHeaders into cors.ts and cover the isOriginAllowed===false
branch directly, plus assert the 403 preemption on the timeout path.
@soberm
soberm merged commit b48aaec into main Aug 7, 2026
67 of 74 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants