Merge branch 'next' into port-detection-lol

This commit is contained in:
Andras Bacsai authored and GitHub committed 2025-11-11 11:21:26 +01:00
commit 45ab79f292
31 files changed
+882 -65

No files matched your search

+24 -4
View File
@@ -17,24 +17,44 @@ function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pul
if (! $server->isSwarm()) {
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server);
$containers = format_docker_command_output_to_json($containers);
$containers = $containers->map(function ($container) use ($pullRequestId, $includePullrequests) {
$labels = data_get($container, 'Labels');
if (! str($labels)->contains('coolify.pullRequestId=')) {
data_set($container, 'Labels', $labels.",coolify.pullRequestId={$pullRequestId}");
$containerName = data_get($container, 'Names');
$hasPrLabel = str($labels)->contains('coolify.pullRequestId=');
$prLabelValue = null;
if ($hasPrLabel) {
preg_match('/coolify\.pullRequestId=(\d+)/', $labels, $matches);
$prLabelValue = $matches[1] ?? null;
}
// Treat pullRequestId=0 or missing label as base deployment (convention: 0 = no PR)
$isBaseDeploy = ! $hasPrLabel || (int) $prLabelValue === 0;
// If we're looking for a specific PR and this is a base deployment, exclude it
if ($pullRequestId !== null && $pullRequestId !== 0 && $isBaseDeploy) {
return null;
}
// If this is a base deployment, include it when not filtering for PRs
if ($isBaseDeploy) {
return $container;
}
if ($includePullrequests) {
return $container;
}
if (str($labels)->contains("coolify.pullRequestId=$pullRequestId")) {
if ($pullRequestId !== null && $pullRequestId !== 0 && str($labels)->contains("coolify.pullRequestId={$pullRequestId}")) {
return $container;
}
return null;
});
return $containers->filter();
$filtered = $containers->filter();
return $filtered;
}
return $containers;
+38 -10
View File
@@ -59,11 +59,13 @@ function validateDockerComposeForInjection(string $composeYaml): void
if (isset($volume['source'])) {
$source = $volume['source'];
if (is_string($source)) {
// Allow simple env vars and env vars with defaults (validated in parseDockerVolumeString)
// Allow env vars and env vars with defaults (validated in parseDockerVolumeString)
// Also allow env vars followed by safe path concatenation (e.g., ${VAR}/path)
$isSimpleEnvVar = preg_match('/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}$/', $source);
$isEnvVarWithDefault = preg_match('/^\$\{[^}]+:-[^}]*\}$/', $source);
$isEnvVarWithPath = preg_match('/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}[\/\w\.\-]*$/', $source);
if (! $isSimpleEnvVar && ! $isEnvVarWithDefault) {
if (! $isSimpleEnvVar && ! $isEnvVarWithDefault && ! $isEnvVarWithPath) {
try {
validateShellSafePath($source, 'volume source');
} catch (\Exception $e) {
@@ -310,15 +312,17 @@ function parseDockerVolumeString(string $volumeString): array
// Validate source path for command injection attempts
// We validate the final source value after environment variable processing
if ($source !== null) {
// Allow simple environment variables like ${VAR_NAME} or ${VAR}
// but validate everything else for shell metacharacters
// Allow environment variables like ${VAR_NAME} or ${VAR}
// Also allow env vars followed by safe path concatenation (e.g., ${VAR}/path)
$sourceStr = is_string($source) ? $source : $source;
// Skip validation for simple environment variable references
// Pattern: ${WORD_CHARS} with no special characters inside
// Pattern 1: ${WORD_CHARS} with no special characters inside
// Pattern 2: ${WORD_CHARS}/path/to/file (env var with path concatenation)
$isSimpleEnvVar = preg_match('/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}$/', $sourceStr);
$isEnvVarWithPath = preg_match('/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}[\/\w\.\-]*$/', $sourceStr);
if (! $isSimpleEnvVar) {
if (! $isSimpleEnvVar && ! $isEnvVarWithPath) {
try {
validateShellSafePath($sourceStr, 'volume source');
} catch (\Exception $e) {
@@ -716,9 +720,12 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
// Validate source and target for command injection (array/long syntax)
if ($source !== null && ! empty($source->value())) {
$sourceValue = $source->value();
// Allow simple environment variable references
// Allow environment variable references and env vars with path concatenation
$isSimpleEnvVar = preg_match('/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}$/', $sourceValue);
if (! $isSimpleEnvVar) {
$isEnvVarWithDefault = preg_match('/^\$\{[^}]+:-[^}]*\}$/', $sourceValue);
$isEnvVarWithPath = preg_match('/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}[\/\w\.\-]*$/', $sourceValue);
if (! $isSimpleEnvVar && ! $isEnvVarWithDefault && ! $isEnvVarWithPath) {
try {
validateShellSafePath($sourceValue, 'volume source');
} catch (\Exception $e) {
@@ -1298,6 +1305,15 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
if ($depends_on->count() > 0) {
$payload['depends_on'] = $depends_on;
}
// Auto-inject .env file so Coolify environment variables are available inside containers
// This makes Applications behave consistently with manual .env file usage
$existingEnvFiles = data_get($service, 'env_file');
$envFiles = collect(is_null($existingEnvFiles) ? [] : (is_array($existingEnvFiles) ? $existingEnvFiles : [$existingEnvFiles]))
->push('.env')
->unique()
->values();
$payload['env_file'] = $envFiles;
if ($isPullRequest) {
$serviceName = addPreviewDeploymentSuffix($serviceName, $pullRequestId);
}
@@ -1824,9 +1840,12 @@ function serviceParser(Service $resource): Collection
// Validate source and target for command injection (array/long syntax)
if ($source !== null && ! empty($source->value())) {
$sourceValue = $source->value();
// Allow simple environment variable references
// Allow environment variable references and env vars with path concatenation
$isSimpleEnvVar = preg_match('/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}$/', $sourceValue);
if (! $isSimpleEnvVar) {
$isEnvVarWithDefault = preg_match('/^\$\{[^}]+:-[^}]*\}$/', $sourceValue);
$isEnvVarWithPath = preg_match('/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}[\/\w\.\-]*$/', $sourceValue);
if (! $isSimpleEnvVar && ! $isEnvVarWithDefault && ! $isEnvVarWithPath) {
try {
validateShellSafePath($sourceValue, 'volume source');
} catch (\Exception $e) {
@@ -2281,6 +2300,15 @@ function serviceParser(Service $resource): Collection
if ($depends_on->count() > 0) {
$payload['depends_on'] = $depends_on;
}
// Auto-inject .env file so Coolify environment variables are available inside containers
// This makes Services behave consistently with Applications
$existingEnvFiles = data_get($service, 'env_file');
$envFiles = collect(is_null($existingEnvFiles) ? [] : (is_array($existingEnvFiles) ? $existingEnvFiles : [$existingEnvFiles]))
->push('.env')
->unique()
->values();
$payload['env_file'] = $envFiles;
$parsedServices->put($serviceName, $payload);
}