From f815b4a31cc239621fe388ad421935b6d70d78a6 Mon Sep 17 00:00:00 2001 From: Kunihiko Sakamoto Date: Mon, 4 Feb 2019 03:41:49 +0000 Subject: [PATCH] SignedExchange: Add http_structured_header::ParseListOfLists() This will be used to parse Variants and Variant-Key headers. Bug: 923229 Change-Id: I3ad3b8b18b4cd396bd01a79d06fffa6c04c86809 Reviewed-on: https://chromium-review.googlesource.com/c/1420509 Reviewed-by: Tsuyoshi Horo Reviewed-by: Kouhei Ueno Reviewed-by: Kinuko Yasuda Reviewed-by: Chris Palmer Commit-Queue: Kunihiko Sakamoto Cr-Commit-Position: refs/heads/master@{#628649} --- .../web_package/http_structured_header.cc | 32 +++++++++++++++++ .../web_package/http_structured_header.h | 5 ++- .../http_structured_header_unittest.cc | 35 +++++++++++++++++++ .../http_structured_header_data/4.txt | 1 + .../http_structured_header_data/5.txt | 1 + .../fuzzer/http_structured_header_fuzzer.cc | 2 ++ 6 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 content/test/data/fuzzer_corpus/http_structured_header_data/4.txt create mode 100644 content/test/data/fuzzer_corpus/http_structured_header_data/5.txt diff --git a/content/browser/web_package/http_structured_header.cc b/content/browser/web_package/http_structured_header.cc index cab233bdc42251..b3284e56810906 100644 --- a/content/browser/web_package/http_structured_header.cc +++ b/content/browser/web_package/http_structured_header.cc @@ -50,6 +50,30 @@ class StructuredHeaderParser { return input_.empty(); } + // Parses a List of Lists ([SH] 4.2.4). + base::Optional ReadListOfLists() { + ListOfLists result; + while (true) { + std::vector inner_list; + while (true) { + base::Optional item = ReadItem(); + if (!item) + return base::nullopt; + inner_list.push_back(*item); + SkipWhitespaces(); + if (!ConsumeChar(';')) + break; + SkipWhitespaces(); + } + result.push_back(std::move(inner_list)); + SkipWhitespaces(); + if (!ConsumeChar(',')) + break; + SkipWhitespaces(); + } + return result; + } + // Parses an Item ([SH] 4.2.7). // Currently only limited types (non-negative integers, strings, tokens and // byte sequences) are supported, and all types are returned as a string @@ -287,5 +311,13 @@ base::Optional ParseParameterisedList( return base::nullopt; } +base::Optional ParseListOfLists(const base::StringPiece& str) { + StructuredHeaderParser parser(str); + base::Optional list_of_lists = parser.ReadListOfLists(); + if (list_of_lists && parser.FinishParsing()) + return list_of_lists; + return base::nullopt; +} + } // namespace http_structured_header } // namespace content diff --git a/content/browser/web_package/http_structured_header.h b/content/browser/web_package/http_structured_header.h index d31dbad3d71c7d..8515445a8605da 100644 --- a/content/browser/web_package/http_structured_header.h +++ b/content/browser/web_package/http_structured_header.h @@ -27,12 +27,15 @@ struct CONTENT_EXPORT ParameterisedIdentifier { ~ParameterisedIdentifier(); }; -typedef std::vector ParameterisedList; +using ParameterisedList = std::vector; +using ListOfLists = std::vector>; CONTENT_EXPORT base::Optional ParseItem( const base::StringPiece& str); CONTENT_EXPORT base::Optional ParseParameterisedList( const base::StringPiece& str); +CONTENT_EXPORT base::Optional ParseListOfLists( + const base::StringPiece& str); } // namespace http_structured_header } // namespace content diff --git a/content/browser/web_package/http_structured_header_unittest.cc b/content/browser/web_package/http_structured_header_unittest.cc index 083a2ec43f2a70..14d8a21cab87c4 100644 --- a/content/browser/web_package/http_structured_header_unittest.cc +++ b/content/browser/web_package/http_structured_header_unittest.cc @@ -77,6 +77,41 @@ TEST(StructuredHeaderTest, ParseItem) { } } +TEST(StructuredHeaderTest, ParseListOfLists) { + struct TestCase { + const char* name; + const char* raw; + ListOfLists expected; // empty if parse error is expected + } cases[] = { + {"basic list of lists", "1;2, 42;43", {{"1", "2"}, {"42", "43"}}}, + {"empty list of lists", "", {}}, + {"single item list of lists", "42", {{"42"}}}, + {"no whitespace list of lists", "1,42", {{"1"}, {"42"}}}, + {"no inner whitespace list of lists", + "1;2, 42;43", + {{"1", "2"}, {"42", "43"}}}, + {"extra whitespace list of lists", "1 , 42", {{"1"}, {"42"}}}, + {"extra inner whitespace list of lists", + "1 ; 2,42 ; 43", + {{"1", "2"}, {"42", "43"}}}, + {"trailing comma list of lists", "1;2, 42,", {}}, + {"trailing semicolon list of lists", "1;2, 42;43;", {}}, + {"leading comma list of lists", ",1;2, 42", {}}, + {"leading semicolon list of lists", ";1;2, 42;43", {}}, + {"empty item list of lists", "1,,42", {}}, + {"empty inner item list of lists", "1;;2,42", {}}, + }; + for (const auto& c : cases) { + base::Optional result = ParseListOfLists(c.raw); + if (!c.expected.empty()) { + EXPECT_TRUE(result.has_value()) << c.name; + EXPECT_EQ(*result, c.expected) << c.name; + } else { + EXPECT_FALSE(result.has_value()) << c.name; + } + } +} + inline bool operator==(const ParameterisedIdentifier& lhs, const ParameterisedIdentifier& rhs) { return lhs.identifier == rhs.identifier && lhs.params == rhs.params; diff --git a/content/test/data/fuzzer_corpus/http_structured_header_data/4.txt b/content/test/data/fuzzer_corpus/http_structured_header_data/4.txt new file mode 100644 index 00000000000000..9d0aa4452d526d --- /dev/null +++ b/content/test/data/fuzzer_corpus/http_structured_header_data/4.txt @@ -0,0 +1 @@ +Accept;text/plain;text/html, Accept-Encoding;gzip;br, Accept-Language;en;fr diff --git a/content/test/data/fuzzer_corpus/http_structured_header_data/5.txt b/content/test/data/fuzzer_corpus/http_structured_header_data/5.txt new file mode 100644 index 00000000000000..449a0dfa25138d --- /dev/null +++ b/content/test/data/fuzzer_corpus/http_structured_header_data/5.txt @@ -0,0 +1 @@ +text/plain;gzip;en, "text/html;charset=utf8";gzip;en, "*/*";"identity";"*" diff --git a/content/test/fuzzer/http_structured_header_fuzzer.cc b/content/test/fuzzer/http_structured_header_fuzzer.cc index b7aea795a973bb..5779a33f91cd53 100644 --- a/content/test/fuzzer/http_structured_header_fuzzer.cc +++ b/content/test/fuzzer/http_structured_header_fuzzer.cc @@ -9,6 +9,8 @@ namespace http_structured_header { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { base::StringPiece input(reinterpret_cast(data), size); + ParseItem(input); + ParseListOfLists(input); ParseParameterisedList(input); return 0; }