Skip to content

Commit

Permalink
Fix validation of header names and values to MessageTrait (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
devanych committed Apr 17, 2023
1 parent 51163fa commit d2e0855
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/MessageTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ private function registerProtocolVersion(string $protocol): void
*/
private function normalizeHeaderName($name): string
{
if (!is_string($name) || !preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $name)) {
if (!is_string($name) || !preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $name)) {
throw new InvalidArgumentException(sprintf(
'`%s` is not valid header name.',
(is_object($name) ? get_class($name) : (is_string($name) ? $name : gettype($name)))
Expand All @@ -403,7 +403,7 @@ private function normalizeHeaderValue($value): array
}

foreach ($value as $v) {
if ((!is_string($v) && !is_numeric($v)) || !preg_match('/^[ \t\x21-\x7E\x80-\xFF]*$/', (string) $v)) {
if ((!is_string($v) && !is_numeric($v)) || !preg_match('/^[ \t\x21-\x7E\x80-\xFF]*$/D', (string) $v)) {
throw new InvalidArgumentException(sprintf(
'"%s" is not valid header value.',
(is_object($v) ? get_class($v) : (is_string($v) ? $v : gettype($v)))
Expand Down
2 changes: 1 addition & 1 deletion src/UploadedFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public function getStream(): StreamInterface
/**
* {@inheritdoc}
*
* @psalm-suppress DocblockTypeContradiction
* @psalm-suppress NoValue, RedundantCondition, TypeDoesNotContainType
*/
public function moveTo($targetPath): void
{
Expand Down
46 changes: 46 additions & 0 deletions tests/RequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use stdClass;

use function array_merge;
use function chr;

final class RequestTest extends TestCase
{
Expand Down Expand Up @@ -175,4 +176,49 @@ private function getInvalidValues(array $values = []): array

return $common;
}

/**
* @dataProvider provideHeaderValuesContainingNotAllowedChars
*/
public function testCannotHaveHeaderWithInvalidValue(string $name): void
{
$this->expectException(InvalidArgumentException::class);
$request = new Request('GET', 'https://example.com/');
$request->withHeader($name, 'Bar');
}

public static function provideHeaderValuesContainingNotAllowedChars(): array
{
// Explicit tests for newlines as the most common exploit vector.
$tests = [
["new\nline"],
["new\r\nline"],
["new\rline"],
["new\r\n line"],
["newline\n"],
["\nnewline"],
["newline\r\n"],
["\n\rnewline"],
];

for ($i = 0; $i <= 0xff; $i++) {
if (chr($i) === "\t") {
continue;
}
if (chr($i) === " ") {
continue;
}
if ($i >= 0x21 && $i <= 0x7e) {
continue;
}
if ($i >= 0x80) {
continue;
}

$tests[] = ["foo" . chr($i) . "bar"];
$tests[] = ["foo" . chr($i)];
}

return $tests;
}
}
30 changes: 30 additions & 0 deletions tests/ResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,34 @@ private function getInvalidValues(array $values = []): array

return $common;
}

/**
* @dataProvider invalidWithHeaderProvider
*/
public function testWithInvalidHeader($header, $headerValue, $expectedMessage): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage($expectedMessage);
$this->response->withHeader($header, $headerValue);
}

public static function invalidWithHeaderProvider(): array
{
return [
['foo', [], 'Header value must be a string or an array of strings, empty array given.'],
['foo', new stdClass(), '"stdClass" is not valid header value.'],
[[], 'foo', '`array` is not valid header name.'],
[false, 'foo', '`boolean` is not valid header name.'],
[new stdClass(), 'foo', '`stdClass` is not valid header name.'],
['', 'foo', '`` is not valid header name.'],
["Content-Type\r\n\r\n", 'foo', "`Content-Type\r\n\r\n` is not valid header name."],
["Content-Type\r\n", 'foo', "`Content-Type\r\n` is not valid header name."],
["Content-Type\n", 'foo', "`Content-Type\n` is not valid header name."],
["\r\nContent-Type", 'foo',"`\r\nContent-Type` is not valid header name."],
["\nContent-Type", 'foo', "`\nContent-Type` is not valid header name."],
["\n", 'foo', "`\n` is not valid header name."],
["\r\n", 'foo', "`\r\n` is not valid header name."],
["\t", 'foo', "`\t` is not valid header name."],
];
}
}

0 comments on commit d2e0855

Please sign in to comment.