From b7f00e72676558bc1e75918e3eabaeb9232a75b1 Mon Sep 17 00:00:00 2001 From: kashik0i Date: Fri, 28 Aug 2026 02:45:17 +0300 Subject: [PATCH 1/2] fix(docker): detect unqualified helper images --- app/Jobs/CleanupHelperContainersJob.php | 7 +- tests/Unit/CleanupHelperContainersJobTest.php | 69 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/Unit/CleanupHelperContainersJobTest.php diff --git a/app/Jobs/CleanupHelperContainersJob.php b/app/Jobs/CleanupHelperContainersJob.php index f1635d6d4..425dd2591 100644 --- a/app/Jobs/CleanupHelperContainersJob.php +++ b/app/Jobs/CleanupHelperContainersJob.php @@ -19,6 +19,11 @@ class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, S public function __construct(public Server $server) {} + private static function helperContainersCommand(): string + { + return 'docker container ps --format \'{{json .}}\' | jq -s \'map(select(.Image|test("^([^/]+/)?coollabsio/coolify-helper(:|@)")))\''; + } + public function handle(): void { try { @@ -36,7 +41,7 @@ public function handle(): void 'active_deployment_uuids' => $activeDeployments, ]); - $containers = instant_remote_process_with_timeout(['docker container ps --format \'{{json .}}\' | jq -s \'map(select(.Image | contains("'.coolifyRegistryUrl().'/coollabsio/coolify-helper")))\''], $this->server, false); + $containers = instant_remote_process_with_timeout([self::helperContainersCommand()], $this->server, false); $helperContainers = collect(json_decode($containers)); if ($helperContainers->count() > 0) { diff --git a/tests/Unit/CleanupHelperContainersJobTest.php b/tests/Unit/CleanupHelperContainersJobTest.php new file mode 100644 index 000000000..a649985d2 --- /dev/null +++ b/tests/Unit/CleanupHelperContainersJobTest.php @@ -0,0 +1,69 @@ +invoke(null); + $directory = sys_get_temp_dir().'/coolify-helper-filter-'.bin2hex(random_bytes(4)); + $docker = $directory.'/docker'; + $images = [ + 'coollabsio/coolify-helper:1.0.15', + 'docker.io/coollabsio/coolify-helper:1.0.16', + 'ghcr.io/coollabsio/coolify-helper@sha256:abc', + 'registry.example/team/coollabsio/coolify-helper:latest', + 'evil/coollabsio/coolify-helper-copy:latest', + 'coollabsio/not-coolify-helper:latest', + ]; + + mkdir($directory); + file_put_contents($docker, "#!/bin/sh\n".implode("\n", array_map( + fn (string $image): string => 'echo '.escapeshellarg(json_encode(['Image' => $image], JSON_THROW_ON_ERROR)), + $images + ))."\n"); + chmod($docker, 0755); + + try { + $process = new Process(['/bin/sh', '-c', $command], env: [ + 'PATH' => $directory.':'.getenv('PATH'), + ]); + $process->mustRun(); + + expect(array_column(json_decode($process->getOutput(), true, flags: JSON_THROW_ON_ERROR), 'Image')) + ->toBe(array_slice($images, 0, 3)); + } finally { + unlink($docker); + rmdir($directory); + } +}); + +it('preserves the helper image filter for non-root servers', function () { + $command = (new ReflectionMethod(CleanupHelperContainersJob::class, 'helperContainersCommand'))->invoke(null); + $server = Mockery::mock(Server::class)->makePartial(); + $server->user = 'ubuntu'; + $command = parseCommandsByLineForSudo(collect([$command]), $server)[0]; + $directory = sys_get_temp_dir().'/coolify-helper-sudo-filter-'.bin2hex(random_bytes(4)); + $docker = $directory.'/docker'; + $sudo = $directory.'/sudo'; + + mkdir($directory); + file_put_contents($docker, "#!/bin/sh\necho '{\"Image\":\"coollabsio/coolify-helper:1.0.15\"}'\n"); + file_put_contents($sudo, "#!/bin/sh\nexec \"\$@\"\n"); + chmod($docker, 0755); + chmod($sudo, 0755); + + try { + $process = new Process(['/bin/sh', '-c', $command], env: [ + 'PATH' => $directory.':'.getenv('PATH'), + ]); + $process->mustRun(); + + expect(array_column(json_decode($process->getOutput(), true, flags: JSON_THROW_ON_ERROR), 'Image')) + ->toBe(['coollabsio/coolify-helper:1.0.15']); + } finally { + unlink($docker); + unlink($sudo); + rmdir($directory); + } +}); From aafef73aa651366d4ec14f8fa2a1d426817eb105 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Fri, 4 Sep 2026 15:53:49 +0200 Subject: [PATCH 2/2] fix(docker): match helper containers at any registry depth --- app/Jobs/CleanupHelperContainersJob.php | 2 +- tests/Unit/CleanupHelperContainersJobTest.php | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Jobs/CleanupHelperContainersJob.php b/app/Jobs/CleanupHelperContainersJob.php index 425dd2591..52b4064fe 100644 --- a/app/Jobs/CleanupHelperContainersJob.php +++ b/app/Jobs/CleanupHelperContainersJob.php @@ -21,7 +21,7 @@ public function __construct(public Server $server) {} private static function helperContainersCommand(): string { - return 'docker container ps --format \'{{json .}}\' | jq -s \'map(select(.Image|test("^([^/]+/)?coollabsio/coolify-helper(:|@)")))\''; + return 'docker container ps --format \'{{json .}}\' | jq -s \'map(select(.Image|test("(^|/)coollabsio/coolify-helper(:|@)")))\''; } public function handle(): void diff --git a/tests/Unit/CleanupHelperContainersJobTest.php b/tests/Unit/CleanupHelperContainersJobTest.php index a649985d2..bd3e2e175 100644 --- a/tests/Unit/CleanupHelperContainersJobTest.php +++ b/tests/Unit/CleanupHelperContainersJobTest.php @@ -13,6 +13,11 @@ 'docker.io/coollabsio/coolify-helper:1.0.16', 'ghcr.io/coollabsio/coolify-helper@sha256:abc', 'registry.example/team/coollabsio/coolify-helper:latest', + 'coollabsio/coolify:latest', + 'coollabsio/coolify:4.3.12', + 'coollabsio/coolify-realtime:1.0.10', + 'coolify-helper:latest', + 'someone/coolify-helper:1.0.16', 'evil/coollabsio/coolify-helper-copy:latest', 'coollabsio/not-coolify-helper:latest', ]; @@ -31,7 +36,7 @@ $process->mustRun(); expect(array_column(json_decode($process->getOutput(), true, flags: JSON_THROW_ON_ERROR), 'Image')) - ->toBe(array_slice($images, 0, 3)); + ->toBe(array_slice($images, 0, 4)); } finally { unlink($docker); rmdir($directory);