No public description PiperOrigin-RevId: 866544937
diff --git a/quiche/balsa/header_properties.cc b/quiche/balsa/header_properties.cc index 03ebe56..57ffbc1 100644 --- a/quiche/balsa/header_properties.cc +++ b/quiche/balsa/header_properties.cc
@@ -146,6 +146,26 @@ return validTokenCharTable; } +constexpr std::array<bool, 256> buildValidChunkExtensionValCharLookupTable() { + std::array<bool, 256> validChunkExtValCharTable = kAllTrueArray; + constexpr uint8_t US_ASCII_CONTROL_START = 0; + constexpr uint8_t US_ASCII_CONTROL_END = 32; // exclusive + // Valid chunk-ext-val characters are the entire range of visible US-ASCII + // characters and all extended ASCII characters. Of the control characters, + // only HTAB is allowed. There are some more precise rules for when DQUOTE + // and backslash can appear in a quoted-string which we skip here. + for (uint8_t c = US_ASCII_CONTROL_START; c < US_ASCII_CONTROL_END; ++c) { + if (c == '\t') { + continue; + } + validChunkExtValCharTable[c] = false; + } + // Also exclude the control character DEL. + validChunkExtValCharTable[127] = false; + + return validChunkExtValCharTable; +} + } // anonymous namespace bool IsMultivaluedHeader(absl::string_view header) { @@ -192,6 +212,23 @@ } return true; } + +bool IsValidChunkExtension(absl::string_view value) { + for (const char c : value) { + if (!IsValidChunkExtensionValChar(static_cast<uint8_t>(c))) { + return false; + } + } + return true; +} + +bool IsValidChunkExtensionValChar(uint8_t c) { + static constexpr std::array<bool, 256> validChunkExtValCharTable = + buildValidChunkExtensionValCharLookupTable(); + + return validChunkExtValCharTable[c]; +} + bool HasInvalidHeaderChars(absl::string_view value) { for (const char c : value) { if (IsInvalidHeaderChar(c)) {
diff --git a/quiche/balsa/header_properties.h b/quiche/balsa/header_properties.h index a540e79..6d72b1b 100644 --- a/quiche/balsa/header_properties.h +++ b/quiche/balsa/header_properties.h
@@ -82,6 +82,12 @@ QUICHE_EXPORT bool IsValidTokenChar(uint8_t c); QUICHE_EXPORT bool IsValidToken(absl::string_view value); +// Returns true if the given `char` is in the set of ASCII characters valid for +// `chunk-ext-val` specified in RFC 9112 Section 7.1.1. Note that Section 7.1.1 +// defines specific ordering of certain characters, which is not checked here. +QUICHE_EXPORT bool IsValidChunkExtensionValChar(uint8_t c); +QUICHE_EXPORT bool IsValidChunkExtension(absl::string_view value); + // Returns true if `value` contains a character not allowed in the path // component of a URI. QUICHE_EXPORT bool HasInvalidPathChar(absl::string_view value);
diff --git a/quiche/balsa/header_properties_test.cc b/quiche/balsa/header_properties_test.cc index ad983f1..f4e749a 100644 --- a/quiche/balsa/header_properties_test.cc +++ b/quiche/balsa/header_properties_test.cc
@@ -3,6 +3,7 @@ #include <string> #include "absl/container/flat_hash_set.h" +#include "absl/strings/ascii.h" #include "absl/strings/string_view.h" #include "quiche/common/platform/api/quiche_test.h" @@ -185,6 +186,7 @@ EXPECT_FALSE(IsValidToken("GET@")); EXPECT_FALSE(IsValidToken("GET[")); EXPECT_FALSE(IsValidToken("GET\\")); + EXPECT_FALSE(IsValidToken("GET\"")); EXPECT_FALSE(IsValidToken("GET]")); EXPECT_FALSE(IsValidToken("GET:")); EXPECT_FALSE(IsValidToken("GET;")); @@ -199,5 +201,57 @@ EXPECT_FALSE(IsValidToken("")); } +TEST(HeaderPropertiesTest, IsValidChunkExtensionValChar) { + EXPECT_TRUE(IsValidChunkExtension("")); + EXPECT_TRUE(IsValidChunkExtension(";")); + EXPECT_TRUE(IsValidChunkExtension(";a")); + EXPECT_TRUE(IsValidChunkExtension("; a")); + EXPECT_TRUE(IsValidChunkExtension("\"")); + EXPECT_TRUE(IsValidChunkExtension(";\t a")); + EXPECT_TRUE(IsValidChunkExtension(";a=")); + EXPECT_TRUE(IsValidChunkExtension(";a=b")); + EXPECT_TRUE(IsValidChunkExtension(";a=\"\"")); + EXPECT_TRUE(IsValidChunkExtension(";a=\"ba\"z\"")); + EXPECT_TRUE(IsValidChunkExtension(";a=foo-quote'")); + EXPECT_TRUE(IsValidChunkExtension(";foo=bar;baz=qux")); + EXPECT_TRUE(IsValidChunkExtension(";foo=bar;baz=\"qu\";x")); + EXPECT_TRUE(IsValidChunkExtension(";foo=bar;baz=\"q \tu\";x")); + EXPECT_TRUE(IsValidChunkExtension("!#$%&'*+-.^_`|~")); + EXPECT_TRUE(IsValidChunkExtension("abcefghijklmnopqrstuvwxyz0123456789")); + EXPECT_TRUE( + IsValidChunkExtension("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "!#$%&'*+-.^_`|~")); + EXPECT_TRUE(IsValidChunkExtension("GET\xFF")); + EXPECT_TRUE(IsValidChunkExtension("GET\x80")); + + EXPECT_FALSE( + IsValidChunkExtension(absl::string_view(";chunky-nulls=\0", 15))); + EXPECT_FALSE(IsValidChunkExtension(";\x1")); + EXPECT_FALSE(IsValidChunkExtension(";\n")); + EXPECT_FALSE(IsValidChunkExtension(";\r")); +} + +TEST(HeaderPropertiesTest, IsValidChunkExtension) { + for (int i = 0; i < 256; ++i) { + unsigned char c = static_cast<unsigned char>(i); + if (c == ' ' || c == '\t') { + continue; + } + if (absl::ascii_iscntrl(c)) { + EXPECT_FALSE(IsValidChunkExtensionValChar(c)); + } else if (absl::ascii_isprint(c)) { + EXPECT_TRUE(IsValidChunkExtensionValChar(c)); + } else if (absl::ascii_isspace(c)) { + EXPECT_TRUE(IsValidChunkExtensionValChar(c)); + } else if (i >= 128) { + EXPECT_TRUE(IsValidChunkExtensionValChar(c)); + } else { + FAIL() << "Unexpected character: [" << c << "], int = [" << i << "]"; + } + } +} + } // namespace } // namespace quiche::header_properties::test