Skip to content

Commit 7160f66

Browse files
authored
Merge pull request #8153 from kenjis/fix-IncomingRequest-getJSON
fix: Validation raises TypeError when invalid JSON comes
2 parents 83e4b71 + d0bf4bc commit 7160f66

File tree

6 files changed

+76
-1
lines changed

6 files changed

+76
-1
lines changed

system/HTTP/Exceptions/HTTPException.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@ public static function forInvalidNegotiationType(string $type)
7272
return new static(lang('HTTP.invalidNegotiationType', [$type]));
7373
}
7474

75+
/**
76+
* Thrown in IncomingRequest when the json_decode() produces
77+
* an error code other than JSON_ERROR_NONE.
78+
*
79+
* @param string $error The error message
80+
*
81+
* @return static
82+
*/
83+
public static function forInvalidJSON(?string $error = null)
84+
{
85+
return new static(lang('HTTP.invalidJSON', [$error]));
86+
}
87+
7588
/**
7689
* For Message
7790
*

system/HTTP/IncomingRequest.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,10 +573,22 @@ public function getVar($index = null, $filter = null, $flags = null)
573573
* @see http://php.net/manual/en/function.json-decode.php
574574
*
575575
* @return array|bool|float|int|stdClass|null
576+
*
577+
* @throws HTTPException When the body is invalid as JSON.
576578
*/
577579
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0)
578580
{
579-
return json_decode($this->body ?? '', $assoc, $depth, $options);
581+
if ($this->body === null) {
582+
return null;
583+
}
584+
585+
$result = json_decode($this->body, $assoc, $depth, $options);
586+
587+
if (json_last_error() !== JSON_ERROR_NONE) {
588+
throw HTTPException::forInvalidJSON(json_last_error_msg());
589+
}
590+
591+
return $result;
580592
}
581593

582594
/**

system/Language/en/HTTP.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

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

2324
// Message
2425
'invalidHTTPProtocol' => 'Invalid HTTP Protocol Version: {0}',

tests/system/HTTP/IncomingRequestTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use CodeIgniter\Test\CIUnitTestCase;
1919
use Config\App;
2020
use InvalidArgumentException;
21+
use JsonException;
2122
use TypeError;
2223

2324
/**
@@ -528,6 +529,32 @@ public function testGetJSONReturnsNullFromNullBody(): void
528529
$this->assertNull($request->getJSON());
529530
}
530531

532+
public function testGetJSONWithInvalidJSONString(): void
533+
{
534+
$this->expectException(HTTPException::class);
535+
$this->expectExceptionMessage('Failed to parse JSON string. Error: Syntax error');
536+
537+
$config = new App();
538+
$config->baseURL = 'http://example.com/';
539+
$json = 'Invalid JSON string';
540+
$request = $this->createRequest($config, $json);
541+
542+
$request->getJSON();
543+
}
544+
545+
public function testGetJSONWithJsonThrowOnErrorAndInvalidJSONString(): void
546+
{
547+
$this->expectException(JsonException::class);
548+
$this->expectExceptionMessage('Syntax error');
549+
550+
$config = new App();
551+
$config->baseURL = 'http://example.com/';
552+
$json = 'Invalid JSON string';
553+
$request = $this->createRequest($config, $json);
554+
555+
$request->getJSON(false, 512, JSON_THROW_ON_ERROR);
556+
}
557+
531558
public function testCanGrabGetRawInput(): void
532559
{
533560
$rawstring = 'username=admin001&role=administrator&usepass=0';

tests/system/Validation/ValidationTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace CodeIgniter\Validation;
1313

14+
use CodeIgniter\HTTP\Exceptions\HTTPException;
1415
use CodeIgniter\HTTP\IncomingRequest;
1516
use CodeIgniter\HTTP\SiteURI;
1617
use CodeIgniter\HTTP\UserAgent;
@@ -789,6 +790,25 @@ public function testJsonInput(): void
789790
unset($_SERVER['CONTENT_TYPE']);
790791
}
791792

793+
public function testJsonInputInvalid(): void
794+
{
795+
$this->expectException(HTTPException::class);
796+
$this->expectExceptionMessage('Failed to parse JSON string. Error: Syntax error');
797+
798+
$config = new App();
799+
$json = 'invalid';
800+
$request = new IncomingRequest($config, new URI(), $json, new UserAgent());
801+
$request->setHeader('Content-Type', 'application/json');
802+
803+
$rules = [
804+
'role' => 'if_exist|max_length[5]',
805+
];
806+
$this->validation
807+
->withRequest($request->withMethod('POST'))
808+
->setRules($rules)
809+
->run();
810+
}
811+
792812
/**
793813
* @see https://github.com/codeigniter4/CodeIgniter4/issues/6466
794814
*/

user_guide_src/source/changelogs/v4.4.4.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ and Traditional rules validate data of non-string types.
3131
Message Changes
3232
***************
3333

34+
- Added ``HTTP.invalidJSON`` error message.
35+
3436
*******
3537
Changes
3638
*******

0 commit comments

Comments
 (0)