From a5394113e51ecaf7c1def5708bbcfb6635597cad Mon Sep 17 00:00:00 2001 From: Azure Linux Security Servicing Account Date: Mon, 6 Jul 2026 17:24:32 +0000 Subject: [PATCH 1/2] Patch rabbitmq-server for CVE-2026-43966 --- SPECS/rabbitmq-server/CVE-2026-43966.patch | 133 +++++++++++++++++++++ SPECS/rabbitmq-server/rabbitmq-server.spec | 6 +- 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 SPECS/rabbitmq-server/CVE-2026-43966.patch diff --git a/SPECS/rabbitmq-server/CVE-2026-43966.patch b/SPECS/rabbitmq-server/CVE-2026-43966.patch new file mode 100644 index 00000000000..fca1cf29874 --- /dev/null +++ b/SPECS/rabbitmq-server/CVE-2026-43966.patch @@ -0,0 +1,133 @@ +From 3d0d824a2f42c9eca5fa057932a8c02b64f33d7f Mon Sep 17 00:00:00 2001 +From: AllSpark +Date: Mon, 6 Jul 2026 17:17:58 +0000 +Subject: [PATCH] Add invalid_response_headers HTTP/1 option + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: AI Backport of https://github.com/ninenines/cowboy/commit/f77cb9b5e730e300fffb551db1ba5d1c4ed878ef.patch https://github.com/ninenines/gun/commit/4f35609eb37109b106a863fc9ba83d7ee64e3e42.patch +--- + deps/cowboy/src/cowboy_http.erl | 66 +++++++++++++++++++++++++++++++++ + 1 file changed, 66 insertions(+) + +diff --git a/deps/cowboy/src/cowboy_http.erl b/deps/cowboy/src/cowboy_http.erl +index ee1e725..a3bfd5b 100644 +--- a/deps/cowboy/src/cowboy_http.erl ++++ b/deps/cowboy/src/cowboy_http.erl +@@ -31,6 +31,7 @@ + idle_timeout => timeout(), + inactivity_timeout => timeout(), + initial_stream_flow_size => non_neg_integer(), ++ invalid_response_headers => error_terminate | ignore, + linger_timeout => timeout(), + logger => module(), + max_authority_length => non_neg_integer(), +@@ -1043,6 +1044,14 @@ commands(State, StreamID, [{error_response, _, _, _}|Tail]) -> + %% Send an informational response. + commands(State0=#state{socket=Socket, transport=Transport, out_state=wait, streams=Streams}, + StreamID, [{inform, StatusCode, Headers}|Tail]) -> ++ case maybe_invalid_response_headers(Headers, State0) of ++ error_terminate -> ++ Reason = {internal_error, invalid_response_header, ++ 'An invalid response header was detected in an informational response.'}, ++ terminate(stream_terminate(State0, StreamID, Reason), Reason); ++ ok -> ++ ok ++ end, + %% @todo I'm pretty sure the last stream in the list is the one we want + %% considering all others are queued. + #stream{version=Version} = lists:keyfind(StreamID, #stream.id, Streams), +@@ -1063,6 +1072,14 @@ commands(State0=#state{socket=Socket, transport=Transport, out_state=wait, strea + %% @todo Same two things above apply to DATA, possibly promise too. + commands(State0=#state{socket=Socket, transport=Transport, out_state=wait, streams=Streams}, StreamID, + [{response, StatusCode, Headers0, Body}|Tail]) -> ++ case maybe_invalid_response_headers(Headers0, State0) of ++ error_terminate -> ++ Reason = {internal_error, invalid_response_header, ++ 'An invalid response header was detected.'}, ++ terminate(stream_terminate(State0, StreamID, Reason), Reason); ++ ok -> ++ ok ++ end, + %% @todo I'm pretty sure the last stream in the list is the one we want + %% considering all others are queued. + #stream{version=Version} = lists:keyfind(StreamID, #stream.id, Streams), +@@ -1084,6 +1101,14 @@ commands(State0=#state{socket=Socket, transport=Transport, out_state=wait, strea + commands(State0=#state{socket=Socket, transport=Transport, + opts=Opts, overriden_opts=Override, streams=Streams0, out_state=OutState}, + StreamID, [{headers, StatusCode, Headers0}|Tail]) -> ++ case maybe_invalid_response_headers(Headers0, State0) of ++ error_terminate -> ++ Reason = {internal_error, invalid_response_header, ++ 'An invalid response header was detected.'}, ++ terminate(stream_terminate(State0, StreamID, Reason), Reason); ++ ok -> ++ ok ++ end, + %% @todo Same as above (about the last stream in the list). + Stream = #stream{version=Version} = lists:keyfind(StreamID, #stream.id, Streams0), + Status = cow_http:status_to_integer(StatusCode), +@@ -1188,6 +1213,16 @@ commands(State0=#state{socket=Socket, transport=Transport, streams=Streams0, out + commands(State#state{streams=Streams}, StreamID, Tail); + commands(State0=#state{socket=Socket, transport=Transport, streams=Streams, out_state=OutState}, + StreamID, [{trailers, Trailers}|Tail]) -> ++ case maybe_invalid_response_headers(Trailers, State0) of ++ error_terminate -> ++ %% When there are invalid trailer headers the only thing ++ %% we can do is drop the connection. ++ Reason = {internal_error, invalid_response_header, ++ 'An invalid response header was detected in trailers.'}, ++ terminate(State0, Reason); ++ ok -> ++ ok ++ end, + case stream_te(OutState, lists:keyfind(StreamID, #stream.id, Streams)) of + trailers -> + ok = maybe_socket_error(State0, +@@ -1209,6 +1244,14 @@ commands(State0=#state{socket=Socket, transport=Transport, streams=Streams, out_ + commands(State0=#state{ref=Ref, parent=Parent, socket=Socket, transport=Transport, + out_state=OutState, opts=Opts, buffer=Buffer, children=Children}, StreamID, + [{switch_protocol, Headers, Protocol, InitialState}|_Tail]) -> ++ case maybe_invalid_response_headers(Headers, State0) of ++ error_terminate -> ++ Reason = {internal_error, invalid_response_header, ++ 'An invalid response header was detected when switching protocol.'}, ++ terminate(stream_terminate(State0, StreamID, Reason), Reason); ++ ok -> ++ ok ++ end, + %% @todo If there's streams opened after this one, fail instead of 101. + State1 = cancel_timeout(State0), + %% Before we send the 101 response we need to stop receiving data +@@ -1267,6 +1310,29 @@ commands(State=#state{opts=Opts}, StreamID, [Log={log, _, _, _}|Tail]) -> + commands(State, StreamID, [{push, _, _, _, _, _, _, _}|Tail]) -> + commands(State, StreamID, Tail). + ++maybe_invalid_response_headers(Headers, #state{opts=Opts}) -> ++ case maps:get(invalid_response_headers, Opts, error_terminate) of ++ error_terminate -> ++ It = maps:iterator(Headers), ++ maybe_invalid_response_headers(maps:next(It)); ++ ignore -> ++ ok ++ end. ++ ++maybe_invalid_response_headers({_, V, It}) when is_binary(V) -> ++ case binary:match(V, [<<$\r>>, <<$\n>>]) of ++ nomatch -> ++ maybe_invalid_response_headers(maps:next(It)); ++ _ -> ++ error_terminate ++ end; ++maybe_invalid_response_headers({K, V, It}) -> ++ %% We build a temporary binary for simplicity's sake ++ %% when we have a list/iolist. ++ maybe_invalid_response_headers({K, iolist_to_binary(V), It}); ++maybe_invalid_response_headers(none) -> ++ ok. ++ + %% The set-cookie header is special; we can only send one cookie per header. + headers_to_list(Headers0=#{<<"set-cookie">> := SetCookies}) -> + Headers1 = maps:to_list(maps:remove(<<"set-cookie">>, Headers0)), +-- +2.45.4 + diff --git a/SPECS/rabbitmq-server/rabbitmq-server.spec b/SPECS/rabbitmq-server/rabbitmq-server.spec index a684828ce07..224d0e48c66 100644 --- a/SPECS/rabbitmq-server/rabbitmq-server.spec +++ b/SPECS/rabbitmq-server/rabbitmq-server.spec @@ -2,7 +2,7 @@ Summary: rabbitmq-server Name: rabbitmq-server Version: 3.13.7 -Release: 6%{?dist} +Release: 7%{?dist} License: Apache-2.0 and MPL 2.0 Vendor: Microsoft Corporation Distribution: Azure Linux @@ -15,6 +15,7 @@ Patch2: CVE-2026-8466.patch Patch3: CVE-2026-43968.patch Patch4: CVE-2026-7790.patch Patch5: CVE-2026-43973.patch +Patch6: CVE-2026-43966.patch BuildRequires: elixir BuildRequires: erlang @@ -71,6 +72,9 @@ done %{_libdir}/rabbitmq/lib/rabbitmq_server-%{version}/* %changelog +* Mon Jul 06 2026 Azure Linux Security Servicing Account - 3.13.7-7 +- Patch for CVE-2026-43966 + * Wed Jun 17 2026 Azure Linux Security Servicing Account - 3.13.7-6 - Patch for CVE-2026-43973 From 8606c0283bb464c2409a51828407946dd2e4a7b5 Mon Sep 17 00:00:00 2001 From: Swapnil Sahu Date: Thu, 9 Jul 2026 00:32:07 +0000 Subject: [PATCH 2/2] backported patch manually Signed-off-by: Swapnil Sahu --- SPECS/rabbitmq-server/CVE-2026-43966.patch | 112 ++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/SPECS/rabbitmq-server/CVE-2026-43966.patch b/SPECS/rabbitmq-server/CVE-2026-43966.patch index fca1cf29874..79902fcd2cf 100644 --- a/SPECS/rabbitmq-server/CVE-2026-43966.patch +++ b/SPECS/rabbitmq-server/CVE-2026-43966.patch @@ -3,11 +3,16 @@ From: AllSpark Date: Mon, 6 Jul 2026 17:17:58 +0000 Subject: [PATCH] Add invalid_response_headers HTTP/1 option +Modified to apply to azurelinux. + Signed-off-by: Azure Linux Security Servicing Account + Upstream-reference: AI Backport of https://github.com/ninenines/cowboy/commit/f77cb9b5e730e300fffb551db1ba5d1c4ed878ef.patch https://github.com/ninenines/gun/commit/4f35609eb37109b106a863fc9ba83d7ee64e3e42.patch + --- deps/cowboy/src/cowboy_http.erl | 66 +++++++++++++++++++++++++++++++++ - 1 file changed, 66 insertions(+) + deps/gun/src/gun.erl | 56 +++++++++++++++++++++++++--- + 2 files changed, 116 insertions(+), 6 deletions(-) diff --git a/deps/cowboy/src/cowboy_http.erl b/deps/cowboy/src/cowboy_http.erl index ee1e725..a3bfd5b 100644 @@ -128,6 +133,111 @@ index ee1e725..a3bfd5b 100644 %% The set-cookie header is special; we can only send one cookie per header. headers_to_list(Headers0=#{<<"set-cookie">> := SetCookies}) -> Headers1 = maps:to_list(maps:remove(<<"set-cookie">>, Headers0)), +diff --git a/deps/gun/src/gun.erl b/deps/gun/src/gun.erl +index b2e2196..791f366 100644 +--- a/deps/gun/src/gun.erl ++++ b/deps/gun/src/gun.erl +@@ -138,6 +138,7 @@ + %% This is of course not required for HTTP/1.1 since the CONNECT takes over + %% the entire connection. + -type req_opts() :: #{ ++ invalid_request_headers => raise | ignore, + reply_to => pid() + }. + -export_type([req_opts/0]). +@@ -159,6 +160,7 @@ + %% @todo keepalive + -type ws_opts() :: #{ + compress => boolean(), ++ invalid_request_headers => raise | ignore, + protocols => [{binary(), module()}] + }. + -export_type([ws_opts/0]). +@@ -411,11 +413,46 @@ request(ServerPid, Method, Path, Headers, Body) -> + %% @todo Accept header names as maps. + -spec request(pid(), iodata(), iodata(), headers(), iodata(), req_opts()) -> reference(). + request(ServerPid, Method, Path, Headers, Body, ReqOpts) -> ++ NormHeaders = normalize_headers(Headers), ++ maybe_invalid_request_headers(NormHeaders, ReqOpts), + StreamRef = make_ref(), + ReplyTo = maps:get(reply_to, ReqOpts, self()), +- _ = ServerPid ! {request, ReplyTo, StreamRef, Method, Path, Headers, Body}, ++ _ = ServerPid ! {request, ReplyTo, StreamRef, Method, Path, NormHeaders, Body}, + StreamRef. + ++normalize_headers([]) -> ++ []; ++normalize_headers([{Name, Value}|Tail]) when is_binary(Name) -> ++ [{string:lowercase(Name), Value}|normalize_headers(Tail)]; ++normalize_headers([{Name, Value}|Tail]) when is_list(Name) -> ++ [{string:lowercase(unicode:characters_to_binary(Name)), Value}|normalize_headers(Tail)]; ++normalize_headers([{Name, Value}|Tail]) when is_atom(Name) -> ++ [{string:lowercase(atom_to_binary(Name, latin1)), Value}|normalize_headers(Tail)]; ++normalize_headers(Headers) when is_map(Headers) -> ++ normalize_headers(maps:to_list(Headers)). ++ ++maybe_invalid_request_headers(Headers, ReqOpts) -> ++ case maps:get(invalid_request_headers, ReqOpts, raise) of ++ raise -> ++ case maybe_invalid_request_headers(Headers) of ++ ok -> ++ ok; ++ {error, Name} -> ++ error({invalid_request_header, Name, ++ "An invalid request header was detected."}) ++ end; ++ ignore -> ++ ok ++ end. ++ ++maybe_invalid_request_headers([{Name, Value}|Tail]) -> ++ case binary:match(iolist_to_binary(Value), [<<$\r>>, <<$\n>>]) of ++ nomatch -> maybe_invalid_request_headers(Tail); ++ _ -> {error, Name} ++ end; ++maybe_invalid_request_headers([]) -> ++ ok. ++ + %% Streaming data. + + -spec data(pid(), reference(), fin | nofin, iodata()) -> ok. +@@ -434,7 +471,9 @@ connect(ServerPid, Destination, Headers) -> + connect(ServerPid, Destination, Headers, #{}). + + -spec connect(pid(), connect_destination(), headers(), req_opts()) -> reference(). +-connect(ServerPid, Destination, Headers, ReqOpts) -> ++connect(ServerPid, Destination, Headers0, ReqOpts) -> ++ Headers = normalize_headers(Headers0), ++ maybe_invalid_request_headers(Headers, ReqOpts), + StreamRef = make_ref(), + ReplyTo = maps:get(reply_to, ReqOpts, self()), + _ = ServerPid ! {connect, ReplyTo, StreamRef, Destination, Headers}, +@@ -621,16 +660,21 @@ ws_upgrade(ServerPid, Path) -> + ws_upgrade(ServerPid, Path, []). + + -spec ws_upgrade(pid(), iodata(), headers()) -> reference(). +-ws_upgrade(ServerPid, Path, Headers) -> ++ws_upgrade(ServerPid, Path, Headers0) -> ++ Headers = normalize_headers(Headers0), ++ maybe_invalid_request_headers(Headers, #{invalid_request_headers => raise}), + StreamRef = make_ref(), + _ = ServerPid ! {ws_upgrade, self(), StreamRef, Path, Headers}, + StreamRef. + + -spec ws_upgrade(pid(), iodata(), headers(), ws_opts()) -> reference(). +-ws_upgrade(ServerPid, Path, Headers, Opts) -> +- ok = gun_ws:check_options(Opts), ++ws_upgrade(ServerPid, Path, Headers0, WsOpts0) -> ++ Headers = normalize_headers(Headers0), ++ maybe_invalid_request_headers(Headers, WsOpts0), ++ WsOpts = maps:without([invalid_request_headers], WsOpts0), ++ ok = gun_ws:check_options(WsOpts), + StreamRef = make_ref(), +- _ = ServerPid ! {ws_upgrade, self(), StreamRef, Path, Headers, Opts}, ++ _ = ServerPid ! {ws_upgrade, self(), StreamRef, Path, Headers, WsOpts}, + StreamRef. + + %% @todo ws_send/2 will need to be deprecated in favor of a variant with StreamRef. -- 2.45.4