From 1639941da715fb979e33258c3ca2af940bb45243 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 28 Aug 2026 09:34:10 +0200 Subject: [PATCH] fix(domains): reject single-label hostnames for app and service domains --- app/Support/ValidationPatterns.php | 10 +++++++++- tests/Feature/ApplicationDomainsTest.php | 9 +++++++++ tests/Feature/ServiceDomainsTest.php | 12 ++++++++++++ tests/Unit/ValidationPatternsTest.php | 6 ++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/app/Support/ValidationPatterns.php b/app/Support/ValidationPatterns.php index 41b27f9ff9..5c02b84265 100644 --- a/app/Support/ValidationPatterns.php +++ b/app/Support/ValidationPatterns.php @@ -570,8 +570,16 @@ class ValidationPatterns continue; } - if (blank(parse_url($url, PHP_URL_HOST))) { + $host = parse_url($url, PHP_URL_HOST); + if (blank($host)) { $errors[] = "Invalid URL: {$url}"; + + continue; + } + + $unwrappedHost = trim((string) $host, '[]'); + if (! str_contains($unwrappedHost, '.') && filter_var($unwrappedHost, FILTER_VALIDATE_IP) === false) { + $errors[] = "Invalid URL: {$url}. The hostname must be a fully qualified domain name."; } } diff --git a/tests/Feature/ApplicationDomainsTest.php b/tests/Feature/ApplicationDomainsTest.php index 95d0593b7d..43d1535596 100644 --- a/tests/Feature/ApplicationDomainsTest.php +++ b/tests/Feature/ApplicationDomainsTest.php @@ -98,6 +98,15 @@ it('uses safe domain validation rules on the domains form', function () { ->and($validator->errors()->has('newDomain'))->toBeTrue(); }); +it('does not add a single-label hostname as an application domain', function () { + Livewire::test(Domains::class, ['application' => $this->application->fresh()]) + ->set('newDomainParts.host', 'aaa') + ->call('addDomain') + ->assertDispatched('error'); + + expect($this->application->fresh()->fqdn)->toBeNull(); +}); + it('lists existing domains as individual rows', function () { $this->application->update([ 'fqdn' => 'https://example.com,https://www.example.com,https://another.example.com,https://www.another.example.com', diff --git a/tests/Feature/ServiceDomainsTest.php b/tests/Feature/ServiceDomainsTest.php index d18a9a5e5d..4c30ef6196 100644 --- a/tests/Feature/ServiceDomainsTest.php +++ b/tests/Feature/ServiceDomainsTest.php @@ -193,6 +193,18 @@ it('resets the add domain dns gate when segmented domain fields change', functio ->assertSet('forceSaveDns', false); }); +it('does not add a single-label hostname as a service domain', function () { + $this->apiApp->update(['fqdn' => null]); + + Livewire::test(Domains::class, ['service' => $this->service->fresh(['applications', 'server'])]) + ->set('newServiceApplicationId', $this->apiApp->id) + ->set('newDomainParts.host', 'aaa') + ->call('addDomain') + ->assertDispatched('error'); + + expect($this->apiApp->fresh()->fqdn)->toBeNull(); +}); + it('shows dns entries control next to Add', function () { Livewire::test(Domains::class, ['service' => $this->service->fresh(['applications', 'server'])]) ->assertSuccessful() diff --git a/tests/Unit/ValidationPatternsTest.php b/tests/Unit/ValidationPatternsTest.php index cfcceac381..0b3252d7e8 100644 --- a/tests/Unit/ValidationPatternsTest.php +++ b/tests/Unit/ValidationPatternsTest.php @@ -191,3 +191,9 @@ it('normalizes application domain scheme and host without lowercasing path query it('validates application domains with underscores in the hostname', function () { expect(ValidationPatterns::validateApplicationDomains('https://myapp_service.example.com'))->toBeEmpty(); }); + +it('rejects single-label application hostnames but allows IP addresses', function () { + expect(ValidationPatterns::validateApplicationDomains('https://aaa'))->not->toBeEmpty() + ->and(ValidationPatterns::validateApplicationDomains('https://localhost'))->not->toBeEmpty() + ->and(ValidationPatterns::validateApplicationDomains('http://192.0.2.10:8000'))->toBeEmpty(); +});