Skip to content

Commit 1029a26

Browse files
authored
Merge pull request from GHSA-wjfc-pgfp-pv9c
Improper Input Validation in headers
1 parent 52887ca commit 1029a26

File tree

3 files changed

+79
-2
lines changed

3 files changed

+79
-2
lines changed

src/MessageTrait.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ private function setHeaders(array $headers): void
187187
*/
188188
private function validateAndTrimHeader($header, $values): array
189189
{
190-
if (!\is_string($header) || 1 !== \preg_match("@^[!#$%&'*+.^_`|~0-9A-Za-z-]+$@", $header)) {
190+
if (!\is_string($header) || 1 !== \preg_match("@^[!#$%&'*+.^_`|~0-9A-Za-z-]+$@D", $header)) {
191191
throw new \InvalidArgumentException('Header name must be an RFC 7230 compatible string');
192192
}
193193

@@ -207,7 +207,7 @@ private function validateAndTrimHeader($header, $values): array
207207
// Assert Non empty array
208208
$returnValues = [];
209209
foreach ($values as $v) {
210-
if ((!\is_numeric($v) && !\is_string($v)) || 1 !== \preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@", (string) $v)) {
210+
if ((!\is_numeric($v) && !\is_string($v)) || 1 !== \preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@D", (string) $v)) {
211211
throw new \InvalidArgumentException('Header values must be RFC 7230 compatible strings');
212212
}
213213

tests/RequestTest.php

+46
Original file line numberDiff line numberDiff line change
@@ -294,4 +294,50 @@ public function testUpdateHostFromUri()
294294
$request = $request->withUri(new Uri('https://nyholm.tech:443'));
295295
$this->assertEquals('nyholm.tech', $request->getHeaderLine('Host'));
296296
}
297+
298+
/**
299+
* @dataProvider provideHeaderValuesContainingNotAllowedChars
300+
*/
301+
public function testCannotHaveHeaderWithInvalidValue(string $name)
302+
{
303+
$this->expectException(\InvalidArgumentException::class);
304+
$this->expectExceptionMessage('Header name must be an RFC 7230 compatible string');
305+
$r = new Request('GET', 'https://example.com/');
306+
$r->withHeader($name, 'Bar');
307+
}
308+
309+
public static function provideHeaderValuesContainingNotAllowedChars(): array
310+
{
311+
// Explicit tests for newlines as the most common exploit vector.
312+
$tests = [
313+
["new\nline"],
314+
["new\r\nline"],
315+
["new\rline"],
316+
["new\r\n line"],
317+
["newline\n"],
318+
["\nnewline"],
319+
["newline\r\n"],
320+
["\n\rnewline"],
321+
];
322+
323+
for ($i = 0; $i <= 0xFF; ++$i) {
324+
if ("\t" == \chr($i)) {
325+
continue;
326+
}
327+
if (' ' == \chr($i)) {
328+
continue;
329+
}
330+
if ($i >= 0x21 && $i <= 0x7E) {
331+
continue;
332+
}
333+
if ($i >= 0x80) {
334+
continue;
335+
}
336+
337+
$tests[] = ['foo' . \chr($i) . 'bar'];
338+
$tests[] = ['foo' . \chr($i)];
339+
}
340+
341+
return $tests;
342+
}
297343
}

tests/ResponseTest.php

+31
Original file line numberDiff line numberDiff line change
@@ -274,4 +274,35 @@ public function testHeaderValuesAreTrimmed($r)
274274
$this->assertSame('Foo', $r->getHeaderLine('OWS'));
275275
$this->assertSame(['Foo'], $r->getHeader('OWS'));
276276
}
277+
278+
/**
279+
* @dataProvider invalidWithHeaderProvider
280+
*/
281+
public function testWithInvalidHeader($header, $headerValue, $expectedMessage): void
282+
{
283+
$r = new Response();
284+
$this->expectException(\InvalidArgumentException::class);
285+
$this->expectExceptionMessage($expectedMessage);
286+
$r->withHeader($header, $headerValue);
287+
}
288+
289+
public function invalidWithHeaderProvider(): iterable
290+
{
291+
return [
292+
['foo', [], 'Header values must be a string or an array of strings, empty array given'],
293+
['foo', new \stdClass(), 'Header values must be RFC 7230 compatible strings'],
294+
[[], 'foo', 'Header name must be an RFC 7230 compatible string'],
295+
[false, 'foo', 'Header name must be an RFC 7230 compatible string'],
296+
[new \stdClass(), 'foo', 'Header name must be an RFC 7230 compatible string'],
297+
['', 'foo', 'Header name must be an RFC 7230 compatible string'],
298+
["Content-Type\r\n\r\n", 'foo', 'Header name must be an RFC 7230 compatible string'],
299+
["Content-Type\r\n", 'foo', 'Header name must be an RFC 7230 compatible string'],
300+
["Content-Type\n", 'foo', 'Header name must be an RFC 7230 compatible string'],
301+
["\r\nContent-Type", 'foo', 'Header name must be an RFC 7230 compatible string'],
302+
["\nContent-Type", 'foo', 'Header name must be an RFC 7230 compatible string'],
303+
["\n", 'foo', 'Header name must be an RFC 7230 compatible string'],
304+
["\r\n", 'foo', 'Header name must be an RFC 7230 compatible string'],
305+
["\t", 'foo', 'Header name must be an RFC 7230 compatible string'],
306+
];
307+
}
277308
}

0 commit comments

Comments
 (0)