diff --git a/libraries/src/Form/Filter/UrlFilter.php b/libraries/src/Form/Filter/UrlFilter.php index a6d478a6b35ce..643dae3bf5e90 100644 --- a/libraries/src/Form/Filter/UrlFilter.php +++ b/libraries/src/Form/Filter/UrlFilter.php @@ -79,7 +79,7 @@ public function filter(\SimpleXMLElement $element, $value, $group = null, ?Regis $host = Uri::getInstance('SERVER')->getHost(); // If it starts with the host string, just prepend the protocol. - if (substr($value, 0) === $host) { + if ($host && preg_match('#^' . preg_quote($host, '#') . '(?:[/:?\#]|$)#i', $value)) { $value = 'http://' . $value; } elseif (!str_starts_with($value, '/')) { // Otherwise if it doesn't start with "/" prepend the prefix of the current site. diff --git a/tests/Unit/Libraries/Cms/Form/Filter/UrlFilterTest.php b/tests/Unit/Libraries/Cms/Form/Filter/UrlFilterTest.php new file mode 100644 index 0000000000000..80d315a14345d --- /dev/null +++ b/tests/Unit/Libraries/Cms/Form/Filter/UrlFilterTest.php @@ -0,0 +1,109 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Tests\Unit\Libraries\Cms\Form\Filter; + +use Joomla\CMS\Form\Filter\UrlFilter; +use Joomla\CMS\Uri\Uri; +use Joomla\Tests\Unit\UnitTestCase; + +/** + * Test class for UrlFilter. + * + * @since __DEPLOY_VERSION__ + */ +class UrlFilterTest extends UnitTestCase +{ + /** + * Backup of the $_SERVER variable + * + * @var array + * @since __DEPLOY_VERSION__ + */ + private $server; + + /** + * Sets up the request for the current site. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + protected function setUp(): void + { + parent::setUp(); + + $this->server = $_SERVER; + Uri::reset(); + + $_SERVER['HTTP_HOST'] = 'www.example.com'; + $_SERVER['SCRIPT_NAME'] = '/joomla/administrator/index.php'; + $_SERVER['PHP_SELF'] = '/joomla/administrator/index.php'; + $_SERVER['REQUEST_URI'] = '/joomla/administrator/index.php'; + + Uri::root(false, '/joomla'); + } + + /** + * Restores the request. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + protected function tearDown(): void + { + $_SERVER = $this->server; + Uri::reset(); + + parent::tearDown(); + } + + /** + * Test data for the testRelativeUrl method + * + * @return array + * + * @since __DEPLOY_VERSION__ + */ + public function dataRelativeUrl(): array + { + return [ + 'host only' => ['www.example.com', 'http://www.example.com'], + 'host and path' => ['www.example.com/blog/post', 'http://www.example.com/blog/post'], + 'host and port' => ['www.example.com:8080/blog', 'http://www.example.com:8080/blog'], + 'host and query' => ['www.example.com?option=com_content', 'http://www.example.com?option=com_content'], + 'host in upper case' => ['WWW.EXAMPLE.COM/blog', 'http://WWW.EXAMPLE.COM/blog'], + 'other host prefix' => ['www.example.com.evil.org/page', '/joomla/www.example.com.evil.org/page'], + 'relative path' => ['blog/post', '/joomla/blog/post'], + 'absolute path' => ['/blog/post', '/blog/post'], + 'full url' => ['https://www.example.com/blog', 'https://www.example.com/blog'], + ]; + } + + /** + * Tests filtering a URL of a url field that allows relative URLs. + * + * @param string $value The value to filter + * @param string $expected The expected result + * + * @return void + * + * @since __DEPLOY_VERSION__ + * + * @dataProvider dataRelativeUrl + */ + public function testRelativeUrl(string $value, string $expected): void + { + $element = new \SimpleXMLElement(''); + + $this->assertSame($expected, (new UrlFilter())->filter($element, $value)); + } +}