diff --git a/lib/SP/Html/Html.php b/lib/SP/Html/Html.php index eeb4072e5..172e7f1e4 100644 --- a/lib/SP/Html/Html.php +++ b/lib/SP/Html/Html.php @@ -184,19 +184,19 @@ public static function stripTags(string $text): string */ public static function getSafeUrl(string $url): string { - $match = preg_match('#^(([a-z]+)://[\w._-]+)(?:/(.*))?#i', $url, $urlParts); + $urlParts = parse_url($url); - if ($match !== 1) { - return htmlspecialchars($url, ENT_QUOTES); + if ($urlParts === false) { + return 'malformed_url'; } - switch (count($urlParts)) { - case 3: - return htmlspecialchars($urlParts[1], ENT_QUOTES).'/'.urlencode($urlParts[2]); - case 2: - return htmlspecialchars($urlParts[1], ENT_QUOTES); - default: - return htmlspecialchars($url, ENT_QUOTES); - } + return preg_replace_callback( + '/[^:\/@?&=#%\w]+/u', + function ($matches) + { + return urlencode($matches[0]); + }, + $url + ); } } diff --git a/lib/SP/Services/Install/Installer.php b/lib/SP/Services/Install/Installer.php index 956b79d40..abc8a665a 100644 --- a/lib/SP/Services/Install/Installer.php +++ b/lib/SP/Services/Install/Installer.php @@ -60,9 +60,9 @@ final class Installer extends Service /** * sysPass' version and build number */ - const VERSION = [3, 2, 9]; + const VERSION = [3, 2, 10]; const VERSION_TEXT = '3.2'; - const BUILD = 22062501; + const BUILD = 22070101; /** * @var DatabaseSetupInterface diff --git a/tests/SP/Html/HtmlTest.php b/tests/SP/Html/HtmlTest.php new file mode 100644 index 000000000..16f0cd639 --- /dev/null +++ b/tests/SP/Html/HtmlTest.php @@ -0,0 +1,72 @@ +. + */ + +namespace SP\Tests\Html; + +use Faker\Factory; +use PHPUnit\Framework\TestCase; +use SP\Html\Html; + +/** + * Class HtmlTest + */ +class HtmlTest extends TestCase +{ + private static $faker; + + public static function setUpBeforeClass(): void + { + parent::setUpBeforeClass(); + + self::$faker = Factory::create(); + } + + + public function testGetSafeUrlOk() + { + $url = self::$faker->url; + + $this->assertEquals($url, Html::getSafeUrl($url)); + } + + /** + * @dataProvider urlProvider + * @return void + */ + public function testGetSafeUrlEncoded(string $url) + { + $this->assertEquals(0, preg_match('/["<>]+/', Html::getSafeUrl($url))); + } + + private function urlProvider(): array + { + return [ + ['https://foo.com/'], + ['https://foo.com/>'], + ['https://foo.com/">'], + ['https://foo.com/"%20onClick="alert(\'TEST\'")'], + ['https://foo.com/" onClick="alert(\'TEST\')"'], + ]; + } +}