Bug#1147175: trixie-pu: package libwww-perl/6.78-1+deb13u1
Salvatore Bonaccorso
carnil at debian.org
Tue Sep 8 20:39:28 BST 2026
Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: libwww-perl at packages.debian.org, team at security.debian.org, debian-perl at lists.debian.org, gregoa at debian.org, carnil at debian.org
Control: affects -1 + src:libwww-perl
User: release.debian.org at packages.debian.org
Usertags: pu
Hi SRMers
libwww-perl in trixie is affected by CVE-2026-8368, where
LWP::UserAgent was leaking Authorization and Proxy-Authorization
headers on cross-origin redirects.
We did mark this issue as no-dsa, but it would be nice to include the
fix in 13.8.
The update was QA tested as well against reverse dependencies on
debusine in:
https://debusine.debian.net/debian/developers/work-request/1230930/
I added a followup upstream commit cherry-picked as well to further
harden to refuse https->http downgrade redirects by default.
Regards,
Salvatore
-------------- next part --------------
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 <carnil at debian.org> 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 <olaf at wundersolutions.com>
+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 <stig at stig.io>
+Co-Authored-By: Claude Opus 4.7 <noreply at anthropic.com>
+---
+ 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', at _); }
+ 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<allow_credentialed_redirects> to a true value to opt out and
+ forward these headers across origins.
+
++A 3xx redirect that downgrades an C<https> request to plain C<http>
++is refused by default; the original response is returned with a
++C<Client-Warning> header explaining the refusal. Set C<allow_downgrade>
++to a true value to opt in to following such redirects.
++
+ The following additional options are also accepted: If the C<env_proxy> option
+ is passed in with a true value, then proxy settings are read from environment
+ variables (see L<LWP::UserAgent/env_proxy>). If C<env_proxy> 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<https> request to a plain
++C<http> URL is followed. Defaults to a false value, meaning such
++redirects are refused; the original response is returned with a
++C<Client-Warning> header. Set to a true value to opt in to following
++the redirect. Note that even when C<allow_downgrade> is true,
++cross-origin credential stripping still applies (see
++L</allow_credentialed_redirects>).
++
+ =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 <olaf at wundersolutions.com>
+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 <kai.aizen.dev at gmail.com>
+Suggested-by: Stig Palmquist <stig at stig.io>
+Co-Authored-By: Claude Opus 4.7 <noreply at anthropic.com>
+---
+ 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', at _); }
+ 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<LWP::UserAgent> 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<LWP::UserAgent> strips C<Authorization>
++and C<Proxy-Authorization> from the cloned request to avoid leaking
++caller-supplied credentials to the redirect target. Set
++C<allow_credentialed_redirects> to a true value to opt out and
++forward these headers across origins.
+
+ The following additional options are also accepted: If the C<env_proxy> 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<Authorization> and C<Proxy-Authorization>
++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
More information about the pkg-perl-maintainers
mailing list