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: |
|
Owned by: |
|
|---|---|---|---|
| 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
$serverparameter toIXR_Clientwith$path = false. For example, this occurs in Jetpack whenJetpack_IXR_Clientconstructs anIXR_ClientandJETPACK__API_BASEis 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
#2
@
4 weeks ago
- Milestone changed from Awaiting Review to 7.0
- Owner set to westonruter
- Status changed from new to reviewing
@westonruter commented on PR #10916:
4 weeks ago
#3
Should the same fix be applied to WP_HTTP_IXR_Client?
@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.
## Summary
IXR_Clientaccesses$bits['host']fromparse_url()without checking if the key exists, which can trigger an undefined index warning for URLs without a host component (e.g. relative paths).??) to fall back to an empty string, consistent with howportandpathare already handled on the adjacent lines.## Trac ticket
https://core.trac.wordpress.org/ticket/64635
## Test plan
IXR_Clientno longer triggers an undefined index warning when instantiated with a URL lacking a host componentphpunit --filter Tests_XMLRPC_Clientand confirm all tests pass