Skip to content

Commit 7126228

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into 4.8
2 parents fd20b62 + cdf18e8 commit 7126228

File tree

11 files changed

+148
-5
lines changed

11 files changed

+148
-5
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## [v4.7.2](https://github.com/codeigniter4/CodeIgniter4/tree/v4.7.2) (2026-03-24)
4+
[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.7.1...v4.7.2)
5+
6+
### Fixed Bugs
7+
8+
* fix: preserve JSON body when CSRF token is sent in header by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10064
9+
310
## [v4.7.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.7.1) (2026-03-22)
411
[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.7.0...v4.7.1)
512

admin/create-new-changelog.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ function replace_file_content(string $path, string $pattern, string $replace): v
9292
);
9393

9494
if (! in_array('--dry-run', $argv, true)) {
95+
system('git add ./system/CodeIgniter.php');
9596
system("git add {$newChangelog} {$changelogIndex}");
9697
system("git add {$newUpgrading} {$upgradingIndex}");
9798
system("git commit -m \"docs: add changelog and upgrade for v{$newVersion}\"");

phpdoc.dist.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<output>api/build/</output>
1111
<cache>api/cache/</cache>
1212
</paths>
13-
<version number="4.7.1">
13+
<version number="4.7.2">
1414
<api format="php">
1515
<source dsn=".">
1616
<path>system</path>

system/Security/Security.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,15 +273,18 @@ private function removeTokenInRequest(IncomingRequest $request): void
273273
$json = null;
274274
}
275275

276-
if (is_object($json) && property_exists($json, $tokenName)) {
277-
unset($json->{$tokenName});
278-
$request->setBody(json_encode($json));
276+
if (is_object($json)) {
277+
if (property_exists($json, $tokenName)) {
278+
unset($json->{$tokenName});
279+
$request->setBody(json_encode($json));
280+
}
279281

280282
return;
281283
}
282284

283285
// If the token is found in form-encoded data, we can safely remove it.
284286
parse_str($body, $result);
287+
285288
unset($result[$tokenName]);
286289
$request->setBody(http_build_query($result));
287290
}

tests/system/Security/SecurityCSRFSessionTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,37 @@ public function testCSRFVerifyJsonReturnsSelfOnMatch(): void
251251
$this->assertSame('{"foo":"bar"}', $request->getBody());
252252
}
253253

254+
public function testCSRFVerifyHeaderWithJsonBodyPreservesBody(): void
255+
{
256+
service('superglobals')->setServer('REQUEST_METHOD', 'POST');
257+
258+
$request = $this->createIncomingRequest();
259+
$body = '{"foo":"bar"}';
260+
261+
$request->setHeader('X-CSRF-TOKEN', '8b9218a55906f9dcc1dc263dce7f005a');
262+
$request->setBody($body);
263+
$security = $this->createSecurity();
264+
265+
$this->assertInstanceOf(Security::class, $security->verify($request));
266+
$this->assertLogged('info', 'CSRF token verified.');
267+
$this->assertSame($body, $request->getBody());
268+
}
269+
270+
public function testCSRFVerifyHeaderWithJsonBodyStripsTokenFromBody(): void
271+
{
272+
service('superglobals')->setServer('REQUEST_METHOD', 'POST');
273+
274+
$request = $this->createIncomingRequest();
275+
276+
$request->setHeader('X-CSRF-TOKEN', '8b9218a55906f9dcc1dc263dce7f005a');
277+
$request->setBody('{"csrf_test_name":"8b9218a55906f9dcc1dc263dce7f005a","foo":"bar"}');
278+
$security = $this->createSecurity();
279+
280+
$this->assertInstanceOf(Security::class, $security->verify($request));
281+
$this->assertLogged('info', 'CSRF token verified.');
282+
$this->assertSame('{"foo":"bar"}', $request->getBody());
283+
}
284+
254285
public function testRegenerateWithFalseSecurityRegenerateProperty(): void
255286
{
256287
service('superglobals')

tests/system/Security/SecurityTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,39 @@ public function testCsrfVerifyJsonReturnsSelfOnMatch(): void
204204
$this->assertSame('{"foo":"bar"}', $request->getBody());
205205
}
206206

