mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
fix(security): stop leaking locked shared variables and proxy secrets
- References to locked (shown once) shared variables keep their reference text in the UI, copy action, and API instead of the locked value. - Remove the saved proxy configuration and validation logs from nested servers (for example destination.server) in API responses unless the token can read sensitive data. - The services list API now applies removeSensitiveData to every service. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a5ec854e35
commit
aa2592b17c
7 files changed
+423
-54
No files matched your search
@@ -184,44 +184,56 @@ class EnvironmentVariable extends BaseModel
|
||||
public function realValue(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
if (! $this->relationLoaded('resourceable')) {
|
||||
$this->load('resourceable');
|
||||
}
|
||||
$resource = $this->resourceable;
|
||||
if (! $resource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Load relationships needed for shared variable resolution
|
||||
if (! $resource->relationLoaded('environment')) {
|
||||
$resource->load('environment');
|
||||
}
|
||||
if (! $resource->relationLoaded('server') && method_exists($resource, 'server')) {
|
||||
$resource->load('server');
|
||||
}
|
||||
if (! $resource->relationLoaded('destination') && method_exists($resource, 'destination')) {
|
||||
$resource->load('destination.server');
|
||||
}
|
||||
|
||||
$real_value = $this->get_real_environment_variables($this->value, $resource);
|
||||
|
||||
// Skip escaping for valid JSON objects/arrays to prevent quote corruption (see #6160)
|
||||
if (json_validate($real_value) && (str_starts_with($real_value, '{') || str_starts_with($real_value, '['))) {
|
||||
return $real_value;
|
||||
}
|
||||
|
||||
if ($this->is_literal || $this->is_multiline) {
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($real_value);
|
||||
}
|
||||
|
||||
return $real_value;
|
||||
}
|
||||
get: fn () => $this->resolveRealValue(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolved value for display and copy. References to locked shared variables keep their
|
||||
* reference text, so the UI never reveals a value that is only shown once.
|
||||
*/
|
||||
public function displayRealValue(): ?string
|
||||
{
|
||||
return $this->resolveRealValue(revealLockedSharedVariables: false);
|
||||
}
|
||||
|
||||
private function resolveRealValue(bool $revealLockedSharedVariables = true): ?string
|
||||
{
|
||||
if (! $this->relationLoaded('resourceable')) {
|
||||
$this->load('resourceable');
|
||||
}
|
||||
$resource = $this->resourceable;
|
||||
if (! $resource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Load relationships needed for shared variable resolution
|
||||
if (! $resource->relationLoaded('environment')) {
|
||||
$resource->load('environment');
|
||||
}
|
||||
if (! $resource->relationLoaded('server') && method_exists($resource, 'server')) {
|
||||
$resource->load('server');
|
||||
}
|
||||
if (! $resource->relationLoaded('destination') && method_exists($resource, 'destination')) {
|
||||
$resource->load('destination.server');
|
||||
}
|
||||
|
||||
$real_value = $this->get_real_environment_variables_internal($this->value, $resource, null, $revealLockedSharedVariables);
|
||||
|
||||
// Skip escaping for valid JSON objects/arrays to prevent quote corruption (see #6160)
|
||||
if (json_validate($real_value) && (str_starts_with($real_value, '{') || str_starts_with($real_value, '['))) {
|
||||
return $real_value;
|
||||
}
|
||||
|
||||
if ($this->is_literal || $this->is_multiline) {
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($real_value);
|
||||
}
|
||||
|
||||
return $real_value;
|
||||
}
|
||||
|
||||
protected function isReallyRequired(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
@@ -267,9 +279,9 @@ class EnvironmentVariable extends BaseModel
|
||||
return preg_match('/^{{\s*(?:'.$types.')\..*}}$/s', trim($this->value)) === 1;
|
||||
}
|
||||
|
||||
public function get_real_environment_variables_with_server(?string $environment_variable = null, $resource = null, $server = null)
|
||||
public function get_real_environment_variables_with_server(?string $environment_variable = null, $resource = null, $server = null, bool $revealLockedSharedVariables = true)
|
||||
{
|
||||
return $this->get_real_environment_variables_internal($environment_variable, $resource, $server);
|
||||
return $this->get_real_environment_variables_internal($environment_variable, $resource, $server, $revealLockedSharedVariables);
|
||||
}
|
||||
|
||||
public function getResolvedValueWithServer($server = null)
|
||||
@@ -351,12 +363,7 @@ class EnvironmentVariable extends BaseModel
|
||||
->first()?->value ?? $value;
|
||||
}
|
||||
|
||||
private function get_real_environment_variables(?string $environment_variable = null, $resource = null)
|
||||
{
|
||||
return $this->get_real_environment_variables_internal($environment_variable, $resource);
|
||||
}
|
||||
|
||||
private function get_real_environment_variables_internal(?string $environment_variable = null, $resource = null, $serverOverride = null)
|
||||
private function get_real_environment_variables_internal(?string $environment_variable = null, $resource = null, $serverOverride = null, bool $revealLockedSharedVariables = true)
|
||||
{
|
||||
if (is_null($environment_variable) || $environment_variable === '' || is_null($resource)) {
|
||||
return $environment_variable;
|
||||
@@ -396,7 +403,7 @@ class EnvironmentVariable extends BaseModel
|
||||
->where('team_id', $resource->team()->id)
|
||||
->where("{$type}_id", $id)
|
||||
->first();
|
||||
if ($found) {
|
||||
if ($found && ($revealLockedSharedVariables || ! $found->is_shown_once)) {
|
||||
$environment_variable = str($environment_variable)->replace("{{{$sharedEnv}}}", $found->value);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user