Repository navigation
Conversation
A part's Content-Type came straight from `options.contentType` and was concatenated verbatim into the multipart header block, so a caller that forwards an attacker-controlled MIME type (e.g. a proxy passing a client's Content-Type into `append`) could embed CRLF to break out of the header line and smuggle additional headers or multipart parts (CWE-93). This is the sibling of the field-name/filename injection fixed in GHSA-hmw2-7cc7-3qxx; that fix escaped `field` and `filename` but left the Content-Type value untouched. Escape `\r` and `\n` as %0D and %0A. Unlike a name/filename, a media type is not a quoted-string, so `"` and `;` are legitimate (e.g. `charset="utf-8"`) and are left intact.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A part's
Content-Typeis taken fromoptions.contentTypeand concatenated verbatim into the multipart header block, with no CR/LF escaping. A caller that forwards an attacker-controlled MIME type — e.g. an upload proxy that preserves the client'sContent-Typeheader — lets an attacker embed CRLF to break out of the header line and smuggle extra headers or whole multipart parts (CWE-93).This is the sibling of the field-name / filename injection fixed in GHSA-hmw2-7cc7-3qxx (
8dff42c). That fix routedfieldandfilenamethroughescapeHeaderParam, but theContent-Typevalue atlib/form_data.js:197was left untouched.Reproduction
The developer appended one field; a WHATWG-compliant parser (
undici/busboy/multer/ python-multipart) reads three, including an attacker-injectedis_admin=true.Fix
Escape
\rand\nas%0Dand%0Aon theContent-Typevalue, matching the WHATWG multipart/form-data encoding already used forname/filename.Unlike a
name/filename(a quoted-string parameter), a media type is not a quoted-string, so",;and=are legitimate in it (e.g.text/plain; charset="utf-8") and are deliberately left intact — only CR/LF can terminate the header line, so escaping just those closes the injection without corrupting valid content types. That's why this uses a CR/LF-only escape rather than reusingescapeHeaderParam(which also%22-escapes").Tests
Extends
test/integration/test-header-injection.js:testContentTypeCRLFInjection— a CRLF-laden content type stays on one header line and smuggles no extra part (fails before this change).testOrdinaryContentTypePreserved— a content type with a quotedcharsetparameter is emitted unchanged.Full suite is green (
0 errors in 29 files).