diff --git a/src/Control/Director.php b/src/Control/Director.php index 91023b7a7..ee7266bd4 100644 --- a/src/Control/Director.php +++ b/src/Control/Director.php @@ -822,10 +822,11 @@ class Director implements TemplateGlobalProvider return ( // Base check for existence of a host on a compliant URL parse_url($url ?? '', PHP_URL_HOST) - // Check for more than one leading slash without a protocol. + // Check for more than one leading slash (forward or backward) without a protocol. // While not a RFC compliant absolute URL, it is completed to a valid URL by some browsers, // and hence a potential security risk. Single leading slashes are not an issue though. - || preg_match('%^\s*/{2,}%', $url ?? '') + // Note: Need 4 backslashes to have a single non-escaped backslash for regex. + || preg_match('%^\s*(\\\\|/){2,}%', $url ?? '') || ( // If a colon is found, check if it's part of a valid scheme definition // (meaning its not preceded by a slash). diff --git a/tests/php/Control/DirectorTest.php b/tests/php/Control/DirectorTest.php index 05e24046b..b8be9ec46 100644 --- a/tests/php/Control/DirectorTest.php +++ b/tests/php/Control/DirectorTest.php @@ -232,6 +232,9 @@ class DirectorTest extends SapphireTest public function testIsAbsoluteUrl() { $this->assertTrue(Director::is_absolute_url('http://test.com/testpage')); + $this->assertTrue(Director::is_absolute_url('https:/\\test.com')); + $this->assertTrue(Director::is_absolute_url('https:\\/test.com')); + $this->assertTrue(Director::is_absolute_url('https:\\\\test.com')); $this->assertTrue(Director::is_absolute_url('ftp://test.com')); $this->assertFalse(Director::is_absolute_url('test.com/testpage')); $this->assertFalse(Director::is_absolute_url('/relative')); @@ -241,6 +244,11 @@ class DirectorTest extends SapphireTest $this->assertTrue(Director::is_absolute_url("https://test.com/?url=http://foo.com")); $this->assertTrue(Director::is_absolute_url("trickparseurl:http://test.com")); $this->assertTrue(Director::is_absolute_url('//test.com')); + $this->assertTrue(Director::is_absolute_url('\\/\\/test.com')); + $this->assertTrue(Director::is_absolute_url('\/\/test.com')); + $this->assertTrue(Director::is_absolute_url('/\\test.com')); + $this->assertTrue(Director::is_absolute_url('\\\\test.com')); + $this->assertFalse(Director::is_absolute_url('\\test.com')); $this->assertTrue(Director::is_absolute_url('/////test.com')); $this->assertTrue(Director::is_absolute_url(' ///test.com')); $this->assertTrue(Director::is_absolute_url('http:test.com')); @@ -258,8 +266,17 @@ class DirectorTest extends SapphireTest { $this->assertFalse(Director::is_relative_url('http://test.com')); $this->assertFalse(Director::is_relative_url('https://test.com')); + $this->assertFalse(Director::is_relative_url('https:/\\test.com')); + $this->assertFalse(Director::is_relative_url('https:\\/test.com')); + $this->assertFalse(Director::is_relative_url('https:\\\\test.com')); $this->assertFalse(Director::is_relative_url(' https://test.com/testpage ')); $this->assertTrue(Director::is_relative_url('test.com/testpage')); + $this->assertFalse(Director::is_relative_url('//test.com')); + $this->assertFalse(Director::is_relative_url('\\/\\/test.com')); + $this->assertFalse(Director::is_relative_url('\/\/test.com')); + $this->assertFalse(Director::is_relative_url('/\\test.com')); + $this->assertFalse(Director::is_relative_url('\\\\test.com')); + $this->assertTrue(Director::is_relative_url('\\test.com')); $this->assertFalse(Director::is_relative_url('ftp://test.com')); $this->assertTrue(Director::is_relative_url('/relative')); $this->assertTrue(Director::is_relative_url('relative')); @@ -401,17 +418,34 @@ class DirectorTest extends SapphireTest ); } - /** - * Mostly tested by {@link testIsRelativeUrl()}, - * just adding the host name matching aspect here. - */ public function testIsSiteUrl() { - $this->assertFalse(Director::is_site_url("http://test.com")); + $this->assertFalse(Director::is_site_url('http://test.com')); + $this->assertTrue(Director::is_site_url('/relative-path')); + $this->assertTrue(Director::is_site_url('relative-path')); $this->assertTrue(Director::is_site_url(Director::absoluteBaseURL())); - $this->assertFalse(Director::is_site_url("http://test.com?url=" . Director::absoluteBaseURL())); - $this->assertFalse(Director::is_site_url("http://test.com?url=" . urlencode(Director::absoluteBaseURL() ?? ''))); - $this->assertFalse(Director::is_site_url("//test.com?url=" . Director::absoluteBaseURL())); + $this->assertFalse(Director::is_site_url('http://test.com?url=' . Director::absoluteBaseURL())); + $this->assertFalse(Director::is_site_url('http://test.com?url=' . urlencode(Director::absoluteBaseURL() ?? ''))); + $this->assertFalse(Director::is_site_url('http:\\\\test.com')); + $this->assertFalse(Director::is_site_url('http:\\\\test.com?url=' . Director::absoluteBaseURL())); + $this->assertFalse(Director::is_site_url('http:\\\\test.com?url=' . urlencode(Director::absoluteBaseURL() ?? ''))); + $this->assertFalse(Director::is_site_url('http:\\/test.com')); + $this->assertFalse(Director::is_site_url('http:\\/test.com?url=' . Director::absoluteBaseURL())); + $this->assertFalse(Director::is_site_url('http:\\/test.com?url=' . urlencode(Director::absoluteBaseURL() ?? ''))); + $this->assertFalse(Director::is_site_url('//test.com')); + $this->assertFalse(Director::is_site_url('//test.com?url=' . Director::absoluteBaseURL())); + $this->assertFalse(Director::is_site_url('\\/\\/test.com')); + $this->assertFalse(Director::is_site_url('\\/\\/test.com?url=' . Director::absoluteBaseURL())); + $this->assertFalse(Director::is_site_url('\/\/test.com')); + $this->assertFalse(Director::is_site_url('\/\/test.com?url=' . Director::absoluteBaseURL())); + $this->assertFalse(Director::is_site_url('\\/test.com')); + $this->assertFalse(Director::is_site_url('\\/test.com?url=' . Director::absoluteBaseURL())); + $this->assertFalse(Director::is_site_url('/\\test.com')); + $this->assertFalse(Director::is_site_url('/\\test.com?url=' . Director::absoluteBaseURL())); + $this->assertFalse(Director::is_site_url('\\\\test.com')); + $this->assertFalse(Director::is_site_url('\\\\test.com?url=' . Director::absoluteBaseURL())); + $this->assertTrue(Director::is_site_url('\\test.com')); + $this->assertTrue(Director::is_site_url('\\test.com?url=' . Director::absoluteBaseURL())); $this->assertFalse(Director::is_site_url('http://google.com\@test.com')); $this->assertFalse(Director::is_site_url('http://google.com/@test.com')); $this->assertFalse(Director::is_site_url('http://google.com:pass\@test.com'));