Skip to content
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## [v4.7.2](https://github.com/codeigniter4/CodeIgniter4/tree/v4.7.2) (2026-03-24)
[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.7.1...v4.7.2)

### Fixed Bugs

* fix: preserve JSON body when CSRF token is sent in header by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10064

## [v4.7.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.7.1) (2026-03-22)
[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.7.0...v4.7.1)

Expand Down
1 change: 1 addition & 0 deletions admin/create-new-changelog.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ function replace_file_content(string $path, string $pattern, string $replace): v
);

if (! in_array('--dry-run', $argv, true)) {
system('git add ./system/CodeIgniter.php');
system("git add {$newChangelog} {$changelogIndex}");
system("git add {$newUpgrading} {$upgradingIndex}");
system("git commit -m \"docs: add changelog and upgrade for v{$newVersion}\"");
Expand Down
2 changes: 1 addition & 1 deletion phpdoc.dist.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<output>api/build/</output>
<cache>api/cache/</cache>
</paths>
<version number="4.7.1">
<version number="4.7.2">
<api format="php">
<source dsn=".">
<path>system</path>
Expand Down
2 changes: 1 addition & 1 deletion system/CodeIgniter.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class CodeIgniter
/**
* The current version of CodeIgniter Framework
*/
public const CI_VERSION = '4.7.1';
public const CI_VERSION = '4.7.2';

/**
* App startup time.
Expand Down
9 changes: 6 additions & 3 deletions system/Security/Security.php
Original file line number Diff line number Diff line change
Expand Up @@ -298,15 +298,18 @@ private function removeTokenInRequest(IncomingRequest $request): void
$json = null;
}

if (is_object($json) && property_exists($json, $tokenName)) {
unset($json->{$tokenName});
$request->setBody(json_encode($json));
if (is_object($json)) {
if (property_exists($json, $tokenName)) {
unset($json->{$tokenName});
$request->setBody(json_encode($json));
}

return;
}

// If the token is found in form-encoded data, we can safely remove it.
parse_str($body, $result);

unset($result[$tokenName]);
$request->setBody(http_build_query($result));
}
Expand Down
31 changes: 31 additions & 0 deletions tests/system/Security/SecurityCSRFSessionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,37 @@ public function testCSRFVerifyJsonReturnsSelfOnMatch(): void
$this->assertSame('{"foo":"bar"}', $request->getBody());
}

public function testCSRFVerifyHeaderWithJsonBodyPreservesBody(): void
{
service('superglobals')->setServer('REQUEST_METHOD', 'POST');

$request = $this->createIncomingRequest();
$body = '{"foo":"bar"}';

$request->setHeader('X-CSRF-TOKEN', '8b9218a55906f9dcc1dc263dce7f005a');
$request->setBody($body);
$security = $this->createSecurity();

$this->assertInstanceOf(Security::class, $security->verify($request));
$this->assertLogged('info', 'CSRF token verified.');
$this->assertSame($body, $request->getBody());
}

public function testCSRFVerifyHeaderWithJsonBodyStripsTokenFromBody(): void
{
service('superglobals')->setServer('REQUEST_METHOD', 'POST');

$request = $this->createIncomingRequest();

$request->setHeader('X-CSRF-TOKEN', '8b9218a55906f9dcc1dc263dce7f005a');
$request->setBody('{"csrf_test_name":"8b9218a55906f9dcc1dc263dce7f005a","foo":"bar"}');
$security = $this->createSecurity();

$this->assertInstanceOf(Security::class, $security->verify($request));
$this->assertLogged('info', 'CSRF token verified.');
$this->assertSame('{"foo":"bar"}', $request->getBody());
}

public function testRegenerateWithFalseSecurityRegenerateProperty(): void
{
service('superglobals')
Expand Down
33 changes: 33 additions & 0 deletions tests/system/Security/SecurityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,39 @@ public function testCsrfVerifyJsonReturnsSelfOnMatch(): void
$this->assertSame('{"foo":"bar"}', $request->getBody());
}

public function testCsrfVerifyHeaderWithJsonBodyPreservesBody(): void
{
service('superglobals')
->setServer('REQUEST_METHOD', 'POST')
->setCookie('csrf_cookie_name', self::CORRECT_CSRF_HASH);

$security = $this->createMockSecurity();
$request = $this->createIncomingRequest();
$body = '{"foo":"bar"}';

$request->setHeader('X-CSRF-TOKEN', self::CORRECT_CSRF_HASH);
$request->setBody($body);

$this->assertInstanceOf(Security::class, $security->verify($request));
$this->assertSame($body, $request->getBody());
}

public function testCsrfVerifyHeaderWithJsonBodyStripsTokenFromBody(): void
{
service('superglobals')
->setServer('REQUEST_METHOD', 'POST')
->setCookie('csrf_cookie_name', self::CORRECT_CSRF_HASH);

$security = $this->createMockSecurity();
$request = $this->createIncomingRequest();

$request->setHeader('X-CSRF-TOKEN', self::CORRECT_CSRF_HASH);
$request->setBody('{"csrf_test_name":"' . self::CORRECT_CSRF_HASH . '","foo":"bar"}');

$this->assertInstanceOf(Security::class, $security->verify($request));
$this->assertSame('{"foo":"bar"}', $request->getBody());
}

public function testCsrfVerifyPutBodyThrowsExceptionOnNoMatch(): void
{
service('superglobals')
Expand Down
1 change: 1 addition & 0 deletions user_guide_src/source/changelogs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ See all the changes.
.. toctree::
:titlesonly:

v4.7.2
v4.7.1
v4.7.0
v4.6.5
Expand Down
23 changes: 23 additions & 0 deletions user_guide_src/source/changelogs/v4.7.2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#############
Version 4.7.2
#############

Release Date: March 24, 2026

**4.7.2 release of CodeIgniter4**

.. contents::
:local:
:depth: 3

**********
Bugs Fixed
**********

- **Security:** Fixed a bug where the CSRF filter could corrupt JSON request bodies after successful
verification when the CSRF token was provided via the ``X-CSRF-TOKEN`` header.
This caused ``IncomingRequest::getJSON()`` to fail on valid ``application/json`` requests.

See the repo's
`CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_
for a complete list of bugs fixed.
2 changes: 1 addition & 1 deletion user_guide_src/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
version = '4.7'

# The full version, including alpha/beta/rc tags.
release = '4.7.1'
release = '4.7.2'

# -- General configuration ---------------------------------------------------

Expand Down
43 changes: 43 additions & 0 deletions user_guide_src/source/installation/upgrade_472.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#############################
Upgrading from 4.7.1 to 4.7.2
#############################

Please refer to the upgrade instructions corresponding to your installation method.

- :ref:`Composer Installation App Starter Upgrading <app-starter-upgrading>`
- :ref:`Composer Installation Adding CodeIgniter4 to an Existing Project Upgrading <adding-codeigniter4-upgrading>`
- :ref:`Manual Installation Upgrading <installing-manual-upgrading>`

.. contents::
:local:
:depth: 2

*************
Project Files
*************

Some files in the **project space** (root, app, public, writable) received updates. Due to
these files being outside of the **system** scope they will not be changed without your intervention.

.. note:: There are some third-party CodeIgniter modules available to assist
with merging changes to the project space:
`Explore on Packagist <https://packagist.org/explore/?query=codeigniter4%20updates>`_.

Content Changes
===============

The following files received significant changes (including deprecations or visual adjustments)
and it is recommended that you merge the updated versions with your application:

Config
------

- No config files were changed in this release.

All Changes
===========

This is a list of all files in the **project space** that received changes;
many will be simple comments or formatting that have no effect on the runtime:

- No project files were changed in this release.
1 change: 1 addition & 0 deletions user_guide_src/source/installation/upgrading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Alternatively, replace it with a new file and add your previous lines.

backward_compatibility_notes

upgrade_472
upgrade_471
upgrade_470
upgrade_465
Expand Down
Loading