Hi!

I have prepared an upload fixing three CVEs for the cpp-httplib package, originally targeting unstable/testing/trixie. I was asked by the release team to coordinate with you instead, and to perform a security update.

You can find a full diff about the version in trixie and the update at <https://salsa.debian.org/debian/cpp-httplib/-/compare/archive%2Fdebian%2F0.18.7-1...debian%2Ftrixie?from_project_id=65963>. I've also attached a debdiff here.

For some more context on the impact of the changes, please see the Cc'd bug and the unblock bug #1110393.

Let me know how to proceed! Bye :)
diff -Nru cpp-httplib-0.18.7/debian/changelog 
cpp-httplib-0.18.7/debian/changelog
--- cpp-httplib-0.18.7/debian/changelog 2025-03-11 18:18:06.000000000 +0100
+++ cpp-httplib-0.18.7/debian/changelog 2025-08-07 00:19:58.000000000 +0200
@@ -1,3 +1,28 @@
+cpp-httplib (0.18.7-1+deb13u1) trixie-security; urgency=medium
+
+  * fix CVE-2025-46728 (DoS via unbounded request line length).
+    While this patch intended to enforce request body size limits for
+    chunked Transfer-Encoding, it actually adds size limits for a unique
+    lines read from HTTP requests, solving another kind of DoS.
+    See the GHSA-px83-72rx-v57c GitHub advisory for more details.
+    Thanks to Yang Wang for the patch!
+    Closes: #1104926
+
+  * fix CVE-2025-52887 (Unlimited number of HTTP headers causes memory leak).
+    This patch adds a limit to the number of headers which
+    can be passed in an HTTP request, mitigating a possible DoS due to memory
+    exhaustion.
+    See bug #1109340 and the GHSA-xjhg-gf59-p92h GitHub advisory for more
+    details.
+
+  * fix CVE-2025-53629 (Unbounded Memory Allocation in Chunked Requests).
+    This patch complements the fix for CVE-2025-46728, actually solving
+    memory exhaustion attacks via chucked HTTP requests.
+    See bug #1109340 and the GHSA-qjmq-h3cc-qv6w GitHub advisory for more
+    details.
+
+ -- Andrea Pappacoda <[email protected]>  Thu, 07 Aug 2025 00:19:58 +0200
+
 cpp-httplib (0.18.7-1) unstable; urgency=medium
 
   * Update to new upstream version 0.18.7.
diff -Nru cpp-httplib-0.18.7/debian/gbp.conf cpp-httplib-0.18.7/debian/gbp.conf
--- cpp-httplib-0.18.7/debian/gbp.conf  2025-03-11 18:18:06.000000000 +0100
+++ cpp-httplib-0.18.7/debian/gbp.conf  2025-08-07 00:16:55.000000000 +0200
@@ -1,7 +1,7 @@
 [DEFAULT]
 
 dist = DEP14
-debian-branch = debian/latest
+debian-branch = debian/trixie
 upstream-branch = upstream/latest
 pristine-tar = True
 pristine-tar-commit = True
diff -Nru 
cpp-httplib-0.18.7/debian/patches/0001-httplib.h-fix-CVE-2025-46728-DoS-via-unbounded-reque.patch
 
cpp-httplib-0.18.7/debian/patches/0001-httplib.h-fix-CVE-2025-46728-DoS-via-unbounded-reque.patch
--- 
cpp-httplib-0.18.7/debian/patches/0001-httplib.h-fix-CVE-2025-46728-DoS-via-unbounded-reque.patch
   1970-01-01 01:00:00.000000000 +0100
+++ 
cpp-httplib-0.18.7/debian/patches/0001-httplib.h-fix-CVE-2025-46728-DoS-via-unbounded-reque.patch
   2025-08-07 00:19:58.000000000 +0200
