Repository navigation
reject control characters in MediaType parameter values - #8743
arshsmith1 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request adds a validation check to MediaType.normalizeParameterValue to reject control characters in parameter values. The reviewer identified that the current implementation using javaIsoControl() incorrectly blocks horizontal tabs ( ), which are permitted by RFC 7230. The reviewer provided a fix to exclude tabs from the check and suggested adding a corresponding test case to verify that tabs are allowed.
| checkArgument( | ||
| javaIsoControl().matchesNoneOf(value), | ||
| "parameter values must not contain control characters: %s", | ||
| value); |
There was a problem hiding this comment.
According to RFC 7230 Section 3.2.6, horizontal tab (\t) is explicitly allowed in quoted-string parameter values (qdtext = HTAB / SP / ...). By using javaIsoControl().matchesNoneOf(value), valid parameter values containing tab characters will be incorrectly rejected.
To remain compliant with the RFC while still preventing header injection/smuggling via CR/LF and other control characters, we should exclude \t from the rejected control characters.
checkArgument(
javaIsoControl().and(CharMatcher.isNot('\\t')).matchesNoneOf(value),
"parameter values must not contain control characters: %s",
value);| public void testWithParameter_controlCharacterInParameterValue() { | ||
| MediaType mediaType = MediaType.parse("text/plain"); | ||
| assertThrows( | ||
| IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\r\nb")); | ||
| assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\nb")); | ||
| assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\u0000b")); | ||
| } |
There was a problem hiding this comment.
Add a test case to verify that horizontal tab (\t) is allowed in parameter values, as it is a valid character according to RFC 7230.
| public void testWithParameter_controlCharacterInParameterValue() { | |
| MediaType mediaType = MediaType.parse("text/plain"); | |
| assertThrows( | |
| IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\r\nb")); | |
| assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\nb")); | |
| assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\u0000b")); | |
| } | |
| public void testWithParameter_controlCharacterInParameterValue() { | |
| MediaType mediaType = MediaType.parse("text/plain"); | |
| assertThrows( | |
| IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\\r\\nb")); | |
| assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\\nb")); | |
| assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\\u0000b")); | |
| assertThat(mediaType.withParameter("a", "a\\tb").toString()).isEqualTo("text/plain; a=\"a\\tb\""); | |
| } |
|
I can take care of the tab handling as part of importing this. (But of course let me know if you see problems with Gemini's proposal—or with #8747 more broadly.) |
MediaType.normalizeParameterValueonly required parameter values to be ASCII, which still permits control characters.parse("text/plain; a=\"b\nc\"")is accepted, andwithParameter("a", "b\r\nc")is accepted;toString()then writes the raw CR/LF back out (escapeAndQuotebackslash-escapes CR but still emits the literal byte, and it never touches LF), so aMediaTypebuilt from attacker-influenced parameter values can smuggle CR/LF into a serializedContent-Typeheader. This rejects ISO control characters atnormalizeParameterValue, the single point bothparseandwithParameter(s)route values through, so no instance can hold one; valid values round-trip unchanged.