Make WordPress Core

Opened 4 weeks ago

Closed 3 weeks ago

#64635 closed defect (bug) (fixed)

IXR_Client: Undefined array key "host" when parse_url returns no host

Reported by: bluefuton's profile bluefuton Owned by: westonruter's profile westonruter
Milestone: 7.0 Priority: normal
Severity: trivial Version: trunk
Component: XML-RPC Keywords: has-patch has-unit-tests
Focuses: Cc:

Description

IXR_Client::__construct() in wp-includes/IXR/class-IXR-client.php accesses $bits['host'] on line 33 without a key existence check after calling parse_url(). This triggers an "Undefined array key" warning when the URL passed has no host (e.g. a relative path like /xmlrpc.php).

Lines 34-35 already guard against missing keys for port and path:

$bits = parse_url($server);
$this->server = $bits['host'];  // <- unguarded
$this->port = $bits['port'] ?? 80;
$this->path = $bits['path'] ?? '/';

To improve resilience, we could apply the same pattern to host:

$this->server = $bits['host'] ?? '';

How to reproduce: Pass a relative URL (no scheme/host) as the $server parameter to IXR_Client with $path = false. For example, this occurs in Jetpack when Jetpack_IXR_Client constructs an IXR_Client and JETPACK__API_BASE is undefined.

Change History (13)

This ticket was mentioned in PR #10916 on WordPress/wordpress-develop by @bluefuton.


4 weeks ago
#1

  • Keywords has-patch has-unit-tests added

## Summary

  • IXR_Client accesses $bits['host'] from parse_url() without checking if the key exists, which can trigger an undefined index warning for URLs without a host component (e.g. relative paths).
  • Uses the null coalescing operator (??) to fall back to an empty string, consistent with how port and path are already handled on the adjacent lines.
  • Adds a unit test to verify no error is triggered when the host is missing.

## Trac ticket

https://core.trac.wordpress.org/ticket/64635

## Test plan

  • [ ] Verify IXR_Client no longer triggers an undefined index warning when instantiated with a URL lacking a host component
  • [ ] Run phpunit --filter Tests_XMLRPC_Client and confirm all tests pass

#2 @westonruter
4 weeks ago

  • Milestone changed from Awaiting Review to 7.0
  • Owner set to westonruter
  • Status changed from new to reviewing

@bluefuton commented on PR #10916:


4 weeks ago
#4

Should the same fix be applied to WP_HTTP_IXR_Client?

Yes, I think so. There's actually an open ticket already for this:

https://core.trac.wordpress.org/ticket/40784

And the suggested patch was:

https://core.trac.wordpress.org/attachment/ticket/40784/40784.diff

Shall I address it in this PR @westonruter?

@westonruter commented on PR #10916:


4 weeks ago
#5

@bluefuton ah, perfect. Yes, could you apply that patch to the PR and update it so we can address both together?

@bluefuton commented on PR #10916:


4 weeks ago
#6

Yes, could you apply that patch to the PR and update it so we can address both together?

Added! 👍 I've modernized the original patch (using ?? to be consistent with the surrounding code and assertSame in tests for strict equality).

Sidenote

It looks like at one point IXR_Client was a third-party library by Simon Willison: https://simonwillison.net/2002/Sep/2/aNewXMLRPCLibraryForPHP/. This was back in 2002 and is not actively maintained any more: https://simonwillison.net/2022/Jun/12/twenty-years/

If we are now treating it as part of WP, we could probably do some further cleanup in there, like dropping PHP4 support.

@bluefuton commented on PR #10916:


4 weeks ago
#7

We should give @tfrommen props for the original patch on #40784 too :)

@westonruter commented on PR #10916:


4 weeks ago
#8

@bluefuton It seems the only difference between the constructors for the two classes is that WP_HTTP_IXR_Client also sets a scheme. That said, could WP_HTTP_IXR_Client::__construct() be further simplified to just:

public function __construct( $server, $path = false, $port = false, $timeout = 15 ) {
                parent::__construct( $server, $path, $port, $timeout );
                $scheme = parse_url( $server, PHP_URL_SCHEME );
                if ( $scheme ) {
                        $this->scheme = $scheme;
                } else {
                        $this->scheme = 'http';
                }
        }

@bluefuton commented on PR #10916:


4 weeks ago
#9

It seems the only difference between the constructors for the two classes is that WP_HTTP_IXR_Client also sets a scheme. That said, could WP_HTTP_IXR_Client::construct() be further simplified

Nice cleanup! There's one subtlety though - the parent IXR_Client::__construct() defaults the port to 80 when parsing a URL, while WP_HTTP_IXR_Client defaults to $port (which is false by default). We'd need to handle that too.

@tfrommen commented on PR #10916:


4 weeks ago
#10

Hey, nice to see some work on this. 😉

The above suggestion by @westonruter would always default to 'http':

public function __construct( $server, $path = false, $port = false, $timeout = 15 ) {
                parent::__construct( $server, $path, $port, $timeout );
                $this->scheme = parse_url( $server, PHP_URL_SCHEME ) ?? 'http';
        }

However, the current code only does this (hard-coded) if there was no $path specified.

To keep that behavior, we would need to put the code inside a conditional. Also covering the different handling of the port, this would give us this:

public function __construct( $server, $path = false, $port = false, $timeout = 15 ) {
                parent::__construct( $server, $path, $port, $timeout );
                if ( ! $path ) {
                        $this->scheme = parse_url( $server, PHP_URL_SCHEME ) ?? 'http';
                        $this->port   = parse_url( $server, PHP_URL_PORT ) ?? '';
                }
        }

Thoughts?

@bluefuton commented on PR #10916:


4 weeks ago
#11

I know we're a few years on from your original patch @tfrommen! Hope we can get it merged for you 🙂

The refactor to call parent::__construct() would definitely slim things down. It looks like there are a few subtle differences to navigate though, and personally I would choose to land the ?? additions and tests, and consider refactoring in a followup PR.

@westonruter commented on PR #10916:


4 weeks ago
#12

@bluefuton Sounds good to me.

@tfrommen How do you feel with the PR as it stands? If you approve, I'll proceed with commit.

#13 @westonruter
3 weeks ago

  • Resolution set to fixed
  • Status changed from reviewing to closed

In 61713:

XML-RPC: Account for parsed server URLs that do not specify host or scheme.

Developed in https://github.com/WordPress/wordpress-develop/pull/10916

Props bluefuton, tfrommen, chrispecoraro, drebbits.web, westonruter.
Fixes #64635, #40784.

Note: See TracTickets for help on using tickets.