Skip to content

Commit

Permalink
Merge branch '2.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
GrahamCampbell committed Mar 20, 2022
2 parents a7b4320 + 53491b6 commit 11d949b
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 5 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## 2.1.1 - 2022-03-20

### Fixed

- Validate header values properly

## 2.1.0 - 2021-10-06

### Changed
Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@
"bamarni/composer-bin-plugin": true
},
"preferred-install": "dist",
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"bamarni/composer-bin-plugin": true
}
}
}
39 changes: 35 additions & 4 deletions src/MessageTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,14 @@ private function setHeaders(array $headers): void
private function normalizeHeaderValue($value): array
{
if (!is_array($value)) {
return $this->trimHeaderValues([$value]);
return $this->trimAndValidateHeaderValues([$value]);
}

if (count($value) === 0) {
throw new \InvalidArgumentException('Header value can not be an empty array.');
}

return $this->trimHeaderValues($value);
return $this->trimAndValidateHeaderValues($value);
}

/**
Expand All @@ -195,7 +195,7 @@ private function normalizeHeaderValue($value): array
*
* @see https://tools.ietf.org/html/rfc7230#section-3.2.4
*/
private function trimHeaderValues(array $values): array
private function trimAndValidateHeaderValues(array $values): array
{
return array_map(function ($value) {
if (!is_scalar($value) && null !== $value) {
Expand All @@ -205,7 +205,10 @@ private function trimHeaderValues(array $values): array
));
}

return trim((string) $value, " \t");
$trimmed = trim((string) $value, " \t");
$this->assertValue($trimmed);

return $trimmed;
}, array_values($values));
}

Expand All @@ -232,4 +235,32 @@ private function assertHeader($header): void
);
}
}

/**
* @see https://tools.ietf.org/html/rfc7230#section-3.2
*
* field-value = *( field-content / obs-fold )
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
* field-vchar = VCHAR / obs-text
* VCHAR = %x21-7E
* obs-text = %x80-FF
* obs-fold = CRLF 1*( SP / HTAB )
*/
private function assertValue(string $value): void
{
// The regular expression intentionally does not support the obs-fold production, because as
// per RFC 7230#3.2.4:
//
// A sender MUST NOT generate a message that includes
// line folding (i.e., that has any field-value that contains a match to
// the obs-fold rule) unless the message is intended for packaging
// within the message/http media type.
//
// Clients must not send a request with line folding and a server sending folded headers is
// likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
// folding is not likely to break any legitimate use case.
if (! preg_match('/^(?:[\x21-\x7E\x80-\xFF](?:[\x20\x09]+[\x21-\x7E\x80-\xFF])?)*$/', $value)) {
throw new \InvalidArgumentException(sprintf('"%s" is not valid header value', $value));
}
}
}
49 changes: 49 additions & 0 deletions tests/RequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -293,4 +293,53 @@ public function testAddsPortToHeaderAndReplacePreviousPort(): void
$r = $r->withUri(new Uri('http://foo.com:8125/bar'));
self::assertSame('foo.com:8125', $r->getHeaderLine('host'));
}

/**
* @dataProvider provideHeaderValuesContainingNotAllowedChars
*/
public function testContainsNotAllowedCharsOnHeaderValue(string $value): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage(sprintf('"%s" is not valid header value', $value));

$r = new Request(
'GET',
'http://foo.com/baz?bar=bam',
[
'testing' => $value
]
);
}

public function provideHeaderValuesContainingNotAllowedChars(): iterable
{
// Explicit tests for newlines as the most common exploit vector.
$tests = [
["new\nline"],
["new\r\nline"],
["new\rline"],
// Line folding is technically allowed, but deprecated.
// We don't support it.
["new\r\n line"],
];

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"];
}

return $tests;
}
}

0 comments on commit 11d949b

Please sign in to comment.