Version in base suite: 6.78-1 Base version: libwww-perl_6.78-1 Target version: libwww-perl_6.78-1+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/libw/libwww-perl/libwww-perl_6.78-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/libw/libwww-perl/libwww-perl_6.78-1+deb13u1.dsc changelog | 9 patches/Refuse-https-http-downgrade-redirects-by-default.patch | 198 +++++ patches/Strip-Authorization-on-cross-origin-redirect-CVE-202.patch | 369 ++++++++++ patches/series | 2 4 files changed, 578 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpnw4ikhz8/libwww-perl_6.78-1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpnw4ikhz8/libwww-perl_6.78-1+deb13u1.dsc: no acceptable signature found diff -Nru libwww-perl-6.78/debian/changelog libwww-perl-6.78/debian/changelog --- libwww-perl-6.78/debian/changelog 2025-03-01 20:17:24.000000000 +0000 +++ libwww-perl-6.78/debian/changelog 2026-09-05 12:14:16.000000000 +0000 @@ -1,3 +1,12 @@ +libwww-perl (6.78-1+deb13u1) trixie; urgency=medium + + * Team upload. + * Strip Authorization on cross-origin redirect (CVE-2026-8368) + (Closes: #1136449) + * Refuse https->http downgrade redirects by default + + -- Salvatore Bonaccorso Sat, 05 Sep 2026 14:14:16 +0200 + libwww-perl (6.78-1) unstable; urgency=medium * Import upstream version 6.78. diff -Nru libwww-perl-6.78/debian/patches/Refuse-https-http-downgrade-redirects-by-default.patch libwww-perl-6.78/debian/patches/Refuse-https-http-downgrade-redirects-by-default.patch --- libwww-perl-6.78/debian/patches/Refuse-https-http-downgrade-redirects-by-default.patch 1970-01-01 00:00:00.000000000 +0000 +++ libwww-perl-6.78/debian/patches/Refuse-https-http-downgrade-redirects-by-default.patch 2026-09-05 12:14:16.000000000 +0000 @@ -0,0 +1,198 @@ +From: Olaf Alders +Date: Mon, 11 May 2026 23:40:37 +0000 +Subject: Refuse https->http downgrade redirects by default +Origin: https://github.com/libwww-perl/libwww-perl/commit/792a5dc7f2be5642bd7fd5a13205dc2bd87dfcc1 + +A 3xx redirect from an https request to a plain http URL leaks the +body of the next request and any caller-supplied headers other than +Authorization/Proxy-Authorization/Cookie (which are stripped +elsewhere) over plaintext. A caller who explicitly requested https +reasonably expects end-to-end TLS for the full transaction. + +This change returns the original 3xx response with a Client-Warning +header instead of following the redirect when the original URI is +https and the target is http. When allow_downgrade is true, the +redirect is followed; cross-origin credential stripping from the +previous commit still applies (scheme change is always cross-origin). + +Opt-out: allow_downgrade => 1 in the constructor, or +$ua->allow_downgrade(1) at runtime. + +Related defense-in-depth alongside the cross-origin credential strip +for CVE-2026-8368 (reported by Kai Zen). PoC and initial patch shape +by Stig Palmquist. + +Suggested-by: Stig Palmquist +Co-Authored-By: Claude Opus 4.7 +--- + Changes | 4 +++ + lib/LWP/UserAgent.pm | 37 ++++++++++++++++++++ + t/redirect-credential-leak.t | 49 +++++++++++++++++++++++++++ + xt/author/live/jigsaw/redirect-post.t | 3 +- + 4 files changed, 92 insertions(+), 1 deletion(-) + +diff --git a/lib/LWP/UserAgent.pm b/lib/LWP/UserAgent.pm +index 0483284f9d7b..450034e79692 100644 +--- a/lib/LWP/UserAgent.pm ++++ b/lib/LWP/UserAgent.pm +@@ -94,6 +94,7 @@ sub new + unless defined $cookie_jar_class; + + my $allow_credentialed_redirects = delete $cnf{allow_credentialed_redirects}; ++ my $allow_downgrade = delete $cnf{allow_downgrade}; + + # Actually ""s are just as good as 0's, but for concision we'll just say: + Carp::croak("protocols_allowed has to be an arrayref or 0, not \"$protocols_allowed\"!") +@@ -125,6 +126,7 @@ sub new + send_te => $send_te, + cookie_jar_class => $cookie_jar_class, + allow_credentialed_redirects => $allow_credentialed_redirects, ++ allow_downgrade => $allow_downgrade, + }, $class; + + $self->agent(defined($agent) ? $agent : $class->_agent) +@@ -393,6 +395,21 @@ sub request { + } + } + ++ # Refuse https->http downgrade by default. A caller who ++ # requested https reasonably expects end-to-end TLS; following ++ # a 3xx to plaintext leaks the body and remaining headers. ++ # Opt-out via allow_downgrade => 1. ++ my $orig_scheme = defined $request->uri->scheme ? $request->uri->scheme : q{}; ++ my $new_scheme = defined $referral->uri->scheme ? $referral->uri->scheme : q{}; ++ if ( $orig_scheme eq 'https' ++ && $new_scheme eq 'http' ++ && !$self->{allow_downgrade}) ++ { ++ $response->header("Client-Warning" => ++ "Refusing https->http redirect (set allow_downgrade => 1 to opt in)"); ++ return $response; ++ } ++ + return $response unless $self->redirect_ok($referral, $response); + return $self->request($referral, $arg, $size, $response); + +@@ -763,6 +780,7 @@ sub local_address{ shift->_elem('local_address',@_); } + sub max_size { shift->_elem('max_size', @_); } + sub max_redirect { shift->_elem('max_redirect', @_); } + sub allow_credentialed_redirects { shift->_elem('allow_credentialed_redirects', @_); } ++sub allow_downgrade { shift->_elem('allow_downgrade', @_); } + sub show_progress{ shift->_elem('show_progress', @_); } + sub send_te { shift->_elem('send_te', @_); } + +@@ -1351,6 +1369,7 @@ The following options correspond to attribute methods described below: + --------------------------- -------------------- + agent "libwww-perl/#.###" + allow_credentialed_redirects undef ++ allow_downgrade undef + conn_cache undef + cookie_jar undef + cookie_jar_class HTTP::Cookies +@@ -1377,6 +1396,11 @@ caller-supplied credentials to the redirect target. Set + C to a true value to opt out and + forward these headers across origins. + ++A 3xx redirect that downgrades an C request to plain C ++is refused by default; the original response is returned with a ++C header explaining the refusal. Set C ++to a true value to opt in to following such redirects. ++ + The following additional options are also accepted: If the C option + is passed in with a true value, then proxy settings are read from environment + variables (see L). If C isn't provided, the +@@ -1430,6 +1454,19 @@ host, or port). Defaults to a false value, meaning the headers are stripped + on cross-origin redirects to avoid leaking credentials to the redirect target. + Same-origin redirects always retain these headers. + ++=head2 allow_downgrade ++ ++ my $allow = $ua->allow_downgrade; ++ $ua->allow_downgrade( 1 ); ++ ++Get/set whether a 3xx redirect from an C request to a plain ++C URL is followed. Defaults to a false value, meaning such ++redirects are refused; the original response is returned with a ++C header. Set to a true value to opt in to following ++the redirect. Note that even when C is true, ++cross-origin credential stripping still applies (see ++L). ++ + =head2 conn_cache + + my $cache_obj = $ua->conn_cache; +diff --git a/t/redirect-credential-leak.t b/t/redirect-credential-leak.t +index 6d31555875e6..c535440ee3be 100644 +--- a/t/redirect-credential-leak.t ++++ b/t/redirect-credential-leak.t +@@ -180,4 +180,53 @@ subtest 'allow_credentialed_redirects opt-out via accessor' => sub { + 'Authorization forwarded after $ua->allow_credentialed_redirects(1)'); + }; + ++subtest 'https -> http downgrade is refused' => sub { ++ my $ua = Test::CapturingUA->new( ++ _responses => [ ++ make_redirect('http://victim.example/profile'), ++ make_ok(), ++ ], ++ ); ++ my $res = $ua->request(build_request('https://victim.example/profile')); ++ ++ is(scalar @{ $ua->{_requests} }, 1, 'follow-up request was NOT issued'); ++ is($res->code, 302, 'returned the original 302 response'); ++ like( ++ $res->header('Client-Warning'), ++ qr/Refusing https->http redirect/, ++ 'Client-Warning explains the refusal' ++ ); ++}; ++ ++subtest 'allow_downgrade opts in to https -> http (constructor)' => sub { ++ my $ua = Test::CapturingUA->new( ++ allow_downgrade => 1, ++ _responses => [ ++ make_redirect('http://victim.example/profile'), ++ make_ok(), ++ ], ++ ); ++ my $res = $ua->request(build_request('https://victim.example/profile')); ++ ++ is(scalar @{ $ua->{_requests} }, 2, 'follow-up request was issued'); ++ is($res->code, 200, 'final response is 200 OK'); ++ my $followup = $ua->{_requests}->[1]; ++ is($followup->header('Authorization'), undef, ++ 'Authorization still stripped (scheme change is cross-origin)'); ++}; ++ ++subtest 'allow_downgrade opts in to https -> http (accessor)' => sub { ++ my $ua = Test::CapturingUA->new( ++ _responses => [ ++ make_redirect('http://victim.example/profile'), ++ make_ok(), ++ ], ++ ); ++ $ua->allow_downgrade(1); ++ my $res = $ua->request(build_request('https://victim.example/profile')); ++ ++ is(scalar @{ $ua->{_requests} }, 2, 'follow-up issued after accessor set'); ++ is($res->code, 200, 'final response is 200 OK'); ++}; ++ + done_testing; +diff --git a/xt/author/live/jigsaw/redirect-post.t b/xt/author/live/jigsaw/redirect-post.t +index 13db724a7d2f..5079591f3e09 100644 +--- a/xt/author/live/jigsaw/redirect-post.t ++++ b/xt/author/live/jigsaw/redirect-post.t +@@ -10,7 +10,8 @@ use Encode qw( encode_utf8 ); + + plan tests => 10; + +-my $ua = LWP::UserAgent->new(keep_alive => 1); ++# jigsaw redirects via https->http; opt in to follow the chain. ++my $ua = LWP::UserAgent->new(keep_alive => 1, allow_downgrade => 1); + + my $data = {foo => 'bar', baz => 'quux'}; + my $encoded_data = encode_utf8(encode_json($data)); +-- +2.55.0 + diff -Nru libwww-perl-6.78/debian/patches/Strip-Authorization-on-cross-origin-redirect-CVE-202.patch libwww-perl-6.78/debian/patches/Strip-Authorization-on-cross-origin-redirect-CVE-202.patch --- libwww-perl-6.78/debian/patches/Strip-Authorization-on-cross-origin-redirect-CVE-202.patch 1970-01-01 00:00:00.000000000 +0000 +++ libwww-perl-6.78/debian/patches/Strip-Authorization-on-cross-origin-redirect-CVE-202.patch 2026-09-05 12:14:16.000000000 +0000 @@ -0,0 +1,369 @@ +From: Olaf Alders +Date: Mon, 11 May 2026 23:40:15 +0000 +Subject: Strip Authorization on cross-origin redirect (CVE-2026-8368) +Origin: https://github.com/libwww-perl/libwww-perl/commit/9c4aeb6f2dd32f2b7eaf2d7827cade31ea6cb2c6 +Bug-Debian: https://bugs.debian.org/1136449 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-8368 + +The redirect handler in LWP::UserAgent::request cloned the original +request and stripped only Host and Cookie. Authorization, +Proxy-Authorization, and caller-supplied custom credential headers +rode through to the redirect target unchanged. A server that returns +a Location: http://attacker/ response could exfiltrate the caller's +bearer token, basic-auth credential, or any caller-set credential +header. + +This change strips Authorization and Proxy-Authorization on +cross-origin redirects (different scheme, host, or port). Same-origin +redirects retain the headers so preemptive-auth callers continue to +work. Default-port normalization (via URI->port) treats http://h/ and +http://h:80/ as the same origin; host comparison is case-insensitive. + +Opt-out: allow_credentialed_redirects => 1 in the constructor, or +$ua->allow_credentialed_redirects(1) at runtime. + +Same defect class as libcurl CVE-2018-1000007, Python requests +CVE-2018-18074, wget CVE-2021-31879, and Go net/http CVE-2024-45336. + +PoC and initial patch shape developed by Stig Palmquist during triage. + +Reported-by: Kai Zen +Suggested-by: Stig Palmquist +Co-Authored-By: Claude Opus 4.7 +--- + Changes | 6 ++ + lib/LWP/UserAgent.pm | 92 +++++++++++++----- + t/redirect-credential-leak.t | 183 +++++++++++++++++++++++++++++++++++ + 3 files changed, 257 insertions(+), 24 deletions(-) + create mode 100644 t/redirect-credential-leak.t + +diff --git a/lib/LWP/UserAgent.pm b/lib/LWP/UserAgent.pm +index 82282f2f357e..0483284f9d7b 100644 +--- a/lib/LWP/UserAgent.pm ++++ b/lib/LWP/UserAgent.pm +@@ -93,6 +93,8 @@ sub new + $cookie_jar_class = 'HTTP::Cookies' + unless defined $cookie_jar_class; + ++ my $allow_credentialed_redirects = delete $cnf{allow_credentialed_redirects}; ++ + # Actually ""s are just as good as 0's, but for concision we'll just say: + Carp::croak("protocols_allowed has to be an arrayref or 0, not \"$protocols_allowed\"!") + if $protocols_allowed and ref($protocols_allowed) ne 'ARRAY'; +@@ -119,9 +121,10 @@ sub new + no_proxy => [ @{ $no_proxy } ], + protocols_allowed => $protocols_allowed, + protocols_forbidden => $protocols_forbidden, +- requests_redirectable => $requests_redirectable, +- send_te => $send_te, +- cookie_jar_class => $cookie_jar_class, ++ requests_redirectable => $requests_redirectable, ++ send_te => $send_te, ++ cookie_jar_class => $cookie_jar_class, ++ allow_credentialed_redirects => $allow_credentialed_redirects, + }, $class; + + $self->agent(defined($agent) ? $agent : $class->_agent) +@@ -369,6 +372,27 @@ sub request { + } + $referral->uri($referral_uri); + ++ # Strip caller-supplied credential headers on cross-origin ++ # redirect (different scheme/host/port). Same fix shape as ++ # libcurl CVE-2018-1000007. Opt-out via ++ # allow_credentialed_redirects => 1. ++ unless ($self->{allow_credentialed_redirects}) { ++ my $orig = $request->uri; ++ my $new = $referral->uri; ++ my $orig_scheme = defined $orig->scheme ? $orig->scheme : q{}; ++ my $new_scheme = defined $new->scheme ? $new->scheme : q{}; ++ my $orig_host = defined $orig->host ? lc $orig->host : q{}; ++ my $new_host = defined $new->host ? lc $new->host : q{}; ++ my $orig_port = eval { $orig->port } || 0; ++ my $new_port = eval { $new->port } || 0; ++ if ( $orig_scheme ne $new_scheme ++ || $orig_host ne $new_host ++ || $orig_port != $new_port) ++ { ++ $referral->remove_header('Authorization', 'Proxy-Authorization'); ++ } ++ } ++ + return $response unless $self->redirect_ok($referral, $response); + return $self->request($referral, $arg, $size, $response); + +@@ -738,6 +762,7 @@ sub timeout + sub local_address{ shift->_elem('local_address',@_); } + sub max_size { shift->_elem('max_size', @_); } + sub max_redirect { shift->_elem('max_redirect', @_); } ++sub allow_credentialed_redirects { shift->_elem('allow_credentialed_redirects', @_); } + sub show_progress{ shift->_elem('show_progress', @_); } + sub send_te { shift->_elem('send_te', @_); } + +@@ -1322,27 +1347,35 @@ This method constructs a new L object and returns it. + Key/value pair arguments may be provided to set up the initial state. + The following options correspond to attribute methods described below: + +- KEY DEFAULT +- ----------- -------------------- +- agent "libwww-perl/#.###" +- conn_cache undef +- cookie_jar undef +- cookie_jar_class HTTP::Cookies +- default_headers HTTP::Headers->new +- from undef +- local_address undef +- max_redirect 7 +- max_size undef +- no_proxy [] +- parse_head 1 +- protocols_allowed undef +- protocols_forbidden undef +- proxy {} +- requests_redirectable ['GET', 'HEAD'] +- send_te 1 +- show_progress undef +- ssl_opts { verify_hostname => 1 } +- timeout 180 ++ KEY DEFAULT ++ --------------------------- -------------------- ++ agent "libwww-perl/#.###" ++ allow_credentialed_redirects undef ++ conn_cache undef ++ cookie_jar undef ++ cookie_jar_class HTTP::Cookies ++ default_headers HTTP::Headers->new ++ from undef ++ local_address undef ++ max_redirect 7 ++ max_size undef ++ no_proxy [] ++ parse_head 1 ++ protocols_allowed undef ++ protocols_forbidden undef ++ proxy {} ++ requests_redirectable ['GET', 'HEAD'] ++ send_te 1 ++ show_progress undef ++ ssl_opts { verify_hostname => 1 } ++ timeout 180 ++ ++When following a 3xx redirect to a different origin (a different ++scheme, host, or port), L strips C ++and C from the cloned request to avoid leaking ++caller-supplied credentials to the redirect target. Set ++C to a true value to opt out and ++forward these headers across origins. + + The following additional options are also accepted: If the C option + is passed in with a true value, then proxy settings are read from environment +@@ -1386,6 +1419,17 @@ string is appended to it. + The user agent string should be one or more simple product identifiers + with an optional version number separated by the C character. + ++=head2 allow_credentialed_redirects ++ ++ my $allow = $ua->allow_credentialed_redirects; ++ $ua->allow_credentialed_redirects( 1 ); ++ ++Get/set whether caller-supplied C and C ++headers are forwarded across cross-origin 3xx redirects (a different scheme, ++host, or port). Defaults to a false value, meaning the headers are stripped ++on cross-origin redirects to avoid leaking credentials to the redirect target. ++Same-origin redirects always retain these headers. ++ + =head2 conn_cache + + my $cache_obj = $ua->conn_cache; +diff --git a/t/redirect-credential-leak.t b/t/redirect-credential-leak.t +new file mode 100644 +index 000000000000..6d31555875e6 +--- /dev/null ++++ b/t/redirect-credential-leak.t +@@ -0,0 +1,183 @@ ++use strict; ++use warnings; ++ ++# Regression test for CVE-2026-8368 — LWP::UserAgent cross-origin ++# redirect credential leak and related https->http downgrade hardening. ++ ++use Test::More; ++use HTTP::Request (); ++use HTTP::Response (); ++ ++{ ++ package Test::CapturingUA; ++ use parent 'LWP::UserAgent'; ++ ++ sub new { ++ my ($class, %opts) = @_; ++ my $responses = delete $opts{_responses} || []; ++ my $self = $class->SUPER::new(%opts); ++ $self->{_responses} = $responses; ++ $self->{_requests} = []; ++ return $self; ++ } ++ ++ sub simple_request { ++ my ($self, $req) = @_; ++ push @{ $self->{_requests} }, $req->clone; ++ my $resp = shift @{ $self->{_responses} } ++ || HTTP::Response->new(500, 'no canned response'); ++ $resp->request($req); ++ return $resp; ++ } ++} ++ ++sub make_redirect { ++ my ($location) = @_; ++ my $r = HTTP::Response->new(302, 'Found'); ++ $r->header(Location => $location); ++ return $r; ++} ++ ++sub make_ok { ++ my $r = HTTP::Response->new(200, 'OK'); ++ $r->content('done'); ++ return $r; ++} ++ ++sub build_request { ++ my ($url) = @_; ++ my $req = HTTP::Request->new(GET => $url); ++ $req->header('Authorization' => 'Bearer s3cr3t'); ++ $req->header('Proxy-Authorization' => 'Basic cHJveHk6c2VjcmV0'); ++ return $req; ++} ++ ++subtest 'scaffold: single request returns canned 200' => sub { ++ my $ua = Test::CapturingUA->new(_responses => [make_ok()]); ++ my $res = $ua->request(build_request('http://example/')); ++ is($res->code, 200, 'got 200'); ++ is(scalar @{ $ua->{_requests} }, 1, 'one request captured'); ++}; ++ ++subtest 'cross-host redirect strips Authorization + Proxy-Authorization' => sub { ++ my $ua = Test::CapturingUA->new( ++ _responses => [ ++ make_redirect('http://attacker.example/loot'), ++ make_ok(), ++ ], ++ ); ++ my $res = $ua->request(build_request('http://victim.example/profile')); ++ ++ is(scalar @{ $ua->{_requests} }, 2, 'two requests issued'); ++ my $followup = $ua->{_requests}->[1]; ++ is($followup->uri, 'http://attacker.example/loot', 'followup hit redirect target'); ++ is($followup->header('Authorization'), undef, 'Authorization stripped cross-host'); ++ is($followup->header('Proxy-Authorization'), undef, 'Proxy-Authorization stripped cross-host'); ++ is($res->code, 200, 'final response is 200'); ++}; ++ ++subtest 'different port counts as cross-origin' => sub { ++ my $ua = Test::CapturingUA->new( ++ _responses => [ ++ make_redirect('http://victim.example:8080/x'), ++ make_ok(), ++ ], ++ ); ++ $ua->request(build_request('http://victim.example/profile')); ++ my $followup = $ua->{_requests}->[1]; ++ is($followup->header('Authorization'), undef, 'Authorization stripped on port change'); ++ is($followup->header('Proxy-Authorization'), undef, 'Proxy-Authorization stripped on port change'); ++}; ++ ++subtest 'different scheme counts as cross-origin' => sub { ++ my $ua = Test::CapturingUA->new( ++ _responses => [ ++ make_redirect('https://victim.example/profile'), ++ make_ok(), ++ ], ++ ); ++ $ua->request(build_request('http://victim.example/profile')); ++ my $followup = $ua->{_requests}->[1]; ++ is($followup->header('Authorization'), undef, 'Authorization stripped on scheme change'); ++ is($followup->header('Proxy-Authorization'), undef, 'Proxy-Authorization stripped on scheme change'); ++}; ++ ++subtest 'constructor accepts allow_credentialed_redirects under -w' => sub { ++ local $SIG{__WARN__} = sub { fail("unexpected warning: $_[0]") }; ++ local $^W = 1; ++ my $ua = LWP::UserAgent->new(allow_credentialed_redirects => 1); ++ pass('constructor accepted allow_credentialed_redirects without warnings'); ++ is($ua->{allow_credentialed_redirects}, 1, 'allow_credentialed_redirects stored'); ++ is($ua->allow_credentialed_redirects, 1, 'accessor reads stored value'); ++}; ++ ++subtest 'same-origin redirect keeps credential headers' => sub { ++ my $ua = Test::CapturingUA->new( ++ _responses => [ ++ make_redirect('http://victim.example/profile/new'), ++ make_ok(), ++ ], ++ ); ++ $ua->request(build_request('http://victim.example/profile')); ++ my $followup = $ua->{_requests}->[1]; ++ is($followup->header('Authorization'), 'Bearer s3cr3t', ++ 'Authorization preserved same-origin'); ++ is($followup->header('Proxy-Authorization'), 'Basic cHJveHk6c2VjcmV0', ++ 'Proxy-Authorization preserved same-origin'); ++}; ++ ++subtest 'host comparison is case-insensitive' => sub { ++ my $ua = Test::CapturingUA->new( ++ _responses => [ ++ make_redirect('http://VICTIM.example/profile/new'), ++ make_ok(), ++ ], ++ ); ++ $ua->request(build_request('http://victim.example/profile')); ++ my $followup = $ua->{_requests}->[1]; ++ is($followup->header('Authorization'), 'Bearer s3cr3t', ++ 'Authorization preserved when host differs only in case'); ++}; ++ ++subtest 'default-port normalization treats http://h/ and http://h:80/ as same origin' => sub { ++ my $ua = Test::CapturingUA->new( ++ _responses => [ ++ make_redirect('http://victim.example:80/profile/new'), ++ make_ok(), ++ ], ++ ); ++ $ua->request(build_request('http://victim.example/profile')); ++ my $followup = $ua->{_requests}->[1]; ++ is($followup->header('Authorization'), 'Bearer s3cr3t', ++ 'Authorization preserved when explicit port matches default'); ++}; ++ ++subtest 'allow_credentialed_redirects opt-out via constructor' => sub { ++ my $ua = Test::CapturingUA->new( ++ allow_credentialed_redirects => 1, ++ _responses => [ ++ make_redirect('http://attacker.example/loot'), ++ make_ok(), ++ ], ++ ); ++ $ua->request(build_request('http://victim.example/profile')); ++ my $followup = $ua->{_requests}->[1]; ++ is($followup->header('Authorization'), 'Bearer s3cr3t', ++ 'Authorization forwarded when allow_credentialed_redirects is true'); ++}; ++ ++subtest 'allow_credentialed_redirects opt-out via accessor' => sub { ++ my $ua = Test::CapturingUA->new( ++ _responses => [ ++ make_redirect('http://attacker.example/loot'), ++ make_ok(), ++ ], ++ ); ++ $ua->allow_credentialed_redirects(1); ++ $ua->request(build_request('http://victim.example/profile')); ++ my $followup = $ua->{_requests}->[1]; ++ is($followup->header('Authorization'), 'Bearer s3cr3t', ++ 'Authorization forwarded after $ua->allow_credentialed_redirects(1)'); ++}; ++ ++done_testing; +-- +2.55.0 + diff -Nru libwww-perl-6.78/debian/patches/series libwww-perl-6.78/debian/patches/series --- libwww-perl-6.78/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ libwww-perl-6.78/debian/patches/series 2026-09-05 12:14:16.000000000 +0000 @@ -0,0 +1,2 @@ +Strip-Authorization-on-cross-origin-redirect-CVE-202.patch +Refuse-https-http-downgrade-redirects-by-default.patch