From 112cf202e76c1602cb0da0054ed39986770877c9 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 --- includes/GlobalFunctions.php | 12 ++++++ .../GlobalFunctions/wfParseUrlTest.php | 40 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index a5f4def18f..dacceec253 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -824,6 +824,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 + . ( isset( $bits['path'] ) ? $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.0