207+
public function testCsrfVerifyHeaderWithJsonBodyPreservesBody(): void
208+
{
209+
service('superglobals')
210+
->setServer('REQUEST_METHOD', 'POST')
211+
->setCookie('csrf_cookie_name', self::CORRECT_CSRF_HASH);
212+
213+
$security = $this->createMockSecurity();
214+
$request = $this->createIncomingRequest();
215+
$body = '{"foo":"bar"}';
216+
217+
$request->setHeader('X-CSRF-TOKEN', self::CORRECT_CSRF_HASH);
218+
$request->setBody($body);
219+
220+
$this->assertInstanceOf(Security::class, $security->verify($request));
221+
$this->assertSame($body, $request->getBody());
222+
}
223+
224+
public function testCsrfVerifyHeaderWithJsonBodyStripsTokenFromBody(): void
225+
{
226+
service('superglobals')
227+
->setServer('REQUEST_METHOD', 'POST')
228+
->setCookie('csrf_cookie_name', self::CORRECT_CSRF_HASH);
229+
230+
$security = $this->createMockSecurity();
231+
$request = $this->createIncomingRequest();
232+
233+
$request->setHeader('X-CSRF-TOKEN', self::CORRECT_CSRF_HASH);
234+
$request->setBody('{"csrf_test_name":"' . self::CORRECT_CSRF_HASH . '","foo":"bar"}');
235+
236+
$this->assertInstanceOf(Security::class, $security->verify($request));
237+
$this->assertSame('{"foo":"bar"}', $request->getBody());
238+
}
239+
207240
public function testCsrfVerifyPutBodyThrowsExceptionOnNoMatch(): void
208241
{
209242
service('superglobals')

user_guide_src/source/changelogs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ See all the changes.
1313
:titlesonly:
1414

1515
v4.8.0
16+
v4.7.2
1617
v4.7.1
1718
v4.7.0
1819
v4.6.5
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#############
2+
Version 4.7.2
3+
#############
4+
5+
Release Date: March 24, 2026
6+
7+
**4.7.2 release of CodeIgniter4**
8+
9+
.. contents::
10+
:local:
11+
:depth: 3
12+
13+
**********
14+
Bugs Fixed
15+
**********
16+
17+
- **Security:** Fixed a bug where the CSRF filter could corrupt JSON request bodies after successful
18+
verification when the CSRF token was provided via the ``X-CSRF-TOKEN`` header.
19+
This caused ``IncomingRequest::getJSON()`` to fail on valid ``application/json`` requests.
20+
21+
See the repo's
22+
`CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_
23+
for a complete list of bugs fixed.

user_guide_src/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
version = '4.7'
2727

2828
# The full version, including alpha/beta/rc tags.
29-
release = '4.7.1'
29+
release = '4.7.2'
3030

3131
# -- General configuration ---------------------------------------------------
3232

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#############################
2+
Upgrading from 4.7.1 to 4.7.2
3+
#############################
4+
5+
Please refer to the upgrade instructions corresponding to your installation method.
6+
7+
- :ref:`Composer Installation App Starter Upgrading <app-starter-upgrading>`
8+
- :ref:`Composer Installation Adding CodeIgniter4 to an Existing Project Upgrading <adding-codeigniter4-upgrading>`
9+
- :ref:`Manual Installation Upgrading <installing-manual-upgrading>`
10+
11+
.. contents::
12+
:local:
13+
:depth: 2
14+
15+
*************
16+
Project Files
17+
*************
18+
19+
Some files in the **project space** (root, app, public, writable) received updates. Due to
20+
these files being outside of the **system** scope they will not be changed without your intervention.
21+
22+
.. note:: There are some third-party CodeIgniter modules available to assist
23+
with merging changes to the project space:
24+
`Explore on Packagist <https://packagist.org/explore/?query=codeigniter4%20updates>`_.
25+
26+
Content Changes
27+
===============
28+
29+
The following files received significant changes (including deprecations or visual adjustments)
30+
and it is recommended that you merge the updated versions with your application:
31+
32+
Config
33+
------
34+
35+
- No config files were changed in this release.
36+
37+
All Changes
38+
===========
39+
40+
This is a list of all files in the **project space** that received changes;
41+
many will be simple comments or formatting that have no effect on the runtime:
42+
43+
- No project files were changed in this release.

0 commit comments

Comments
 (0)