Files
coolify/tests/Unit/ComposeBuildPathSecurityTest.php
T
Andras Bacsai 554b79e8dd feat: add Traefik ACME cert UI and shared managed DNS record ownership
- Proxy: list and delete Traefik ACME certificates from the server proxy
  page via new TraefikAcmeService and Get/DeleteTraefikCertificate actions
- DNS: track ownership and cross-resource references for managed DNS
  records so records are only deleted when no longer referenced; release
  records asynchronously on resource deletion via ReleaseManagedDnsRecordsJob
  and ManagedDnsRecordCleanup; harden Cloudflare provider deletion results
- Databases: fail closed on start when prerequisites or the CA certificate
  are missing (DatabaseStartException, Server::ensureCaCertificate) and
  clean up stale start activities via ResourceStartActivity
- Webhooks: throttle repeated manual webhook signature failures for
  GitHub, GitLab, Gitea and Bitbucket
- Deployments: improve compose build-context handling and compose file
  load error reporting
- Install scripts: rework terminal UI output in install.sh (stable and
  nightly)
- Misc: settings sidebar accordion fixes, log drain toggle rollback,
  add Serverside to README sponsors
- Add migrations and tests covering the above
2026-09-25 19:00:00 +02:00

40 lines
2.1 KiB
PHP

<?php
use App\Jobs\ApplicationDeploymentJob;
function resolveComposeDockerfilePath(mixed $build): ?string
{
$job = (new ReflectionClass(ApplicationDeploymentJob::class))->newInstanceWithoutConstructor();
$method = new ReflectionMethod(ApplicationDeploymentJob::class, 'resolveComposeDockerfilePath');
return $method->invoke($job, $build);
}
test('compose build paths resolve for short and long syntax', function (string|array $build, string $expected) {
// The path is not trusted here: the ARG injection step quotes it and confines the resolved file.
expect(resolveComposeDockerfilePath($build))->toBe($expected);
})->with([
'short syntax' => ['services/api', 'services/api/Dockerfile'],
'current directory short syntax' => ['.', './Dockerfile'],
'long syntax defaults' => [['context' => 'services/api'], 'services/api/Dockerfile'],
'nested Dockerfile' => [['context' => './services/api', 'dockerfile' => 'docker/prod.Dockerfile'], './services/api/docker/prod.Dockerfile'],
'standard Dockerfile variants' => [['context' => '.', 'dockerfile' => 'Dockerfile.prod'], './Dockerfile.prod'],
'monorepo context above the base directory' => [['context' => '..'], '../Dockerfile'],
'path with a space' => ['my app', 'my app/Dockerfile'],
'absolute Dockerfile' => [['context' => '.', 'dockerfile' => '/srv/app/Dockerfile'], '/srv/app/Dockerfile'],
]);
test('compose build contexts that Coolify cannot inspect locally are skipped', function (mixed $build) {
expect(resolveComposeDockerfilePath($build))->toBeNull();
})->with([
'git URL' => ['https://github.com/coollabsio/coolify.git#main:docker'],
'git SSH URL' => [['context' => 'git@github.com:coollabsio/coolify.git']],
'context variable' => ['${APP_DIR:-.}'],
'Dockerfile variable' => [['context' => '.', 'dockerfile' => '${DOCKERFILE}']],
'command substitution' => [['context' => '$(touch /tmp/context-pwned)']],
'inline Dockerfile' => [['context' => '.', 'dockerfile_inline' => "FROM alpine\n"]],
'empty context' => [''],
'non-string context' => [['context' => ['nested']]],
'invalid build definition' => [42],
]);