Add validation field for chunk extension validation.
PiperOrigin-RevId: 620832541
diff --git a/quiche/balsa/balsa_enums.cc b/quiche/balsa/balsa_enums.cc
index bc6b68f..fa072fc 100644
--- a/quiche/balsa/balsa_enums.cc
+++ b/quiche/balsa/balsa_enums.cc
@@ -76,6 +76,8 @@
return "INVALID_CHUNK_LENGTH";
case CHUNK_LENGTH_OVERFLOW:
return "CHUNK_LENGTH_OVERFLOW";
+ case INVALID_CHUNK_EXTENSION:
+ return "INVALID_CHUNK_EXTENSION";
case CALLED_BYTES_SPLICED_WHEN_UNSAFE_TO_DO_SO:
return "CALLED_BYTES_SPLICED_WHEN_UNSAFE_TO_DO_SO";
case CALLED_BYTES_SPLICED_AND_EXCEEDED_SAFE_SPLICE_AMOUNT:
diff --git a/quiche/balsa/balsa_enums.h b/quiche/balsa/balsa_enums.h
index 60537a3..61c487e 100644
--- a/quiche/balsa/balsa_enums.h
+++ b/quiche/balsa/balsa_enums.h
@@ -81,6 +81,7 @@
// Chunking errors
INVALID_CHUNK_LENGTH,
CHUNK_LENGTH_OVERFLOW,
+ INVALID_CHUNK_EXTENSION,
// Other errors.
CALLED_BYTES_SPLICED_WHEN_UNSAFE_TO_DO_SO,
diff --git a/quiche/balsa/balsa_frame.cc b/quiche/balsa/balsa_frame.cc
index 03bc994..1e6080f 100644
--- a/quiche/balsa/balsa_frame.cc
+++ b/quiche/balsa/balsa_frame.cc
@@ -1129,6 +1129,12 @@
return current - input;
}
const char c = *current;
+ if (http_validation_policy_.disallow_lone_cr_in_chunk_extension &&
+ c == '\r' && (current + 1 == end || *(current + 1) != '\n')) {
+ // We have a lone carriage return.
+ HandleError(BalsaFrameEnums::INVALID_CHUNK_EXTENSION);
+ return current - input;
+ }
if (c == '\r' || c == '\n') {
extensions_length = (extensions_start == current)
? 0
diff --git a/quiche/balsa/balsa_frame_test.cc b/quiche/balsa/balsa_frame_test.cc
index 294e11f..e495bc0 100644
--- a/quiche/balsa/balsa_frame_test.cc
+++ b/quiche/balsa/balsa_frame_test.cc
@@ -1667,6 +1667,29 @@
EXPECT_EQ(BalsaFrameEnums::BALSA_NO_ERROR, balsa_frame_.ErrorCode());
}
+TEST_F(HTTPBalsaFrameTest, InvalidChunkExtensionWithCarriageReturn) {
+ balsa_frame_.set_http_validation_policy(
+ HttpValidationPolicy{.disallow_lone_cr_in_chunk_extension = true});
+ std::string message_headers =
+ "POST /potato?salad=withmayo HTTP/1.1\r\n"
+ "transfer-encoding: chunked\r\n"
+ "\r\n";
+ std::string message_body =
+ "9; bad\rextension\r\n"
+ "012345678\r\n"
+ "0\r\n"
+ "\r\n";
+ std::string message =
+ std::string(message_headers) + std::string(message_body);
+
+ EXPECT_CALL(visitor_mock_,
+ HandleError(BalsaFrameEnums::INVALID_CHUNK_EXTENSION));
+ ASSERT_EQ(message_headers.size(),
+ balsa_frame_.ProcessInput(message.data(), message.size()));
+ balsa_frame_.ProcessInput(message.data() + message_headers.size(),
+ message.size());
+}
+
TEST_F(HTTPBalsaFrameTest,
VisitorInvokedProperlyForRequestWithTransferEncoding) {
std::string message_headers =
diff --git a/quiche/balsa/http_validation_policy.h b/quiche/balsa/http_validation_policy.h
index 9ea158ec..b35799a 100644
--- a/quiche/balsa/http_validation_policy.h
+++ b/quiche/balsa/http_validation_policy.h
@@ -59,6 +59,10 @@
// neither, depending on InvalidCharsLevel, if a request header value contains
// a carriage return that is not succeeded by a line feed.
bool disallow_lone_cr_in_request_headers = false;
+
+ // The RFC is quite specific about chunk extensions formatting, but we only
+ // verify that there are no CR without a subsequent LF.
+ bool disallow_lone_cr_in_chunk_extension = false;
};
} // namespace quiche