Reject control characters in MediaType parameter values.

Fixes https://github.com/google/guava/pull/8743

RELNOTES=n/a
PiperOrigin-RevId: 1000906952
This commit is contained in:
arshiya tabasum authored and Google Java Core Libraries committed 2026-10-16 09:31:15 -07:00
1 parent dea4984457
commit 5e82f2f528
4 files changed
+64 -8

No files matched your search

@@ -275,6 +275,18 @@ public class MediaTypeTest extends TestCase {
assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "…"));
}
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\rb"));
assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\u0000b"));
assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\u007fb"));
assertThat(mediaType.withParameter("a", "a\tb"))
.isEqualTo(MediaType.parse("text/plain; a=\"a\tb\""));
assertThat(mediaType.withParameter("a", "a\tb").toString()).isEqualTo("text/plain; a=\"a\tb\"");
}
public void testWithParameter_emptyParameter() {
MediaType mediaType = MediaType.parse("text/plain");
assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("", "a"));
@@ -377,6 +389,15 @@ public class MediaTypeTest extends TestCase {
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=1; b"));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=1; b="));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\u2025"));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\r\nc\""));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\nc\""));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\rc\""));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\\\rc\""));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\\\nc\""));
assertThrows(
IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\u0000c\""));
assertThrows(
IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\\\u0000c\""));
}
// https://github.com/google/guava/issues/6663
@@ -85,7 +85,11 @@ public final class MediaType {
.and(CharMatcher.isNot(' '))
.and(CharMatcher.noneOf("()<>@,;:\\\"/[]?="));
private static final CharMatcher QUOTED_TEXT_MATCHER = ascii().and(CharMatcher.noneOf("\"\\\r"));
private static final CharMatcher PARAMETER_VALUE_MATCHER =
ascii().and(javaIsoControl().negate()).or(CharMatcher.is('\t'));
private static final CharMatcher QUOTED_TEXT_MATCHER =
PARAMETER_VALUE_MATCHER.and(CharMatcher.noneOf("\"\\"));
/*
* This matches the same characters as linear-white-space from RFC 822, but we make no effort to
@@ -1241,7 +1245,10 @@ public final class MediaType {
private static String normalizeParameterValue(String attribute, String value) {
checkNotNull(value); // for GWT
checkArgument(ascii().matchesAllOf(value), "parameter values must be ASCII: %s", value);
checkArgument(
PARAMETER_VALUE_MATCHER.matchesAllOf(value),
"parameter values must contain only ASCII and no control characters: %s",
value);
return attribute.equals(CHARSET_ATTRIBUTE) ? Ascii.toLowerCase(value) : value;
}
@@ -1270,7 +1277,7 @@ public final class MediaType {
while (tokenizer.previewChar() != '"') {
if (tokenizer.previewChar() == '\\') {
tokenizer.consumeCharacter('\\');
valueBuilder.append(tokenizer.consumeCharacter(ascii()));
valueBuilder.append(tokenizer.consumeCharacter(PARAMETER_VALUE_MATCHER));
} else {
valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER));
}
@@ -1406,7 +1413,7 @@ public final class MediaType {
StringBuilder escaped = new StringBuilder(value.length() + 16).append('"');
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
if (ch == '\r' || ch == '\\' || ch == '"') {
if (ch == '\\' || ch == '"') {
escaped.append('\\');
}
escaped.append(ch);
@@ -275,6 +275,18 @@ public class MediaTypeTest extends TestCase {
assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "…"));
}
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\rb"));
assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\u0000b"));
assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "a\u007fb"));
assertThat(mediaType.withParameter("a", "a\tb"))
.isEqualTo(MediaType.parse("text/plain; a=\"a\tb\""));
assertThat(mediaType.withParameter("a", "a\tb").toString()).isEqualTo("text/plain; a=\"a\tb\"");
}
public void testWithParameter_emptyParameter() {
MediaType mediaType = MediaType.parse("text/plain");
assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("", "a"));
@@ -377,6 +389,15 @@ public class MediaTypeTest extends TestCase {
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=1; b"));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=1; b="));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\u2025"));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\r\nc\""));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\nc\""));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\rc\""));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\\\rc\""));
assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\\\nc\""));
assertThrows(
IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\u0000c\""));
assertThrows(
IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"b\\\u0000c\""));
}
// https://github.com/google/guava/issues/6663
+11 -4
View File
@@ -85,7 +85,11 @@ public final class MediaType {
.and(CharMatcher.isNot(' '))
.and(CharMatcher.noneOf("()<>@,;:\\\"/[]?="));
private static final CharMatcher QUOTED_TEXT_MATCHER = ascii().and(CharMatcher.noneOf("\"\\\r"));
private static final CharMatcher PARAMETER_VALUE_MATCHER =
ascii().and(javaIsoControl().negate()).or(CharMatcher.is('\t'));
private static final CharMatcher QUOTED_TEXT_MATCHER =
PARAMETER_VALUE_MATCHER.and(CharMatcher.noneOf("\"\\"));
/*
* This matches the same characters as linear-white-space from RFC 822, but we make no effort to
@@ -1241,7 +1245,10 @@ public final class MediaType {
private static String normalizeParameterValue(String attribute, String value) {
checkNotNull(value); // for GWT
checkArgument(ascii().matchesAllOf(value), "parameter values must be ASCII: %s", value);
checkArgument(
PARAMETER_VALUE_MATCHER.matchesAllOf(value),
"parameter values must contain only ASCII and no control characters: %s",
value);
return attribute.equals(CHARSET_ATTRIBUTE) ? Ascii.toLowerCase(value) : value;
}
@@ -1270,7 +1277,7 @@ public final class MediaType {
while (tokenizer.previewChar() != '"') {
if (tokenizer.previewChar() == '\\') {
tokenizer.consumeCharacter('\\');
valueBuilder.append(tokenizer.consumeCharacter(ascii()));
valueBuilder.append(tokenizer.consumeCharacter(PARAMETER_VALUE_MATCHER));
} else {
valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER));
}
@@ -1406,7 +1413,7 @@ public final class MediaType {
StringBuilder escaped = new StringBuilder(value.length() + 16).append('"');
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
if (ch == '\r' || ch == '\\' || ch == '"') {
if (ch == '\\' || ch == '"') {
escaped.append('\\');
}
escaped.append(ch);