From 2e44d2295b49e37e15cab542a28949c243763baa Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 2 Oct 2026 16:00:51 +0200 Subject: [PATCH] fix(deployments): fail with a clear error for an unsupported static image Older versions accepted any static image through the API. Such a stored value now stops the deployment with a message that lists the supported web servers instead of a ValueError. Co-Authored-By: Claude Opus 5.5 --- app/Jobs/ApplicationDeploymentJob.php | 15 +++++- tests/Feature/StaticImageDeploymentTest.php | 52 +++++++++++++++++++++ tests/Feature/StaticImageSecurityTest.php | 3 +- 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 tests/Feature/StaticImageDeploymentTest.php diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 57c5503092..96408c1aa2 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -4153,9 +4153,22 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); return $default; } + /** + * Older versions accepted any static image through the API, so a stored value can be one that + * Coolify no longer supports. Such a value stops the deployment with a clear error instead of + * silently building with another web server image. + */ private function staticImage(): string { - return StaticImageTypes::from($this->application->static_image)->value; + $image = StaticImageTypes::tryFrom((string) $this->application->static_image); + + if ($image === null) { + $supported = implode(', ', array_column(StaticImageTypes::cases(), 'value')); + + throw new DeploymentException("The static image '{$this->application->static_image}' is not supported. Select a supported web server ({$supported}) in the application settings and deploy again."); + } + + return $image->value; } private function pull_latest_image(string $image): void diff --git a/tests/Feature/StaticImageDeploymentTest.php b/tests/Feature/StaticImageDeploymentTest.php new file mode 100644 index 0000000000..e7f3c14495 --- /dev/null +++ b/tests/Feature/StaticImageDeploymentTest.php @@ -0,0 +1,52 @@ + InstanceSettings::firstOrCreate(['id' => 0])); + + $team = Team::factory()->create(); + $server = Server::factory()->create(['team_id' => $team->id]); + $destination = StandaloneDocker::query()->where('server_id', $server->id)->firstOrFail(); + $project = Project::factory()->create(['team_id' => $team->id]); + $environment = Environment::factory()->create(['project_id' => $project->id]); + + $this->application = Application::factory()->create([ + 'environment_id' => $environment->id, + 'destination_id' => $destination->id, + 'destination_type' => $destination->getMorphClass(), + 'static_image' => 'nginx:alpine', + ]); +}); + +function staticImageForDeployment(Application $application): string +{ + $job = (new ReflectionClass(ApplicationDeploymentJob::class))->newInstanceWithoutConstructor(); + (new ReflectionProperty($job, 'application'))->setValue($job, $application); + + return (new ReflectionMethod($job, 'staticImage'))->invoke($job); +} + +test('a supported static image is used for the deployment', function () { + expect(staticImageForDeployment($this->application))->toBe('nginx:alpine'); +}); + +test('an unsupported stored static image stops the deployment with a clear error', function () { + // Older versions accepted any static image through the API update endpoint. + $this->application->forceFill(['static_image' => 'nginx:1.27-alpine'])->saveQuietly(); + + expect(fn () => staticImageForDeployment($this->application->fresh())) + ->toThrow(DeploymentException::class, "The static image 'nginx:1.27-alpine' is not supported."); +}); diff --git a/tests/Feature/StaticImageSecurityTest.php b/tests/Feature/StaticImageSecurityTest.php index 2b5b1f955b..0630f052b3 100644 --- a/tests/Feature/StaticImageSecurityTest.php +++ b/tests/Feature/StaticImageSecurityTest.php @@ -1,5 +1,6 @@ setValue($job, $application); expect(fn () => (new ReflectionMethod(ApplicationDeploymentJob::class, 'staticImage'))->invoke($job)) - ->toThrow(ValueError::class); + ->toThrow(DeploymentException::class, 'is not supported'); })->with(['shell' => 'nginx:alpine;id>/tmp/pwn', 'dockerfile newline' => "nginx:alpine\nRUN id"]); test('deployment accepts the allowed static image', function () {