diff --git a/app/Validators/CustomValidator.php b/app/Validators/CustomValidator.php index 7012f4a8..a8d547f5 100644 --- a/app/Validators/CustomValidator.php +++ b/app/Validators/CustomValidator.php @@ -111,7 +111,7 @@ public function validateApplicationtype($attribute, $value) public function validateSslurl($attribute, $value) { - return preg_match(";^https:\/\/([\w@][\w.:@]+)\/?[\w\.?=%&=\-@/$,]*$;i", $value) == 1; + return preg_match(";^https:\/\/([\w@][\w.:@\-]+)\/?[\w\.?=%&=\-@/$,]*$;i", $value) == 1; } public function validateFreeText($attribute, $value) diff --git a/resources/js/oauth2/profile/edit_client/components/logout_options.js b/resources/js/oauth2/profile/edit_client/components/logout_options.js index 113a2173..c05a2075 100644 --- a/resources/js/oauth2/profile/edit_client/components/logout_options.js +++ b/resources/js/oauth2/profile/edit_client/components/logout_options.js @@ -22,7 +22,7 @@ const LogoutOptions = ({appTypes, initialValues, onSavePromise}) => { if (initialValues.application_type === appTypes.Native) { return isValidNativeUri(value); } - const regex = /^https:\/\/([\w@][\w.:@]+)\/?[\w\.?=%&=\-@/$,]*$/ig; + const regex = /^https:\/\/([\w@][\w.:@\-]+)\/?[\w\.?=%&=\-@/$,]*$/ig; return regex.test(value); } diff --git a/tests/unit/CustomUrlSetValidatorTest.php b/tests/unit/CustomUrlSetValidatorTest.php new file mode 100644 index 00000000..bd1628b8 --- /dev/null +++ b/tests/unit/CustomUrlSetValidatorTest.php @@ -0,0 +1,95 @@ + $value, 'application_type' => $app_type]; + $validator = new CustomValidator + ( + new Translator(new ArrayLoader(), 'en'), + $data, + ['redirect_uris' => 'custom_url_set:application_type'] + ); + return $validator->passes(); + } + + public static function validUrlsProvider(): array + { + return [ + 'hyphenated host with port' => ['https://macbook-air.tail3c93e5.ts.net:10000/auth/callback'], + 'hyphenated host with port root' => ['https://macbook-air.tail3c93e5.ts.net:10000/'], + 'hyphenated host without port' => ['https://macbook-air.tail3c93e5.ts.net/auth/callback'], + 'double hyphen host' => ['https://pr-1031--show-admin-preview.netlify.app/auth/callback'], + 'ip with port' => ['https://192.168.68.106:8080/auth/callback'], + 'localhost with port' => ['https://localhost:3000/auth/callback'], + 'plain host' => ['https://showadmin.dev.fnopen.com/auth/callback'], + ]; + } + + #[DataProvider('validUrlsProvider')] + public function testAcceptsValidSslUrl(string $url): void + { + $this->assertTrue($this->validate($url), sprintf('%s should be accepted', $url)); + } + + public function testAcceptsCommaSeparatedSetWithHyphenatedHostAndPort(): void + { + $this->assertTrue($this->validate(implode(',', [ + 'https://localhost:8080/auth/callback', + 'https://macbook-air.tail3c93e5.ts.net:10000/auth/callback', + ]))); + } + + public static function invalidUrlsProvider(): array + { + return [ + 'http scheme' => ['http://my-host.com:8080/cb'], + 'no scheme' => ['my-host.com:8080/cb'], + 'host starting with hyphen' => ['https://-my-host.com:8080/cb'], + ]; + } + + #[DataProvider('invalidUrlsProvider')] + public function testRejectsInvalidSslUrl(string $url): void + { + $this->assertFalse($this->validate($url), sprintf('%s should be rejected', $url)); + } + + public function testRejectsSetWhenAnyUrlIsInvalid(): void + { + $this->assertFalse($this->validate(implode(',', [ + 'https://macbook-air.tail3c93e5.ts.net:10000/auth/callback', + 'http://localhost:8080/auth/callback', + ]))); + } +}