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