diff --git a/include/tscore/ParseRules.h b/include/tscore/ParseRules.h index 4ce4ed3d22e..92bd687da04 100644 --- a/include/tscore/ParseRules.h +++ b/include/tscore/ParseRules.h @@ -128,7 +128,7 @@ class ParseRules static CTypeResult is_empty(char c); // wslfcr,# static CTypeResult is_alnum(char c); // 0-9,A-Z,a-z static CTypeResult is_space(char c); // ' ' HT,VT,NP,CR,LF - static CTypeResult is_control(char c); // 0-31 127 + static CTypeResult is_control(char c); // 0x00-0x08, 0x0a-0x1f, 0x7f static CTypeResult is_mime_sep(char c); // ()<>,;\"/[]?{} \t static CTypeResult is_http_field_name(char c); // not : or mime_sep except for @ static CTypeResult is_http_field_value(char c); // not CR, LF, comma, or " @@ -667,14 +667,24 @@ ParseRules::is_space(char c) #endif } +/** + Return true if @c is a control char except HTAB(0x09) and SP(0x20). + If you need to check @c is HTAB or SP, use `ParseRules::is_ws`. + */ inline CTypeResult ParseRules::is_control(char c) { #ifndef COMPILE_PARSE_RULES return (parseRulesCType[(unsigned char)c] & is_control_BIT); #else - if (((unsigned char)c) < 32 || ((unsigned char)c) == 127) + if (c == CHAR_HT || c == CHAR_SP) { + return false; + } + + if (((unsigned char)c) < 0x20 || c == 0x7f) { return true; + } + return false; #endif } diff --git a/proxy/hdrs/MIME.cc b/proxy/hdrs/MIME.cc index 1a2dc05db31..902fd62c4cf 100644 --- a/proxy/hdrs/MIME.cc +++ b/proxy/hdrs/MIME.cc @@ -2621,6 +2621,21 @@ mime_parser_parse(MIMEParser *parser, HdrHeap *heap, MIMEHdrImpl *mh, const char int field_name_wks_idx = hdrtoken_tokenize(field_name.data(), field_name.size()); + if (field_name_wks_idx < 0) { + for (auto i : field_name) { + if (ParseRules::is_control(i)) { + return PARSE_RESULT_ERROR; + } + } + } + + // RFC 9110 Section 5.5. Field Values + for (char i : field_value) { + if (ParseRules::is_control(i)) { + return PARSE_RESULT_ERROR; + } + } + /////////////////////////////////////////// // build and insert the new field object // /////////////////////////////////////////// diff --git a/proxy/hdrs/unit_tests/test_Hdrs.cc b/proxy/hdrs/unit_tests/test_Hdrs.cc index 40448aea50a..7d9c39eef58 100644 --- a/proxy/hdrs/unit_tests/test_Hdrs.cc +++ b/proxy/hdrs/unit_tests/test_Hdrs.cc @@ -531,6 +531,54 @@ TEST_CASE("HdrTest", "[proxy][hdrtest]") mime_init(); http_init(); + SECTION("Field Char Check") + { + static struct { + std::string_view line; + ParseResult expected; + } test_cases[] = { + //// + // Field Name + {"Content-Length: 10\r\n", PARSE_RESULT_CONT}, + {"Content-Length\x0b: 10\r\n", PARSE_RESULT_ERROR}, + //// + // Field Value + // SP + {"Content-Length: 10\r\n", PARSE_RESULT_CONT}, + // HTAB + {"Foo: ab\td/cd\r\n", PARSE_RESULT_CONT}, + // VCHAR + {"Foo: ab\x21/cd\r\n", PARSE_RESULT_CONT}, + {"Foo: ab\x7e/cd\r\n", PARSE_RESULT_CONT}, + // DEL + {"Foo: ab\x7f/cd\r\n", PARSE_RESULT_ERROR}, + // obs-text + {"Foo: ab\x80/cd\r\n", PARSE_RESULT_CONT}, + {"Foo: ab\xff/cd\r\n", PARSE_RESULT_CONT}, + // control char + {"Content-Length: 10\x0b\r\n", PARSE_RESULT_ERROR}, + {"Content-Length:\x0b 10\r\n", PARSE_RESULT_ERROR}, + {"Foo: ab\x1d/cd\r\n", PARSE_RESULT_ERROR}, + }; + + MIMEHdr hdr; + MIMEParser parser; + mime_parser_init(&parser); + + for (const auto &t : test_cases) { + mime_parser_clear(&parser); + + const char *start = t.line.data(); + const char *end = start + t.line.size(); + + int r = hdr.parse(&parser, &start, end, false, false, false); + if (r != t.expected) { + std::printf("Expected %s is %s, but not", t.line.data(), t.expected == PARSE_RESULT_ERROR ? "invalid" : "valid"); + CHECK(false); + } + } + } + SECTION("Test parse date") { static struct { diff --git a/tests/gold_tests/headers/gold/invalid_character_in_te_value.gold b/tests/gold_tests/headers/gold/invalid_character_in_te_value.gold new file mode 100644 index 00000000000..30a27d819b0 --- /dev/null +++ b/tests/gold_tests/headers/gold/invalid_character_in_te_value.gold @@ -0,0 +1,23 @@ +HTTP/1.1 400 Invalid HTTP Request +Date:`` +Connection: close +Server:`` +Cache-Control: no-store +Content-Type: text/html +Content-Language: en +Content-Length:`` + + +
+