@@ -0,0 +1,90 @@
+From: Ville Vesilehto <[email protected]>
+Date: Sat, 3 May 2025 11:39:01 +0300
+Subject: httplib.h: fix CVE-2025-46728 (DoS via unbounded request line
+ lenght)
+
+While this patch intended to enforce request body size limits for
+chunked Transfer-Encoding, it actually adds size limits for a unique
+lines read from HTTP requests, solving another kind of DoS.
+
+Author: Ville Vesilehto <[email protected]>
+Origin: upstream, 
https://github.com/yhirose/cpp-httplib/commit/7b752106ac42bd5b907793950d9125a0972c8e8e
+Bug-Debian: https://bugs.debian.org/1104926
+Bug: 
https://github.com/yhirose/cpp-httplib/security/advisories/GHSA-px83-72rx-v57c
+Forwarded: not-needed
+Reviewed-By: Yang Wang <[email protected]>
+Reviewed-By: Andrea Pappacoda <[email protected]>
+
+Closes: #1104926
+---
+ httplib.h    |  9 +++++++++
+ test/test.cc | 15 +++++++++++++++
+ 2 files changed, 24 insertions(+)
+
+diff --git a/httplib.h b/httplib.h
+index eb592d2..ec81a75 100644
+--- a/httplib.h
++++ b/httplib.h
+@@ -141,6 +141,10 @@
+ #define CPPHTTPLIB_LISTEN_BACKLOG 5
+ #endif
+ 
++#ifndef CPPHTTPLIB_MAX_LINE_LENGTH
++#define CPPHTTPLIB_MAX_LINE_LENGTH 32768
++#endif
++
+ /*
+  * Headers
+  */
+@@ -2961,6 +2965,11 @@ inline bool stream_line_reader::getline() {
+ #endif
+ 
+   for (size_t i = 0;; i++) {
++    if (size() >= CPPHTTPLIB_MAX_LINE_LENGTH) {
++      // Treat exceptionally long lines as an error to
++      // prevent infinite loops/memory exhaustion
++      return false;
++    }
+     char byte;
+     auto n = strm_.read(&byte, 1);
+ 
+diff --git a/test/test.cc b/test/test.cc
+index b69be5c..fe7151e 100644
+--- a/test/test.cc
++++ b/test/test.cc
+@@ -42,6 +42,9 @@ const int PORT = 1234;
+ const string LONG_QUERY_VALUE = string(25000, '@');
+ const string LONG_QUERY_URL = "/long-query-value?key=" + LONG_QUERY_VALUE;
+ 
++const string TOO_LONG_QUERY_VALUE = string(35000, '@');
++const string TOO_LONG_QUERY_URL = "/too-long-query-value?key=" + 
TOO_LONG_QUERY_VALUE;
++
+ const std::string JSON_DATA = "{\"hello\":\"world\"}";
+ 
+ const string LARGE_DATA = string(1024 * 1024 * 100, '@'); // 100MB
+@@ -2837,6 +2840,11 @@ protected:
+                EXPECT_EQ(LONG_QUERY_URL, req.target);
+                EXPECT_EQ(LONG_QUERY_VALUE, req.get_param_value("key"));
+              })
++        .Get("/too-long-query-value",
++             [&](const Request &req, Response & /*res*/) {
++               EXPECT_EQ(TOO_LONG_QUERY_URL, req.target);
++               EXPECT_EQ(TOO_LONG_QUERY_VALUE, req.get_param_value("key"));
++             })
+         .Get("/array-param",
+              [&](const Request &req, Response & /*res*/) {
+                EXPECT_EQ(3u, req.get_param_value_count("array"));
+@@ -3611,6 +3619,13 @@ TEST_F(ServerTest, LongQueryValue) {
+   EXPECT_EQ(StatusCode::UriTooLong_414, res->status);
+ }
+ 
++TEST_F(ServerTest, TooLongQueryValue) {
++  auto res = cli_.Get(TOO_LONG_QUERY_URL.c_str());
++
++  ASSERT_FALSE(res);
++  EXPECT_EQ(Error::Read, res.error());
++}
++
+ TEST_F(ServerTest, TooLongHeader) {
+   Request req;
+   req.method = "GET";
diff -Nru 
cpp-httplib-0.18.7/debian/patches/0002-httplib.h-fix-CVE-2025-52887-Unlimited-number-of-htt.patch
 
cpp-httplib-0.18.7/debian/patches/0002-httplib.h-fix-CVE-2025-52887-Unlimited-number-of-htt.patch
--- 
cpp-httplib-0.18.7/debian/patches/0002-httplib.h-fix-CVE-2025-52887-Unlimited-number-of-htt.patch
   1970-01-01 01:00:00.000000000 +0100
+++ 
cpp-httplib-0.18.7/debian/patches/0002-httplib.h-fix-CVE-2025-52887-Unlimited-number-of-htt.patch
   2025-08-07 00:19:58.000000000 +0200
@@ -0,0 +1,185 @@
+From: yhirose <[email protected]>
+Date: Tue, 24 Jun 2025 07:56:00 -0400
+Subject: httplib.h: fix CVE-2025-52887 (Unlimited number of http header
+ fields causes memory leak)
+
+This patch adds a limit to the number of headers which can be passed in
+an HTTP request, mitigating a possible DoS due to memory exhaustion.
+
+Author: Yuji Hirose <[email protected]>
+Origin: upstream, 
https://github.com/yhirose/cpp-httplib/commit/28dcf379e82a2cdb544d812696a7fd46067eb7f9
+Bug-Debian: https://bugs.debian.org/1109340
+Bug: 
https://github.com/yhirose/cpp-httplib/security/advisories/GHSA-xjhg-gf59-p92h
+Forwarded: not-needed
+Reviewed-By: Andrea Pappacoda <[email protected]>
+---
+ httplib.h    | 17 ++++++++++++++
+ test/test.cc | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 91 insertions(+)
+
+diff --git a/httplib.h b/httplib.h
+index ec81a75..0a5c6fc 100644
+--- a/httplib.h
++++ b/httplib.h
+@@ -86,6 +86,10 @@
+ #define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
+ #endif
+ 
++#ifndef CPPHTTPLIB_HEADER_MAX_COUNT
++#define CPPHTTPLIB_HEADER_MAX_COUNT 100
++#endif
++
+ #ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
+ #define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
+ #endif
+@@ -4249,6 +4253,8 @@ inline bool read_headers(Stream &strm, Headers &headers) 
{
+   char buf[bufsiz];
+   stream_line_reader line_reader(strm, buf, bufsiz);
+ 
++  size_t header_count = 0;
++
+   for (;;) {
+     if (!line_reader.getline()) { return false; }
+ 
+@@ -4269,6 +4275,9 @@ inline bool read_headers(Stream &strm, Headers &headers) 
{
+ 
+     if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
+ 
++    // Check header count limit
++    if (header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
++
+     // Exclude line terminator
+     auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
+ 
+@@ -4278,6 +4287,8 @@ inline bool read_headers(Stream &strm, Headers &headers) 
{
+                       })) {
+       return false;
+     }
++
++    header_count++;
+   }
+ 
+   return true;
+@@ -4379,9 +4390,13 @@ inline bool read_content_chunked(Stream &strm, T &x,
+   // chuncked transfer coding data without the final CRLF.
+   if (!line_reader.getline()) { return true; }
+ 
++  size_t trailer_header_count = 0;
+   while (strcmp(line_reader.ptr(), "\r\n") != 0) {
+     if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
+ 
++    // Check trailer header count limit
++    if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
++
+     // Exclude line terminator
+     constexpr auto line_terminator_len = 2;
+     auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
+@@ -4391,6 +4406,8 @@ inline bool read_content_chunked(Stream &strm, T &x,
+                    x.headers.emplace(key, val);
+                  });
+ 
++    trailer_header_count++;
++
+     if (!line_reader.getline()) { return false; }
+   }
+ 
+diff --git a/test/test.cc b/test/test.cc
+index fe7151e..e29be7d 100644
+--- a/test/test.cc
++++ b/test/test.cc
+@@ -3,7 +3,11 @@
+ #include <signal.h>
+ 
+ #ifndef _WIN32
++#include <arpa/inet.h>
+ #include <curl/curl.h>
++#include <netinet/in.h>
++#include <sys/socket.h>
++#include <unistd.h>
+ #endif
+ #include <gtest/gtest.h>
+ 
+@@ -3680,6 +3684,50 @@ TEST_F(ServerTest, TooLongHeader) {
+   EXPECT_EQ(StatusCode::OK_200, res->status);
+ }
+ 
++TEST_F(ServerTest, HeaderCountAtLimit) {
++  // Test with headers just under the 100 limit
++  httplib::Headers headers;
++  
++  // Add 95 custom headers (the client will add Host, User-Agent, Accept, 
etc.)
++  // This should keep us just under the 100 header limit
++  for (int i = 0; i < 95; i++) {
++    std::string name = "X-Test-Header-" + std::to_string(i);
++    std::string value = "value" + std::to_string(i);
++    headers.emplace(name, value);
++  }
++  
++  // This should work fine as we're under the limit
++  auto res = cli_.Get("/hi", headers);
++  EXPECT_TRUE(res);
++  if (res) {
++    EXPECT_EQ(StatusCode::OK_200, res->status);
++  }
++}
++
++TEST_F(ServerTest, HeaderCountExceedsLimit) {
++  // Test with many headers to exceed the 100 limit
++  httplib::Headers headers;
++  
++  // Add 150 headers to definitely exceed the 100 limit
++  for (int i = 0; i < 150; i++) {
++    std::string name = "X-Test-Header-" + std::to_string(i);
++    std::string value = "value" + std::to_string(i);
++    headers.emplace(name, value);
++  }
++  
++  // This should fail due to exceeding header count limit
++  auto res = cli_.Get("/hi", headers);
++  
++  // The request should either fail or return 400 Bad Request
++  if (res) {
++    // If we get a response, it should be 400 Bad Request
++    EXPECT_EQ(StatusCode::BadRequest_400, res->status);
++  } else {
++    // Or the request should fail entirely
++    EXPECT_FALSE(res);
++  }
++}
++
+ TEST_F(ServerTest, PercentEncoding) {
+   auto res = cli_.Get("/e%6edwith%");
+   ASSERT_TRUE(res);
+@@ -3717,6 +3765,32 @@ TEST_F(ServerTest, PlusSignEncoding) {
+   EXPECT_EQ("a +b", res->body);
+ }
+ 
++TEST_F(ServerTest, HeaderCountSecurityTest) {
++  // This test simulates a potential DoS attack using many headers
++  // to verify our security fix prevents memory exhaustion
++  
++  httplib::Headers attack_headers;
++  
++  // Attempt to add many headers like an attacker would (200 headers to far 
exceed limit)
++  for (int i = 0; i < 200; i++) {
++    std::string name = "X-Attack-Header-" + std::to_string(i);
++    std::string value = "attack_payload_" + std::to_string(i);
++    attack_headers.emplace(name, value);
++  }
++  
++  // Try to POST with excessive headers
++  auto res = cli_.Post("/", attack_headers, "test_data", "text/plain");
++  
++  // Should either fail or return 400 Bad Request due to security limit
++  if (res) {
++    // If we get a response, it should be 400 Bad Request
++    EXPECT_EQ(StatusCode::BadRequest_400, res->status);
++  } else {
++    // Request failed, which is the expected behavior for DoS protection
++    EXPECT_FALSE(res);
++  }
++}
++
+ TEST_F(ServerTest, MultipartFormData) {
+   MultipartFormDataItems items = {
+       {"text1", "text default", "", ""},
diff -Nru 
cpp-httplib-0.18.7/debian/patches/0003-httplib.h-fix-CVE-2025-53629-Unbounded-Memory-Alloca.patch
 
cpp-httplib-0.18.7/debian/patches/0003-httplib.h-fix-CVE-2025-53629-Unbounded-Memory-Alloca.patch
--- 
cpp-httplib-0.18.7/debian/patches/0003-httplib.h-fix-CVE-2025-53629-Unbounded-Memory-Alloca.patch
   1970-01-01 01:00:00.000000000 +0100
+++ 
cpp-httplib-0.18.7/debian/patches/0003-httplib.h-fix-CVE-2025-53629-Unbounded-Memory-Alloca.patch
   2025-08-07 00:19:58.000000000 +0200
@@ -0,0 +1,450 @@
+From: yhirose <[email protected]>
+Date: Tue, 8 Jul 2025 17:11:13 -0400
+Subject: httplib.h: fix CVE-2025-53629 (Unbounded Memory Allocation in
+ Chunked/No-Length Requests)
+
+This patch complements the fix for CVE-2025-46728, actually solving
+memory exhaustion attacks via chucked HTTP requests.
+
+Origin: backport, 
https://github.com/yhirose/cpp-httplib/commit/082acacd4581d10e05fccbe9cb336aa7822c4ea2
+Bug-Debian: https://bugs.debian.org/1109340
+Bug: 
https://github.com/yhirose/cpp-httplib/security/advisories/GHSA-qjmq-h3cc-qv6w
+Forwarded: not-needed
+Reviewed-By: Andrea Pappacoda <[email protected]>
+---
+ httplib.h    |  95 ++++++++++++++++------
+ test/test.cc | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 327 insertions(+), 23 deletions(-)
+
+diff --git a/httplib.h b/httplib.h
+index 0a5c6fc..bc887ea 100644
+--- a/httplib.h
++++ b/httplib.h
+@@ -4327,51 +4327,79 @@ inline void skip_content_with_length(Stream &strm, 
uint64_t len) {
+   }
+ }
+ 
+-inline bool read_content_without_length(Stream &strm,
+-                                        ContentReceiverWithProgress out) {
++enum class ReadContentResult {
++  Success,         // Successfully read the content
++  PayloadTooLarge, // The content exceeds the specified payload limit
++  Error            // An error occurred while reading the content
++};
++
++inline ReadContentResult
++read_content_without_length(Stream &strm, size_t payload_max_length,
++                            ContentReceiverWithProgress out) {
+   char buf[CPPHTTPLIB_RECV_BUFSIZ];
+   uint64_t r = 0;
+   for (;;) {
+     auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
+-    if (n <= 0) { return true; }
++    if (n == 0) { return ReadContentResult::Success; }
++    if (n < 0) { return ReadContentResult::Error; }
++
++    // Check if adding this data would exceed the payload limit
++    if (r > payload_max_length ||
++        payload_max_length - r < static_cast<uint64_t>(n)) {
++      return ReadContentResult::PayloadTooLarge;
++    }
+ 
+-    if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
++    if (!out(buf, static_cast<size_t>(n), r, 0)) {
++      return ReadContentResult::Error;
++    }
+     r += static_cast<uint64_t>(n);
+   }
+ 
+-  return true;
++  return ReadContentResult::Success;
+ }
+ 
+ template <typename T>
+-inline bool read_content_chunked(Stream &strm, T &x,
+-                                 ContentReceiverWithProgress out) {
++inline ReadContentResult read_content_chunked(Stream &strm, T &x,
++                                              size_t payload_max_length,
++                                              ContentReceiverWithProgress 
out) {
+   const auto bufsiz = 16;
+   char buf[bufsiz];
+ 
+   stream_line_reader line_reader(strm, buf, bufsiz);
+ 
+-  if (!line_reader.getline()) { return false; }
++  if (!line_reader.getline()) { return ReadContentResult::Error; }
+ 
+   unsigned long chunk_len;
++  uint64_t total_len = 0;
+   while (true) {
+     char *end_ptr;
+ 
+     chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
+ 
+-    if (end_ptr == line_reader.ptr()) { return false; }
+-    if (chunk_len == ULONG_MAX) { return false; }
++    if (end_ptr == line_reader.ptr()) { return ReadContentResult::Error; }
++    if (chunk_len == ULONG_MAX) { return ReadContentResult::Error; }
+ 
+     if (chunk_len == 0) { break; }
+ 
++    // Check if adding this chunk would exceed the payload limit
++    if (total_len > payload_max_length ||
++        payload_max_length - total_len < chunk_len) {
++      return ReadContentResult::PayloadTooLarge;
++    }
++
++    total_len += chunk_len;
++
+     if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
+-      return false;
++      return ReadContentResult::Error;
+     }
+ 
+-    if (!line_reader.getline()) { return false; }
++    if (!line_reader.getline()) { return ReadContentResult::Error; }
+ 
+-    if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; }
++    if (strcmp(line_reader.ptr(), "\r\n") != 0) {
++      return ReadContentResult::Error;
++    }
+ 
+-    if (!line_reader.getline()) { return false; }
++    if (!line_reader.getline()) { return ReadContentResult::Error; }
+   }
+ 
+   assert(chunk_len == 0);
+@@ -4386,16 +4414,20 @@ inline bool read_content_chunked(Stream &strm, T &x,
+   // to be ok whether the final CRLF exists or not in the chunked data.
+   // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
+   //
+-  // According to the reference code in RFC 9112, cpp-htpplib now allows
+-  // chuncked transfer coding data without the final CRLF.
+-  if (!line_reader.getline()) { return true; }
++  // According to the reference code in RFC 9112, cpp-httplib now allows
++  // chunked transfer coding data without the final CRLF.
++  if (!line_reader.getline()) { return ReadContentResult::Success; }
+ 
+   size_t trailer_header_count = 0;
+   while (strcmp(line_reader.ptr(), "\r\n") != 0) {
+-    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
++    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
++      return ReadContentResult::Error;
++    }
+ 
+     // Check trailer header count limit
+-    if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
++    if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) {
++      return ReadContentResult::Error;
++    }
+ 
+     // Exclude line terminator
+     constexpr auto line_terminator_len = 2;
+@@ -4408,10 +4440,10 @@ inline bool read_content_chunked(Stream &strm, T &x,
+ 
+     trailer_header_count++;
+ 
+-    if (!line_reader.getline()) { return false; }
++    if (!line_reader.getline()) { return ReadContentResult::Error; }
+   }
+ 
+-  return true;
++  return ReadContentResult::Success;
+ }
+ 
+ inline bool is_chunked_transfer_encoding(const Headers &headers) {
+@@ -4478,9 +4510,26 @@ bool read_content(Stream &strm, T &x, size_t 
payload_max_length, int &status,
+         auto exceed_payload_max_length = false;
+ 
+         if (is_chunked_transfer_encoding(x.headers)) {
+-          ret = read_content_chunked(strm, x, out);
++          auto result = read_content_chunked(strm, x, payload_max_length, 
out);
++          if (result == ReadContentResult::Success) {
++            ret = true;
++          } else if (result == ReadContentResult::PayloadTooLarge) {
++            exceed_payload_max_length = true;
++            ret = false;
++          } else {
++            ret = false;
++          }
+         } else if (!has_header(x.headers, "Content-Length")) {
+-          ret = read_content_without_length(strm, out);
++          auto result =
++              read_content_without_length(strm, payload_max_length, out);
++          if (result == ReadContentResult::Success) {
++            ret = true;
++          } else if (result == ReadContentResult::PayloadTooLarge) {
++            exceed_payload_max_length = true;
++            ret = false;
++          } else {
++            ret = false;
++          }
+         } else {
+           auto is_invalid_value = false;
+           auto len = get_header_value_u64(
+diff --git a/test/test.cc b/test/test.cc
+index e29be7d..4d4a742 100644
+--- a/test/test.cc
++++ b/test/test.cc
+@@ -6134,6 +6134,261 @@ TEST_F(PayloadMaxLengthTest, ExceedLimit) {
+   EXPECT_EQ(StatusCode::OK_200, res->status);
+ }
+ 
++TEST_F(PayloadMaxLengthTest, ChunkedEncodingSecurityTest) {
++  // Test chunked encoding with payload exceeding the 8-byte limit
++  std::string large_chunked_data(16, 'A'); // 16 bytes, exceeds 8-byte limit
++
++  auto res = cli_.Post("/test", large_chunked_data, "text/plain");
++  ASSERT_TRUE(res);
++  EXPECT_EQ(StatusCode::PayloadTooLarge_413, res->status);
++}
++
++TEST_F(PayloadMaxLengthTest, ChunkedEncodingWithinLimit) {
++  // Test chunked encoding with payload within the 8-byte limit
++  std::string small_chunked_data(4, 'B'); // 4 bytes, within 8-byte limit
++
++  auto res = cli_.Post("/test", small_chunked_data, "text/plain");
++  ASSERT_TRUE(res);
++  EXPECT_EQ(StatusCode::OK_200, res->status);
++}
++
++TEST_F(PayloadMaxLengthTest, RawSocketChunkedTest) {
++  // Test using send_request to send chunked data exceeding payload limit
++  std::string chunked_request = "POST /test HTTP/1.1\r\n"
++                                "Host: " +
++                                std::string(HOST) + ":" + 
std::to_string(PORT) +
++                                "\r\n"
++                                "Transfer-Encoding: chunked\r\n"
++                                "Connection: close\r\n"
++                                "\r\n"
++                                "a\r\n" // 10 bytes chunk (exceeds 8-byte 
limit)
++                                "0123456789\r\n"
++                                "0\r\n" // End chunk
++                                "\r\n";
++
++  std::string response;
++  bool result = send_request(1, chunked_request, &response);
++
++  if (!result) {
++    // If send_request fails, it might be because the server closed the
++    // connection due to payload limit enforcement, which is acceptable
++    SUCCEED()
++        << "Server rejected oversized chunked request (connection closed)";
++  } else {
++    // If we got a response, check if it's an error response or connection was
++    // closed early Short response length indicates connection was closed due 
to
++    // payload limit
++    if (response.length() <= 10) {
++      SUCCEED() << "Server closed connection for oversized chunked request";
++    } else {
++      // Check for error status codes
++      EXPECT_TRUE(response.find("413") != std::string::npos ||
++                  response.find("Payload Too Large") != std::string::npos ||
++                  response.find("400") != std::string::npos);
++    }
++  }
++}
++
++TEST_F(PayloadMaxLengthTest, NoContentLengthPayloadLimit) {
++  // Test request without Content-Length header exceeding payload limit
++  std::string request_without_content_length = "POST /test HTTP/1.1\r\n"
++                                               "Host: " +
++                                               std::string(HOST) + ":" +
++                                               std::to_string(PORT) +
++                                               "\r\n"
++                                               "Connection: close\r\n"
++                                               "\r\n";
++
++  // Add payload exceeding the 8-byte limit
++  std::string large_payload(16, 'X'); // 16 bytes, exceeds 8-byte limit
++  request_without_content_length += large_payload;
++
++  std::string response;
++  bool result = send_request(1, request_without_content_length, &response);
++
++  if (!result) {
++    // If send_request fails, server likely closed connection due to payload
++    // limit
++    SUCCEED() << "Server rejected oversized request without Content-Length "
++                 "(connection closed)";
++  } else {
++    // Check if server responded with error or closed connection early
++    if (response.length() <= 10) {
++      SUCCEED() << "Server closed connection for oversized request without "
++                   "Content-Length";
++    } else {
++      // Check for error status codes
++      EXPECT_TRUE(response.find("413") != std::string::npos ||
++                  response.find("Payload Too Large") != std::string::npos ||
++                  response.find("400") != std::string::npos);
++    }
++  }
++}
++
++TEST_F(PayloadMaxLengthTest, NoContentLengthWithinLimit) {
++  // Test request without Content-Length header within payload limit
++  std::string request_without_content_length = "POST /test HTTP/1.1\r\n"
++                                               "Host: " +
++                                               std::string(HOST) + ":" +
++                                               std::to_string(PORT) +
++                                               "\r\n"
++                                               "Connection: close\r\n"
++                                               "\r\n";
++
++  // Add payload within the 8-byte limit
++  std::string small_payload(4, 'Y'); // 4 bytes, within 8-byte limit
++  request_without_content_length += small_payload;
++
++  std::string response;
++  bool result = send_request(1, request_without_content_length, &response);
++
++  // For requests without Content-Length, the server may have different 
behavior
++  // The key is that it should not reject due to payload limit for small
++  // payloads
++  if (result) {
++    // Check for any HTTP response (success or error, but not connection 
closed)
++    if (response.length() > 10) {
++      SUCCEED()
++          << "Server processed request without Content-Length within limit";
++    } else {
++      // Short response might indicate connection closed, which is acceptable
++      SUCCEED() << "Server closed connection for request without "
++                   "Content-Length (acceptable behavior)";
++    }
++  } else {
++    // Connection failure might be due to protocol requirements
++    SUCCEED() << "Connection issue with request without Content-Length "
++                 "(environment-specific)";
++  }
++}
++
++class LargePayloadMaxLengthTest : public ::testing::Test {
++protected:
++  LargePayloadMaxLengthTest()
++      : cli_(HOST, PORT)
++#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
++        ,
++        svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE)
++#endif
++  {
++#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
++    cli_.enable_server_certificate_verification(false);
++#endif
++  }
++
++  virtual void SetUp() {
++    // Set 10MB payload limit
++    const size_t LARGE_PAYLOAD_LIMIT = 10 * 1024 * 1024; // 10MB
++    svr_.set_payload_max_length(LARGE_PAYLOAD_LIMIT);
++
++    svr_.Post("/test", [&](const Request & /*req*/, Response &res) {
++      res.set_content("Large payload test", "text/plain");
++    });
++
++    t_ = thread([&]() { ASSERT_TRUE(svr_.listen(HOST, PORT)); });
++    svr_.wait_until_ready();
++  }
++
++  virtual void TearDown() {
++    svr_.stop();
++    t_.join();
++  }
++
++#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
++  SSLClient cli_;
++  SSLServer svr_;
++#else
++  Client cli_;
++  Server svr_;
++#endif
++  thread t_;
++};
++
++TEST_F(LargePayloadMaxLengthTest, ChunkedEncodingWithin10MB) {
++  // Test chunked encoding with payload within 10MB limit
++  std::string medium_payload(5 * 1024 * 1024,
++                             'A'); // 5MB payload, within 10MB limit
++
++  auto res = cli_.Post("/test", medium_payload, "application/octet-stream");
++  ASSERT_TRUE(res);
++  EXPECT_EQ(StatusCode::OK_200, res->status);
++}
++
++TEST_F(LargePayloadMaxLengthTest, ChunkedEncodingExceeds10MB) {
++  // Test chunked encoding with payload exceeding 10MB limit
++  std::string large_payload(12 * 1024 * 1024,
++                            'B'); // 12MB payload, exceeds 10MB limit
++
++  auto res = cli_.Post("/test", large_payload, "application/octet-stream");
++  ASSERT_TRUE(res);
++  EXPECT_EQ(StatusCode::PayloadTooLarge_413, res->status);
++}
++
++TEST_F(LargePayloadMaxLengthTest, NoContentLengthWithin10MB) {
++  // Test request without Content-Length header within 10MB limit
++  std::string request_without_content_length = "POST /test HTTP/1.1\r\n"
++                                               "Host: " +
++                                               std::string(HOST) + ":" +
++                                               std::to_string(PORT) +
++                                               "\r\n"
++                                               "Connection: close\r\n"
++                                               "\r\n";
++
++  // Add 1MB payload (within 10MB limit)
++  std::string medium_payload(1024 * 1024, 'C'); // 1MB payload
++  request_without_content_length += medium_payload;
++
++  std::string response;
++  bool result = send_request(5, request_without_content_length, &response);
++
++  if (result) {
++    // Should get a proper HTTP response for payloads within limit
++    if (response.length() > 10) {
++      SUCCEED() << "Server processed 1MB request without Content-Length 
within "
++                   "10MB limit";
++    } else {
++      SUCCEED() << "Server closed connection (acceptable behavior for no "
++                   "Content-Length)";
++    }
++  } else {
++    SUCCEED() << "Connection issue with 1MB payload (environment-specific)";
++  }
++}
++
++TEST_F(LargePayloadMaxLengthTest, NoContentLengthExceeds10MB) {
++  // Test request without Content-Length header exceeding 10MB limit
++  std::string request_without_content_length = "POST /test HTTP/1.1\r\n"
++                                               "Host: " +
++                                               std::string(HOST) + ":" +
++                                               std::to_string(PORT) +
++                                               "\r\n"
++                                               "Connection: close\r\n"
++                                               "\r\n";
++
++  // Add 12MB payload (exceeds 10MB limit)
++  std::string large_payload(12 * 1024 * 1024, 'D'); // 12MB payload
++  request_without_content_length += large_payload;
++
++  std::string response;
++  bool result = send_request(10, request_without_content_length, &response);
++
++  if (!result) {
++    // Server should close connection due to payload limit
++    SUCCEED() << "Server rejected 12MB request without Content-Length "
++                 "(connection closed)";
++  } else {
++    // Check for error response
++    if (response.length() <= 10) {
++      SUCCEED()
++          << "Server closed connection for 12MB request exceeding 10MB limit";
++    } else {
++      EXPECT_TRUE(response.find("413") != std::string::npos ||
++                  response.find("Payload Too Large") != std::string::npos ||
++                  response.find("400") != std::string::npos);
++    }
++  }
++}
++
+ TEST(HostAndPortPropertiesTest, NoSSL) {
+   httplib::Client cli("www.google.com", 1234);
+   ASSERT_EQ("www.google.com", cli.host());
diff -Nru cpp-httplib-0.18.7/debian/patches/series 
cpp-httplib-0.18.7/debian/patches/series
--- cpp-httplib-0.18.7/debian/patches/series    1970-01-01 01:00:00.000000000 
+0100
+++ cpp-httplib-0.18.7/debian/patches/series    2025-08-07 00:19:58.000000000 
+0200
@@ -0,0 +1,3 @@
+0001-httplib.h-fix-CVE-2025-46728-DoS-via-unbounded-reque.patch
+0002-httplib.h-fix-CVE-2025-52887-Unlimited-number-of-htt.patch
+0003-httplib.h-fix-CVE-2025-53629-Unbounded-Memory-Alloca.patch

Attachment: signature.asc
Description: PGP signature

Reply via email to