cpp-httplib/CVE-2025-0825.patch
starlet-dx 71728ab942 Fix CVE-2025-0825
(cherry picked from commit 92b0b3b4b44fc1d59f8c5ccd88a64fb820636b80)
2025-02-07 11:15:29 +08:00

105 lines
3.1 KiB
Diff

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 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 59 insertions(+), 3 deletions(-)
diff --git a/httplib.h b/httplib.h
index 5a4b64a..86a7452 100644
--- a/httplib.h
+++ b/httplib.h
@@ -2159,6 +2159,60 @@ private:
void *addr_;
};
+// 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
// ----------------------------------------------------------------------------
@@ -5118,7 +5172,8 @@ inline size_t Request::get_header_value_count(const std::string &key) const {
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);
}
}
@@ -5183,13 +5238,14 @@ inline size_t Response::get_header_value_count(const std::string &key) const {
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;
--
2.43.0