Yellow Theme

Next start · November 2026

Notes / Engineering note

How we deliver · methodology

2026-09-03

Why authenticated downloads lose their file extensions

JavaScript cannot read Content-Disposition across origins unless CORS exposes it. When that header disappears, the UI guesses a filename—and usually drops the extension.

Thesis: Cross-origin fetch cannot read Content-Disposition (or a custom X-Filename header) unless the API’s CORS configuration exposes them. If JavaScript cannot see the header, your Save-As fallback will sanitize the artifact title — and titles rarely include .tf or .md.

The failure mode

Authenticated downloads often look like this:

  1. SPA calls GET /artifacts/{id}/download with a Bearer token.
  2. API returns binary bytes plus Content-Disposition: attachment; filename="thing.tf".
  3. Client builds a Blob and calls a.download = filename.

Step 3 only works if step 2’s headers are visible to script. Under the Fetch CORS rules, most response headers are opaque unless listed in Access-Control-Expose-Headers. Lambda can emit the right headers and still lose: when API Gateway HTTP API manages CORS, its expose list wins.

Fix the gateway, not only the Lambda

On API Gateway HTTP APIs, set CORS expose_headers to include at least:

Confirm with a real browser session, not only curl. Curl always sees headers; JavaScript does not.

Defense in depth: basename on the list DTO

Even with expose-headers fixed, keep a safe download basename on the list payload — the leaf name only (pipeline.tf), not the full object key. The UI can prefer:

  1. Header / X-Filename
  2. DTO filename
  3. Sanitized title (last resort)

That avoids re-introducing path disclosure while surviving the next CORS regression. Do not put storage prefixes or assessor notes on the public list DTO.

Titles vs files

Humans want titles like “CI pipeline and release evidence.” Filesystems want pipeline.tf. Keep both: display the title, download the basename. When they collide in the UI fallback, you get underscores and no extension — exactly the bug users report as “weird filename.”

Checklist

Series start: WAF in front of Function URLs.

Engineering commentary only — not audit, legal, or certification advice.