Skip to content

fix(s3): combine case-insensitive user metadata headers - #1462

Open
hsusul wants to merge 1 commit into
supabase:masterfrom
hsusul:fix/s3-metadata-header-collisions
Open

hsusul wants to merge 1 commit into
supabase:masterfrom
hsusul:fix/s3-metadata-header-collisions

Conversation

@hsusul

@hsusul hsusul commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Combine valid user-metadata entries whose names differ only in case when returning S3 HEAD/GET responses. Add ten AWS SDK-deserialized regression cases covering both commands.

Problem and root cause

Fixes #1461.

Storage JSON metadata can contain both Title and title. The shared toAwsMetadataHeaders mapper lowercases names but overwrites earlier entries, so S3 clients silently receive only the last value. S3's documented behavior combines same-name headers, ignoring case, into a comma-delimited list.

Solution

Append colliding valid values with a comma and validate the resulting header before assigning it. Preserve the existing ASCII/name/value validation, header-length limit, and x-amz-missing-meta accounting. HEAD and GET already share this mapper; no route or upload changes are needed.

Tests

The first regression failed on clean current master 76b70ba8: expected first,second, received second. It passed after the mapper change. Tests use the real SDK deserializer and production mapper with in-memory storage boundaries; no network or live credentials are required.

Coverage: two/three colliding names, empty values, existing commas, distinct names, invalid entries, and combined-header overflow, for both HEAD and GET.

npm run test:unit -- src/storage/protocols/s3/s3-metadata.test.ts src/storage/protocols/s3/s3-handler.test.ts src/http/routes/s3/index.test.ts src/http/routes/s3/router.test.ts src/internal/http/header.test.ts --maxWorkers=2 --testTimeout=30000 --hookTimeout=30000
# 247 passed across 5 files

Validation

  • npm run test:unit -- --maxWorkers=2 --testTimeout=30000 --hookTimeout=30000: 2,551 passed across 160 files.
  • npm run build: passed, including TypeScript checking.
  • npm run lint: passed, including repository-wide Biome and Prettier.
  • git diff --check: passed.
  • The default 5-second feature-gating test timeout also failed on unchanged upstream; both baseline cases pass with the 30-second allowance above. No timeout configuration was changed.
  • Docker/Postgres integration tests were not run: their required ports 5432/5433 are occupied by unrelated databases, which were left untouched.

Compatibility, documentation, and prior work

Only colliding response metadata changes; ordinary headers and invalid-entry handling remain unchanged. No API/schema/dependency changes or documentation updates are needed for this S3-compatibility fix. Searched issues, PRs, discussions and history for the collision behavior. Active #1455 validates decoded metadata shape and does not modify this response mapper.

@hsusul
hsusul requested a review from a team as a code owner October 2, 2026 00:12
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 36944784621

Coverage increased (+0.08%) to 83.912%

Details

  • Coverage increased (+0.08%) from the base build.
  • Patch coverage: 7 of 7 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 14302
Covered Lines: 12426
Line Coverage: 86.88%
Relevant Branches: 8778
Covered Branches: 6941
Branch Coverage: 79.07%
Branches in Coverage %: Yes
Coverage Strength: 788.2 hits per line

💛 - Coveralls

This branch has not been deployed

No deployments
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.

S3 HEAD/GET overwrite case-insensitive user metadata collisions

2 participants