Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package nghttp2 for openSUSE:Factory checked in at 2026-06-30 15:11:30 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/nghttp2 (Old) and /work/SRC/openSUSE:Factory/.nghttp2.new.11887 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "nghttp2" Tue Jun 30 15:11:30 2026 rev:91 rq:1362476 version:1.69.0 Changes: -------- --- /work/SRC/openSUSE:Factory/nghttp2/nghttp2.changes 2026-04-30 20:26:18.808790294 +0200 +++ /work/SRC/openSUSE:Factory/.nghttp2.new.11887/nghttp2.changes 2026-06-30 15:11:55.984994303 +0200 @@ -1,0 +2,8 @@ +Mon Jun 29 09:22:53 UTC 2026 - Valentin Lefebvre <[email protected]> + +- stricter validation for HTTP CONNECT and Upgrade requests across HTTP/1, + HTTP/2, and HTTP/3 upstreams, specifically rejecting requests that + incorrectly include Content-Length or Transfer-Encoding headers + * Add 0001-nghttpx-Tighten-up-CONNECT-and-HTTP-Upgrade-handling.patch + CVE-2026-58055, bsc#1269489 +------------------------------------------------------------------- New: ---- 0001-nghttpx-Tighten-up-CONNECT-and-HTTP-Upgrade-handling.patch ----------(New B)---------- New: incorrectly include Content-Length or Transfer-Encoding headers * Add 0001-nghttpx-Tighten-up-CONNECT-and-HTTP-Upgrade-handling.patch CVE-2026-58055, bsc#1269489 ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ nghttp2.spec ++++++ --- /var/tmp/diff_new_pack.bVVUGf/_old 2026-06-30 15:11:56.761020587 +0200 +++ /var/tmp/diff_new_pack.bVVUGf/_new 2026-06-30 15:11:56.765020723 +0200 @@ -34,6 +34,8 @@ Source1: https://github.com/nghttp2/nghttp2/releases/download/v%{version}/nghttp2-%{version}.tar.xz.asc Source2: nghttp2.keyring Source3: baselibs.conf +# PATCH-FIX-UPSTREAM: CVE-2026-58055 +Patch: 0001-nghttpx-Tighten-up-CONNECT-and-HTTP-Upgrade-handling.patch BuildRequires: libboost_thread-devel BuildRequires: pkgconfig BuildRequires: pkgconfig(cunit) ++++++ 0001-nghttpx-Tighten-up-CONNECT-and-HTTP-Upgrade-handling.patch ++++++ >From ab28105c4a0197da24f8bfc414bc116055249e1e Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa <[email protected]> Date: Fri, 22 May 2026 21:26:44 +0900 Subject: [PATCH] nghttpx: Tighten up CONNECT and HTTP Upgrade handling [vlefebvre: adapt changes] --- src/shrpx_downstream.cc | 3 +- src/shrpx_downstream.h | 4 ++ src/shrpx_http2_upstream.cc | 8 +++ src/shrpx_http3_upstream.cc | 8 +++ src/shrpx_http_downstream_connection.cc | 70 +++++++++++++++++++++---- src/shrpx_http_downstream_connection.h | 5 ++ src/shrpx_https_upstream.cc | 35 +++++++++++-- 7 files changed, 117 insertions(+), 16 deletions(-) Index: nghttp2-1.69.0/src/shrpx_downstream.cc =================================================================== --- nghttp2-1.69.0.orig/src/shrpx_downstream.cc +++ nghttp2-1.69.0/src/shrpx_downstream.cc @@ -1146,7 +1146,8 @@ bool Downstream::can_detach_downstream_c // state, especially for HTTP/1.1 return dconn_ && response_state_ == DownstreamState::MSG_COMPLETE && request_state_ == DownstreamState::MSG_COMPLETE && !upgraded_ && - !resp_.connection_close && request_buf_.rleft() == 0; + !resp_.connection_close && blocked_request_buf_.rleft() == 0 && + request_buf_.rleft() == 0; } DefaultMemchunks Downstream::pop_response_buf() { Index: nghttp2-1.69.0/src/shrpx_downstream.h =================================================================== --- nghttp2-1.69.0.orig/src/shrpx_downstream.h +++ nghttp2-1.69.0/src/shrpx_downstream.h @@ -231,6 +231,10 @@ struct Request { // orig_authority and orig_path have the authority and path which // are used for the first backend selection. bool forwarded_once; + // true if HTTP/1 request message has been completed. This field is + // added because Downstream::get_request_state() might be altered + // from DownstreamState::MSG_COMPLETE. + bool http1_msg_complete; }; struct Response { Index: nghttp2-1.69.0/src/shrpx_http2_upstream.cc =================================================================== --- nghttp2-1.69.0.orig/src/shrpx_http2_upstream.cc +++ nghttp2-1.69.0/src/shrpx_http2_upstream.cc @@ -328,6 +328,14 @@ int Http2Upstream::on_request_headers(Do return 0; } + if (method_token == HTTP_CONNECT && content_length) { + if (log_enabled(INFO)) { + Log{INFO, this} << "content-length are not allowed in CONNECT request"; + } + + return error_reply(downstream, 400); + } + auto faddr = handler_->get_upstream_addr(); // For HTTP/2 proxy, we require :authority. Index: nghttp2-1.69.0/src/shrpx_http3_upstream.cc =================================================================== --- nghttp2-1.69.0.orig/src/shrpx_http3_upstream.cc +++ nghttp2-1.69.0/src/shrpx_http3_upstream.cc @@ -2224,6 +2224,14 @@ int Http3Upstream::http_end_request_head return 0; } + if (method_token == HTTP_CONNECT && content_length) { + if (log_enabled(INFO)) { + Log{INFO, this} << "content-length are not allowed in CONNECT request"; + } + + return error_reply(downstream, 400); + } + auto faddr = handler_->get_upstream_addr(); auto config = get_config(); Index: nghttp2-1.69.0/src/shrpx_http_downstream_connection.cc =================================================================== --- nghttp2-1.69.0.orig/src/shrpx_http_downstream_connection.cc +++ nghttp2-1.69.0/src/shrpx_http_downstream_connection.cc @@ -712,6 +712,34 @@ int HttpDownstreamConnection::push_reque return 0; } +bool HttpDownstreamConnection::should_block_request_body() const { + const auto &req = downstream_->request(); + + return !downstream_->get_request_header_sent() || + (req.upgrade_request && !downstream_->get_upgraded()); +} + +bool HttpDownstreamConnection::should_unblock_request_body_before_response() + const { + const auto &req = downstream_->request(); + + return !req.upgrade_request; +} + +void HttpDownstreamConnection::process_blocked_request_buf_on_response() { + if (blocked_request_buf_processed_) { + return; + } + + process_blocked_request_buf(); + + auto buf = downstream_->get_blocked_request_buf(); + buf->reset(); + blocked_request_buf_processed_ = true; + + signal_write(); +} + int HttpDownstreamConnection::process_blocked_request_buf() { auto src = downstream_->get_blocked_request_buf(); @@ -741,7 +769,7 @@ int HttpDownstreamConnection::process_bl int HttpDownstreamConnection::push_upload_data_chunk( std::span<const uint8_t> data) { - if (!downstream_->get_request_header_sent()) { + if (should_block_request_body()) { auto output = downstream_->get_blocked_request_buf(); auto &req = downstream_->request(); output->append(data); @@ -773,7 +801,7 @@ int HttpDownstreamConnection::push_uploa } int HttpDownstreamConnection::end_upload_data() { - if (!downstream_->get_request_header_sent()) { + if (should_block_request_body()) { downstream_->set_blocked_request_data_eof(true); if (request_header_written_) { signal_write(); @@ -974,6 +1002,11 @@ int htp_hdrs_completecb(llhttp_t *htp) { // upgrade succeeded, 101 response is treated as final in nghttpx. downstream->check_upgrade_fulfilled_http1(); + if (req.method == HTTP_CONNECT && resp.http_status / 100 == 2 && + !downstream->get_upgraded()) { + resp.http_status = 502; + } + if (downstream->get_non_final_response()) { // Reset content-length because we reuse same Downstream for the // next response. @@ -995,7 +1028,7 @@ int htp_hdrs_completecb(llhttp_t *htp) { downstream->set_response_state(DownstreamState::HEADER_COMPLETE); downstream->inspect_http1_response(); - if (htp->flags & F_CHUNKED) { + if (!downstream->get_upgraded() && (htp->flags & F_CHUNKED)) { downstream->set_chunked_response(true); } @@ -1010,13 +1043,22 @@ int htp_hdrs_completecb(llhttp_t *htp) { resp.connection_close = true; // transfer-encoding not applied to upgraded connection downstream->set_chunked_response(false); - } else if (http2::legacy_http1(req.http_major, req.http_minor)) { - if (resp.fs.content_length == -1) { + + static_cast<HttpDownstreamConnection *>(dconn) + ->process_blocked_request_buf_on_response(); + } else { + if (req.upgrade_request) { resp.connection_close = true; } - downstream->set_chunked_response(false); - } else if (!downstream->expect_response_body()) { - downstream->set_chunked_response(false); + + if (http2::legacy_http1(req.http_major, req.http_minor)) { + if (resp.fs.content_length == -1) { + resp.connection_close = true; + } + downstream->set_chunked_response(false); + } else if (!downstream->expect_response_body()) { + downstream->set_chunked_response(false); + } } if (loggingconf.access.write_early && downstream->accesslog_ready()) { @@ -1194,7 +1236,10 @@ int htp_msg_completecb(llhttp_t *htp) { int HttpDownstreamConnection::write_first() { int rv; - process_blocked_request_buf(); + auto should_unblock_req_body = should_unblock_request_body_before_response(); + if (should_unblock_req_body) { + process_blocked_request_buf(); + } if (conn_.tls.ssl) { rv = write_tls(); @@ -1215,8 +1260,11 @@ int HttpDownstreamConnection::write_firs first_write_done_ = true; downstream_->set_request_header_sent(true); - auto buf = downstream_->get_blocked_request_buf(); - buf->reset(); + if (should_unblock_req_body) { + auto buf = downstream_->get_blocked_request_buf(); + buf->reset(); + blocked_request_buf_processed_ = true; + } // upstream->resume_read() might be called in // write_tls()/write_clear(), but before blocked_request_buf_ is Index: nghttp2-1.69.0/src/shrpx_http_downstream_connection.h =================================================================== --- nghttp2-1.69.0.orig/src/shrpx_http_downstream_connection.h +++ nghttp2-1.69.0/src/shrpx_http_downstream_connection.h @@ -91,6 +91,9 @@ public: int noop(); int process_blocked_request_buf(); + void process_blocked_request_buf_on_response(); + bool should_unblock_request_body_before_response() const; + bool should_block_request_body() const; private: Connection conn_; @@ -117,6 +120,8 @@ private: bool reusable_; // true if request header is written to request buffer. bool request_header_written_; + // true if blocked request buffer has been processed. + bool blocked_request_buf_processed_; }; } // namespace shrpx Index: nghttp2-1.69.0/src/shrpx_https_upstream.cc =================================================================== --- nghttp2-1.69.0.orig/src/shrpx_https_upstream.cc +++ nghttp2-1.69.0/src/shrpx_https_upstream.cc @@ -402,6 +402,17 @@ int htp_hdrs_completecb(llhttp_t *htp) { downstream->inspect_http1_request(); + if ((req.upgrade_request || llhttp_get_upgrade(htp)) && + (req.fs.header(http2::HD_TRANSFER_ENCODING) || + req.fs.header(http2::HD_CONTENT_LENGTH))) { + if (log_enabled(INFO)) { + Log{INFO, upstream} << "transfer-encoding and content-length are not " + "allowed in CONNECT or upgrade request"; + } + + return -1; + } + if (htp->flags & F_CHUNKED) { downstream->set_chunked_request(true); } @@ -554,6 +565,16 @@ int htp_bodycb(llhttp_t *htp, const char int rv; auto upstream = static_cast<HttpsUpstream *>(htp->data); auto downstream = upstream->get_downstream(); + const auto &req = downstream->request(); + + if (req.upgrade_request || llhttp_get_upgrade(htp)) { + if (log_enabled(INFO)) { + Log{INFO, upstream} << "Request body for Upgrade request is not allowed"; + } + + return HPE_USER; + } + rv = downstream->push_upload_data_chunk(as_uint8_span(std::span{data, len})); if (rv != 0) { // Ignore error if response has been completed. We will end up in @@ -586,6 +607,7 @@ int htp_msg_completecb(llhttp_t *htp) { } downstream->set_request_state(DownstreamState::MSG_COMPLETE); + req.http1_msg_complete = true; rv = downstream->end_upload_data(); if (rv != 0) { if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) { @@ -626,7 +648,8 @@ int HttpsUpstream::on_read() { // downstream can be nullptr here, because it is initialized in the // callback chain called by llhttp_execute() - if (downstream && downstream->get_upgraded()) { + if (downstream && downstream->request().http1_msg_complete && + downstream->get_upgraded()) { auto rv = downstream->push_upload_data_chunk(rb->peek()); if (rv != 0) { @@ -699,9 +722,13 @@ int HttpsUpstream::on_read() { if (htperr != HPE_OK) { if (log_enabled(INFO)) { - Log{INFO, this} << "HTTP parse failure: " - << "(" << llhttp_errno_name(htperr) << ") " - << llhttp_get_error_reason(&htp_); + if (htperr == HPE_USER) { + Log{INFO, this} << "HTTP callback error"; + } else { + Log{INFO, this} << "HTTP parse failure: " + << "(" << llhttp_errno_name(htperr) << ") " + << llhttp_get_error_reason(&htp_); + } } if (downstream &&
