From 01a7c14a82abdca49f4b1a9c0fd0cae5e2537676 Mon Sep 17 00:00:00 2001 From: selsta Date: Tue, 1 Sep 2026 18:27:48 +0200 Subject: [PATCH] epee: prevent log injection from malformed HTTP headers (cherry picked from commit 281c1d41aae7b579fa1307d82622e18abcd038ff) --- contrib/epee/include/net/http_client.h | 32 +++++++++---------- .../include/net/http_protocol_handler.inl | 14 ++++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index 540a85ab5..a73181666 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -594,7 +594,7 @@ namespace net_utils } if(!get_chunk_head(m_chunked_cache, m_len_in_remain, is_matched)) { - LOG_ERROR("http_stream_filter::handle_chunked(*) Failed to get length from chunked head:" << m_chunked_cache); + LOG_ERROR("http_stream_filter::handle_chunked(*) Failed to get length from chunked head (" << m_chunked_cache.size() << " bytes)"); m_state = reciev_machine_state_error; return false; } @@ -660,7 +660,7 @@ namespace net_utils while(cur < m_cache_to_process.size()) { const size_t line_end = m_cache_to_process.find('\n', cur); - CHECK_AND_ASSERT_MES(line_end != std::string::npos, false, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process); + CHECK_AND_ASSERT_MES(line_end != std::string::npos, false, "http_stream_filter::parse_cached_header() invalid header (" << m_cache_to_process.size() << " bytes)"); boost::string_view line(m_cache_to_process.data() + cur, line_end - cur); cur = line_end + 1; @@ -670,7 +670,7 @@ namespace net_utils boost::string_view name; boost::string_view value; - CHECK_AND_ASSERT_MES(detail::parse_header_line(line, name, value), false, "http_stream_filter::parse_cached_header() invalid header in: " << m_cache_to_process); + CHECK_AND_ASSERT_MES(detail::parse_header_line(line, name, value), false, "http_stream_filter::parse_cached_header() invalid header (" << m_cache_to_process.size() << " bytes)"); std::string key(name.data(), name.size()); std::string val(value.data(), value.size()); @@ -704,25 +704,25 @@ namespace net_utils { //First line response, look like this: "HTTP/1.1 200 OK" const char *ptr = m_header_cache.c_str(); - CHECK_AND_ASSERT_MES(!memcmp(ptr, "HTTP/", 5), false, "Invalid first response line: " + m_header_cache); + CHECK_AND_ASSERT_MES(!memcmp(ptr, "HTTP/", 5), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)"); ptr += 5; - CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line: " + m_header_cache); + CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)"); unsigned long ul; char *end; ul = strtoul(ptr, &end, 10); - CHECK_AND_ASSERT_MES(ul <= INT_MAX && *end =='.', false, "Invalid first response line: " + m_header_cache); + CHECK_AND_ASSERT_MES(ul <= INT_MAX && *end =='.', false, "Invalid first response line (" << m_header_cache.size() << " header bytes)"); m_response_info.m_http_ver_hi = ul; ptr = end + 1; - CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr); + CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)"); ul = strtoul(ptr, &end, 10); - CHECK_AND_ASSERT_MES(ul <= INT_MAX && isblank(*end), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr); + CHECK_AND_ASSERT_MES(ul <= INT_MAX && isblank(*end), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)"); m_response_info.m_http_ver_lo = ul; ptr = end + 1; while (isblank(*ptr)) ++ptr; - CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line: " + m_header_cache); + CHECK_AND_ASSERT_MES(epee::misc_utils::parse::isdigit(*ptr), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)"); ul = strtoul(ptr, &end, 10); - CHECK_AND_ASSERT_MES(ul >= 100 && ul <= 999 && isspace(*end), false, "Invalid first response line: " + m_header_cache); + CHECK_AND_ASSERT_MES(ul >= 100 && ul <= 999 && isspace(*end), false, "Invalid first response line (" << m_header_cache.size() << " header bytes)"); m_response_info.m_response_code = ul; ptr = end; // ignore the optional text, till the end @@ -730,7 +730,7 @@ namespace net_utils ++ptr; if (*ptr == '\r') ++ptr; - CHECK_AND_ASSERT_MES(*ptr == '\n', false, "Invalid first response line: " << m_header_cache); + CHECK_AND_ASSERT_MES(*ptr == '\n', false, "Invalid first response line (" << m_header_cache.size() << " header bytes)"); ++ptr; m_header_cache.erase(0, ptr - m_header_cache.c_str()); @@ -762,7 +762,7 @@ namespace net_utils std::string fake_str; //gcc error workaround bool res = parse_header(m_response_info.m_header_info, m_header_cache); - CHECK_AND_ASSERT_MES(res, false, "http_stream_filter::analize_cached_reply_header_and_invoke_state(): failed to anilize reply header: " << m_header_cache); + CHECK_AND_ASSERT_MES(res, false, "http_stream_filter::analize_cached_reply_header_and_invoke_state(): failed to anilize reply header (" << m_header_cache.size() << " bytes)"); set_reply_content_encoder(); @@ -784,7 +784,7 @@ namespace net_utils string_tools::trim(m_response_info.m_header_info.m_transfer_encoding); if(string_tools::compare_no_case(m_response_info.m_header_info.m_transfer_encoding, "chunked")) { - LOG_ERROR("Wrong Transfer-Encoding:" << m_response_info.m_header_info.m_transfer_encoding); + LOG_ERROR("Wrong Transfer-Encoding (" << m_response_info.m_header_info.m_transfer_encoding.size() << " bytes)"); m_state = reciev_machine_state_error; return false; } @@ -797,7 +797,7 @@ namespace net_utils //In the response header the length was specified if(!content_len_valid) { - LOG_ERROR("http_stream_filter::analize_cached_reply_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<(result[field_etc_name], result[field_val])); else { - LOG_ERROR("simple_http_connection_handler::parse_header() not matched last entry in:"<::handle_invoke_query_line(): Failed to match first line: " << m_cache); + LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_invoke_query_line(): Failed to match first line (" << m_cache.size() << " bytes)"); return false; } @@ -454,14 +454,14 @@ namespace net_utils template bool simple_http_connection_handler::analize_cached_request_header_and_invoke_state(size_t pos) { - LOG_PRINT_L3("HTTP HEAD:\r\n" << m_cache.substr(0, pos)); + LOG_PRINT_L3("HTTP HEAD: " << pos << " bytes"); m_query_info.m_full_request_buf_size = pos; m_query_info.m_request_head.assign(m_cache.begin(), m_cache.begin()+pos); if(!parse_cached_header(m_query_info.m_header_info, m_cache, pos)) { - LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): failed to anilize request header: " << m_cache); + LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): failed to anilize request header (" << pos << " bytes)"); m_state = http_state_error; return false; } @@ -477,7 +477,7 @@ namespace net_utils m_body_transfer_type = http_body_transfer_measure; if(!get_len_from_content_lenght(m_query_info.m_header_info.m_content_length, m_len_summary)) { - LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght() (" << m_query_info.m_header_info.m_content_length.size() << " bytes)"); m_state = http_state_error; return false; }