Make WordPress Core

Opened 13 months ago

Last modified 11 days ago

#61061 new defect (bug)

PHP Warning with invalid JSON input

Reported by: dd32's profile dd32 Owned by:
Milestone: 6.9 Priority: low
Severity: normal Version: trunk
Component: REST API Keywords: has-patch has-unit-tests has-test-info
Focuses: rest-api Cc:

Description

A request such as the following will generate a PHP Warning:

curl https://example.org/wp-json/wp/v2/users/1 --data '"+response.write(document.domain)+"' -H 'Content-Type: application/json'

The warning:

E_WARNING: Invalid argument supplied for foreach() in wp-includes/rest-api/class-wp-rest-request.php:816

The relevant part of the backtrace:

[24-Apr-2024 04:11:35 UTC] PHP  10. WP_REST_Server->serve_request($path = '/wp/v2/users/1') wp-includes/rest-api.php:428
[24-Apr-2024 04:11:35 UTC] PHP  11. WP_REST_Server->dispatch($request = class WP_REST_Request { protected $method = 'POST'; protected $params = ['URL' => ['id' => '1'], 'GET' => [], 'POST' => [], 'FILES' => [], 'JSON' => '+response.write(document.domain)+', 'defaults' => []]; protected $headers = ['content_type' => [0 => 'application/json'] ]; protected $body = '"+response.write(document.domain)+"'; protected $route = '/wp/v2/users/1'; protected $attributes = ['methods' => ['POST' => TRUE, 'PUT' => TRUE, 'PATCH' => TRUE], 'accept_json' => FALSE, 'accept_raw' => FALSE, 'show_in_index' => TRUE,  [.......]; protected $parsed_json = TRUE; protected $parsed_body = FALSE }) wp-includes/rest-api/class-wp-rest-server.php:439
[24-Apr-2024 04:11:35 UTC] PHP  12. WP_REST_Request->sanitize_params() wp-includes/rest-api/class-wp-rest-server.php:1056

Change History (30)

#1 @dd32
13 months ago

Of course, this is actually a valid JSON input.

These are also valid, and produce the same result.

curl https://example.org/wp-json/wp/v2/users/1 --data '"foobar"' -H 'Content-Type: application/json'
curl https://example.org/wp-json/wp/v2/users/1 --data '1' -H 'Content-Type: application/json'

It seems that the expected outcome here is to simply skip sanitisation of the param, ultimately a missing field should be picked up by the schema of the API endpoint, and the endpoint may expect/handle a string/numeric input in the field.

See attached.

#3 @antonvlasenko
11 months ago

Test Report

This report validates that the indicated patch addresses the issue.

Patch tested: โ€‹https://github.com/WordPress/wordpress-develop/pull/6491

Environment

  • WordPress: 6.6-beta3-58440-src
  • PHP: 7.3.33
  • Server: Apache/2.4.57 (Unix) PHP/7.3.33
  • Database: mysqli (Server: 5.7.43 / Client: mysqlnd 5.0.12-dev)
  • Browser: Safari 17.5 (macOS)
  • Theme: Twenty Twenty-Four 1.1
  • MU-Plugins: None activated
  • Plugins:
    • JSON Basic Authentication 0.1

Actual Results

  • โœ… No error in the debug.log file after applying the patch.

Additional Notes

The curl request used for testing:

curl --user admin:password http://wordpress.test/wp-json/wp/v2/users/1 --data '1' -H 'Content-Type: application/json'

Supplemental Artifacts

Before applying the patch, the following error was recorded in the debug.log file:

[03-Jul-2024 14:32:18 UTC] PHP Warning:  Invalid argument supplied for foreach() in /src/wp-includes/rest-api/class-wp-rest-request.php on line 816

#4 @swissspidy
7 months ago

  • Keywords commit added
  • Milestone changed from Awaiting Review to 6.7

โ€‹@peterwilsoncc commented on โ€‹PR #6491:


7 months ago
#5

@dd32 Would you be able to add a test for this?

โ€‹@dd32 commented on โ€‹PR #6491:


7 months ago
#6

@peterwilsoncc I don't think I'll have time until next week, but I'll see what I can do! I don't actually fully know how to trigger this from within PHP, I just repro'd it with curl and filed ๐Ÿคท

โ€‹@peterwilsoncc commented on โ€‹PR #6491:


7 months ago
#7

Thanks Dion,

I figured out how to test some things but not sure if we'll need more. I added this to tests/phpunit/tests/rest-api/rest-request.php

The one thing I am unsure of is whether strings are intended to be valid. @TimothyBJacobs, @kadamwhite Do either of you know?

/**
 * @ticket 61061
 */
public function test_sanitize_params_with_string_parameters() {
        $this->request->set_url_params( 'foobar' );

        $this->request->set_attributes(
                array(
                        'args' => array(
                                'someinteger' => array(
                                        'sanitize_callback' => 'absint',
                                ),
                                'somestring'  => array(
                                        'sanitize_callback' => 'absint',
                                ),
                        ),
                )
        );

        $this->request->sanitize_params();

        $this->assertSame( 'foobar', $this->request->get_url_params() );
}

/**
 * @ticket 61061
 */
public function test_has_valid_params_with_string_parameter_and_required_attribute() {
        $this->request->set_url_params( 'foobar' );

        $this->request->set_attributes(
                array(
                        'args' => array(
                                'someinteger' => array(
                                        'sanitize_callback' => 'absint',
                                        'required'          => true,
                                ),
                        ),
                )
        );

        $this->assertWPError( $this->request->has_valid_params() );
}

/**
 * @ticket 61061
 */
public function test_has_valid_params_with_string_parameter_without_required_attribute() {
        $this->request->set_url_params( 'foobar' );

        $this->request->set_attributes(
                array(
                        'args' => array(
                                'someinteger' => array(
                                        'sanitize_callback' => 'absint',
                                ),
                        ),
                )
        );

        $this->assertTrue( $this->request->has_valid_params() );
}

โ€‹@antonvlasenko commented on โ€‹PR #6491:


7 months ago
#8

I'm currently working on adding more tests to this PR (unless @dd32 has any objections).

This ticket was mentioned in โ€‹Slack in #core by chaion07. โ€‹View the logs.


7 months ago

This ticket was mentioned in โ€‹Slack in #core by stoyangeorgiev. โ€‹View the logs.


7 months ago

#11 @peterwilsoncc
7 months ago

  • Keywords needs-refresh needs-unit-tests added; commit removed
  • Milestone changed from 6.7 to 6.8

I've removed the commit keyword and moved this off the release as @antonvlasenko picked up some potentially related issues while working on some tests.

This ticket was mentioned in โ€‹PR #7961 on โ€‹WordPress/wordpress-develop by โ€‹@sukhendu2002.


6 months ago
#12

  • Keywords has-unit-tests added; needs-refresh needs-unit-tests removed

This ticket was mentioned in โ€‹Slack in #core by audrasjb. โ€‹View the logs.


3 months ago

โ€‹@audrasjb commented on โ€‹PR #7961:


3 months ago
#14

Hello @Sukhendu2002 thanks for the PR. It has some conflicts, would you mind rebasing it so it can be tested? Thanks!

โ€‹@sukhendu2002 commented on โ€‹PR #7961:


2 months ago
#15

Hi @audrasjb, I've rebased the PR and resolved the conflicts. It should be ready for testing now. Let me know if you need any further changes. Thanks!

This ticket was mentioned in โ€‹Slack in #core by audrasjb. โ€‹View the logs.


2 months ago

#17 @oglekler
2 months ago

  • Keywords needs-testing added

This ticket was mentioned in โ€‹Slack in #core-test by oglekler. โ€‹View the logs.


2 months ago

#19 @tusharaddweb
2 months ago

  • Keywords has-testing-info added
  • Version set to trunk

Test Environment:

WordPress Version: 6.8
PHP Version: 8.1
Debug Mode: Enabled (WP_DEBUG_LOG set to true)


Test Scenario

Execute the following cURL request:

curl โ€‹https://example.org/wp-json/wp/v2/users/1 --data '"+response.write(document.domain)+"' -H 'Content-Type: application/json'

Monitor the debug.log file (wp-content/debug.log) for any warnings or errors.
Apply the patch: GitHub Pull Request #6491.
Re-run the same cURL request.
Verify that no warnings or errors are logged.

Expected Result

No PHP warnings or errors should be logged in debug.log when processing the request.

Actual Result (Before Patch)

PHP Warning appears in debug.log.
The issue occurs due to improper input handling in the REST API request.

Actual Result (After Patch Applied)

โœ… No PHP warnings or errors in debug.log.
The request is handled securely without generating unnecessary logs.

Attachments

[Attach relevant screenshots of debug.log before and after the patch]

Test Conclusion

Status: โœ… Issue Fixed After Patch
Impact: Medium (Affects API request handling and debug logs)
Recommendation:

Ensure the patch is merged into future WordPress core updates.
Perform further testing with other unexpected input payloads to confirm robustness.
Verify compatibility with different PHP versions and REST API authentication methods.

screenshots of debug.log before and after the patch :
Before Patch: โ€‹https://prnt.sc/Tbtg4znZfbNi
After Patch: โ€‹https://prnt.sc/FHGcYJu9AorY

#20 @SirLouen
2 months ago

  • Keywords needs-testing removed

Combined Bug and Test Reproduction Report

Description

This report validates that the patch works as expected.

Patch tested: โ€‹https://github.com/WordPress/wordpress-develop/pull/7961.diff

Environment

  • WordPress: 6.8-beta3-60042-src
  • PHP: 8.2.28
  • Server: nginx/1.27.4
  • Database: mysqli (Server: 8.4.4 / Client: mysqlnd 8.2.28)
  • Browser: Chrome 134.0.0.0
  • OS: Windows 10/11
  • Theme: Twenty Twenty-Five 1.1
  • MU Plugins: None activated
  • Plugins:
    • Test Reports 1.2.0

Steps to Reproduce

  1. Using wordpress-develop, executing command
    $ curl http://localhost:8889/wp-json/wp/v2/users/1 --data '"+response.write(document.domain)+"' -H 'Content-Type: application/json'
    {"code":"rest_cannot_edit","message":"Sorry, you are not allowed to edit this user.","data":{"status":401}}
    
  2. ๐Ÿž Returns warning:
    Warning:  foreach() argument must be of type array|object, string given in /var/www/src/wp-includes/rest-api/class-wp-rest-request.php on line 828
    

Actual Results after the PR 7961 patch

  1. โœ… No warning after curl has been executed

Additional Notes

  • โœ… Unit tests passing
  • Two unit tests are passing both previously and now, so we can assume that further checks are being implemented
  • ๐ŸŸ  Some tests could be unified into one single test. More info in the PR.
Last edited 2 months ago by SirLouen (previous) (diff)

This ticket was mentioned in โ€‹Slack in #core by audrasjb. โ€‹View the logs.


2 months ago

This ticket was mentioned in โ€‹Slack in #core by audrasjb. โ€‹View the logs.


2 months ago

#23 @jorbin
2 months ago

I tagged @TimothyBlynJacobs on github. One thing that stands out to me is that while the majority of the code change happens inside set_param, none of the tests are directly calling that function.

This ticket was mentioned in โ€‹Slack in #core by audrasjb. โ€‹View the logs.


2 months ago

#25 @devsahadat
8 weeks ago

Thank you for reporting the bug and for providing a solution. Iโ€™ve thoroughly reviewed the issue and tested the proposed patch.

Bug Summary:
The problem occurs when a malformed JSON input (e.g., a string or numeric value) is sent in a REST API request, causing a PHP warning due to the foreach() function attempting to iterate over a non-array input.

Solution Evaluation:
The proposed patch effectively skips the sanitization step for scalar inputs, such as strings or numbers, ensuring that only arrays are processed with the foreach() function. This solution resolves the PHP warning as intended.

Test Results:
I have tested the patch by replicating the original issue with the cURL command and verifying the debug logs. After applying the patch, no warnings or errors were logged, confirming that the issue has been resolved.

Additional Insights:
While the patch addresses the specific warning, I suggest further testing with different types of invalid inputs (e.g., arrays with invalid types) to ensure robustness. Additionally, it would be good to verify whether this change affects any existing validation or sanitization rules that may be in place for specific API endpoints.

This ticket was mentioned in โ€‹Slack in #core by audrasjb. โ€‹View the logs.


8 weeks ago

#27 @nazmul111
8 weeks ago

Test Report

This report validates that the patch works as expected.

Patch tested: โ€‹https://github.com/WordPress/wordpress-develop/pull/7961.diff

Environment
WordPress: 6.7
PHP: 8.2.13
Server: nginx/1.27.4
Database: mysqli (Server: 8.4.4 / Client: mysqlnd 8.2.28)
Browser: Chrome 134.0.0.0
OS: Windows 10/11
Theme: Twenty Twenty-Five 1.1
MU Plugins: None activated

This ticket was mentioned in โ€‹Slack in #core by audrasjb. โ€‹View the logs.


7 weeks ago

#29 @audrasjb
7 weeks ago

  • Milestone changed from 6.8 to 6.9

We are a couple hours before 6.8 RC2, so let's move this ticket to 6.9.

#30 @wordpressdotorg
11 days ago

  • Keywords has-test-info added; has-testing-info removed
Note: See TracTickets for help on using tickets.