Skip to content

Commit 99f6022

Browse files
committed
fix(api): avoid lazy loading nested server secrets
1 parent 1317284 commit 99f6022

5 files changed

Lines changed: 28 additions & 58 deletions

File tree

app/Http/Controllers/Api/ApplicationsController.php

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ private function removeSensitiveData($application)
6969
$application->makeHidden([
7070
'private_key_id',
7171
]);
72-
$this->hideNestedServerSecrets($application);
7372
}
7473

7574
if ($application->is_shown_once ?? false) {
@@ -79,31 +78,6 @@ private function removeSensitiveData($application)
7978
return serializeApiResponse($application);
8079
}
8180

82-
private function hideNestedServerSecrets($model): void
83-
{
84-
$server = $model->destination?->server ?? null;
85-
if (! $server) {
86-
return;
87-
}
88-
89-
$server->makeHidden([
90-
'logdrain_axiom_api_key',
91-
'logdrain_newrelic_license_key',
92-
]);
93-
94-
$settings = $server->settings ?? null;
95-
if ($settings) {
96-
$settings->makeHidden([
97-
'sentinel_token',
98-
'sentinel_custom_url',
99-
'logdrain_newrelic_license_key',
100-
'logdrain_axiom_api_key',
101-
'logdrain_custom_config',
102-
'logdrain_custom_config_parser',
103-
]);
104-
}
105-
}
106-
10781
/**
10882
* Expose sensitive fields on eager-loaded nested Server + ServerSetting
10983
* relations for callers with the `read:sensitive` or `root` token ability.
@@ -184,8 +158,12 @@ public function applications(Request $request)
184158
}
185159

186160
$tagName = $request->query('tag');
161+
$applicationRelations = $request->attributes->get('can_read_sensitive', false) === true
162+
? ['destination.server.settings']
163+
: [];
187164

188165
$applications = Application::ownedByCurrentTeamAPI($teamId)
166+
->with($applicationRelations)
189167
->when($tagName, function ($query, $tagName) {
190168
$query->whereHas('tags', function ($query) use ($tagName) {
191169
$query->where('name', $tagName);

app/Http/Controllers/Api/DatabasesController.php

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -60,37 +60,11 @@ private function removeSensitiveData($database)
6060
'mariadb_root_password',
6161
]);
6262
$this->exposeNestedServerSecrets($database);
63-
} else {
64-
$this->hideNestedServerSecrets($database);
6563
}
6664

6765
return serializeApiResponse($database);
6866
}
6967

70-
private function hideNestedServerSecrets(Model $model): void
71-
{
72-
$server = $model->destination?->server;
73-
if ($server === null) {
74-
return;
75-
}
76-
77-
$server->makeHidden([
78-
'logdrain_axiom_api_key',
79-
'logdrain_newrelic_license_key',
80-
]);
81-
82-
if ($server->settings !== null) {
83-
$server->settings->makeHidden([
84-
'sentinel_token',
85-
'sentinel_custom_url',
86-
'logdrain_newrelic_license_key',
87-
'logdrain_axiom_api_key',
88-
'logdrain_custom_config',
89-
'logdrain_custom_config_parser',
90-
]);
91-
}
92-
}
93-
9468
/**
9569
* Expose sensitive fields on eager-loaded nested Server + ServerSetting
9670
* relations for callers with the `read:sensitive` or `root` token ability.

templates/service-templates-latest.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@
803803
"category": "backend",
804804
"logo": "svgs/convex.svg",
805805
"minversion": "0.0.0",
806-
"template_last_updated_at": "2026-05-09T19:26:30+05:30",
806+
"template_last_updated_at": "2026-06-12T10:45:52+02:00",
807807
"port": "6791"
808808
},
809809
"cryptgeon": {
@@ -1779,7 +1779,7 @@
17791779
"category": "devtools",
17801780
"logo": "svgs/gitea.svg",
17811781
"minversion": "0.0.0",
1782-
"template_last_updated_at": "2026-06-01T07:54:27-05:00"
1782+
"template_last_updated_at": "2026-06-06T00:11:24+02:00"
17831783
},
17841784
"gitea-with-mariadb": {
17851785
"documentation": "https://docs.gitea.com?utm_source=coolify.io",
@@ -2361,7 +2361,7 @@
23612361
"category": "automation",
23622362
"logo": "svgs/inngest.png",
23632363
"minversion": "0.0.0",
2364-
"template_last_updated_at": null,
2364+
"template_last_updated_at": "2026-07-02T13:25:47+02:00",
23652365
"port": "8288"
23662366
},
23672367
"invoice-ninja": {

templates/service-templates.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@
803803
"category": "backend",
804804
"logo": "svgs/convex.svg",
805805
"minversion": "0.0.0",
806-
"template_last_updated_at": "2026-05-09T19:26:30+05:30",
806+
"template_last_updated_at": "2026-06-12T10:45:52+02:00",
807807
"port": "6791"
808808
},
809809
"cryptgeon": {
@@ -1779,7 +1779,7 @@
17791779
"category": "devtools",
17801780
"logo": "svgs/gitea.svg",
17811781
"minversion": "0.0.0",
1782-
"template_last_updated_at": "2026-06-01T07:54:27-05:00"
1782+
"template_last_updated_at": "2026-06-06T00:11:24+02:00"
17831783
},
17841784
"gitea-with-mariadb": {
17851785
"documentation": "https://docs.gitea.com?utm_source=coolify.io",
@@ -2361,7 +2361,7 @@
23612361
"category": "automation",
23622362
"logo": "svgs/inngest.png",
23632363
"minversion": "0.0.0",
2364-
"template_last_updated_at": null,
2364+
"template_last_updated_at": "2026-07-02T13:25:47+02:00",
23652365
"port": "8288"
23662366
},
23672367
"invoice-ninja": {

tests/Feature/Security/ApiSensitiveFieldsTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use App\Models\StandalonePostgresql;
1212
use App\Models\Team;
1313
use App\Models\User;
14+
use Illuminate\Database\Eloquent\Model;
1415
use Illuminate\Foundation\Testing\RefreshDatabase;
1516
use Illuminate\Support\Facades\DB;
1617

@@ -550,6 +551,23 @@ function makeTeamUser(): array
550551
]);
551552
});
552553

554+
test('read token database list does not lazy load nested server relations', function () {
555+
$token = makeApiToken($this->user, $this->team, ['read']);
556+
557+
$preventedLazyLoading = Model::preventsLazyLoading();
558+
Model::preventLazyLoading();
559+
560+
try {
561+
$response = $this->withoutExceptionHandling()->withHeaders([
562+
'Authorization' => 'Bearer '.$token,
563+
])->getJson('/api/v1/databases');
564+
} finally {
565+
Model::preventLazyLoading($preventedLazyLoading);
566+
}
567+
568+
$response->assertStatus(200);
569+
});
570+
553571
test('read token does not leak postgres_password or db urls', function () {
554572
$token = makeApiToken($this->user, $this->team, ['read']);
555573

0 commit comments

Comments
 (0)