Add HttpValidationPolicy::disallow_double_quote_in_header_name.
PiperOrigin-RevId: 544115479
diff --git a/quiche/balsa/balsa_frame.cc b/quiche/balsa/balsa_frame.cc
index 6d48767..8a7dde9 100644
--- a/quiche/balsa/balsa_frame.cc
+++ b/quiche/balsa/balsa_frame.cc
@@ -407,8 +407,16 @@
break;
}
- if (header_properties::IsInvalidHeaderKeyChar(*current)) {
- // Generally invalid characters were found earlier.
+ // Generally invalid characters were found earlier.
+ if (http_validation_policy().disallow_double_quote_in_header_name) {
+ if (header_properties::IsInvalidHeaderKeyChar(*current)) {
+ HandleError(is_trailer
+ ? BalsaFrameEnums::INVALID_TRAILER_NAME_CHARACTER
+ : BalsaFrameEnums::INVALID_HEADER_NAME_CHARACTER);
+ return false;
+ }
+ } else if (header_properties::IsInvalidHeaderKeyCharAllowDoubleQuote(
+ *current)) {
HandleError(is_trailer
? BalsaFrameEnums::INVALID_TRAILER_NAME_CHARACTER
: BalsaFrameEnums::INVALID_HEADER_NAME_CHARACTER);
diff --git a/quiche/balsa/balsa_frame_test.cc b/quiche/balsa/balsa_frame_test.cc
index 52f84ce..0482425 100644
--- a/quiche/balsa/balsa_frame_test.cc
+++ b/quiche/balsa/balsa_frame_test.cc
@@ -3296,6 +3296,22 @@
EXPECT_TRUE(headers_.HasHeader("key\"hasquote"));
}
+TEST_F(HTTPBalsaFrameTest, KeyHasDisallowedDoubleQuote) {
+ HttpValidationPolicy http_validation_policy;
+ http_validation_policy.disallow_double_quote_in_header_name = true;
+ balsa_frame_.set_http_validation_policy(http_validation_policy);
+
+ const std::string message =
+ "GET / HTTP/1.1\r\n"
+ "key\"hasquote: lock\r\n"
+ "\r\n";
+ EXPECT_EQ(message.size(),
+ balsa_frame_.ProcessInput(message.data(), message.size()));
+ EXPECT_TRUE(balsa_frame_.Error());
+ EXPECT_EQ(BalsaFrameEnums::INVALID_HEADER_NAME_CHARACTER,
+ balsa_frame_.ErrorCode());
+}
+
// Missing colon is a warning, not an error.
TEST_F(HTTPBalsaFrameTest, TrailerMissingColon) {
std::string headers =
diff --git a/quiche/balsa/header_properties.cc b/quiche/balsa/header_properties.cc
index 240979c..53978df 100644
--- a/quiche/balsa/header_properties.cc
+++ b/quiche/balsa/header_properties.cc
@@ -68,6 +68,15 @@
return invalidCharTable;
}
+std::array<bool, 256> buildInvalidHeaderKeyCharLookupTableAllowDoubleQuote() {
+ std::array<bool, 256> invalidCharTable;
+ invalidCharTable.fill(false);
+ for (uint8_t c : kInvalidHeaderKeyCharListAllowDoubleQuote) {
+ invalidCharTable[c] = true;
+ }
+ return invalidCharTable;
+}
+
std::array<bool, 256> buildInvalidCharLookupTable() {
std::array<bool, 256> invalidCharTable;
invalidCharTable.fill(false);
@@ -92,6 +101,13 @@
return invalidHeaderKeyCharTable[c];
}
+bool IsInvalidHeaderKeyCharAllowDoubleQuote(uint8_t c) {
+ static const std::array<bool, 256> invalidHeaderKeyCharTable =
+ buildInvalidHeaderKeyCharLookupTableAllowDoubleQuote();
+
+ return invalidHeaderKeyCharTable[c];
+}
+
bool IsInvalidHeaderChar(uint8_t c) {
static const std::array<bool, 256> invalidCharTable =
buildInvalidCharLookupTable();
diff --git a/quiche/balsa/header_properties.h b/quiche/balsa/header_properties.h
index cbd5c59..5a8cc24 100644
--- a/quiche/balsa/header_properties.h
+++ b/quiche/balsa/header_properties.h
@@ -21,13 +21,23 @@
// An array of characters that are invalid in HTTP header field names.
// These are control characters, including \t, \n, \r, as well as space and
// (),/;<=>?@[\]{} and \x7f (see
+// https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.2, also
// https://tools.ietf.org/html/rfc7230#section-3.2.6).
inline constexpr char kInvalidHeaderKeyCharList[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
+ 0x1E, 0x1F, ' ', '"', '(', ')', ',', '/', ';', '<',
+ '=', '>', '?', '@', '[', '\\', ']', '{', '}', 0x7F};
+
+// This is a non-compliant variant of `kInvalidHeaderKeyCharList`
+// that allows the character '"'.
+inline constexpr char kInvalidHeaderKeyCharListAllowDoubleQuote[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
0x1E, 0x1F, ' ', '(', ')', ',', '/', ';', '<', '=',
- '>', '?', '@', '[', '\\', ']', '{', '}', 0x7f};
+ '>', '?', '@', '[', '\\', ']', '{', '}', 0x7F};
// An array of characters that are invalid in HTTP header field values,
// according to RFC 7230 Section 3.2. Valid low characters not in this array
@@ -38,8 +48,10 @@
0x0C, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x7F};
-// Returns true if the given `c` is invalid in a header field name.
+// Returns true if the given `c` is invalid in a header field name. The first
+// version is spec compliant, the second one incorrectly allows '"'.
QUICHE_EXPORT bool IsInvalidHeaderKeyChar(uint8_t c);
+QUICHE_EXPORT bool IsInvalidHeaderKeyCharAllowDoubleQuote(uint8_t c);
// Returns true if the given `c` is invalid in a header field or the `value` has
// invalid characters.
QUICHE_EXPORT bool IsInvalidHeaderChar(uint8_t c);
diff --git a/quiche/balsa/header_properties_test.cc b/quiche/balsa/header_properties_test.cc
index ef5f8fc..1930354 100644
--- a/quiche/balsa/header_properties_test.cc
+++ b/quiche/balsa/header_properties_test.cc
@@ -25,15 +25,39 @@
EXPECT_TRUE(IsInvalidHeaderKeyChar(0x1F));
EXPECT_TRUE(IsInvalidHeaderKeyChar(0x7F));
EXPECT_TRUE(IsInvalidHeaderKeyChar(' '));
+ EXPECT_TRUE(IsInvalidHeaderKeyChar('"'));
EXPECT_TRUE(IsInvalidHeaderKeyChar('\t'));
EXPECT_TRUE(IsInvalidHeaderKeyChar('\r'));
EXPECT_TRUE(IsInvalidHeaderKeyChar('\n'));
+ EXPECT_TRUE(IsInvalidHeaderKeyChar('}'));
EXPECT_FALSE(IsInvalidHeaderKeyChar('a'));
EXPECT_FALSE(IsInvalidHeaderKeyChar('B'));
EXPECT_FALSE(IsInvalidHeaderKeyChar('7'));
EXPECT_FALSE(IsInvalidHeaderKeyChar(0x42));
- EXPECT_FALSE(IsInvalidHeaderChar(0x7D));
+ EXPECT_FALSE(IsInvalidHeaderKeyChar(0x7C));
+ EXPECT_FALSE(IsInvalidHeaderKeyChar(0x7E));
+}
+
+TEST(HeaderPropertiesTest, IsInvalidHeaderKeyCharAllowDoubleQuote) {
+ EXPECT_TRUE(IsInvalidHeaderKeyCharAllowDoubleQuote(0x00));
+ EXPECT_TRUE(IsInvalidHeaderKeyCharAllowDoubleQuote(0x06));
+ EXPECT_TRUE(IsInvalidHeaderKeyCharAllowDoubleQuote(0x09));
+ EXPECT_TRUE(IsInvalidHeaderKeyCharAllowDoubleQuote(0x1F));
+ EXPECT_TRUE(IsInvalidHeaderKeyCharAllowDoubleQuote(0x7F));
+ EXPECT_TRUE(IsInvalidHeaderKeyCharAllowDoubleQuote(' '));
+ EXPECT_TRUE(IsInvalidHeaderKeyCharAllowDoubleQuote('\t'));
+ EXPECT_TRUE(IsInvalidHeaderKeyCharAllowDoubleQuote('\r'));
+ EXPECT_TRUE(IsInvalidHeaderKeyCharAllowDoubleQuote('\n'));
+ EXPECT_TRUE(IsInvalidHeaderKeyCharAllowDoubleQuote('}'));
+
+ EXPECT_FALSE(IsInvalidHeaderKeyCharAllowDoubleQuote('"'));
+ EXPECT_FALSE(IsInvalidHeaderKeyCharAllowDoubleQuote('a'));
+ EXPECT_FALSE(IsInvalidHeaderKeyCharAllowDoubleQuote('B'));
+ EXPECT_FALSE(IsInvalidHeaderKeyCharAllowDoubleQuote('7'));
+ EXPECT_FALSE(IsInvalidHeaderKeyCharAllowDoubleQuote(0x42));
+ EXPECT_FALSE(IsInvalidHeaderKeyCharAllowDoubleQuote(0x7C));
+ EXPECT_FALSE(IsInvalidHeaderKeyCharAllowDoubleQuote(0x7E));
}
TEST(HeaderPropertiesTest, IsInvalidHeaderChar) {
diff --git a/quiche/balsa/http_validation_policy.h b/quiche/balsa/http_validation_policy.h
index a67093b..2e783d7 100644
--- a/quiche/balsa/http_validation_policy.h
+++ b/quiche/balsa/http_validation_policy.h
@@ -13,7 +13,8 @@
// An HttpValidationPolicy captures policy choices affecting parsing of HTTP
// requests. It offers individual Boolean members to be consulted during the
-// parsing of an HTTP request.
+// parsing of an HTTP request. For historical reasons, every member is set up
+// such that `true` means more strict validation.
struct QUICHE_EXPORT HttpValidationPolicy {
// https://tools.ietf.org/html/rfc7230#section-3.2.4 deprecates "folding"
// of long header lines onto continuation lines.
@@ -42,6 +43,11 @@
// with a method POST or PUT, which requires a body, has neither a
// "Content-Length" nor a "Transfer-Encoding: chunked" header.
bool require_content_length_if_body_required = true;
+
+ // If true, signal an INVALID_HEADER_NAME_CHARACTER or
+ // INVALID_TRAILER_NAME_CHARACTER error if the header or trailer name contains
+ // the character '"'.
+ bool disallow_double_quote_in_header_name = false;
};
} // namespace quiche