diff --git a/src/Whoops/Util/TemplateHelper.php b/src/Whoops/Util/TemplateHelper.php index 5612c0b..b77b160 100644 --- a/src/Whoops/Util/TemplateHelper.php +++ b/src/Whoops/Util/TemplateHelper.php @@ -80,6 +80,9 @@ class TemplateHelper * Escapes a string for output in an HTML document, but preserves * URIs within it, and converts them to clickable anchor elements. * + * Only http and https URIs are linkified; other schemes are left as + * plain escaped text so that a link cannot execute script when clicked. + * * @param string $raw * @return string */ @@ -87,7 +90,7 @@ class TemplateHelper { $escaped = $this->escape($raw); return preg_replace( - "@([A-z]+?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@", + "@(https?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@i", "$1", $escaped ); diff --git a/tests/Whoops/Util/TemplateHelperTest.php b/tests/Whoops/Util/TemplateHelperTest.php index 086645a..100b463 100644 --- a/tests/Whoops/Util/TemplateHelperTest.php +++ b/tests/Whoops/Util/TemplateHelperTest.php @@ -63,6 +63,44 @@ class TemplateHelperTest extends TestCase ); } + /** + * @covers Whoops\Util\TemplateHelper::escapeButPreserveUris + */ + public function testEscapeButPreserveUrisRejectsDangerousSchemes() + { + $dangerous = [ + 'javascript://x.co/?%0Aalert(1)', + 'vbscript://x.co/?%0Amsgbox(1)', + 'data://text/html,', + ]; + + foreach ($dangerous as $payload) { + $output = $this->helper->escapeButPreserveUris($payload); + + $this->assertStringNotContainsString( + 'assertEquals( + "http://google.com", + $this->helper->escapeButPreserveUris('http://google.com') + ); + + $this->assertEquals( + "https://google.com", + $this->helper->escapeButPreserveUris('https://google.com') + ); + } + /** * @covers Whoops\Util\TemplateHelper::breakOnDelimiter */