No public description PiperOrigin-RevId: 944543945
diff --git a/quiche/balsa/balsa_frame.cc b/quiche/balsa/balsa_frame.cc index 1274da1..7da637e 100644 --- a/quiche/balsa/balsa_frame.cc +++ b/quiche/balsa/balsa_frame.cc
@@ -166,16 +166,17 @@ BalsaFrameEnums::ErrorCode* error_code, FirstLineValidationOption whitespace_option, FirstLineValidationOption multiple_spaces_option, - bool& has_multiple_spaces) { + bool& has_multiple_spaces, bool& has_cr_tab) { while (begin < end && (end[-1] == '\n' || end[-1] == '\r')) { --end; } - if (whitespace_option != FirstLineValidationOption::NONE) { - constexpr absl::string_view kBadWhitespace = "\r\t"; - char* pos = std::find_first_of(begin, end, kBadWhitespace.begin(), - kBadWhitespace.end()); - if (pos != end) { + constexpr absl::string_view kBadWhitespace = "\r\t"; + char* pos = std::find_first_of(begin, end, kBadWhitespace.begin(), + kBadWhitespace.end()); + if (pos != end) { + has_cr_tab = true; + if (whitespace_option != FirstLineValidationOption::NONE) { if (whitespace_option == FirstLineValidationOption::REJECT) { QUICHE_CODE_COUNT(sanitize_cr_tab_in_first_line_rejected); *error_code = static_cast<BalsaFrameEnums::ErrorCode>( @@ -382,11 +383,13 @@ // at most one newline, which must be at the end of the line. void BalsaFrame::ProcessFirstLine(char* begin, char* end) { bool has_multiple_spaces = false; + bool has_cr_tab = false; BalsaFrameEnums::ErrorCode previous_error = last_error_; - const bool parse_success = ParseHTTPFirstLine( - begin, end, is_request_, headers_, &last_error_, - http_validation_policy().sanitize_cr_tab_in_first_line, - http_validation_policy().sanitize_firstline_spaces, has_multiple_spaces); + const bool parse_success = + ParseHTTPFirstLine(begin, end, is_request_, headers_, &last_error_, + http_validation_policy().sanitize_cr_tab_in_first_line, + http_validation_policy().sanitize_firstline_spaces, + has_multiple_spaces, has_cr_tab); if (!parse_success) { parse_state_ = BalsaFrameEnums::ERROR; @@ -619,13 +622,14 @@ return false; } - if (http_validation_policy().disallow_obs_text_in_field_names && - IsObsTextChar(c)) { - QUICHE_CODE_COUNT(disallow_obs_text_in_field_names_enforced); - HandleError(is_trailer - ? BalsaFrameEnums::INVALID_TRAILER_NAME_CHARACTER - : BalsaFrameEnums::INVALID_HEADER_NAME_CHARACTER); - return false; + if (IsObsTextChar(c)) { + if (http_validation_policy().disallow_obs_text_in_field_names) { + QUICHE_CODE_COUNT(disallow_obs_text_in_field_names_enforced); + HandleError(is_trailer + ? BalsaFrameEnums::INVALID_TRAILER_NAME_CHARACTER + : BalsaFrameEnums::INVALID_HEADER_NAME_CHARACTER); + return false; + } } }
diff --git a/quiche/balsa/balsa_frame_test.cc b/quiche/balsa/balsa_frame_test.cc index 1dedddb..6ca8ef9 100644 --- a/quiche/balsa/balsa_frame_test.cc +++ b/quiche/balsa/balsa_frame_test.cc
@@ -821,58 +821,64 @@ const absl::string_view parsed; // Expected output. FirstLineValidationOption option; // Whether to sanitize/reject. BalsaFrameEnums::ErrorCode expected_error; + bool expected_tab_or_cr_defect; }; const std::vector<TestCase> cases = { // No invalid whitespace. {"GET / HTTP/1.1\r\n", "GET / HTTP/1.1", FirstLineValidationOption::NONE, - BalsaFrameEnums::BALSA_NO_ERROR}, + BalsaFrameEnums::BALSA_NO_ERROR, false}, {"GET / HTTP/1.1\r\n", "GET / HTTP/1.1", - FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR}, + FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR, + false}, {"GET / HTTP/1.1\r\n", "GET / HTTP/1.1", - FirstLineValidationOption::REJECT, BalsaFrameEnums::BALSA_NO_ERROR}, + FirstLineValidationOption::REJECT, BalsaFrameEnums::BALSA_NO_ERROR, + false}, // Illegal CR in the request-line. {"GET /\rHTTP/1.1\r\n", "GET /\rHTTP/1.1", - FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR}, + FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR, true}, {"GET /\rHTTP/1.1\r\n", "GET / HTTP/1.1", - FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR}, + FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR, + true}, {"GET /\rHTTP/1.1\r\n", "", FirstLineValidationOption::REJECT, - BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE}, + BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE, true}, // Invalid tab in the request-line. {"GET \t/ HTTP/1.1\r\n", "GET \t/ HTTP/1.1", - FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR}, + FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR, true}, {"GET \t/ HTTP/1.1\r\n", "GET / HTTP/1.1", - FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR}, + FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR, + true}, {"GET \t/ HTTP/1.1\r\n", "", FirstLineValidationOption::REJECT, - BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE}, + BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE, true}, // Both CR and tab in the request-line. {"GET \t/\rHTTP/1.1 \r\n", "GET \t/\rHTTP/1.1", - FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR}, + FirstLineValidationOption::NONE, BalsaFrameEnums::BALSA_NO_ERROR, true}, {"GET \t/\rHTTP/1.1 \r\n", "GET / HTTP/1.1", - FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR}, + FirstLineValidationOption::SANITIZE, BalsaFrameEnums::BALSA_NO_ERROR, + true}, {"GET \t/\rHTTP/1.1 \r\n", "", FirstLineValidationOption::REJECT, - BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE}, + BalsaFrameEnums::INVALID_WS_IN_REQUEST_LINE, true}, }; const absl::string_view kHeaderLineAndEnding = "Foo: bar\r\n\r\n"; - for (auto& [firstline, parsed, ws_option, expected_error] : cases) { + for (const auto& tc : cases) { SCOPED_TRACE( - absl::StrCat("Input: ", absl::CEscape(firstline), - " Expected output: ", absl::CEscape(parsed), - " whitespace option: ", static_cast<int>(ws_option))); - const std::string input = absl::StrCat(firstline, kHeaderLineAndEnding); + absl::StrCat("Input: ", absl::CEscape(tc.input), + " Expected output: ", absl::CEscape(tc.parsed), + " whitespace option: ", static_cast<int>(tc.option))); + const std::string input = absl::StrCat(tc.input, kHeaderLineAndEnding); BalsaHeaders headers; BalsaFrame framer; HttpValidationPolicy policy; - policy.sanitize_cr_tab_in_first_line = ws_option; + policy.sanitize_cr_tab_in_first_line = tc.option; framer.set_http_validation_policy(policy); framer.set_is_request(true); framer.set_balsa_headers(&headers); framer.ProcessInput(input.data(), input.size()); - EXPECT_EQ(headers.first_line(), parsed); - EXPECT_EQ(framer.ErrorCode(), expected_error); + EXPECT_EQ(headers.first_line(), tc.parsed); + EXPECT_EQ(framer.ErrorCode(), tc.expected_error); } }
diff --git a/quiche/balsa/balsa_headers.h b/quiche/balsa/balsa_headers.h index da57922..9f63838 100644 --- a/quiche/balsa/balsa_headers.h +++ b/quiche/balsa/balsa_headers.h
@@ -1066,7 +1066,7 @@ BalsaFrameEnums::ErrorCode* error_code, HttpValidationPolicy::FirstLineValidationOption whitespace_option, HttpValidationPolicy::FirstLineValidationOption multiple_spaces_option, - bool& has_multiple_spaces); + bool& has_multiple_spaces, bool& has_cr_tab); // Reverse iterators have been removed for lack of use, refer to // cl/30618773 in case they are needed.