Your message dated Sat, 11 Jul 2026 10:32:48 +0000
with message-id <[email protected]>
and subject line Released in 13.6
has caused the Debian Bug report #1139278,
regarding trixie-pu: package php-guzzlehttp-psr7/2.7.1-1+deb13u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1139278: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1139278
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:php-guzzlehttp-psr7
User: [email protected]
Usertags: pu

Hi,

As agreed with the security team, I’dd like to address CVE-2026-48998
and CVE-2026-49214 in the next point release since they don’t deserve a
DSA. Please note that I had to include three more upstream patches in
order to properly include the fix for CVE-2026-48998.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

Thanks in advance for considering.

Regards,

taffit
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/changelog php-guzzlehttp-psr7-2.7.1/debian/changelog
--- php-guzzlehttp-psr7-2.7.1/debian/changelog	2025-03-31 12:19:42.000000000 +0200
+++ php-guzzlehttp-psr7-2.7.1/debian/changelog	2026-05-30 13:39:01.000000000 +0200
@@ -1,3 +1,16 @@
+php-guzzlehttp-psr7 (2.7.1-1+deb13u1) trixie; urgency=medium
+
+  * Backport fixes from upstream
+    - Encode plus sign in withQueryValue() and withQueryValues() (#636)
+    - Harden ServerRequest globals handling (#660)
+    - Normalize global header values (#718)
+    - Reject control characters in URI hosts (#715) [CVE-2026-49214]
+    - Reject malformed Host authorities (#717) [CVE-2026-48998]
+    (Closes: #1138265)
+  * Track debian/trixie branch
+
+ -- David Prévot <[email protected]>  Sat, 30 May 2026 13:39:01 +0200
+
 php-guzzlehttp-psr7 (2.7.1-1) unstable; urgency=medium
 
   [ Graham Campbell ]
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/control php-guzzlehttp-psr7-2.7.1/debian/control
--- php-guzzlehttp-psr7-2.7.1/debian/control	2025-03-31 12:19:42.000000000 +0200
+++ php-guzzlehttp-psr7-2.7.1/debian/control	2026-05-30 13:39:01.000000000 +0200
@@ -15,7 +15,7 @@
                psmisc
 Standards-Version: 4.7.2
 Homepage: https://github.com/guzzle/psr7
-Vcs-Git: https://salsa.debian.org/php-team/pear/php-guzzlehttp-psr7.git
+Vcs-Git: https://salsa.debian.org/php-team/pear/php-guzzlehttp-psr7.git -b debian/trixie
 Vcs-Browser: https://salsa.debian.org/php-team/pear/php-guzzlehttp-psr7
 Rules-Requires-Root: no
 
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/gbp.conf php-guzzlehttp-psr7-2.7.1/debian/gbp.conf
--- php-guzzlehttp-psr7-2.7.1/debian/gbp.conf	2024-07-21 02:49:36.000000000 +0200
+++ php-guzzlehttp-psr7-2.7.1/debian/gbp.conf	2026-05-30 13:39:01.000000000 +0200
@@ -1,5 +1,5 @@
 [DEFAULT]
-debian-branch = debian/latest
+debian-branch = debian/trixie
 filter = [ '.gitattributes' ]
 pristine-tar = True
 upstream-vcs-tag = %(version%~%-)s
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/patches/0004-2.8-Encode-plus-sign-in-withQueryValue-and-withQuery.patch php-guzzlehttp-psr7-2.7.1/debian/patches/0004-2.8-Encode-plus-sign-in-withQueryValue-and-withQuery.patch
--- php-guzzlehttp-psr7-2.7.1/debian/patches/0004-2.8-Encode-plus-sign-in-withQueryValue-and-withQuery.patch	1970-01-01 01:00:00.000000000 +0100
+++ php-guzzlehttp-psr7-2.7.1/debian/patches/0004-2.8-Encode-plus-sign-in-withQueryValue-and-withQuery.patch	2026-05-30 13:39:01.000000000 +0200
@@ -0,0 +1,61 @@
+From: =?utf-8?q?Ey=C3=BCp_Can_Akman?= <[email protected]>
+Date: Tue, 10 Mar 2026 12:53:17 +0300
+Subject: [2.8] Encode plus sign in withQueryValue() and withQueryValues()
+ (#636)
+
+* Encode plus sign in withQueryValue() and withQueryValues()
+
+* Add plus sign note to Uri query comment
+
+Origin: upstrem, https://github.com/guzzle/psr7/commit/bf5b5784ad30e4d01d7723366977ae54dc1d06ff
+Bug: https://github.com/guzzle/psr7/pull/636
+---
+ src/Uri.php       |  5 +++--
+ tests/UriTest.php | 11 +++++++++++
+ 2 files changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/src/Uri.php b/src/Uri.php
+index a7cdfb0..bef82e2 100644
+--- a/src/Uri.php
++++ b/src/Uri.php
+@@ -51,7 +51,7 @@ class Uri implements UriInterface, \JsonSerializable
+      * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
+      */
+     private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
+-    private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26'];
++    private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26', '+' => '%2B'];
+ 
+     /** @var string Uri scheme. */
+     private $scheme = '';
+@@ -661,7 +661,8 @@ class Uri implements UriInterface, \JsonSerializable
+ 
+     private static function generateQueryString(string $key, ?string $value): string
+     {
+-        // Query string separators ("=", "&") within the key or value need to be encoded
++        // Query string separators ("=", "&") and literal plus signs ("+") within the
++        // key or value need to be encoded
+         // (while preventing double-encoding) before setting the query string. All other
+         // chars that need percent-encoding will be encoded by withQuery().
+         $queryString = strtr($key, self::QUERY_SEPARATORS_REPLACEMENT);
+diff --git a/tests/UriTest.php b/tests/UriTest.php
+index 185b7f1..3fb0716 100644
+--- a/tests/UriTest.php
++++ b/tests/UriTest.php
+@@ -390,6 +390,17 @@ class UriTest extends TestCase
+         self::assertSame('E%3Dmc%5e2=ein%26stein', $uri->getQuery(), 'Encoded key/value do not get double-encoded');
+     }
+ 
++    public function testWithQueryValueEncodesPlusSign(): void
++    {
++        $uri = new Uri();
++        $uri = Uri::withQueryValue($uri, 'a+b', 'c+d');
++        self::assertSame('a%2Bb=c%2Bd', $uri->getQuery(), 'Plus signs in key and value get encoded to %2B');
++
++        $uri = new Uri();
++        $uri = Uri::withQueryValue($uri, 'query', 'a+b c');
++        self::assertSame('query=a%2Bb%20c', $uri->getQuery(), 'Plus sign is encoded distinctly from space');
++    }
++
+     public function testWithoutQueryValueHandlesEncoding(): void
+     {
+         // It also tests that the case of the percent-encoding does not matter,
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/patches/0004-Modernize-PHPUnit-syntax.patch php-guzzlehttp-psr7-2.7.1/debian/patches/0004-Modernize-PHPUnit-syntax.patch
--- php-guzzlehttp-psr7-2.7.1/debian/patches/0004-Modernize-PHPUnit-syntax.patch	2025-03-31 12:18:24.000000000 +0200
+++ php-guzzlehttp-psr7-2.7.1/debian/patches/0004-Modernize-PHPUnit-syntax.patch	2026-05-30 13:39:01.000000000 +0200
@@ -6,20 +6,21 @@
  tests/AppendStreamTest.php         |  5 ++---
  tests/FnStreamTest.php             |  5 ++---
  tests/HeaderTest.php               | 13 ++++---------
+ tests/MessageTest.php              |  9 +++------
  tests/PumpStreamTest.php           |  5 ++---
  tests/QueryTest.php                |  9 +++------
  tests/RequestTest.php              | 21 ++++++---------------
  tests/ResponseTest.php             | 21 +++++++--------------
- tests/ServerRequestTest.php        |  9 +++------
+ tests/ServerRequestTest.php        | 13 ++++---------
  tests/StreamDecoratorTraitTest.php |  5 ++---
  tests/StreamTest.php               | 12 ++++--------
  tests/UploadedFileTest.php         | 21 ++++++---------------
  tests/UriComparatorTest.php        |  5 ++---
  tests/UriNormalizerTest.php        | 13 ++++---------
  tests/UriResolverTest.php          | 13 ++++---------
- tests/UriTest.php                  | 21 ++++++---------------
+ tests/UriTest.php                  | 25 +++++++------------------
  tests/UtilsTest.php                |  9 ++++-----
- 16 files changed, 61 insertions(+), 126 deletions(-)
+ 17 files changed, 66 insertions(+), 138 deletions(-)
 
 diff --git a/tests/AppendStreamTest.php b/tests/AppendStreamTest.php
 index 302470a..b0c6a2e 100644
@@ -111,6 +112,40 @@
      public function testSplitList($header, $result): void
      {
          self::assertSame($result, Psr7\Header::splitList($header));
+diff --git a/tests/MessageTest.php b/tests/MessageTest.php
+index 42909be..4df397a 100644
+--- a/tests/MessageTest.php
++++ b/tests/MessageTest.php
+@@ -6,6 +6,7 @@ namespace GuzzleHttp\Tests\Psr7;
+ 
+ use GuzzleHttp\Psr7;
+ use GuzzleHttp\Psr7\FnStream;
++use PHPUnit\Framework\Attributes\DataProvider;
+ use PHPUnit\Framework\TestCase;
+ 
+ class MessageTest extends TestCase
+@@ -107,9 +108,7 @@ class MessageTest extends TestCase
+         self::assertSame('http://foo.com/', (string) $request->getUri());
+     }
+ 
+-    /**
+-     * @dataProvider invalidHostHeaderProvider
+-     */
++    #[DataProvider('invalidHostHeaderProvider')]
+     public function testParseRequestRejectsInvalidHostHeader(string $host): void
+     {
+         $this->expectException(\InvalidArgumentException::class);
+@@ -136,9 +135,7 @@ class MessageTest extends TestCase
+         yield 'unexpected closing bracket' => ['foo]bar'];
+     }
+ 
+-    /**
+-     * @dataProvider validHostHeaderProvider
+-     */
++    #[DataProvider('validHostHeaderProvider')]
+     public function testParseRequestAcceptsValidHostHeader(string $host, string $expectedUri): void
+     {
+         $request = Psr7\Message::parseRequest("GET / HTTP/1.1\r\nHost: {$host}\r\n\r\n");
 diff --git a/tests/PumpStreamTest.php b/tests/PumpStreamTest.php
 index a4f3f2c..52ded59 100644
 --- a/tests/PumpStreamTest.php
@@ -169,7 +204,7 @@
      {
          $result = Psr7\Query::parse($input, false);
 diff --git a/tests/RequestTest.php b/tests/RequestTest.php
-index 19d71c3..92c9733 100644
+index 3a62c57..0ea5069 100644
 --- a/tests/RequestTest.php
 +++ b/tests/RequestTest.php
 @@ -7,6 +7,7 @@ namespace GuzzleHttp\Tests\Psr7;
@@ -179,8 +214,8 @@
 +use PHPUnit\Framework\Attributes\DataProvider;
  use PHPUnit\Framework\TestCase;
  use Psr\Http\Message\StreamInterface;
- 
-@@ -95,18 +96,14 @@ class RequestTest extends TestCase
+ use Psr\Http\Message\UriInterface;
+@@ -96,18 +97,14 @@ class RequestTest extends TestCase
          self::assertSame($u1, $r1->getUri());
      }
  
@@ -201,7 +236,7 @@
      public function testWithInvalidMethods($method): void
      {
          $r = new Request('get', '/');
-@@ -197,9 +194,7 @@ class RequestTest extends TestCase
+@@ -198,9 +195,7 @@ class RequestTest extends TestCase
          self::assertSame('', $r->getHeaderLine('Bar'));
      }
  
@@ -212,7 +247,7 @@
      public function testContainsNotAllowedCharsOnHeaderField($header): void
      {
          $this->expectExceptionMessage(
-@@ -222,9 +217,7 @@ class RequestTest extends TestCase
+@@ -223,9 +218,7 @@ class RequestTest extends TestCase
          return [[' key '], ['key '], [' key'], ['key/'], ['key('], ['key\\'], [' ']];
      }
  
@@ -223,8 +258,8 @@
      public function testContainsAllowedCharsOnHeaderField($header): void
      {
          $r = new Request(
-@@ -306,9 +299,7 @@ class RequestTest extends TestCase
-         self::assertSame('foo.com:8125', $r->getHeaderLine('host'));
+@@ -329,9 +322,7 @@ class RequestTest extends TestCase
+         self::assertSame('example.com:8080', $request->getHeaderLine('Host'));
      }
  
 -    /**
@@ -317,7 +352,7 @@
      {
          $response = new Response();
 diff --git a/tests/ServerRequestTest.php b/tests/ServerRequestTest.php
-index f770078..50d173b 100644
+index 914f949..b81b663 100644
 --- a/tests/ServerRequestTest.php
 +++ b/tests/ServerRequestTest.php
 @@ -7,6 +7,7 @@ namespace GuzzleHttp\Tests\Psr7;
@@ -339,7 +374,7 @@
      public function testNormalizeFiles($files, $expected): void
      {
          $result = ServerRequest::normalizeFiles($files);
-@@ -369,9 +368,7 @@ class ServerRequestTest extends TestCase
+@@ -433,9 +432,7 @@ class ServerRequestTest extends TestCase
          ];
      }
  
@@ -350,6 +385,17 @@
      public function testGetUriFromGlobals($expected, $serverParams): void
      {
          $_SERVER = $serverParams;
+@@ -576,9 +573,7 @@ class ServerRequestTest extends TestCase
+         self::assertSame('1.1', $server->getProtocolVersion());
+     }
+ 
+-    /**
+-     * @dataProvider invalidHostHeaderFromGlobalsProvider
+-     */
++    #[DataProvider('invalidHostHeaderFromGlobalsProvider')]
+     public function testFromGlobalsDropsInvalidHostHeaderWhenUriFallsBack(string $host): void
+     {
+         if (!\function_exists('getallheaders')) {
 diff --git a/tests/StreamDecoratorTraitTest.php b/tests/StreamDecoratorTraitTest.php
 index aaa9f28..11e699c 100644
 --- a/tests/StreamDecoratorTraitTest.php
@@ -598,7 +644,7 @@
      {
          $baseUri = new Uri($base);
 diff --git a/tests/UriTest.php b/tests/UriTest.php
-index 185b7f1..b0b39a4 100644
+index da229a7..01d7df5 100644
 --- a/tests/UriTest.php
 +++ b/tests/UriTest.php
 @@ -6,6 +6,7 @@ namespace GuzzleHttp\Tests\Psr7;
@@ -642,7 +688,18 @@
      public function testInvalidUrisThrowException(string $invalidUri): void
      {
          $this->expectException(MalformedUriException::class);
-@@ -205,9 +200,7 @@ class UriTest extends TestCase
+@@ -153,9 +148,7 @@ class UriTest extends TestCase
+         (new Uri())->withHost([]);
+     }
+ 
+-    /**
+-     * @dataProvider getInvalidHostsWithControlCharacters
+-     */
++    #[DataProvider('getInvalidHostsWithControlCharacters')]
+     public function testHostMustRejectControlCharacters(string $host): void
+     {
+         $this->expectException(\InvalidArgumentException::class);
+@@ -231,9 +224,7 @@ class UriTest extends TestCase
          self::assertSame('0://0:0@0/0?0#0', (string) $uri);
      }
  
@@ -653,7 +710,7 @@
      public function testIsDefaultPort(string $scheme, ?int $port, bool $isDefaultPort): void
      {
          $uri = $this->createMock(UriInterface::class);
-@@ -553,9 +546,7 @@ class UriTest extends TestCase
+@@ -590,9 +581,7 @@ class UriTest extends TestCase
          ];
      }
  
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/patches/0005-Harden-ServerRequest-globals-handling-660.patch php-guzzlehttp-psr7-2.7.1/debian/patches/0005-Harden-ServerRequest-globals-handling-660.patch
--- php-guzzlehttp-psr7-2.7.1/debian/patches/0005-Harden-ServerRequest-globals-handling-660.patch	1970-01-01 01:00:00.000000000 +0100
+++ php-guzzlehttp-psr7-2.7.1/debian/patches/0005-Harden-ServerRequest-globals-handling-660.patch	2026-05-30 13:39:01.000000000 +0200
@@ -0,0 +1,181 @@
+From: Graham Campbell <[email protected]>
+Date: Tue, 19 May 2026 18:20:12 +0100
+Subject: Harden ServerRequest globals handling (#660)
+
+* Harden server request globals
+
+* Document ServerRequest globals hardening
+
+* Move ServerRequest changelog entry
+
+* Reorder ServerRequest changelog entry
+
+Origin: backport, https://github.com/guzzle/psr7/commit/ed659d5c2254b9d2af3a1b77257b91a4899e313c
+Bug: https://github.com/guzzle/psr7/pull/660
+---
+ src/ServerRequest.php       | 46 +++++++++++++++++++++++++++++----------------
+ tests/ServerRequestTest.php | 41 ++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 71 insertions(+), 16 deletions(-)
+
+diff --git a/src/ServerRequest.php b/src/ServerRequest.php
+index 3cc9534..bc795fc 100644
+--- a/src/ServerRequest.php
++++ b/src/ServerRequest.php
+@@ -165,11 +165,12 @@ class ServerRequest extends Request implements ServerRequestInterface
+      */
+     public static function fromGlobals(): ServerRequestInterface
+     {
+-        $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
++        $method = self::getServerParam('REQUEST_METHOD') ?? 'GET';
+         $headers = getallheaders();
+         $uri = self::getUriFromGlobals();
+         $body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
+-        $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
++        $serverProtocol = self::getServerParam('SERVER_PROTOCOL');
++        $protocol = $serverProtocol !== null ? str_replace('HTTP/', '', $serverProtocol) : '1.1';
+ 
+         $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
+ 
+@@ -180,11 +181,19 @@ class ServerRequest extends Request implements ServerRequestInterface
+             ->withUploadedFiles(self::normalizeFiles($_FILES));
+     }
+ 
++    private static function getServerParam(string $key): ?string
++    {
++        return isset($_SERVER[$key]) && is_string($_SERVER[$key]) ? $_SERVER[$key] : null;
++    }
++
++    /**
++     * @return array{0: string|null, 1: int|null}
++     */
+     private static function extractHostAndPortFromAuthority(string $authority): array
+     {
+         $uri = 'http://'.$authority;
+         $parts = parse_url($uri);
+-        if (false === $parts) {
++        if (!is_array($parts)) {
+             return [null, null];
+         }
+ 
+@@ -201,11 +210,13 @@ class ServerRequest extends Request implements ServerRequestInterface
+     {
+         $uri = new Uri('');
+ 
+-        $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
++        $https = self::getServerParam('HTTPS');
++        $uri = $uri->withScheme(!empty($https) && $https !== 'off' ? 'https' : 'http');
+ 
+         $hasPort = false;
+-        if (isset($_SERVER['HTTP_HOST'])) {
+-            [$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']);
++        $authority = self::getServerParam('HTTP_HOST');
++        if ($authority !== null) {
++            [$host, $port] = self::extractHostAndPortFromAuthority($authority);
+             if ($host !== null) {
+                 $uri = $uri->withHost($host);
+             }
+@@ -214,19 +225,21 @@ class ServerRequest extends Request implements ServerRequestInterface
+                 $hasPort = true;
+                 $uri = $uri->withPort($port);
+             }
+-        } elseif (isset($_SERVER['SERVER_NAME'])) {
+-            $uri = $uri->withHost($_SERVER['SERVER_NAME']);
+-        } elseif (isset($_SERVER['SERVER_ADDR'])) {
+-            $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
++        } elseif (($serverName = self::getServerParam('SERVER_NAME')) !== null) {
++            $uri = $uri->withHost($serverName);
++        } elseif (($serverAddr = self::getServerParam('SERVER_ADDR')) !== null) {
++            $uri = $uri->withHost($serverAddr);
+         }
+ 
+-        if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
+-            $uri = $uri->withPort($_SERVER['SERVER_PORT']);
++        $serverPort = self::getServerParam('SERVER_PORT');
++        if (!$hasPort && $serverPort !== null && preg_match('/^[+-]?\d+$/', $serverPort) === 1) {
++            $uri = $uri->withPort((int) $serverPort);
+         }
+ 
+         $hasQuery = false;
+-        if (isset($_SERVER['REQUEST_URI'])) {
+-            $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);
++        $requestUri = self::getServerParam('REQUEST_URI');
++        if ($requestUri !== null) {
++            $requestUriParts = explode('?', $requestUri, 2);
+             $uri = $uri->withPath($requestUriParts[0]);
+             if (isset($requestUriParts[1])) {
+                 $hasQuery = true;
+@@ -234,8 +247,9 @@ class ServerRequest extends Request implements ServerRequestInterface
+             }
+         }
+ 
+-        if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
+-            $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
++        $queryString = self::getServerParam('QUERY_STRING');
++        if (!$hasQuery && $queryString !== null) {
++            $uri = $uri->withQuery($queryString);
+         }
+ 
+         return $uri;
+diff --git a/tests/ServerRequestTest.php b/tests/ServerRequestTest.php
+index f770078..dbd2288 100644
+--- a/tests/ServerRequestTest.php
++++ b/tests/ServerRequestTest.php
+@@ -358,10 +358,34 @@ class ServerRequestTest extends TestCase
+                 'https://www.example.org:8324/blog/article.php?id=10&user=foo',
+                 array_merge($server, ['SERVER_PORT' => '8324']),
+             ],
++            'Invalid SERVER_PORT is ignored instead of coerced to zero' => [
++                'https://www.example.org/blog/article.php?id=10&user=foo',
++                array_merge($server, ['SERVER_PORT' => 'not-a-port']),
++            ],
++            'Non-string SERVER_PORT is ignored' => [
++                'https://www.example.org/blog/article.php?id=10&user=foo',
++                array_merge($server, ['SERVER_PORT' => ['443']]),
++            ],
++            'Non-string HTTP_HOST falls back to SERVER_NAME' => [
++                'https://www.example.org/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => ['www.example.org']]),
++            ],
++            'Non-string SERVER_NAME falls back to SERVER_ADDR' => [
++                'https://217.112.82.20/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => null, 'SERVER_NAME' => ['www.example.org']]),
++            ],
+             'REQUEST_URI missing query string' => [
+                 'https://www.example.org/blog/article.php?id=10&user=foo',
+                 array_merge($server, ['REQUEST_URI' => '/blog/article.php']),
+             ],
++            'Non-string REQUEST_URI is treated as missing' => [
++                'https://www.example.org?id=10&user=foo',
++                array_merge($server, ['REQUEST_URI' => ['bad']]),
++            ],
++            'Non-string QUERY_STRING is treated as missing' => [
++                'https://www.example.org/blog/article.php',
++                array_merge($server, ['REQUEST_URI' => '/blog/article.php', 'QUERY_STRING' => ['bad']]),
++            ],
+             'Empty server variable' => [
+                 'http://localhost',
+                 [],
+@@ -461,6 +485,23 @@ class ServerRequestTest extends TestCase
+         self::assertEquals($expectedFiles, $server->getUploadedFiles());
+     }
+ 
++    public function testFromGlobalsDefaultsNonStringMethodAndProtocol(): void
++    {
++        $_SERVER = [
++            'REQUEST_METHOD' => ['POST'],
++            'SERVER_PROTOCOL' => ['HTTP/1.1'],
++            'REQUEST_URI' => '/',
++            'SERVER_PORT' => '80',
++        ];
++
++        $_COOKIE = $_POST = $_GET = $_FILES = [];
++
++        $server = ServerRequest::fromGlobals();
++
++        self::assertSame('GET', $server->getMethod());
++        self::assertSame('1.1', $server->getProtocolVersion());
++    }
++
+     public function testUploadedFiles(): void
+     {
+         $request1 = new ServerRequest('GET', '/');
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/patches/0006-Normalize-global-header-values-718.patch php-guzzlehttp-psr7-2.7.1/debian/patches/0006-Normalize-global-header-values-718.patch
--- php-guzzlehttp-psr7-2.7.1/debian/patches/0006-Normalize-global-header-values-718.patch	1970-01-01 01:00:00.000000000 +0100
+++ php-guzzlehttp-psr7-2.7.1/debian/patches/0006-Normalize-global-header-values-718.patch	2026-05-30 13:39:01.000000000 +0200
@@ -0,0 +1,102 @@
+From: Graham Campbell <[email protected]>
+Date: Mon, 25 May 2026 21:50:08 +0100
+Subject: Normalize global header values (#718)
+
+Origin: backport, https://github.com/guzzle/psr7/commit/a0fda818b0f74482925e66e814fe9afb48fd2fa5
+Bug: https://github.com/guzzle/psr7/pull/718
+---
+ src/ServerRequest.php       | 28 +++++++++++++++++++++++++++-
+ tests/ServerRequestTest.php | 34 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 61 insertions(+), 1 deletion(-)
+
+diff --git a/src/ServerRequest.php b/src/ServerRequest.php
+index bc795fc..341af9c 100644
+--- a/src/ServerRequest.php
++++ b/src/ServerRequest.php
+@@ -166,7 +166,7 @@ class ServerRequest extends Request implements ServerRequestInterface
+     public static function fromGlobals(): ServerRequestInterface
+     {
+         $method = self::getServerParam('REQUEST_METHOD') ?? 'GET';
+-        $headers = getallheaders();
++        $headers = self::getAllHeaders();
+         $uri = self::getUriFromGlobals();
+         $body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
+         $serverProtocol = self::getServerParam('SERVER_PROTOCOL');
+@@ -181,6 +181,32 @@ class ServerRequest extends Request implements ServerRequestInterface
+             ->withUploadedFiles(self::normalizeFiles($_FILES));
+     }
+ 
++    /**
++     * @return array<array-key, string>
++     */
++    private static function getAllHeaders(): array
++    {
++        return self::normalizeHeaderValues(getallheaders());
++    }
++
++    /**
++     * @param array<array-key, mixed> $headers
++     *
++     * @return array<array-key, string>
++     */
++    private static function normalizeHeaderValues(array $headers): array
++    {
++        $normalized = [];
++
++        foreach ($headers as $name => $value) {
++            if (is_scalar($value) || (is_object($value) && method_exists($value, '__toString'))) {
++                $normalized[$name] = (string) $value;
++            }
++        }
++
++        return $normalized;
++    }
++
+     private static function getServerParam(string $key): ?string
+     {
+         return isset($_SERVER[$key]) && is_string($_SERVER[$key]) ? $_SERVER[$key] : null;
+diff --git a/tests/ServerRequestTest.php b/tests/ServerRequestTest.php
+index dbd2288..939ab19 100644
+--- a/tests/ServerRequestTest.php
++++ b/tests/ServerRequestTest.php
+@@ -485,6 +485,40 @@ class ServerRequestTest extends TestCase
+         self::assertEquals($expectedFiles, $server->getUploadedFiles());
+     }
+ 
++    public function testFromGlobalsNormalizesUnexpectedHeaderValueTypes(): void
++    {
++        $_SERVER = [
++            'REQUEST_URI' => '/',
++            'HTTP_HOST' => 'www.example.org',
++            'HTTP_X_INT' => 123,
++            'HTTP_X_FLOAT' => 1.5,
++            'HTTP_X_FALSE' => false,
++            'HTTP_X_TRUE' => true,
++            'HTTP_X_STRINGABLE' => new class {
++                public function __toString(): string
++                {
++                    return 'stringable';
++                }
++            },
++            'HTTP_X_ARRAY' => ['bad'],
++            'HTTP_X_OBJECT' => new \stdClass(),
++            'HTTP_123' => 'numeric header',
++        ];
++
++        $_COOKIE = $_POST = $_GET = $_FILES = [];
++
++        $server = ServerRequest::fromGlobals();
++
++        self::assertSame('123', $server->getHeaderLine('X-Int'));
++        self::assertSame('1.5', $server->getHeaderLine('X-Float'));
++        self::assertSame([''], $server->getHeader('X-False'));
++        self::assertSame('1', $server->getHeaderLine('X-True'));
++        self::assertSame('stringable', $server->getHeaderLine('X-Stringable'));
++        self::assertSame('numeric header', $server->getHeaderLine('123'));
++        self::assertFalse($server->hasHeader('X-Array'));
++        self::assertFalse($server->hasHeader('X-Object'));
++    }
++
+     public function testFromGlobalsDefaultsNonStringMethodAndProtocol(): void
+     {
+         $_SERVER = [
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/patches/0007-Reject-malformed-Host-authorities-717.patch php-guzzlehttp-psr7-2.7.1/debian/patches/0007-Reject-malformed-Host-authorities-717.patch
--- php-guzzlehttp-psr7-2.7.1/debian/patches/0007-Reject-malformed-Host-authorities-717.patch	1970-01-01 01:00:00.000000000 +0100
+++ php-guzzlehttp-psr7-2.7.1/debian/patches/0007-Reject-malformed-Host-authorities-717.patch	2026-05-30 13:39:01.000000000 +0200
@@ -0,0 +1,476 @@
+From: Graham Campbell <[email protected]>
+Date: Mon, 25 May 2026 22:42:00 +0100
+Subject: Reject malformed Host authorities (#717)
+
+* Reject malformed Host authorities
+
+* Tighten Host authority validation
+
+* Mark Host authority fix as security
+
+* Remove redundant Host header type check
+
+* Share Host header authority parser
+
+* Move RFC 3986 character classes
+
+* Order RFC 3986 constants by section
+
+* Remove unused Host port flag
+
+* Avoid port zero parseRequest case
+
+Origin: backport, https://github.com/guzzle/psr7/commit/c68fe44ea6b56eb0a7ebdeb9012fb7efbc37c2d3
+Bug: https://github.com/guzzle/psr7/security/advisories/GHSA-34xg-wgjx-8xph
+Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-48998
+---
+ src/Message.php             | 26 +++++++++++---
+ src/Rfc3986.php             | 25 ++++++++++++++
+ src/Rfc7230.php             | 83 +++++++++++++++++++++++++++++++++++++++++++++
+ src/ServerRequest.php       | 31 +++++++++++------
+ src/Uri.php                 | 19 ++---------
+ tests/MessageTest.php       | 56 ++++++++++++++++++++++++++++++
+ tests/ServerRequestTest.php | 78 ++++++++++++++++++++++++++++++++++++++++++
+ 7 files changed, 288 insertions(+), 30 deletions(-)
+ create mode 100644 src/Rfc3986.php
+
+diff --git a/src/Message.php b/src/Message.php
+index 5561a51..d3aafca 100644
+--- a/src/Message.php
++++ b/src/Message.php
+@@ -174,6 +174,23 @@ final class Message
+      * @param array  $headers Array of headers (each value an array).
+      */
+     public static function parseRequestUri(string $path, array $headers): string
++    {
++        $host = self::getHostFromHeaders($headers);
++
++        // If no host is found, then a full URI cannot be constructed.
++        if ($host === null) {
++            return $path;
++        }
++
++        $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
++
++        return $scheme.'://'.$host.'/'.ltrim($path, '/');
++    }
++
++    /**
++     * @param array $headers Array of headers (each value an array).
++     */
++    private static function getHostFromHeaders(array $headers): ?string
+     {
+         $hostKey = array_filter(array_keys($headers), function ($k) {
+             // Numeric array keys are converted to int by PHP.
+@@ -182,15 +199,16 @@ final class Message
+             return strtolower($k) === 'host';
+         });
+ 
+-        // If no host is found, then a full URI cannot be constructed.
+         if (!$hostKey) {
+-            return $path;
++            return null;
+         }
+ 
+         $host = $headers[reset($hostKey)][0];
+-        $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
++        if (!is_string($host) || Rfc7230::parseHostHeader($host) === null) {
++            throw new \InvalidArgumentException('Invalid request string');
++        }
+ 
+-        return $scheme.'://'.$host.'/'.ltrim($path, '/');
++        return $host;
+     }
+ 
+     /**
+diff --git a/src/Rfc3986.php b/src/Rfc3986.php
+new file mode 100644
+index 0000000..6cc2ec0
+--- /dev/null
++++ b/src/Rfc3986.php
+@@ -0,0 +1,25 @@
++<?php
++
++declare(strict_types=1);
++
++namespace GuzzleHttp\Psr7;
++
++/**
++ * @internal
++ */
++final class Rfc3986
++{
++    /**
++     * Sub-delims for use in a regex.
++     *
++     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
++     */
++    public const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
++
++    /**
++     * Unreserved characters for use in a regex.
++     *
++     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
++     */
++    public const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
++}
+diff --git a/src/Rfc7230.php b/src/Rfc7230.php
+index 8219dba..9cd4863 100644
+--- a/src/Rfc7230.php
++++ b/src/Rfc7230.php
+@@ -20,4 +20,87 @@ final class Rfc7230
+      */
+     public const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m";
+     public const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)";
++
++    /**
++     * @return array{0: string, 1: int|null}|null
++     */
++    public static function parseHostHeader(string $authority): ?array
++    {
++        if ($authority === '') {
++            return null;
++        }
++
++        $host = $authority;
++        $port = null;
++
++        if ($authority[0] === '[') {
++            $closingBracket = strpos($authority, ']');
++            if ($closingBracket === false) {
++                return null;
++            }
++
++            $host = substr($authority, 0, $closingBracket + 1);
++            $remainder = substr($authority, $closingBracket + 1);
++            if ($remainder !== '') {
++                if ($remainder[0] !== ':') {
++                    return null;
++                }
++
++                $port = self::parseAuthorityPort(substr($remainder, 1));
++                if ($port === null) {
++                    return null;
++                }
++            }
++        } elseif (false !== ($colon = strpos($authority, ':'))) {
++            $host = substr($authority, 0, $colon);
++            $port = self::parseAuthorityPort(substr($authority, $colon + 1));
++            if ($port === null) {
++                return null;
++            }
++        }
++
++        if ($host === '' || !self::isValidHostHeaderHost($host)) {
++            return null;
++        }
++
++        return [$host, $port];
++    }
++
++    private static function isValidHostHeaderHost(string $host): bool
++    {
++        if (preg_match('/[\x00-\x20\x7F\/\?#@\\\\]/', $host)) {
++            return false;
++        }
++
++        if (strpos($host, '[') !== false || strpos($host, ']') !== false) {
++            if ($host[0] !== '[' || substr($host, -1) !== ']') {
++                return false;
++            }
++
++            $address = substr($host, 1, -1);
++
++            return filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6) !== false
++                || preg_match('/^v[0-9a-f]+\.['.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.':]+$/iD', $address) === 1;
++        }
++
++        return strpos($host, ':') === false;
++    }
++
++    private static function parseAuthorityPort(string $port): ?int
++    {
++        if ($port === '' || !ctype_digit($port)) {
++            return null;
++        }
++
++        $normalized = ltrim($port, '0');
++        if ($normalized === '') {
++            return 0;
++        }
++
++        if (strlen($normalized) > 5 || (int) $normalized > 0xFFFF) {
++            return null;
++        }
++
++        return (int) $normalized;
++    }
+ }
+diff --git a/src/ServerRequest.php b/src/ServerRequest.php
+index 341af9c..3f87f96 100644
+--- a/src/ServerRequest.php
++++ b/src/ServerRequest.php
+@@ -166,7 +166,7 @@ class ServerRequest extends Request implements ServerRequestInterface
+     public static function fromGlobals(): ServerRequestInterface
+     {
+         $method = self::getServerParam('REQUEST_METHOD') ?? 'GET';
+-        $headers = self::getAllHeaders();
++        $headers = self::removeInvalidHostHeader(self::getAllHeaders());
+         $uri = self::getUriFromGlobals();
+         $body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
+         $serverProtocol = self::getServerParam('SERVER_PROTOCOL');
+@@ -213,20 +213,31 @@ class ServerRequest extends Request implements ServerRequestInterface
+     }
+ 
+     /**
+-     * @return array{0: string|null, 1: int|null}
++     * @param array<array-key, string> $headers
++     *
++     * @return array<array-key, string>
+      */
+-    private static function extractHostAndPortFromAuthority(string $authority): array
++    private static function removeInvalidHostHeader(array $headers): array
+     {
+-        $uri = 'http://'.$authority;
+-        $parts = parse_url($uri);
+-        if (!is_array($parts)) {
+-            return [null, null];
++        foreach ($headers as $name => $value) {
++            if (strtolower((string) $name) !== 'host') {
++                continue;
++            }
++
++            if (Rfc7230::parseHostHeader($value) === null) {
++                unset($headers[$name]);
++            }
+         }
+ 
+-        $host = $parts['host'] ?? null;
+-        $port = $parts['port'] ?? null;
++        return $headers;
++    }
+ 
+-        return [$host, $port];
++    /**
++     * @return array{0: string|null, 1: int|null}
++     */
++    private static function extractHostAndPortFromAuthority(string $authority): array
++    {
++        return Rfc7230::parseHostHeader($authority) ?? [null, null];
+     }
+ 
+     /**
+diff --git a/src/Uri.php b/src/Uri.php
+index bef82e2..54bbb0a 100644
+--- a/src/Uri.php
++++ b/src/Uri.php
+@@ -38,19 +38,6 @@ class Uri implements UriInterface, \JsonSerializable
+         'ldap' => 389,
+     ];
+ 
+-    /**
+-     * Unreserved characters for use in a regex.
+-     *
+-     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
+-     */
+-    private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
+-
+-    /**
+-     * Sub-delims for use in a regex.
+-     *
+-     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
+-     */
+-    private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
+     private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26', '+' => '%2B'];
+ 
+     /** @var string Uri scheme. */
+@@ -596,7 +583,7 @@ class Uri implements UriInterface, \JsonSerializable
+         }
+ 
+         return preg_replace_callback(
+-            '/(?:[^%'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.']+|%(?![A-Fa-f0-9]{2}))/',
++            '/(?:[^%'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.']+|%(?![A-Fa-f0-9]{2}))/',
+             [$this, 'rawurlencodeMatchZero'],
+             $component
+         );
+@@ -695,7 +682,7 @@ class Uri implements UriInterface, \JsonSerializable
+         }
+ 
+         return preg_replace_callback(
+-            '/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
++            '/(?:[^'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
+             [$this, 'rawurlencodeMatchZero'],
+             $path
+         );
+@@ -715,7 +702,7 @@ class Uri implements UriInterface, \JsonSerializable
+         }
+ 
+         return preg_replace_callback(
+-            '/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
++            '/(?:[^'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
+             [$this, 'rawurlencodeMatchZero'],
+             $str
+         );
+diff --git a/tests/MessageTest.php b/tests/MessageTest.php
+index 1a43152..42909be 100644
+--- a/tests/MessageTest.php
++++ b/tests/MessageTest.php
+@@ -107,6 +107,62 @@ class MessageTest extends TestCase
+         self::assertSame('http://foo.com/', (string) $request->getUri());
+     }
+ 
++    /**
++     * @dataProvider invalidHostHeaderProvider
++     */
++    public function testParseRequestRejectsInvalidHostHeader(string $host): void
++    {
++        $this->expectException(\InvalidArgumentException::class);
++
++        Psr7\Message::parseRequest("GET / HTTP/1.1\r\nHost: {$host}\r\n\r\n");
++    }
++
++    public static function invalidHostHeaderProvider(): iterable
++    {
++        yield 'userinfo delimiter' => ['[email protected]'];
++        yield 'path delimiter' => ['example.com/path'];
++        yield 'query delimiter' => ['example.com?query'];
++        yield 'fragment delimiter' => ['example.com#fragment'];
++        yield 'backslash delimiter' => ['example.com\\evil'];
++        yield 'space' => ['bad host'];
++        yield 'tab' => ["bad\thost"];
++        yield 'control character' => ['example'.chr(1).'com'];
++        yield 'delete' => ['example'.chr(0x7F).'com'];
++        yield 'multiple ports' => ['example.com:443:8443'];
++        yield 'missing closing bracket' => ['[::1'];
++        yield 'unexpected bracket suffix' => ['[::1]x'];
++        yield 'invalid ip literal' => ['[bad]'];
++        yield 'unexpected opening bracket' => ['foo[bar'];
++        yield 'unexpected closing bracket' => ['foo]bar'];
++    }
++
++    /**
++     * @dataProvider validHostHeaderProvider
++     */
++    public function testParseRequestAcceptsValidHostHeader(string $host, string $expectedUri): void
++    {
++        $request = Psr7\Message::parseRequest("GET / HTTP/1.1\r\nHost: {$host}\r\n\r\n");
++
++        self::assertSame($host, $request->getHeaderLine('Host'));
++        self::assertSame($expectedUri, (string) $request->getUri());
++    }
++
++    public static function validHostHeaderProvider(): iterable
++    {
++        yield 'host' => ['foo.com', 'http://foo.com/'];
++        yield 'https default port' => ['foo.com:443', 'https://foo.com/'];
++        yield 'non-default port' => ['foo.com:8080', 'http://foo.com:8080/'];
++        yield 'ipv6' => ['[::1]', 'http://[::1]/'];
++        yield 'ipv6 port' => ['[::1]:443', 'https://[::1]/'];
++    }
++
++    public function testParseRequestAcceptsMissingHostHeader(): void
++    {
++        $request = Psr7\Message::parseRequest("GET /abc HTTP/1.1\r\nFoo: bar\r\n\r\n");
++
++        self::assertSame('/abc', (string) $request->getUri());
++    }
++
+     public function testParsesRequestMessagesWithFullUri(): void
+     {
+         $req = "GET https://www.google.com:443/search?q=foobar HTTP/1.1\r\nHost: www.google.com\r\n\r\n";
+diff --git a/tests/ServerRequestTest.php b/tests/ServerRequestTest.php
+index 939ab19..914f949 100644
+--- a/tests/ServerRequestTest.php
++++ b/tests/ServerRequestTest.php
+@@ -354,6 +354,46 @@ class ServerRequestTest extends TestCase
+                 'https://localhost/blog/article.php?id=10&user=foo',
+                 array_merge($server, ['HTTP_HOST' => 'a:b']),
+             ],
++            'Host header with userinfo delimiter' => [
++                'https://localhost/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => '[email protected]']),
++            ],
++            'Host header with path delimiter' => [
++                'https://localhost/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => 'example.com/path']),
++            ],
++            'Host header with query delimiter' => [
++                'https://localhost/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => 'example.com?x=1']),
++            ],
++            'Host header with fragment delimiter' => [
++                'https://localhost/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => 'example.com#frag']),
++            ],
++            'Host header with backslash delimiter' => [
++                'https://localhost/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => 'example.com\\evil']),
++            ],
++            'Host header with space' => [
++                'https://localhost/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => 'bad host']),
++            ],
++            'Host header with multiple ports' => [
++                'https://localhost/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => 'example.com:80:90']),
++            ],
++            'Host header with invalid ip literal' => [
++                'https://localhost/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => '[bad]']),
++            ],
++            'Host header with unexpected opening bracket' => [
++                'https://localhost/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => 'foo[bar']),
++            ],
++            'Host header with unexpected closing bracket' => [
++                'https://localhost/blog/article.php?id=10&user=foo',
++                array_merge($server, ['HTTP_HOST' => 'foo]bar']),
++            ],
+             'Different port with SERVER_PORT' => [
+                 'https://www.example.org:8324/blog/article.php?id=10&user=foo',
+                 array_merge($server, ['SERVER_PORT' => '8324']),
+@@ -536,6 +576,44 @@ class ServerRequestTest extends TestCase
+         self::assertSame('1.1', $server->getProtocolVersion());
+     }
+ 
++    /**
++     * @dataProvider invalidHostHeaderFromGlobalsProvider
++     */
++    public function testFromGlobalsDropsInvalidHostHeaderWhenUriFallsBack(string $host): void
++    {
++        if (!\function_exists('getallheaders')) {
++            self::markTestSkipped('getallheaders() is not available.');
++        }
++
++        $_SERVER = [
++            'REQUEST_URI' => '/',
++            'HTTP_HOST' => $host,
++            'SERVER_PORT' => '443',
++            'HTTPS' => 'on',
++        ];
++
++        $_COOKIE = $_POST = $_GET = $_FILES = [];
++
++        $request = ServerRequest::fromGlobals();
++
++        self::assertSame('localhost', $request->getUri()->getHost());
++        self::assertSame('localhost', $request->getHeaderLine('Host'));
++    }
++
++    public static function invalidHostHeaderFromGlobalsProvider(): iterable
++    {
++        yield 'userinfo delimiter' => ['[email protected]'];
++        yield 'path delimiter' => ['example.com/path'];
++        yield 'query delimiter' => ['example.com?x=1'];
++        yield 'fragment delimiter' => ['example.com#frag'];
++        yield 'backslash delimiter' => ['example.com\\evil'];
++        yield 'space' => ['bad host'];
++        yield 'multiple ports' => ['example.com:80:90'];
++        yield 'invalid ip literal' => ['[bad]'];
++        yield 'unexpected opening bracket' => ['foo[bar'];
++        yield 'unexpected closing bracket' => ['foo]bar'];
++    }
++
+     public function testUploadedFiles(): void
+     {
+         $request1 = new ServerRequest('GET', '/');
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/patches/0008-Reject-control-characters-in-URI-hosts-715.patch php-guzzlehttp-psr7-2.7.1/debian/patches/0008-Reject-control-characters-in-URI-hosts-715.patch
--- php-guzzlehttp-psr7-2.7.1/debian/patches/0008-Reject-control-characters-in-URI-hosts-715.patch	1970-01-01 01:00:00.000000000 +0100
+++ php-guzzlehttp-psr7-2.7.1/debian/patches/0008-Reject-control-characters-in-URI-hosts-715.patch	2026-05-30 13:39:01.000000000 +0200
@@ -0,0 +1,185 @@
+From: Graham Campbell <[email protected]>
+Date: Mon, 25 May 2026 21:16:23 +0100
+Subject: Reject control characters in URI hosts (#715)
+
+* Reject control characters in URI hosts
+
+* Validate generated Host header values
+
+* Mark URI host fix as security
+
+Origin: backport, https://github.com/guzzle/psr7/commit/12caca7f2302477216a460fabf93a92659835a06
+Bug: https://github.com/guzzle/psr7/security/advisories/GHSA-hq7v-mx3g-29hw
+Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-49214
+---
+ src/Request.php       |  4 ++++
+ src/Uri.php           | 39 +++++++++++++++++++++++++++++++++++----
+ tests/RequestTest.php | 23 +++++++++++++++++++++++
+ tests/UriTest.php     | 26 ++++++++++++++++++++++++++
+ 4 files changed, 88 insertions(+), 4 deletions(-)
+
+diff --git a/src/Request.php b/src/Request.php
+index faafe1a..743c5d2 100644
+--- a/src/Request.php
++++ b/src/Request.php
+@@ -132,10 +132,14 @@ class Request implements RequestInterface
+             return;
+         }
+ 
++        Uri::assertValidHost($host);
++
+         if (($port = $this->uri->getPort()) !== null) {
+             $host .= ':'.$port;
+         }
+ 
++        $this->assertValue($host);
++
+         if (isset($this->headerNames['host'])) {
+             $header = $this->headerNames['host'];
+         } else {
+diff --git a/src/Uri.php b/src/Uri.php
+index 54bbb0a..b26e12d 100644
+--- a/src/Uri.php
++++ b/src/Uri.php
+@@ -71,7 +71,13 @@ class Uri implements UriInterface, \JsonSerializable
+             if ($parts === false) {
+                 throw new MalformedUriException("Unable to parse URI: $uri");
+             }
+-            $this->applyParts($parts);
++            try {
++                $this->applyParts($parts);
++            } catch (MalformedUriException $e) {
++                throw $e;
++            } catch (\InvalidArgumentException $e) {
++                throw new MalformedUriException($e->getMessage(), 0, $e);
++            }
+         }
+     }
+ 
+@@ -347,12 +353,34 @@ class Uri implements UriInterface, \JsonSerializable
+     public static function fromParts(array $parts): UriInterface
+     {
+         $uri = new self();
+-        $uri->applyParts($parts);
+-        $uri->validateState();
++        try {
++            $uri->applyParts($parts);
++            $uri->validateState();
++        } catch (MalformedUriException $e) {
++            throw $e;
++        } catch (\InvalidArgumentException $e) {
++            throw new MalformedUriException($e->getMessage(), 0, $e);
++        }
+ 
+         return $uri;
+     }
+ 
++    /**
++     * @throws \InvalidArgumentException If the host is invalid.
++     *
++     * @internal
++     */
++    public static function assertValidHost(string $host): void
++    {
++        if ($host === '') {
++            return;
++        }
++
++        if (preg_match('/[\x00-\x20\x7F]/', $host)) {
++            throw new \InvalidArgumentException(sprintf('Invalid host: "%s"', $host));
++        }
++    }
++
+     public function getScheme(): string
+     {
+         return $this->scheme;
+@@ -600,7 +628,10 @@ class Uri implements UriInterface, \JsonSerializable
+             throw new \InvalidArgumentException('Host must be a string');
+         }
+ 
+-        return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
++        $host = \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
++        self::assertValidHost($host);
++
++        return $host;
+     }
+ 
+     /**
+diff --git a/tests/RequestTest.php b/tests/RequestTest.php
+index 19d71c3..3a62c57 100644
+--- a/tests/RequestTest.php
++++ b/tests/RequestTest.php
+@@ -9,6 +9,7 @@ use GuzzleHttp\Psr7\Request;
+ use GuzzleHttp\Psr7\Uri;
+ use PHPUnit\Framework\TestCase;
+ use Psr\Http\Message\StreamInterface;
++use Psr\Http\Message\UriInterface;
+ 
+ /**
+  * @covers \GuzzleHttp\Psr7\MessageTrait
+@@ -306,6 +307,28 @@ class RequestTest extends TestCase
+         self::assertSame('foo.com:8125', $r->getHeaderLine('host'));
+     }
+ 
++    public function testGeneratedHostHeaderRejectsInvalidUriHostFromCustomUri(): void
++    {
++        $uri = $this->createMock(UriInterface::class);
++        $uri->method('getHost')->willReturn("foo\nbar");
++        $uri->method('getPort')->willReturn(null);
++
++        $this->expectException(\InvalidArgumentException::class);
++
++        new Request('GET', $uri);
++    }
++
++    public function testGeneratedHostHeaderValidatesAssembledHostWithPort(): void
++    {
++        $uri = $this->createMock(UriInterface::class);
++        $uri->method('getHost')->willReturn('example.com');
++        $uri->method('getPort')->willReturn(8080);
++
++        $request = new Request('GET', $uri);
++
++        self::assertSame('example.com:8080', $request->getHeaderLine('Host'));
++    }
++
+     /**
+      * @dataProvider provideHeaderValuesContainingNotAllowedChars
+      */
+diff --git a/tests/UriTest.php b/tests/UriTest.php
+index 3fb0716..da229a7 100644
+--- a/tests/UriTest.php
++++ b/tests/UriTest.php
+@@ -153,6 +153,32 @@ class UriTest extends TestCase
+         (new Uri())->withHost([]);
+     }
+ 
++    /**
++     * @dataProvider getInvalidHostsWithControlCharacters
++     */
++    public function testHostMustRejectControlCharacters(string $host): void
++    {
++        $this->expectException(\InvalidArgumentException::class);
++
++        (new Uri())->withHost($host);
++    }
++
++    public static function getInvalidHostsWithControlCharacters(): iterable
++    {
++        for ($i = 0; $i <= 0x20; ++$i) {
++            yield 'ascii 0x'.strtoupper(dechex($i)) => ['example'.chr($i).'com'];
++        }
++
++        yield 'ascii 0x7F' => ['example'.chr(0x7F).'com'];
++    }
++
++    public function testParseUriRejectsHostWithControlCharacter(): void
++    {
++        $this->expectException(MalformedUriException::class);
++
++        new Uri("http://example.com\r\nX-Injected:%20yes/";);
++    }
++
+     public function testPathMustHaveCorrectType(): void
+     {
+         $this->expectException(\InvalidArgumentException::class);
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/patches/series php-guzzlehttp-psr7-2.7.1/debian/patches/series
--- php-guzzlehttp-psr7-2.7.1/debian/patches/series	2025-03-31 12:18:24.000000000 +0200
+++ php-guzzlehttp-psr7-2.7.1/debian/patches/series	2026-05-30 13:39:01.000000000 +0200
@@ -1,4 +1,9 @@
 0001-Adapt-path-to-Debian-expectations.patch
 0002-Compatibility-with-recent-PHPUnit-10.patch
 0003-Make-provider-functions-static-PHPUnit-11-Fix.patch
+0004-2.8-Encode-plus-sign-in-withQueryValue-and-withQuery.patch
+0005-Harden-ServerRequest-globals-handling-660.patch
+0006-Normalize-global-header-values-718.patch
+0007-Reject-malformed-Host-authorities-717.patch
+0008-Reject-control-characters-in-URI-hosts-715.patch
 0004-Modernize-PHPUnit-syntax.patch

Attachment: signature.asc
Description: PGP signature


--- End Message ---
--- Begin Message ---
Version: 13.6

This update was released as part of 13.6.

--- End Message ---

Reply via email to