Skip to content

Commit

Permalink
Release 1.8.4 (#486)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim Düsterhus <tim@bastelstu.be>
  • Loading branch information
GrahamCampbell and TimWolla authored Mar 20, 2022
1 parent 1afdd86 commit 902db15
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## Unreleased

## 1.8.4 - 2022-03-20

### Fixed

- Validate header values properly

## 1.8.3 - 2021-10-05

### Fixed
Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
},
"config": {
"preferred-install": "dist",
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"bamarni/composer-bin-plugin": true
}
}
}
66 changes: 61 additions & 5 deletions src/MessageTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,17 +157,22 @@ private function setHeaders(array $headers)
}
}

/**
* @param mixed $value
*
* @return string[]
*/
private function normalizeHeaderValue($value)
{
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 @@ -178,13 +183,13 @@ private function normalizeHeaderValue($value)
* header-field = field-name ":" OWS field-value OWS
* OWS = *( SP / HTAB )
*
* @param string[] $values Header values
* @param mixed[] $values Header values
*
* @return string[] Trimmed header values
*
* @see https://tools.ietf.org/html/rfc7230#section-3.2.4
*/
private function trimHeaderValues(array $values)
private function trimAndValidateHeaderValues(array $values)
{
return array_map(function ($value) {
if (!is_scalar($value) && null !== $value) {
Expand All @@ -194,10 +199,20 @@ private function trimHeaderValues(array $values)
));
}

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

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

/**
* @see https://tools.ietf.org/html/rfc7230#section-3.2
*
* @param mixed $header
*
* @return void
*/
private function assertHeader($header)
{
if (!is_string($header)) {
Expand All @@ -210,5 +225,46 @@ private function assertHeader($header)
if ($header === '') {
throw new \InvalidArgumentException('Header name can not be empty.');
}

if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $header)) {
throw new \InvalidArgumentException(
sprintf(
'"%s" is not valid header name',
$header
)
);
}
}

/**
* @param string $value
*
* @return 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($value)
{
// 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));
}
}
}
50 changes: 50 additions & 0 deletions tests/RequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,54 @@ public function testAddsPortToHeaderAndReplacePreviousPort()
$r = $r->withUri(new Uri('http://foo.com:8125/bar'));
self::assertSame('foo.com:8125', $r->getHeaderLine('host'));
}

/**
* @dataProvider provideHeaderValuesContainingNotAllowedChars
*/
public function testContainsNotAllowedCharsOnHeaderValue($value)
{
$this->expectExceptionGuzzle('InvalidArgumentException', sprintf('"%s" is not valid header value', $value));
$r = new Request(
'GET',
'http://foo.com/baz?bar=bam',
[
'testing' => $value
]
);
}

/**
* @return iterable
*/
public function provideHeaderValuesContainingNotAllowedChars()
{
// 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 902db15

Please sign in to comment.