Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8b7dbba
fix(api): hide sensitive fields by default, expose via makeVisible fo…
andrasbacsai Apr 30, 2026
e828058
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai Apr 30, 2026
8dc79f4
fix(api): expose nested server secrets for privileged tokens
andrasbacsai Apr 30, 2026
ea0ade4
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai Apr 30, 2026
5f124ae
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai May 5, 2026
0888198
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai May 5, 2026
c553b6a
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai May 6, 2026
224b9e4
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai May 7, 2026
ffa494d
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai May 11, 2026
c97f916
fix(api): hide application compose PR fields
andrasbacsai May 11, 2026
12aba41
fix(api): gate service server secrets by sensitive scope
andrasbacsai May 11, 2026
b29bdce
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai May 11, 2026
f40bb80
style(api): tighten nested server secret visibility checks
andrasbacsai May 11, 2026
81a3bb0
fix(api): hide sensitive fields by default
andrasbacsai May 11, 2026
db23ee7
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai May 11, 2026
fb13a6c
fix(api): expose cloud tokens with sensitive read access
andrasbacsai May 13, 2026
0038a8f
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai Jun 2, 2026
b01c2dd
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai Jun 4, 2026
2fcc42b
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai Jun 4, 2026
70eda65
fix(api): hide nested server secrets from read tokens
andrasbacsai Jun 4, 2026
2ebe2e8
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai Jun 15, 2026
f5ecdfa
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai Jun 15, 2026
6871160
fix(api): gate sensitive storage and GitHub fields
andrasbacsai Jul 2, 2026
1317284
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai Jul 2, 2026
99f6022
fix(api): avoid lazy loading nested server secrets
andrasbacsai Jul 2, 2026
e9bef84
fix(api): hide nested server secrets in database responses
andrasbacsai Jul 7, 2026
9a2c432
fix(api): expose sensitive fields for privileged tokens
andrasbacsai Jul 7, 2026
ff976a1
Merge remote-tracking branch 'origin/next' into api-sensitive-data-sc…
andrasbacsai Jul 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions app/Http/Controllers/Api/ApplicationsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ private function removeSensitiveData($application)
'resourceable_id',
'resourceable_type',
]);
if (request()->attributes->get('can_read_sensitive', false) === false) {
$application->makeHidden([
if (request()->attributes->get('can_read_sensitive', false) === true) {
$application->makeVisible([
'custom_labels',
'dockerfile',
'docker_compose',
Expand All @@ -53,16 +53,48 @@ private function removeSensitiveData($application)
'manual_webhook_secret_gitea',
'manual_webhook_secret_github',
'manual_webhook_secret_gitlab',
'private_key_id',
'http_basic_auth_password',
'value',
'real_value',
'http_basic_auth_password',
]);
$this->exposeNestedServerSecrets($application);
} else {
Comment thread
andrasbacsai marked this conversation as resolved.
$application->makeHidden([
'private_key_id',
]);
}

return serializeApiResponse($application);
}

/**
* Expose sensitive fields on eager-loaded nested Server + ServerSetting
* relations for callers with the `read:sensitive` or `root` token ability.
* Models hide these by default via $hidden; this re-exposes them per-request.
*/
private function exposeNestedServerSecrets($model): void
{
$server = $model->destination?->server ?? null;
if (! $server) {
return;
}
$server->makeVisible([
'logdrain_axiom_api_key',
'logdrain_newrelic_license_key',
]);
$settings = $server->settings ?? null;
if ($settings) {
$settings->makeVisible([
'sentinel_token',
'sentinel_custom_url',
'logdrain_newrelic_license_key',
'logdrain_axiom_api_key',
'logdrain_custom_config',
'logdrain_custom_config_parser',
]);
}
}

#[OA\Get(
summary: 'List',
description: 'List all applications.',
Expand Down
41 changes: 37 additions & 4 deletions app/Http/Controllers/Api/DatabasesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,55 @@ private function removeSensitiveData($database)
'id',
'laravel_through_key',
]);
if (request()->attributes->get('can_read_sensitive', false) === false) {
$database->makeHidden([
if (request()->attributes->get('can_read_sensitive', false) === true) {
$database->makeVisible([
'internal_db_url',
'external_db_url',
'init_scripts',
'postgres_password',
'dragonfly_password',
'redis_password',
'mongo_initdb_root_password',
'keydb_password',
'clickhouse_admin_password',
'mysql_password',
'mysql_root_password',
'mariadb_password',
'mariadb_root_password',
]);
$this->exposeNestedServerSecrets($database);
}

return serializeApiResponse($database);
}

/**
* Expose sensitive fields on eager-loaded nested Server + ServerSetting
* relations for callers with the `read:sensitive` or `root` token ability.
*/
private function exposeNestedServerSecrets($model): void
{
$server = $model->destination?->server ?? null;
if (! $server) {
return;
}
$server->makeVisible([
'logdrain_axiom_api_key',
'logdrain_newrelic_license_key',
]);
$settings = $server->settings ?? null;
if ($settings) {
$settings->makeVisible([
'sentinel_token',
'sentinel_custom_url',
'logdrain_newrelic_license_key',
'logdrain_axiom_api_key',
'logdrain_custom_config',
'logdrain_custom_config_parser',
]);
}
}
Comment thread
andrasbacsai marked this conversation as resolved.
Outdated
Comment thread
andrasbacsai marked this conversation as resolved.

#[OA\Get(
summary: 'List',
description: 'List all databases.',
Expand Down Expand Up @@ -2957,8 +2990,8 @@ private function removeSensitiveEnvData($env)
'resourceable_id',
'resourceable_type',
]);
if (request()->attributes->get('can_read_sensitive', false) === false) {
$env->makeHidden([
if (request()->attributes->get('can_read_sensitive', false) === true) {
$env->makeVisible([
'value',
'real_value',
]);
Expand Down
16 changes: 12 additions & 4 deletions app/Http/Controllers/Api/ServersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ class ServersController extends Controller
{
private function removeSensitiveDataFromSettings($settings)
{
if (request()->attributes->get('can_read_sensitive', false) === false) {
$settings = $settings->makeHidden([
if (request()->attributes->get('can_read_sensitive', false) === true) {
$settings = $settings->makeVisible([
'sentinel_token',
'sentinel_custom_url',
'logdrain_newrelic_license_key',
'logdrain_axiom_api_key',
'logdrain_custom_config',
'logdrain_custom_config_parser',
]);
}

Expand All @@ -36,8 +41,11 @@ private function removeSensitiveData($server)
$server->makeHidden([
'id',
]);
if (request()->attributes->get('can_read_sensitive', false) === false) {
// Do nothing
if (request()->attributes->get('can_read_sensitive', false) === true) {
$server->makeVisible([
'logdrain_axiom_api_key',
'logdrain_newrelic_license_key',
]);
}

return serializeApiResponse($server);
Expand Down
42 changes: 40 additions & 2 deletions app/Http/Controllers/Api/ServicesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use App\Support\ValidationPatterns;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Validator;
use OpenApi\Attributes as OA;
use Symfony\Component\Yaml\Yaml;
Expand All @@ -30,18 +31,55 @@ private function removeSensitiveData($service)
'resourceable_id',
'resourceable_type',
]);
if (request()->attributes->get('can_read_sensitive', false) === false) {
$service->makeHidden([
if (request()->attributes->get('can_read_sensitive', false) === true) {
$service->makeVisible([
'docker_compose_raw',
'docker_compose',
'value',
'real_value',
]);
$this->exposeNestedServerSecrets($service);
}

return serializeApiResponse($service);
}

/**
* Expose sensitive fields on eager-loaded nested Server + ServerSetting
* relations for callers with the `read:sensitive` or `root` token ability.
* Handles both single models and Eloquent Collections (the listing endpoint
* passes a Collection of Services per project to removeSensitiveData()).
*/
private function exposeNestedServerSecrets($model): void
{
if ($model instanceof Collection || $model instanceof \Illuminate\Database\Eloquent\Collection) {
foreach ($model as $item) {
$this->exposeNestedServerSecrets($item);
}

return;
}
$server = $model->destination?->server ?? $model->server ?? null;
if (! $server) {
return;
}
$server->makeVisible([
'logdrain_axiom_api_key',
'logdrain_newrelic_license_key',
]);
$settings = $server->settings ?? null;
if ($settings) {
$settings->makeVisible([
'sentinel_token',
'sentinel_custom_url',
'logdrain_newrelic_license_key',
'logdrain_axiom_api_key',
'logdrain_custom_config',
'logdrain_custom_config_parser',
]);
}
}

Comment thread
andrasbacsai marked this conversation as resolved.
private function applyServiceUrls(Service $service, array $urlsArray, string $teamId, bool $forceDomainOverride = false): ?array
{
$errors = [];
Expand Down
18 changes: 18 additions & 0 deletions app/Models/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,24 @@ class Application extends BaseModel

protected $appends = ['server_status'];

/**
* Sensitive fields hidden by default in serialized output (toArray/toJson).
* API controllers should call makeVisible([...]) for callers with the
* `read:sensitive` or `root` token ability. Internal serializers (deployment
* job, compose generation) must makeVisible explicitly before toArray().
*/
protected $hidden = [
'http_basic_auth_password',
'manual_webhook_secret_github',
'manual_webhook_secret_gitlab',
'manual_webhook_secret_bitbucket',
'manual_webhook_secret_gitea',
'dockerfile',
'docker_compose',
'docker_compose_raw',
'custom_labels',
];
Comment thread
andrasbacsai marked this conversation as resolved.

protected function casts(): array
{
return [
Expand Down
14 changes: 12 additions & 2 deletions app/Models/EnvironmentVariable.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,19 @@ class EnvironmentVariable extends BaseModel

protected $appends = ['real_value', 'is_shared', 'is_really_required', 'is_nixpacks', 'is_coolify'];

/**
* Sensitive fields hidden by default in serialized output (toArray/toJson).
* API controllers should call makeVisible([...]) for callers with the
* `read:sensitive` or `root` token ability.
*/
protected $hidden = [
'value',
'real_value',
];

protected static function booted()
{
static::created(function (EnvironmentVariable $environment_variable) {
static::created(function (ModelsEnvironmentVariable $environment_variable) {
if ($environment_variable->resourceable_type === Application::class && ! $environment_variable->is_preview) {
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)
->where('resourceable_type', Application::class)
Expand Down Expand Up @@ -109,7 +119,7 @@ protected static function booted()
]);
});

static::saving(function (EnvironmentVariable $environmentVariable) {
static::saving(function (ModelsEnvironmentVariable $environmentVariable) {
$environmentVariable->updateIsShared();
});
}
Expand Down
10 changes: 10 additions & 0 deletions app/Models/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,16 @@ public static function flushIdentityMap(): void
'force_disabled' => 'boolean',
];

/**
* Sensitive fields hidden by default in serialized output (toArray/toJson).
* API controllers should call makeVisible([...]) for callers with the
* `read:sensitive` or `root` token ability.
*/
protected $hidden = [
'logdrain_axiom_api_key',
'logdrain_newrelic_license_key',
];

protected $schemalessAttributes = [
'proxy',
];
Expand Down
14 changes: 14 additions & 0 deletions app/Models/ServerSetting.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,20 @@ class ServerSetting extends Model
'connection_timeout' => 'integer',
];

/**
* Sensitive fields hidden by default in serialized output (toArray/toJson).
* API controllers should call makeVisible([...]) for callers with the
* `read:sensitive` or `root` token ability.
*/
protected $hidden = [
'sentinel_token',
'sentinel_custom_url',
'logdrain_newrelic_license_key',
'logdrain_axiom_api_key',
'logdrain_custom_config',
'logdrain_custom_config_parser',
];

protected static function booted()
{
static::creating(function ($setting) {
Expand Down
11 changes: 11 additions & 0 deletions app/Models/Service.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ class Service extends BaseModel

protected $appends = ['server_status', 'status'];

/**
* Sensitive fields hidden by default in serialized output (toArray/toJson).
* API controllers should call makeVisible([...]) for callers with the
* `read:sensitive` or `root` token ability. Internal compose generators
* must makeVisible explicitly before toArray().
*/
protected $hidden = [
'docker_compose',
'docker_compose_raw',
];

protected static function booted()
{
static::creating(function ($service) {
Expand Down
11 changes: 11 additions & 0 deletions app/Models/StandaloneClickhouse.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ class StandaloneClickhouse extends BaseModel

protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];

/**
* Sensitive fields hidden by default in serialized output (toArray/toJson).
* API controllers should call makeVisible([...]) for callers with the
* `read:sensitive` or `root` token ability.
*/
protected $hidden = [
'clickhouse_admin_password',
'internal_db_url',
'external_db_url',
];

protected $casts = [
'clickhouse_admin_password' => 'encrypted',
'public_port_timeout' => 'integer',
Expand Down
11 changes: 11 additions & 0 deletions app/Models/StandaloneDragonfly.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ class StandaloneDragonfly extends BaseModel

protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];

/**
* Sensitive fields hidden by default in serialized output (toArray/toJson).
* API controllers should call makeVisible([...]) for callers with the
* `read:sensitive` or `root` token ability.
*/
protected $hidden = [
'dragonfly_password',
'internal_db_url',
'external_db_url',
];

protected $casts = [
'dragonfly_password' => 'encrypted',
'public_port_timeout' => 'integer',
Expand Down
11 changes: 11 additions & 0 deletions app/Models/StandaloneKeydb.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ class StandaloneKeydb extends BaseModel

protected $appends = ['internal_db_url', 'external_db_url', 'server_status'];

/**
* Sensitive fields hidden by default in serialized output (toArray/toJson).
* API controllers should call makeVisible([...]) for callers with the
* `read:sensitive` or `root` token ability.
*/
protected $hidden = [
'keydb_password',
'internal_db_url',
'external_db_url',
];

protected $casts = [
'keydb_password' => 'encrypted',
'public_port_timeout' => 'integer',
Expand Down
Loading
Loading