Skip to content

fix(upload): compare media type only when enforcing pinned Content-Type - #29

Merged
acoshift merged 1 commit into
mainfrom
dropbox-ct-params
Jun 29, 2026
Merged

fix(upload): compare media type only when enforcing pinned Content-Type#29
acoshift merged 1 commit into
mainfrom
dropbox-ct-params

Conversation

@acoshift

Copy link
Copy Markdown
Member

What

The signed direct-upload handler (PUT /uploads/{token}) enforced the grant's pinned Content-Type with an exact string match:

if g.ContentType != "" && r.Header.Get("Content-Type") != g.ContentType {
    jsonFail(w, "content type mismatch", http.StatusOK)
    return
}

So a client that adds a standard parameter — e.g. application/pdf; charset=utf-8 against a grant for application/pdf — was wrongly rejected, even though it's the same media type.

Fix: compare only the media type (type/subtype) via mime.ParseMediaType on both sides, per RFC 7231 §3.1.1.1.

Why it's safe (not a constraint bypass)

  • The body is streamed to storage without sniffing, so the pin was always a declared-media-type check, never a content guarantee. Ignoring parameters that don't change the media type preserves exactly that constraint.
  • A real mismatch is still rejected: text/plain vs application/pdfgot != want → fail (existing TestUploadDirect_ContentTypeMismatch still passes).
  • Fail-closed: a malformed/empty client Content-Type parses to "" and is rejected when the grant pins a type.
  • ParseMediaType lowercases, so the comparison is correctly case-insensitive (media types are case-insensitive per RFC) — not a regression.
  • Still gated by g.ContentType != "" (unpinned grants accept anything, unchanged).

Tests

TestUploadDirect_ContentTypeAllowsParams: grant application/pdf, PUT application/pdf; charset=utf-8200 ok. Genuine regression guard (fails on the old exact-match). The existing mismatch test is retained.

Verification

  • go build ./... ok; gofmt/go vet clean.
  • go test -run TestUploadDirect_ContentType . — 2 pass (mismatch + new allows-params); dropbox tests run on the in-memory bucket.

The signed direct-upload handler (PUT /uploads/{token}) enforced the grant's
pinned Content-Type with an exact string match, so a client that adds a standard
parameter — e.g. 'application/pdf; charset=utf-8' against a grant for
'application/pdf' — was wrongly rejected with 'content type mismatch'.

Compare only the media type (type/subtype) via mime.ParseMediaType on both sides,
per RFC 7231 §3.1.1.1. This is not a weakening: the body is streamed to storage
without sniffing, so the pin was always a declared-media-type check — ignoring
parameters that don't change the media type preserves the constraint. A real
mismatch (text/plain vs application/pdf) is still rejected, and a malformed/empty
client Content-Type parses to "" and is rejected (fail-closed). ParseMediaType
also lowercases, so the match is correctly case-insensitive.

Add TestUploadDirect_ContentTypeAllowsParams (charset param accepted); the
existing TestUploadDirect_ContentTypeMismatch (text/plain rejected) still passes.

go build ./... ok; gofmt/go vet clean; go test -run TestUploadDirect_ContentType = 2 pass.
@acoshift
acoshift merged commit 51c4f74 into main Jun 29, 2026
2 checks passed
@acoshift
acoshift deleted the dropbox-ct-params branch June 29, 2026 02:04
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.

1 participant