From e11dbec7b3761b428224044578491b62c776466b Mon Sep 17 00:00:00 2001 From: yhirose Date: Sun, 27 Sep 2026 23:35:09 -0400 Subject: [PATCH] Reject control characters in the request-target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RFC 9112 §3.2 does not allow control characters in the request-target, and §2.2 requires a bare CR to be treated as invalid. parse_request_line accepted them, so e.g. "GET /a\rb HTTP/1.1" was routed normally. Reject any byte that is not VCHAR or obs-text with 400 Bad Request. obs-text is still allowed since some clients send raw UTF-8 in the target. --- httplib.h | 7 +++++++ test/test.cc | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/httplib.h b/httplib.h index dd6f8bc8..fd09f5e0 100644 --- a/httplib.h +++ b/httplib.h @@ -13296,6 +13296,13 @@ inline bool Server::parse_request_line(const char *s, Request &req) const { return false; } + // RFC 9112 §2.2/§3.2: reject control characters (incl. bare CR) in the + // request-target. obs-text is allowed since some clients send raw UTF-8. + if (!std::all_of(req.target.begin(), req.target.end(), + detail::fields::is_field_vchar)) { + return false; + } + { // Skip URL fragment for (size_t i = 0; i < req.target.size(); i++) { diff --git a/test/test.cc b/test/test.cc index 072bc625..cb8e7bf6 100644 --- a/test/test.cc +++ b/test/test.cc @@ -10390,6 +10390,22 @@ TEST(ServerRequestParsingTest, InvalidSpaceInURL) { EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24)); } +TEST(ServerRequestParsingTest, InvalidControlCharInURL) { + for (auto target : {"/h\ri", "/h\x7fi"}) { + std::string out; + test_raw_request(std::string("GET ") + target + " HTTP/1.1\r\n\r\n", &out); + EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24)) << target; + } +} + +TEST(ServerRequestParsingTest, NonAsciiInURLAccepted) { + std::string out; + test_raw_request("GET /hi?q=\xE3\x81\x82 HTTP/1.1\r\n" + "Connection: close\r\n\r\n", + &out); + EXPECT_EQ("HTTP/1.1 200 OK", out.substr(0, 15)); +} + TEST(ServerRequestParsingTest, RemoteAddrSetOnBadRequest) { Server svr;