Merge pull request #11224

9075361 epee: prevent log injection from malformed HTTP headers (selsta)

ACKs: jpk68*, vtnerd*, SyntheticBird45*, tobtoht
This commit is contained in:
tobtoht
2026-09-10 13:27:56 +00:00
2 changed files with 23 additions and 23 deletions
+16 -16
View File
@@ -541,7 +541,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;
}
@@ -607,7 +607,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;
@@ -617,7 +617,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());
@@ -651,25 +651,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(static_cast<unsigned char>(*end)), false, "Invalid first response line: " + m_header_cache + ", ptr: " << ptr);
CHECK_AND_ASSERT_MES(ul <= INT_MAX && isblank(static_cast<unsigned char>(*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(static_cast<unsigned char>(*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(static_cast<unsigned char>(*end)), false, "Invalid first response line: " + m_header_cache);
CHECK_AND_ASSERT_MES(ul >= 100 && ul <= 999 && isspace(static_cast<unsigned char>(*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
@@ -677,7 +677,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());
@@ -709,7 +709,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();
@@ -731,7 +731,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;
}
@@ -744,7 +744,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="<<m_response_info.m_header_info.m_content_length);
LOG_ERROR("http_stream_filter::analize_cached_reply_header_and_invoke_state(): Failed to get_len_from_content_lenght() (" << m_response_info.m_header_info.m_content_length.size() << " bytes)");
m_state = reciev_machine_state_error;
return false;
}
@@ -770,7 +770,7 @@ namespace net_utils
}else
{ //Apparently there are no signs of the form of transfer, will receive data until the connection is closed
m_state = reciev_machine_state_error;
MERROR("Undefined transfer type, consider http_body_transfer_connection_close method. header: " << m_header_cache);
MERROR("Undefined transfer type, consider http_body_transfer_connection_close method (" << m_header_cache.size() << " header bytes)");
return false;
}
return false;
@@ -801,7 +801,7 @@ namespace net_utils
boundary = result[7];
else
{
LOG_ERROR("Failed to match boundary in content-type=" << head_info.m_content_type);
LOG_ERROR("Failed to match boundary in content-type (" << head_info.m_content_type.size() << " bytes)");
return false;
}
return true;
@@ -101,7 +101,7 @@ namespace net_utils
entry.m_etc_header_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val]));
else
{
LOG_ERROR("simple_http_connection_handler::parse_header() not matched last entry in:"<<std::string(it_current_bound, it_end));
LOG_ERROR("simple_http_connection_handler::parse_header() not matched last entry (" << std::distance(it_current_bound, it_end) << " bytes)");
}
it_current_bound = result[(int)result.size()-1].first;
@@ -122,7 +122,7 @@ namespace net_utils
if(!parse_header(it_begin, end_header_it+4, entry))
{
LOG_ERROR("Failed to parse header:" << std::string(it_begin, end_header_it+2));
LOG_ERROR("Failed to parse header (" << std::distance(it_begin, end_header_it + 2) << " bytes)");
return false;
}
@@ -139,7 +139,7 @@ namespace net_utils
std::string boundary;
if(!match_boundary(content_type, boundary))
{
MERROR("Failed to match boundary in content type: " << content_type);
MERROR("Failed to match boundary in content type (" << content_type.size() << " bytes)");
return false;
}
@@ -430,7 +430,7 @@ namespace net_utils
}else
{
m_state = http_state_error;
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::handle_invoke_query_line(): Failed to match first line: " << m_cache);
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::handle_invoke_query_line(): Failed to match first line (" << m_cache.size() << " bytes)");
return false;
}
@@ -454,14 +454,14 @@ namespace net_utils
template<class t_connection_context>
bool simple_http_connection_handler<t_connection_context>::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<t_connection_context>::analize_cached_request_header_and_invoke_state(): failed to anilize request header: " << m_cache);
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::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<t_connection_context>::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<<m_query_info.m_header_info.m_content_length);
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::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;
}