Skip to content

Commit eb41814

Browse files
committed
fix(validators): accept hyphenated hostnames with explicit port in sslurl (#168)
* fix(validators): accept hyphenated hostnames with explicit port in sslurl The host part of the sslurl regex did not allow '-' and the path part does not allow ':', so any https URL with a hyphenated hostname and a port (e.g. https://macbook-air.example.ts.net:10000/auth/callback) failed the custom_url_set rule on redirect_uris / allowed_origins. * fix(ui): accept hyphenated hostnames with explicit port in post logout uris The post logout redirect URI validator in the edit client UI still used the old sslurl regex, whose host part did not allow '-', so TagsInput silently dropped URLs like https://macbook-air.example.ts.net:10000/logout even though the API now accepts them.
1 parent 6ccc9d9 commit eb41814

3 files changed

Lines changed: 97 additions & 2 deletions

File tree

‎app/Validators/CustomValidator.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public function validateApplicationtype($attribute, $value)
111111

112112
public function validateSslurl($attribute, $value)
113113
{
114-
return preg_match(";^https:\/\/([\w@][\w.:@]+)\/?[\w\.?=%&=\-@/$,]*$;i", $value) == 1;
114+
return preg_match(";^https:\/\/([\w@][\w.:@\-]+)\/?[\w\.?=%&=\-@/$,]*$;i", $value) == 1;
115115
}
116116

117117
public function validateFreeText($attribute, $value)

‎resources/js/oauth2/profile/edit_client/components/logout_options.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const LogoutOptions = ({appTypes, initialValues, onSavePromise}) => {
2222
if (initialValues.application_type === appTypes.Native) {
2323
return isValidNativeUri(value);
2424
}
25-
const regex = /^https:\/\/([\w@][\w.:@]+)\/?[\w\.?=%&=\-@/$,]*$/ig;
25+
const regex = /^https:\/\/([\w@][\w.:@\-]+)\/?[\w\.?=%&=\-@/$,]*$/ig;
2626
return regex.test(value);
2727
}
2828

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php namespace Tests\unit;
2+
3+
/**
4+
* Copyright 2026 OpenStack Foundation
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
**/
15+
16+
use Illuminate\Translation\ArrayLoader;
17+
use Illuminate\Translation\Translator;
18+
use OAuth2\Models\IClient;
19+
use PHPUnit\Framework\Attributes\DataProvider;
20+
use PHPUnit\Framework\TestCase;
21+
use Validators\CustomValidator;
22+
23+
/**
24+
* Class CustomUrlSetValidatorTest
25+
*
26+
* Regression: custom_url_set rejected https URLs whose hostname contains a hyphen
27+
* AND carries an explicit port (e.g. https://macbook-air.example.ts.net:10000/auth/callback),
28+
* because the host part of the sslurl regex did not allow '-' and the path part does not allow ':'.
29+
*
30+
* @package Tests\unit
31+
*/
32+
class CustomUrlSetValidatorTest extends TestCase
33+
{
34+
private function validate(string $value, string $app_type = IClient::ApplicationType_JS_Client): bool
35+
{
36+
$data = ['redirect_uris' => $value, 'application_type' => $app_type];
37+
$validator = new CustomValidator
38+
(
39+
new Translator(new ArrayLoader(), 'en'),
40+
$data,
41+
['redirect_uris' => 'custom_url_set:application_type']
42+
);
43+
return $validator->passes();
44+
}
45+
46+
public static function validUrlsProvider(): array
47+
{
48+
return [
49+
'hyphenated host with port' => ['https://macbook-air.tail3c93e5.ts.net:10000/auth/callback'],
50+
'hyphenated host with port root' => ['https://macbook-air.tail3c93e5.ts.net:10000/'],
51+
'hyphenated host without port' => ['https://macbook-air.tail3c93e5.ts.net/auth/callback'],
52+
'double hyphen host' => ['https://pr-1031--show-admin-preview.netlify.app/auth/callback'],
53+
'ip with port' => ['https://192.168.68.106:8080/auth/callback'],
54+
'localhost with port' => ['https://localhost:3000/auth/callback'],
55+
'plain host' => ['https://showadmin.dev.fnopen.com/auth/callback'],
56+
];
57+
}
58+
59+
#[DataProvider('validUrlsProvider')]
60+
public function testAcceptsValidSslUrl(string $url): void
61+
{
62+
$this->assertTrue($this->validate($url), sprintf('%s should be accepted', $url));
63+
}
64+
65+
public function testAcceptsCommaSeparatedSetWithHyphenatedHostAndPort(): void
66+
{
67+
$this->assertTrue($this->validate(implode(',', [
68+
'https://localhost:8080/auth/callback',
69+
'https://macbook-air.tail3c93e5.ts.net:10000/auth/callback',
70+
])));
71+
}
72+
73+
public static function invalidUrlsProvider(): array
74+
{
75+
return [
76+
'http scheme' => ['http://my-host.com:8080/cb'],
77+
'no scheme' => ['my-host.com:8080/cb'],
78+
'host starting with hyphen' => ['https://-my-host.com:8080/cb'],
79+
];
80+
}
81+
82+
#[DataProvider('invalidUrlsProvider')]
83+
public function testRejectsInvalidSslUrl(string $url): void
84+
{
85+
$this->assertFalse($this->validate($url), sprintf('%s should be rejected', $url));
86+
}
87+
88+
public function testRejectsSetWhenAnyUrlIsInvalid(): void
89+
{
90+
$this->assertFalse($this->validate(implode(',', [
91+
'https://macbook-air.tail3c93e5.ts.net:10000/auth/callback',
92+
'http://localhost:8080/auth/callback',
93+
])));
94+
}
95+
}

0 commit comments

Comments
 (0)