File cpp-httplib-CVE-2025-0825.patch of Package cpp-httplib.18891
From 9c36aae4b73e2b6e493f4133e4173103c9266289 Mon Sep 17 00:00:00 2001
From: yhirose <yuji.hirose.bug@gmail.com>
Date: Thu, 16 Jan 2025 00:04:17 -0500
Subject: [PATCH] Fix HTTP Response Splitting Vulnerability
---
httplib.h | 62 +++++++++++++++++++++++++++++++++++++--
test/test.cc | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 141 insertions(+), 3 deletions(-)
Index: cpp-httplib-0.12.5/httplib.h
===================================================================
--- cpp-httplib-0.12.5.orig/httplib.h
+++ cpp-httplib-0.12.5/httplib.h
@@ -1972,6 +1972,60 @@ private:
std::string glowable_buffer_;
};
+// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
+namespace fields {
+
+inline bool is_token_char(char c) {
+ return std::isalnum(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
+ c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
+ c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
+}
+
+inline bool is_token(const std::string &s) {
+ if (s.empty()) { return false; }
+ for (auto c : s) {
+ if (!is_token_char(c)) { return false; }
+ }
+ return true;
+}
+
+inline bool is_field_name(const std::string &s) { return is_token(s); }
+
+inline bool is_vchar(char c) { return c >= 33 && c <= 126; }
+
+inline bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(c); }
+
+inline bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); }
+
+inline bool is_field_content(const std::string &s) {
+ if (s.empty()) { return false; }
+
+ if (s.size() == 1) {
+ return is_field_vchar(s[0]);
+ } else if (s.size() == 2) {
+ return is_field_vchar(s[0]) && is_field_vchar(s[1]);
+ } else {
+ size_t i = 0;
+
+ if (!is_field_vchar(s[i])) { return false; }
+ i++;
+
+ while (i < s.size() - 1) {
+ auto c = s[i++];
+ if (c == ' ' || c == '\t' || is_field_vchar(c)) {
+ } else {
+ return false;
+ }
+ }
+
+ return is_field_vchar(s[i]);
+ }
+}
+
+inline bool is_field_value(const std::string &s) { return is_field_content(s); }
+
+}; // namespace fields
+
} // namespace detail
// ----------------------------------------------------------------------------
@@ -4848,7 +4902,8 @@ inline size_t Request::get_header_value_
inline void Request::set_header(const std::string &key,
const std::string &val) {
- if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
+ if (detail::fields::is_field_name(key) &&
+ detail::fields::is_field_value(val)) {
headers.emplace(key, val);
}
}
@@ -4913,13 +4968,14 @@ inline size_t Response::get_header_value
inline void Response::set_header(const std::string &key,
const std::string &val) {
- if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
+ if (detail::fields::is_field_name(key) &&
+ detail::fields::is_field_value(val)) {
headers.emplace(key, val);
}
}
inline void Response::set_redirect(const std::string &url, int stat) {
- if (!detail::has_crlf(url)) {
+ if (detail::fields::is_field_value(url)) {
set_header("Location", url);
if (300 <= stat && stat < 400) {
this->status = stat;