--- 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’d like to fix CVE-2026-55766 in the
point release since it doesn’t deserve a DSA.
[ 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 2026-05-30 13:39:01.000000000 +0200
+++ php-guzzlehttp-psr7-2.7.1/debian/changelog 2026-06-19 08:13:30.000000000 +0200
@@ -1,3 +1,10 @@
+php-guzzlehttp-psr7 (2.7.1-1+deb13u2) trixie; urgency=medium
+
+ * Reject CR/LF in HTTP method, protocol version, and reason phrase
+ (GHSA-vm85-hxw5-5432) [CVE-2026-55766]
+
+ -- David Prévot <[email protected]> Fri, 19 Jun 2026 08:13:30 +0200
+
php-guzzlehttp-psr7 (2.7.1-1+deb13u1) trixie; urgency=medium
* Backport fixes from upstream
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 2026-05-30 13:39:01.000000000 +0200
+++ php-guzzlehttp-psr7-2.7.1/debian/patches/0004-Modernize-PHPUnit-syntax.patch 2026-06-19 08:13:30.000000000 +0200
@@ -9,8 +9,8 @@
tests/MessageTest.php | 9 +++------
tests/PumpStreamTest.php | 5 ++---
tests/QueryTest.php | 9 +++------
- tests/RequestTest.php | 21 ++++++---------------
- tests/ResponseTest.php | 21 +++++++--------------
+ tests/RequestTest.php | 37 ++++++++++---------------------------
+ tests/ResponseTest.php | 37 +++++++++++--------------------------
tests/ServerRequestTest.php | 13 ++++---------
tests/StreamDecoratorTraitTest.php | 5 ++---
tests/StreamTest.php | 12 ++++--------
@@ -20,7 +20,7 @@
tests/UriResolverTest.php | 13 ++++---------
tests/UriTest.php | 25 +++++++------------------
tests/UtilsTest.php | 9 ++++-----
- 17 files changed, 66 insertions(+), 138 deletions(-)
+ 17 files changed, 74 insertions(+), 162 deletions(-)
diff --git a/tests/AppendStreamTest.php b/tests/AppendStreamTest.php
index 302470a..b0c6a2e 100644
@@ -113,7 +113,7 @@
{
self::assertSame($result, Psr7\Header::splitList($header));
diff --git a/tests/MessageTest.php b/tests/MessageTest.php
-index 42909be..4df397a 100644
+index 1862a95..31f3bb1 100644
--- a/tests/MessageTest.php
+++ b/tests/MessageTest.php
@@ -6,6 +6,7 @@ namespace GuzzleHttp\Tests\Psr7;
@@ -204,7 +204,7 @@
{
$result = Psr7\Query::parse($input, false);
diff --git a/tests/RequestTest.php b/tests/RequestTest.php
-index 3a62c57..0ea5069 100644
+index 9acc022..ec5c44b 100644
--- a/tests/RequestTest.php
+++ b/tests/RequestTest.php
@@ -7,6 +7,7 @@ namespace GuzzleHttp\Tests\Psr7;
@@ -236,7 +236,51 @@
public function testWithInvalidMethods($method): void
{
$r = new Request('get', '/');
-@@ -198,9 +195,7 @@ class RequestTest extends TestCase
+@@ -125,9 +122,7 @@ class RequestTest extends TestCase
+ ];
+ }
+
+- /**
+- * @dataProvider startLineSeparatorProvider
+- */
++ #[DataProvider('startLineSeparatorProvider')]
+ public function testConstructWithLineSeparatorsInMethod(string $lineSeparator): void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+@@ -135,9 +130,7 @@ class RequestTest extends TestCase
+ new Request('GET'.$lineSeparator.'X-Injected: yes', '/');
+ }
+
+- /**
+- * @dataProvider startLineSeparatorProvider
+- */
++ #[DataProvider('startLineSeparatorProvider')]
+ public function testWithMethodRejectsLineSeparators(string $lineSeparator): void
+ {
+ $r = new Request('GET', '/');
+@@ -147,9 +140,7 @@ class RequestTest extends TestCase
+ $r->withMethod('GET'.$lineSeparator.'X-Injected: yes');
+ }
+
+- /**
+- * @dataProvider startLineSeparatorProvider
+- */
++ #[DataProvider('startLineSeparatorProvider')]
+ public function testConstructWithLineSeparatorsInProtocolVersion(string $lineSeparator): void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+@@ -157,9 +148,7 @@ class RequestTest extends TestCase
+ new Request('GET', '/', [], null, '1.1'.$lineSeparator.'X-Injected: yes');
+ }
+
+- /**
+- * @dataProvider startLineSeparatorProvider
+- */
++ #[DataProvider('startLineSeparatorProvider')]
+ public function testWithProtocolVersionRejectsLineSeparators(string $lineSeparator): void
+ {
+ $r = new Request('GET', '/');
+@@ -249,9 +238,7 @@ class RequestTest extends TestCase
self::assertSame('', $r->getHeaderLine('Bar'));
}
@@ -247,7 +291,7 @@
public function testContainsNotAllowedCharsOnHeaderField($header): void
{
$this->expectExceptionMessage(
-@@ -223,9 +218,7 @@ class RequestTest extends TestCase
+@@ -274,9 +261,7 @@ class RequestTest extends TestCase
return [[' key '], ['key '], [' key'], ['key/'], ['key('], ['key\\'], [' ']];
}
@@ -258,7 +302,7 @@
public function testContainsAllowedCharsOnHeaderField($header): void
{
$r = new Request(
-@@ -329,9 +322,7 @@ class RequestTest extends TestCase
+@@ -380,9 +365,7 @@ class RequestTest extends TestCase
self::assertSame('example.com:8080', $request->getHeaderLine('Host'));
}
@@ -270,7 +314,7 @@
{
$this->expectException(\InvalidArgumentException::class);
diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php
-index 70d3ecc..2ce74e4 100644
+index d15d726..c05eb9e 100644
--- a/tests/ResponseTest.php
+++ b/tests/ResponseTest.php
@@ -6,6 +6,7 @@ namespace GuzzleHttp\Tests\Psr7;
@@ -281,7 +325,51 @@
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\StreamInterface;
-@@ -253,9 +254,7 @@ class ResponseTest extends TestCase
+@@ -135,9 +136,7 @@ class ResponseTest extends TestCase
+ self::assertSame('1000', $r->getProtocolVersion());
+ }
+
+- /**
+- * @dataProvider startLineSeparatorProvider
+- */
++ #[DataProvider('startLineSeparatorProvider')]
+ public function testConstructWithLineSeparatorsInProtocolVersion(string $lineSeparator): void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+@@ -145,9 +144,7 @@ class ResponseTest extends TestCase
+ new Response(200, [], null, '1.1'.$lineSeparator.'X-Injected: yes');
+ }
+
+- /**
+- * @dataProvider startLineSeparatorProvider
+- */
++ #[DataProvider('startLineSeparatorProvider')]
+ public function testWithProtocolVersionRejectsLineSeparators(string $lineSeparator): void
+ {
+ $r = new Response();
+@@ -157,9 +154,7 @@ class ResponseTest extends TestCase
+ $r->withProtocolVersion('1.1'.$lineSeparator.'X-Injected: yes');
+ }
+
+- /**
+- * @dataProvider startLineSeparatorProvider
+- */
++ #[DataProvider('startLineSeparatorProvider')]
+ public function testConstructWithLineSeparatorsInReasonPhrase(string $lineSeparator): void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+@@ -167,9 +162,7 @@ class ResponseTest extends TestCase
+ new Response(200, [], null, '1.1', 'OK'.$lineSeparator.'X-Injected: yes');
+ }
+
+- /**
+- * @dataProvider startLineSeparatorProvider
+- */
++ #[DataProvider('startLineSeparatorProvider')]
+ public function testWithStatusRejectsLineSeparatorsInReasonPhrase(string $lineSeparator): void
+ {
+ $r = new Response();
+@@ -304,9 +297,7 @@ class ResponseTest extends TestCase
self::assertSame('bar', $r->getHeaderLine('123'));
}
@@ -292,7 +380,7 @@
public function testConstructResponseInvalidHeader($header, $headerValue, $expectedMessage): void
{
$this->expectException(\InvalidArgumentException::class);
-@@ -272,9 +271,7 @@ class ResponseTest extends TestCase
+@@ -323,9 +314,7 @@ class ResponseTest extends TestCase
];
}
@@ -303,7 +391,7 @@
public function testWithInvalidHeader($header, $headerValue, $expectedMessage): void
{
$r = new Response();
-@@ -323,10 +320,9 @@ class ResponseTest extends TestCase
+@@ -374,10 +363,9 @@ class ResponseTest extends TestCase
}
/**
@@ -315,7 +403,7 @@
public function testConstructResponseWithNonIntegerStatusCode($invalidValues): void
{
$this->expectException(\TypeError::class);
-@@ -334,10 +330,9 @@ class ResponseTest extends TestCase
+@@ -385,10 +373,9 @@ class ResponseTest extends TestCase
}
/**
@@ -327,7 +415,7 @@
public function testResponseChangeStatusCodeWithNonInteger($invalidValues): void
{
$response = new Response();
-@@ -357,10 +352,9 @@ class ResponseTest extends TestCase
+@@ -408,10 +395,9 @@ class ResponseTest extends TestCase
}
/**
@@ -339,7 +427,7 @@
public function testConstructResponseWithInvalidRangeStatusCode($invalidValues): void
{
$this->expectException(\InvalidArgumentException::class);
-@@ -369,10 +363,9 @@ class ResponseTest extends TestCase
+@@ -420,10 +406,9 @@ class ResponseTest extends TestCase
}
/**
diff -Nru php-guzzlehttp-psr7-2.7.1/debian/patches/0009-Mitigate-CRLF-Injection-in-HTTP-Start-Line-Serializa.patch php-guzzlehttp-psr7-2.7.1/debian/patches/0009-Mitigate-CRLF-Injection-in-HTTP-Start-Line-Serializa.patch
--- php-guzzlehttp-psr7-2.7.1/debian/patches/0009-Mitigate-CRLF-Injection-in-HTTP-Start-Line-Serializa.patch 1970-01-01 01:00:00.000000000 +0100
+++ php-guzzlehttp-psr7-2.7.1/debian/patches/0009-Mitigate-CRLF-Injection-in-HTTP-Start-Line-Serializa.patch 2026-06-19 08:13:30.000000000 +0200
@@ -0,0 +1,297 @@
+From: Graham Campbell <[email protected]>
+Date: Thu, 18 Jun 2026 10:47:13 +0100
+Subject: Mitigate CRLF Injection in HTTP Start-Line Serialization (#798)
+
+Origin: backport, https://github.com/guzzle/psr7/commit/f3f94b4d344fa9788afb9558ac032923a9767380
+Bug: https://github.com/guzzle/psr7/security/advisories/GHSA-vm85-hxw5-5432
+Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-55766
+---
+ src/Message.php | 8 ++++++++
+ src/MessageTrait.php | 19 +++++++++++++++++++
+ src/Request.php | 4 ++++
+ src/Response.php | 12 +++++++++---
+ tests/MessageTest.php | 14 ++++++++++++++
+ tests/RequestTest.php | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ tests/ResponseTest.php | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ 7 files changed, 156 insertions(+), 3 deletions(-)
+
+diff --git a/src/Message.php b/src/Message.php
+index d3aafca..3bd88eb 100644
+--- a/src/Message.php
++++ b/src/Message.php
+@@ -219,6 +219,10 @@ final class Message
+ public static function parseRequest(string $message): RequestInterface
+ {
+ $data = self::parseMessage($message);
++ if (strpbrk($data['start-line'], "\r\n") !== false) {
++ throw new \InvalidArgumentException('Invalid request string');
++ }
++
+ $matches = [];
+ if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
+ throw new \InvalidArgumentException('Invalid request string');
+@@ -245,6 +249,10 @@ final class Message
+ public static function parseResponse(string $message): ResponseInterface
+ {
+ $data = self::parseMessage($message);
++ if (strpbrk($data['start-line'], "\r\n") !== false) {
++ throw new \InvalidArgumentException('Invalid response string');
++ }
++
+ // According to https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
+ // the space between status-code and reason-phrase is required. But
+ // browsers accept responses without space and reason as well.
+diff --git a/src/MessageTrait.php b/src/MessageTrait.php
+index 65dbc4b..8341ddb 100644
+--- a/src/MessageTrait.php
++++ b/src/MessageTrait.php
+@@ -31,6 +31,8 @@ trait MessageTrait
+
+ public function withProtocolVersion($version): MessageInterface
+ {
++ $this->assertProtocolVersion($version);
++
+ if ($this->protocol === $version) {
+ return $this;
+ }
+@@ -233,6 +235,23 @@ trait MessageTrait
+ }
+ }
+
++ /**
++ * @param mixed $version
++ */
++ private function assertProtocolVersion($version): void
++ {
++ if (is_string($version)) {
++ $this->assertNoLineSeparators($version, 'Protocol version');
++ }
++ }
++
++ private function assertNoLineSeparators(string $value, string $field): void
++ {
++ if (strpbrk($value, "\r\n") !== false) {
++ throw new \InvalidArgumentException($field.' must not contain CR or LF characters.');
++ }
++ }
++
+ /**
+ * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
+ *
+diff --git a/src/Request.php b/src/Request.php
+index 743c5d2..bae3349 100644
+--- a/src/Request.php
++++ b/src/Request.php
+@@ -40,6 +40,8 @@ class Request implements RequestInterface
+ string $version = '1.1'
+ ) {
+ $this->assertMethod($method);
++ $this->assertProtocolVersion($version);
++
+ if (!($uri instanceof UriInterface)) {
+ $uri = new Uri($uri);
+ }
+@@ -159,5 +161,7 @@ class Request implements RequestInterface
+ if (!is_string($method) || $method === '') {
+ throw new InvalidArgumentException('Method must be a non-empty string.');
+ }
++
++ $this->assertNoLineSeparators($method, 'Method');
+ }
+ }
+diff --git a/src/Response.php b/src/Response.php
+index 34e612f..1f10b67 100644
+--- a/src/Response.php
++++ b/src/Response.php
+@@ -99,6 +99,7 @@ class Response implements ResponseInterface
+ ?string $reason = null
+ ) {
+ $this->assertStatusCodeRange($status);
++ $this->assertProtocolVersion($version);
+
+ $this->statusCode = $status;
+
+@@ -108,11 +109,14 @@ class Response implements ResponseInterface
+
+ $this->setHeaders($headers);
+ if ($reason == '' && isset(self::PHRASES[$this->statusCode])) {
+- $this->reasonPhrase = self::PHRASES[$this->statusCode];
++ $reasonPhrase = self::PHRASES[$this->statusCode];
+ } else {
+- $this->reasonPhrase = (string) $reason;
++ $reasonPhrase = (string) $reason;
+ }
+
++ $this->assertNoLineSeparators($reasonPhrase, 'Reason phrase');
++ $this->reasonPhrase = $reasonPhrase;
++
+ $this->protocol = $version;
+ }
+
+@@ -137,7 +141,9 @@ class Response implements ResponseInterface
+ if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) {
+ $reasonPhrase = self::PHRASES[$new->statusCode];
+ }
+- $new->reasonPhrase = (string) $reasonPhrase;
++ $reasonPhrase = (string) $reasonPhrase;
++ $this->assertNoLineSeparators($reasonPhrase, 'Reason phrase');
++ $new->reasonPhrase = $reasonPhrase;
+
+ return $new;
+ }
+diff --git a/tests/MessageTest.php b/tests/MessageTest.php
+index 42909be..1862a95 100644
+--- a/tests/MessageTest.php
++++ b/tests/MessageTest.php
+@@ -231,6 +231,13 @@ class MessageTest extends TestCase
+ Psr7\Message::parseRequest("HTTP/1.1 200 OK\r\n\r\n");
+ }
+
++ public function testParseRequestRejectsStartLineWithBareCarriageReturn(): void
++ {
++ $this->expectException(\InvalidArgumentException::class);
++
++ Psr7\Message::parseRequest("GET / HTTP/1.1\rX-Injected: yes\nHost: foo.com\n\n");
++ }
++
+ public function testParsesResponseMessages(): void
+ {
+ $res = "HTTP/1.0 200 OK\r\nFoo: Bar\r\nBaz: Bam\r\nBaz: Qux\r\n\r\nTest";
+@@ -309,6 +316,13 @@ class MessageTest extends TestCase
+ Psr7\Message::parseResponse("GET / HTTP/1.1\r\n\r\n");
+ }
+
++ public function testParseResponseRejectsStartLineWithBareCarriageReturn(): void
++ {
++ $this->expectException(\InvalidArgumentException::class);
++
++ Psr7\Message::parseResponse("HTTP/1.1 200 OK\rX-Injected: yes\n\n");
++ }
++
+ public function testMessageBodySummaryWithSmallBody(): void
+ {
+ $message = new Psr7\Response(200, [], 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.');
+diff --git a/tests/RequestTest.php b/tests/RequestTest.php
+index 3a62c57..9acc022 100644
+--- a/tests/RequestTest.php
++++ b/tests/RequestTest.php
+@@ -125,6 +125,57 @@ class RequestTest extends TestCase
+ ];
+ }
+
++ /**
++ * @dataProvider startLineSeparatorProvider
++ */
++ public function testConstructWithLineSeparatorsInMethod(string $lineSeparator): void
++ {
++ $this->expectException(\InvalidArgumentException::class);
++
++ new Request('GET'.$lineSeparator.'X-Injected: yes', '/');
++ }
++
++ /**
++ * @dataProvider startLineSeparatorProvider
++ */
++ public function testWithMethodRejectsLineSeparators(string $lineSeparator): void
++ {
++ $r = new Request('GET', '/');
++
++ $this->expectException(\InvalidArgumentException::class);
++
++ $r->withMethod('GET'.$lineSeparator.'X-Injected: yes');
++ }
++
++ /**
++ * @dataProvider startLineSeparatorProvider
++ */
++ public function testConstructWithLineSeparatorsInProtocolVersion(string $lineSeparator): void
++ {
++ $this->expectException(\InvalidArgumentException::class);
++
++ new Request('GET', '/', [], null, '1.1'.$lineSeparator.'X-Injected: yes');
++ }
++
++ /**
++ * @dataProvider startLineSeparatorProvider
++ */
++ public function testWithProtocolVersionRejectsLineSeparators(string $lineSeparator): void
++ {
++ $r = new Request('GET', '/');
++
++ $this->expectException(\InvalidArgumentException::class);
++
++ $r->withProtocolVersion('1.1'.$lineSeparator.'X-Injected: yes');
++ }
++
++ public static function startLineSeparatorProvider(): iterable
++ {
++ yield 'line feed' => ["\n"];
++ yield 'carriage return' => ["\r"];
++ yield 'CRLF' => ["\r\n"];
++ }
++
+ public function testSameInstanceWhenSameUri(): void
+ {
+ $r1 = new Request('GET', 'http://foo.com');
+diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php
+index 70d3ecc..d15d726 100644
+--- a/tests/ResponseTest.php
++++ b/tests/ResponseTest.php
+@@ -135,6 +135,57 @@ class ResponseTest extends TestCase
+ self::assertSame('1000', $r->getProtocolVersion());
+ }
+
++ /**
++ * @dataProvider startLineSeparatorProvider
++ */
++ public function testConstructWithLineSeparatorsInProtocolVersion(string $lineSeparator): void
++ {
++ $this->expectException(\InvalidArgumentException::class);
++
++ new Response(200, [], null, '1.1'.$lineSeparator.'X-Injected: yes');
++ }
++
++ /**
++ * @dataProvider startLineSeparatorProvider
++ */
++ public function testWithProtocolVersionRejectsLineSeparators(string $lineSeparator): void
++ {
++ $r = new Response();
++
++ $this->expectException(\InvalidArgumentException::class);
++
++ $r->withProtocolVersion('1.1'.$lineSeparator.'X-Injected: yes');
++ }
++
++ /**
++ * @dataProvider startLineSeparatorProvider
++ */
++ public function testConstructWithLineSeparatorsInReasonPhrase(string $lineSeparator): void
++ {
++ $this->expectException(\InvalidArgumentException::class);
++
++ new Response(200, [], null, '1.1', 'OK'.$lineSeparator.'X-Injected: yes');
++ }
++
++ /**
++ * @dataProvider startLineSeparatorProvider
++ */
++ public function testWithStatusRejectsLineSeparatorsInReasonPhrase(string $lineSeparator): void
++ {
++ $r = new Response();
++
++ $this->expectException(\InvalidArgumentException::class);
++
++ $r->withStatus(200, 'OK'.$lineSeparator.'X-Injected: yes');
++ }
++
++ public static function startLineSeparatorProvider(): iterable
++ {
++ yield 'line feed' => ["\n"];
++ yield 'carriage return' => ["\r"];
++ yield 'CRLF' => ["\r\n"];
++ }
++
+ public function testSameInstanceWhenSameProtocol(): void
+ {
+ $r = new Response();
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 2026-05-30 13:39:01.000000000 +0200
+++ php-guzzlehttp-psr7-2.7.1/debian/patches/series 2026-06-19 08:13:30.000000000 +0200
@@ -6,4 +6,5 @@
0006-Normalize-global-header-values-718.patch
0007-Reject-malformed-Host-authorities-717.patch
0008-Reject-control-characters-in-URI-hosts-715.patch
+0009-Mitigate-CRLF-Injection-in-HTTP-Start-Line-Serializa.patch
0004-Modernize-PHPUnit-syntax.patch
signature.asc
Description: PGP signature
--- End Message ---