From 3599f29cc3a1ca43c3ae1522e27d77a6e9222d29 Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Mon, 17 Dec 2018 13:20:12 -0500 Subject: [PATCH] SECURITY: Work around PHP bug in parse_url It gets confused by URLs with a query portion but no path. Bug: T212067 Change-Id: I15c15161a668115d68eb2e2f8004826b47148fc1 --- RELEASE-NOTES-1.31 | 1 + includes/GlobalFunctions.php | 12 ++++++ .../GlobalFunctions/wfParseUrlTest.php | 40 +++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/RELEASE-NOTES-1.31 b/RELEASE-NOTES-1.31 index 41b8184334..52f2b78dbc 100644 --- a/RELEASE-NOTES-1.31 +++ b/RELEASE-NOTES-1.31 @@ -16,6 +16,7 @@ THIS IS NOT A RELEASE YET * (T233342) rdbms: Log debug message traces as 'exception.trace' instead of 'trace'. * (T226751) media: Log and fail gracefully on invalid EXIF coordinates. +* (T212067) Work around PHP bug in parse_url. == MediaWiki 1.31.5 == diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 0152209d07..c025553374 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -811,6 +811,18 @@ function wfParseUrl( $url ) { Wikimedia\suppressWarnings(); $bits = parse_url( $url ); Wikimedia\restoreWarnings(); + + // T212067: PHP < 5.6.28, 7.0.0–7.0.12, and HHVM (all relevant versions) screw up parsing + // the query part of pathless URLs + if ( isset( $bits['host'] ) && strpos( $bits['host'], '?' ) !== false ) { + list( $host, $query ) = explode( '?', $bits['host'], 2 ); + $bits['host'] = $host; + $bits['query'] = $query + . ( $bits['path'] ?? '' ) + . ( isset( $bits['query'] ) ? '?' . $bits['query'] : '' ); + unset( $bits['path'] ); + } + // parse_url() returns an array without scheme for some invalid URLs, e.g. // parse_url("%0Ahttp://example.com") == [ 'host' => '%0Ahttp', 'path' => 'example.com' ] if ( !$bits || !isset( $bits['scheme'] ) ) { diff --git a/tests/phpunit/includes/GlobalFunctions/wfParseUrlTest.php b/tests/phpunit/includes/GlobalFunctions/wfParseUrlTest.php index b20cfb5c22..25a2342953 100644 --- a/tests/phpunit/includes/GlobalFunctions/wfParseUrlTest.php +++ b/tests/phpunit/includes/GlobalFunctions/wfParseUrlTest.php @@ -152,6 +152,46 @@ class WfParseUrlTest extends MediaWikiTestCase { 'invalid://test/', false ], + // T212067 + [ + '//evil.com?example.org/foo/bar', + [ + 'scheme' => '', + 'delimiter' => '//', + 'host' => 'evil.com', + 'query' => 'example.org/foo/bar', + ] + ], + [ + '//evil.com?example.org/foo/bar?baz#quux', + [ + 'scheme' => '', + 'delimiter' => '//', + 'host' => 'evil.com', + 'query' => 'example.org/foo/bar?baz', + 'fragment' => 'quux', + ] + ], + [ + '//evil.com?example.org?baz#quux', + [ + 'scheme' => '', + 'delimiter' => '//', + 'host' => 'evil.com', + 'query' => 'example.org?baz', + 'fragment' => 'quux', + ] + ], + [ + '//evil.com?example.org#quux', + [ + 'scheme' => '', + 'delimiter' => '//', + 'host' => 'evil.com', + 'query' => 'example.org', + 'fragment' => 'quux', + ] + ], ]; } } -- 2.20.1