Skip to content

fix: #9407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed

fix: #9407

Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion system/HTTP/Header.php
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@

namespace CodeIgniter\HTTP;

use InvalidArgumentException;
use Stringable;

/**
@@ -54,7 +55,7 @@ class Header implements Stringable
*/
public function __construct(string $name, $value = null)
{
$this->name = $name;
$this->setName($name);
$this->setValue($value);
}

@@ -81,9 +82,13 @@ public function getValue()
* Sets the name of the header, overwriting any previous value.
*
* @return $this
*
* @throws InvalidArgumentException
*/
public function setName(string $name)
{
$this->validateName($name);

$this->name = $name;

return $this;
@@ -95,11 +100,15 @@ public function setName(string $name)
* @param array<int|string, array<string, string>|string>|string|null $value
*
* @return $this
*
* @throws InvalidArgumentException
*/
public function setValue($value = null)
{
$this->value = is_array($value) ? $value : (string) $value;

$this->validateValue($value);

return $this;
}

@@ -110,13 +119,17 @@ public function setValue($value = null)
* @param array<string, string>|string|null $value
*
* @return $this
*
* @throws InvalidArgumentException
*/
public function appendValue($value = null)
{
if ($value === null) {
return $this;
}

$this->validateValue($value);

if (! is_array($this->value)) {
$this->value = [$this->value];
}
@@ -135,9 +148,13 @@ public function appendValue($value = null)
* @param array<string, string>|string|null $value
*
* @return $this
*
* @throws InvalidArgumentException
*/
public function prependValue($value = null)
{
$this->validateValue($value);

if ($value === null) {
return $this;
}
@@ -193,4 +210,34 @@ public function __toString(): string
{
return $this->name . ': ' . $this->getValueLine();
}

/**
* @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
*
* @throws InvalidArgumentException
*/
private function validateName(string $name): void
{
if (preg_match("@^[!#$%&'*+.^_`|~0-9A-Za-z-]+$@D", $name) !== 1) {
throw new InvalidArgumentException('Header name must be an RFC 7230 compatible string.');
}
}

/**
* @param array<string, string>|string|null $value
*
* @throws InvalidArgumentException
*/
private function validateValue($value): void
{
if (is_string($value) && preg_match("@^[ \t\x21-\x7E\x80-\xFF]*$@D", $value) !== 1) {
throw new InvalidArgumentException('Header values must be RFC 7230 compatible strings.');
}

if (is_array($value)) {
array_map(function ($v): void {
$this->validateValue($v);
}, $value);
}
}
}
68 changes: 68 additions & 0 deletions tests/system/HTTP/HeaderTest.php
Original file line number Diff line number Diff line change
@@ -15,6 +15,8 @@

use CodeIgniter\Test\CIUnitTestCase;
use Error;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use stdClass;

@@ -234,4 +236,70 @@ public function testHeaderToStringShowsEntireHeader(): void

$this->assertSame($expected, (string) $header);
}

/**
* @param string $name
*/
#[DataProvider('invalidNamesProvider')]
public function testInvalidHeaderNames($name): void
{
$this->expectException(InvalidArgumentException::class);

new Header($name, 'text/html');
}

/**
* @return list<list<string>>
*/
public static function invalidNamesProvider(): array
{
return [
["Content-Type\r\n\r\n"],
["Content-Type\r\n"],
["Content-Type\n"],
["\tContent-Type\t"],
["\n\nContent-Type\n\n"],
["\r\nContent-Type"],
["\nContent-Type"],
["Content\r\n-Type"],
["\n"],
["\r\n"],
["\t"],
[' Content-Type '],
['Content - Type'],
[''],
];
}

/**
* @param array<int|string, array<string, string>|string>|string|null $value
*/
#[DataProvider('invalidValuesProvider')]
public function testInvalidHeaderValues($value): void
{
$this->expectException(InvalidArgumentException::class);

new Header('X-Test-Header', $value);
}

/**
* @return list<list<list<string>|string>>
*/
public static function invalidValuesProvider(): array
{
return [
["Header\n Value"],
["Header\r\n Value"],
["Header\r Value"],
["Header Value\n"],
["\nHeader Value"],
["Header Value\r\n"],
["\n\rHeader Value"],
["\n\nHeader Value\n\n"],
[
["Header\n Value"],
["Header\r\n Value"],
],
];
}
}
6 changes: 6 additions & 0 deletions user_guide_src/source/changelogs/v4.5.8.rst
Original file line number Diff line number Diff line change
@@ -14,6 +14,11 @@ Release Date: Unreleased
BREAKING
********

Header
======

Added validation of the name and value for ``CodeIgniter\HTTP\Header``. Some specific headers can cause the system to crash.

***************
Message Changes
***************
@@ -31,6 +36,7 @@ Bugs Fixed
**********

- **Database:** Fixed a bug where ``Builder::affectedRows()`` threw an error when the previous query call failed in ``Postgre`` and ``SQLSRV`` drivers.
- **Header:** Improper headers parsing. Line breaks and other incorrect characters in headers (``CodeIgniter\HTTP\Header``) may break the HTTP request. See https://datatracker.ietf.org/doc/html/rfc7230 for more details.

See the repo's
`CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_
Loading