mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-09-30 20:52:31 +07:00
Limit the number of header lines per multipart form-data part
The multipart/form-data parser bounded the length of each individual header line via CPPHTTPLIB_HEADER_MAX_LENGTH but did not cap how many header lines a single part could contain. Within the overall payload limit, a client could pack a large number of small header lines into one part, each of which is parsed twice, inflating CPU usage. Add a per-part header-line counter that resets when a new entry begins and abort parsing once it reaches CPPHTTPLIB_HEADER_MAX_COUNT, mirroring the limit already enforced by read_headers() for request headers.
This commit is contained in:
committed by
Mario Limonciello (AMD)
parent
0c1cc8c986
commit
877a52f6b3
@@ -8095,6 +8095,13 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
// Check header count limit
|
||||
if (header_count_ >= CPPHTTPLIB_HEADER_MAX_COUNT) {
|
||||
is_valid_ = false;
|
||||
return false;
|
||||
}
|
||||
header_count_++;
|
||||
|
||||
const auto header = buf_head(pos);
|
||||
|
||||
if (!parse_header(header.data(), header.data() + header.size(),
|
||||
@@ -8210,6 +8217,7 @@ private:
|
||||
file_.filename.clear();
|
||||
file_.content_type.clear();
|
||||
file_.headers.clear();
|
||||
header_count_ = 0;
|
||||
}
|
||||
|
||||
bool start_with_case_ignore(const std::string &a, const char *b,
|
||||
@@ -8263,6 +8271,7 @@ private:
|
||||
size_t state_ = 0;
|
||||
bool is_valid_ = false;
|
||||
FormData file_;
|
||||
size_t header_count_ = 0;
|
||||
|
||||
// Buffer
|
||||
bool start_with(const std::string &a, size_t spos, size_t epos,
|
||||
|
||||
@@ -13612,6 +13612,43 @@ TEST(MultipartFormDataTest, MakeFileProvider) {
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
}
|
||||
|
||||
TEST(MultipartFormDataTest, ExcessivePartHeaders) {
|
||||
// Verify that a multipart part with more than CPPHTTPLIB_HEADER_MAX_COUNT
|
||||
// (100) headers is rejected with a 400 Bad Request response.
|
||||
Server svr;
|
||||
svr.Post("/post", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("ok", "text/plain");
|
||||
});
|
||||
|
||||
thread t = thread([&] { svr.listen(HOST, PORT); });
|
||||
auto se = detail::scope_exit([&] {
|
||||
svr.stop();
|
||||
t.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
});
|
||||
|
||||
svr.wait_until_ready();
|
||||
|
||||
// Build a multipart part with 101 custom headers (exceeding CPPHTTPLIB_HEADER_MAX_COUNT=100)
|
||||
std::stringstream body;
|
||||
body << "--simpleboundary\r\n"
|
||||
<< "Content-Disposition: form-data; name=\"field1\"\r\n";
|
||||
for (int i = 0; i < 101; i++) {
|
||||
body << "X-Custom-Header-" << i << ": value\r\n";
|
||||
}
|
||||
body << "\r\n"
|
||||
<< "value1\r\n"
|
||||
<< "--simpleboundary--\r\n";
|
||||
|
||||
std::string content_type = "multipart/form-data; boundary=simpleboundary";
|
||||
|
||||
Client cli(HOST, PORT);
|
||||
auto res = cli.Post("/post", body.str(), content_type.c_str());
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(StatusCode::BadRequest_400, res->status);
|
||||
}
|
||||
|
||||
TEST(MakeFileBodyTest, Basic) {
|
||||
const std::string file_content(4096, 'Z');
|
||||
const std::string tmp_path = "./httplib_test_make_file_body.bin";
|
||||
|
||||
Reference in New Issue
Block a user