diff --git a/SPECS/rabbitmq-server/CVE-2026-43968.patch b/SPECS/rabbitmq-server/CVE-2026-43968.patch new file mode 100644 index 00000000000..810523f10cb --- /dev/null +++ b/SPECS/rabbitmq-server/CVE-2026-43968.patch @@ -0,0 +1,111 @@ +From df89c6e2e6924b0820467e61bea252486e9baacd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= +Date: Mon, 11 May 2026 12:15:58 +0200 +Subject: [PATCH] Make building SSE events more closely match the spec + +Also add many more tests. + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/ninenines/cowlib/commit/6165fc40efa159ba1cceee7e7981e790acba5d9c.patch +--- + deps/cowlib/src/cow_sse.erl | 64 +++++++++++++++++++++++++++++++++++-- + 1 file changed, 61 insertions(+), 3 deletions(-) + +diff --git a/deps/cowlib/src/cow_sse.erl b/deps/cowlib/src/cow_sse.erl +index 6e7081f..3503089 100644 +--- a/deps/cowlib/src/cow_sse.erl ++++ b/deps/cowlib/src/cow_sse.erl +@@ -301,7 +301,8 @@ event_comment(_) -> + []. + + event_id(#{id := ID}) -> +- nomatch = binary:match(iolist_to_binary(ID), <<"\n">>), ++ nomatch = binary:match(iolist_to_binary(ID), ++ [<<"\r\n">>, <<"\r">>, <<"\n">>]), + [<<"id: ">>, ID, $\n]; + event_id(_) -> + []. +@@ -311,7 +312,8 @@ event_name(#{event := Name0}) -> + is_atom(Name0) -> atom_to_binary(Name0, utf8); + true -> iolist_to_binary(Name0) + end, +- nomatch = binary:match(Name, <<"\n">>), ++ nomatch = binary:match(Name, ++ [<<"\r\n">>, <<"\r">>, <<"\n">>]), + [<<"event: ">>, Name, $\n]; + event_name(_) -> + []. +@@ -327,7 +329,8 @@ event_retry(_) -> + []. + + prefix_lines(IoData, Prefix) -> +- Lines = binary:split(iolist_to_binary(IoData), <<"\n">>, [global]), ++ Lines = binary:split(iolist_to_binary(IoData), ++ [<<"\r\n">>, <<"\r">>, <<"\n">>], [global]), + [[Prefix, <<": ">>, Line, $\n] || Line <- Lines]. + + -ifdef(TEST). +@@ -345,5 +348,60 @@ event_test() -> + _ = event(#{retry => 5000}), + _ = event(#{event => "test", data => "test"}), + _ = event(#{id => "test", event => "test", data => "test"}), ++ _ = event(#{data => "test\r\ntest"}), ++ _ = event(#{data => "test\rtest\r"}), ++ _ = event(#{data => "test\ntest"}), + ok. ++ ++event_error_test() -> ++ {'EXIT', _} = (catch event(#{id => "test\n"})), ++ {'EXIT', _} = (catch event(#{id => "test\r"})), ++ {'EXIT', _} = (catch event(#{id => "test\r\n"})), ++ {'EXIT', _} = (catch event(#{event => "test\n"})), ++ {'EXIT', _} = (catch event(#{event => "test\r"})), ++ {'EXIT', _} = (catch event(#{event => "test\r\n"})), ++ ok. ++ ++identity_test_() -> ++ Tests = [ ++ #{data => <<"hello">>}, ++ #{event => <<"update">>, data => <<"hello">>}, ++ #{id => <<"42">>, data => <<"hello">>}, ++ #{data => <<"a\nb">>}, ++ #{data => <<"multi\nline\ndata">>}, ++ #{event => <<"update">>, data => <<"hello">>}, ++ #{id => <<"abc">>, data => <<"x">>}, ++ #{comment => <<"c1">>, data => <<"d1">>, event => <<"e1">>, id => <<"i1">>}, ++ #{data => <<>>}, ++ #{data => <<"data with trailing newline\n">>}, ++ #{data => <<"\n">>}, ++ #{data => <<"\n\n">>}, ++ #{data => <<"">>, id => <<"1">>}, ++ #{data => <<"z">>}, ++ #{id => <<"17">>}, ++ #{data => << <<$a>> || _ <- lists:seq(1,200) >>}, ++ #{data => <<"こんにちは世界">>}, ++ #{retry => 30000, data => <<"reconnect">>} ++ ], ++ [{lists:flatten(io_lib:format("~0p", [V])), ++ fun() -> true = do_identity_result(V) =:= do_identity_build_parse(V) end} ++ || V <- Tests]. ++ ++do_identity_build_parse(Event) -> ++ {event, Parsed, _} = parse(iolist_to_binary(event(Event)), init()), ++ case Parsed of ++ #{data := Data} -> Parsed#{data => iolist_to_binary(Data)}; ++ _ -> Parsed ++ end. ++ ++do_identity_result(E=#{id := ID}) when map_size(E) =:= 1 -> ++ #{ ++ last_event_id => ID ++ }; ++do_identity_result(Event) -> ++ #{ ++ event_type => maps:get(event, Event, <<"message">>), ++ data => maps:get(data, Event, <<>>), ++ last_event_id => maps:get(id, Event, <<>>) ++ }. + -endif. +-- +2.45.4 + diff --git a/SPECS/rabbitmq-server/CVE-2026-7790.patch b/SPECS/rabbitmq-server/CVE-2026-7790.patch new file mode 100644 index 00000000000..ffe3d9fd16b --- /dev/null +++ b/SPECS/rabbitmq-server/CVE-2026-7790.patch @@ -0,0 +1,136 @@ +From d83b148d75a76db9a42b6c0dc50526a8d5b0ba28 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= +Date: Mon, 11 May 2026 10:57:28 +0200 +Subject: [PATCH] Limit length of transfer-encoding: chunked chunks + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/ninenines/cowlib/commit/a4b8039ce8c93ab00867ef6b7e888822c09f4369.patch +--- + deps/cowlib/src/cow_http_te.erl | 78 +++++++++++++++++---------------- + 1 file changed, 40 insertions(+), 38 deletions(-) + +diff --git a/deps/cowlib/src/cow_http_te.erl b/deps/cowlib/src/cow_http_te.erl +index e3473cf..c78b5db 100644 +--- a/deps/cowlib/src/cow_http_te.erl ++++ b/deps/cowlib/src/cow_http_te.erl +@@ -138,7 +138,7 @@ stream_chunked(Data, State) -> + + %% New chunk. + stream_chunked(Data = << C, _/bits >>, {0, Streamed}, Acc) when C =/= $\r -> +- case chunked_len(Data, Streamed, Acc, 0) of ++ case chunked_len(Data, Streamed, Acc, 0, 0) of + {next, Rest, State, Acc2} -> + stream_chunked(Rest, State, Acc2); + {more, State, Acc2} -> +@@ -174,54 +174,54 @@ stream_chunked(Data, {Rem, Streamed}, Acc) when Rem > 2 -> + {more, << Acc/binary, Data/binary >>, Rem2, {Rem2, Streamed + DataSize}} + end. + +-chunked_len(<< $0, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16); +-chunked_len(<< $1, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 1); +-chunked_len(<< $2, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 2); +-chunked_len(<< $3, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 3); +-chunked_len(<< $4, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 4); +-chunked_len(<< $5, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 5); +-chunked_len(<< $6, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 6); +-chunked_len(<< $7, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 7); +-chunked_len(<< $8, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 8); +-chunked_len(<< $9, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 9); +-chunked_len(<< $A, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 10); +-chunked_len(<< $B, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 11); +-chunked_len(<< $C, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 12); +-chunked_len(<< $D, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 13); +-chunked_len(<< $E, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 14); +-chunked_len(<< $F, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 15); +-chunked_len(<< $a, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 10); +-chunked_len(<< $b, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 11); +-chunked_len(<< $c, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 12); +-chunked_len(<< $d, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 13); +-chunked_len(<< $e, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 14); +-chunked_len(<< $f, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 15); ++chunked_len(<< $0, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16, D + 1); ++chunked_len(<< $1, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 1, D + 1); ++chunked_len(<< $2, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 2, D + 1); ++chunked_len(<< $3, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 3, D + 1); ++chunked_len(<< $4, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 4, D + 1); ++chunked_len(<< $5, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 5, D + 1); ++chunked_len(<< $6, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 6, D + 1); ++chunked_len(<< $7, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 7, D + 1); ++chunked_len(<< $8, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 8, D + 1); ++chunked_len(<< $9, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 9, D + 1); ++chunked_len(<< $A, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 10, D + 1); ++chunked_len(<< $B, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 11, D + 1); ++chunked_len(<< $C, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 12, D + 1); ++chunked_len(<< $D, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 13, D + 1); ++chunked_len(<< $E, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 14, D + 1); ++chunked_len(<< $F, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 15, D + 1); ++chunked_len(<< $a, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 10, D + 1); ++chunked_len(<< $b, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 11, D + 1); ++chunked_len(<< $c, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 12, D + 1); ++chunked_len(<< $d, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 13, D + 1); ++chunked_len(<< $e, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 14, D + 1); ++chunked_len(<< $f, R/bits >>, S, A, Len, D) when D < 16 -> chunked_len(R, S, A, Len * 16 + 15, D + 1); + %% Chunk extensions. + %% + %% Note that we currently skip the first character we encounter here, + %% and not in the skip_chunk_ext function. If we latter implement + %% chunk extensions (unlikely) we will need to change this clause too. +-chunked_len(<< C, R/bits >>, S, A, Len) when ?IS_WS(C); C =:= $; -> skip_chunk_ext(R, S, A, Len, 0); ++chunked_len(<< C, R/bits >>, S, A, Len, _) when ?IS_WS(C); C =:= $; -> skip_chunk_ext(R, S, A, Len, 0); + %% Final chunk. + %% + %% When trailers are following we simply return them as the Rest. + %% Then the user code can decide to call the stream_trailers function + %% to parse them. The user can therefore ignore trailers as necessary + %% if they do not wish to handle them. +-chunked_len(<< "\r\n\r\n", R/bits >>, _, <<>>, 0) -> {done, no_trailers, R}; +-chunked_len(<< "\r\n\r\n", R/bits >>, _, A, 0) -> {done, A, no_trailers, R}; +-chunked_len(<< "\r\n", R/bits >>, _, <<>>, 0) when byte_size(R) > 2 -> {done, trailers, R}; +-chunked_len(<< "\r\n", R/bits >>, _, A, 0) when byte_size(R) > 2 -> {done, A, trailers, R}; +-chunked_len(_, _, _, 0) -> more; ++chunked_len(<< "\r\n\r\n", R/bits >>, _, <<>>, 0, _) -> {done, no_trailers, R}; ++chunked_len(<< "\r\n\r\n", R/bits >>, _, A, 0, _) -> {done, A, no_trailers, R}; ++chunked_len(<< "\r\n", R/bits >>, _, <<>>, 0, _) when byte_size(R) > 2 -> {done, trailers, R}; ++chunked_len(<< "\r\n", R/bits >>, _, A, 0, _) when byte_size(R) > 2 -> {done, A, trailers, R}; ++chunked_len(_, _, _, 0, _) -> more; + %% Normal chunk. Add 2 to Len for the trailing \r\n. +-chunked_len(<< "\r\n", R/bits >>, S, A, Len) -> {next, R, {Len + 2, S}, A}; +-chunked_len(<<"\r">>, _, <<>>, _) -> more; +-chunked_len(<<"\r">>, S, A, _) -> {more, {0, S}, A}; +-chunked_len(<<>>, _, <<>>, _) -> more; +-chunked_len(<<>>, S, A, _) -> {more, {0, S}, A}. +- +-skip_chunk_ext(R = << "\r", _/bits >>, S, A, Len, _) -> chunked_len(R, S, A, Len); +-skip_chunk_ext(R = <<>>, S, A, Len, _) -> chunked_len(R, S, A, Len); ++chunked_len(<< "\r\n", R/bits >>, S, A, Len, _) -> {next, R, {Len + 2, S}, A}; ++chunked_len(<<"\r">>, _, <<>>, _, _) -> more; ++chunked_len(<<"\r">>, S, A, _, _) -> {more, {0, S}, A}; ++chunked_len(<<>>, _, <<>>, _, _) -> more; ++chunked_len(<<>>, S, A, _, _) -> {more, {0, S}, A}. ++ ++skip_chunk_ext(R = << "\r", _/bits >>, S, A, Len, _) -> chunked_len(R, S, A, Len, 0); ++skip_chunk_ext(R = <<>>, S, A, Len, _) -> chunked_len(R, S, A, Len, 0); + %% We skip up to 128 characters of chunk extensions. The value + %% is hardcoded: chunk extensions are very rarely seen in the + %% wild and Cowboy doesn't do anything with them anyway. +@@ -305,6 +305,7 @@ stream_chunked_n_passes_test() -> + {more, <<"abc">>, 2, {2, 3}} = stream_chunked(<<"\n3\r\nabc">>, {1, 0}), + {more, <<"abc">>, {1, 3}} = stream_chunked(<<"3\r\nabc\r">>, {0, 0}), + {more, <<"abc">>, <<"123">>, {0, 3}} = stream_chunked(<<"3\r\nabc\r\n123">>, {0, 0}), ++ {more, <<>>, 18446744073709551617, _} = stream_chunked(<<"FFFFFFFFFFFFFFFF\r\n">>, {0, 0}), + ok. + + stream_chunked_dripfeed_test() -> +@@ -339,7 +340,8 @@ stream_chunked_dripfeed2_test() -> + stream_chunked_error_test_() -> + Tests = [ + {<<>>, undefined}, +- {<<"\n\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">>, {2, 0}} ++ {<<"\n\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">>, {2, 0}}, ++ {<<"10000000000000000\r\n">>, {0, 0}} + ], + [{lists:flatten(io_lib:format("value ~p state ~p", [V, S])), + fun() -> {'EXIT', _} = (catch stream_chunked(V, S)) end} +-- +2.45.4 + diff --git a/SPECS/rabbitmq-server/rabbitmq-server.spec b/SPECS/rabbitmq-server/rabbitmq-server.spec index a201de79088..d2dd158f21e 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: 3%{?dist} +Release: 4%{?dist} License: Apache-2.0 and MPL 2.0 Vendor: Microsoft Corporation Distribution: Azure Linux @@ -11,6 +11,8 @@ URL: https://rabbitmq.com Source0: https://github.com/rabbitmq/%{name}/releases/download/v%{version}/%{name}-%{version}.tar.xz Patch0: CVE-2025-30219.patch Patch1: CVE-2025-50200.patch +Patch2: CVE-2026-43968.patch +Patch3: CVE-2026-7790.patch BuildRequires: elixir BuildRequires: erlang @@ -67,6 +69,9 @@ done %{_libdir}/rabbitmq/lib/rabbitmq_server-%{version}/* %changelog +* Fri May 15 2026 Azure Linux Security Servicing Account - 3.13.7-4 +- Patch for CVE-2026-7790, CVE-2026-43968 + * Wed Oct 29 2025 Azure Linux Security Servicing Account - 3.13.7-3 - Patch for CVE-2025-50200