From 27bb601e6671504402d9f915a80da737454d892a Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 9 Sep 2026 13:07:55 +0200 Subject: [PATCH] fix(dns): skip auto DNS jobs when server IP is invalid ConfigureDnsRecordJob requires a string content value. Guard configureDnsAfterDomainAdd with the same blank and FILTER_VALIDATE_IP checks used by createManagedDnsRecord so a missing or hostname-only server address cannot TypeError after the domain is saved. --- .../Concerns/InteractsWithDnsProviders.php | 3 ++ tests/Feature/ApplicationDomainsTest.php | 49 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/app/Livewire/Concerns/InteractsWithDnsProviders.php b/app/Livewire/Concerns/InteractsWithDnsProviders.php index 493c6fe22d..59cb352f9e 100644 --- a/app/Livewire/Concerns/InteractsWithDnsProviders.php +++ b/app/Livewire/Concerns/InteractsWithDnsProviders.php @@ -83,6 +83,9 @@ trait InteractsWithDnsProviders if ($this->dnsProviderProposals === []) { return false; } + if (blank($this->serverIp) || filter_var($this->serverIp, FILTER_VALIDATE_IP) === false) { + return false; + } $this->markDnsPending($hostnames); $proposalsByHostname = collect($this->dnsProviderProposals)->groupBy('hostname'); diff --git a/tests/Feature/ApplicationDomainsTest.php b/tests/Feature/ApplicationDomainsTest.php index 2c8ea4fe51..5196baadef 100644 --- a/tests/Feature/ApplicationDomainsTest.php +++ b/tests/Feature/ApplicationDomainsTest.php @@ -1,13 +1,16 @@ toBe(['https://app.example.com', 'https://www.app.example.com']); }); +it('dispatches configure dns jobs when a matching automatic zone and valid server ip exist', function () { + Queue::fake(); + + $token = IntegrationToken::factory()->for($this->team)->create([ + 'provider' => 'cloudflare', + 'capabilities' => ['dns'], + ]); + DnsProviderZone::factory()->for($token)->create(['name' => 'example.com']); + + Livewire::test(Domains::class, ['application' => $this->application->fresh()]) + ->set('newDomain', 'https://app.example.com') + ->call('addDomain') + ->assertHasNoErrors() + ->assertDispatched('success', 'Domain added.'); + + Queue::assertPushed(ConfigureDnsRecordJob::class, 2); + Queue::assertPushed(ConfigureDnsRecordJob::class, fn (ConfigureDnsRecordJob $job): bool => $job->hostname === 'app.example.com' + && $job->content === '203.0.113.10' + && $job->teamId === $this->team->id); + Queue::assertPushed(ConfigureDnsRecordJob::class, fn (ConfigureDnsRecordJob $job): bool => $job->hostname === 'www.app.example.com' + && $job->content === '203.0.113.10'); +}); + +it('does not dispatch configure dns jobs when the server ip is missing or invalid', function () { + Queue::fake(); + + $this->server->update(['ip' => 'not-an-ip']); + + $token = IntegrationToken::factory()->for($this->team)->create([ + 'provider' => 'cloudflare', + 'capabilities' => ['dns'], + ]); + DnsProviderZone::factory()->for($token)->create(['name' => 'example.com']); + + Livewire::test(Domains::class, ['application' => $this->application->fresh()]) + ->set('newDomain', 'https://app.example.com') + ->call('addDomain') + ->assertHasNoErrors() + ->assertNotDispatched('error'); + + expect(explode(',', (string) $this->application->fresh()->fqdn)) + ->toContain('https://app.example.com'); + + Queue::assertNotPushed(ConfigureDnsRecordJob::class); +}); + it('composes the complete port on the server without duplicating an existing www domain', function () { $this->application->update(['fqdn' => 'https://www.example.com:3000']);