Skip to content

fix: Validation raises TypeError when invalid JSON comes #8153

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

Merged
merged 5 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
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
13 changes: 13 additions & 0 deletions system/HTTP/Exceptions/HTTPException.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ public static function forInvalidNegotiationType(string $type)
return new static(lang('HTTP.invalidNegotiationType', [$type]));
}

/**
* Thrown in IncomingRequest when the json_decode() produces
* an error code other than JSON_ERROR_NONE.
*
* @param string $error The error message
*
* @return static
*/
public static function forInvalidJSON(?string $error = null)
{
return new static(lang('HTTP.invalidJSON', [$error]));
}

/**
* For Message
*
Expand Down
14 changes: 13 additions & 1 deletion system/HTTP/IncomingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -573,10 +573,22 @@ public function getVar($index = null, $filter = null, $flags = null)
* @see http://php.net/manual/en/function.json-decode.php
*
* @return array|bool|float|int|stdClass|null
*
* @throws HTTPException When the body is invalid as JSON.
*/
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0)
{
return json_decode($this->body ?? '', $assoc, $depth, $options);
if ($this->body === null) {
return null;
}

$result = json_decode($this->body, $assoc, $depth, $options);

if (json_last_error() !== JSON_ERROR_NONE) {
throw HTTPException::forInvalidJSON(json_last_error_msg());
}

return $result;
}

/**
Expand Down
1 change: 1 addition & 0 deletions system/Language/en/HTTP.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

// IncomingRequest
'invalidNegotiationType' => '"{0}" is not a valid negotiation type. Must be one of: media, charset, encoding, language.',
'invalidJSON' => 'Failed to parse JSON string. Error: {0}',

// Message
'invalidHTTPProtocol' => 'Invalid HTTP Protocol Version: {0}',
Expand Down
27 changes: 27 additions & 0 deletions tests/system/HTTP/IncomingRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use CodeIgniter\Test\CIUnitTestCase;
use Config\App;
use InvalidArgumentException;
use JsonException;
use TypeError;

/**
Expand Down Expand Up @@ -528,6 +529,32 @@ public function testGetJSONReturnsNullFromNullBody(): void
$this->assertNull($request->getJSON());
}

public function testGetJSONWithInvalidJSONString(): void
{
$this->expectException(HTTPException::class);
$this->expectExceptionMessage('Failed to parse JSON string. Error: Syntax error');

$config = new App();
$config->baseURL = 'http://example.com/';
$json = 'Invalid JSON string';
$request = $this->createRequest($config, $json);

$request->getJSON();
}

public function testGetJSONWithJsonThrowOnErrorAndInvalidJSONString(): void
{
$this->expectException(JsonException::class);
$this->expectExceptionMessage('Syntax error');

$config = new App();
$config->baseURL = 'http://example.com/';
$json = 'Invalid JSON string';
$request = $this->createRequest($config, $json);

$request->getJSON(false, 512, JSON_THROW_ON_ERROR);
}

public function testCanGrabGetRawInput(): void
{
$rawstring = 'username=admin001&role=administrator&usepass=0';
Expand Down
20 changes: 20 additions & 0 deletions tests/system/Validation/ValidationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace CodeIgniter\Validation;

use CodeIgniter\HTTP\Exceptions\HTTPException;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
Expand Down Expand Up @@ -789,6 +790,25 @@ public function testJsonInput(): void
unset($_SERVER['CONTENT_TYPE']);
}

public function testJsonInputInvalid(): void
{
$this->expectException(HTTPException::class);
$this->expectExceptionMessage('Failed to parse JSON string. Error: Syntax error');

$config = new App();
$json = 'invalid';
$request = new IncomingRequest($config, new URI(), $json, new UserAgent());
$request->setHeader('Content-Type', 'application/json');

$rules = [
'role' => 'if_exist|max_length[5]',
];
$this->validation
->withRequest($request->withMethod('POST'))
->setRules($rules)
->run();
}

/**
* @see https://github.com/codeigniter4/CodeIgniter4/issues/6466
*/
Expand Down
2 changes: 2 additions & 0 deletions user_guide_src/source/changelogs/v4.4.4.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ and Traditional rules validate data of non-string types.
Message Changes
***************

- Added ``HTTP.invalidJSON`` error message.

*******
Changes
*******
Expand Down