Summary
GET /api/v1/servers/{server_uuid}/domains?uuid={app_uuid} bypasses team scoping when the optional uuid query parameter is provided. Any authenticated API user can enumerate domain names (FQDNs) of applications belonging to other teams.
This is a separate finding from GHSA-5p5w-h58c-2h5m (cross-team deployment info disclosure via deployment_by_uuid), affecting a different controller and different data.
Details
File: app/Http/Controllers/Api/ServersController.php, lines 285-296
public function domains_by_server(Request $request)
{
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
return invalidTokenResponse();
}
$uuid = $request->get('uuid');
if ($uuid) {
// SHORT-CIRCUIT: No team check, queries globally
$domains = Application::getDomainsByUuid($uuid);
return response()->json(serializeApiResponse($domains));
}
// Only the fallback path (no ?uuid= param) is team-scoped:
$projects = Project::where('team_id', $teamId)->get();
...
}
Application::getDomainsByUuid() in app/Models/Application.php, lines 1962-1971:
public static function getDomainsByUuid(string $uuid): array
{
$application = self::where('uuid', $uuid)->first(); // Global query, no team filter
if ($application) {
return $application->fqdns;
}
return [];
}
When the ?uuid= query parameter is provided, the endpoint short-circuits to Application::getDomainsByUuid() which performs a global query without team scoping. The {server_uuid} path parameter is not even validated against the caller's team in this code path — the function returns before reaching the team-scoped fallback logic.
Correct Pattern (same controller, other endpoints)
All other ServersController methods correctly scope by team:
// servers(), server_by_uuid(), etc.
$server = ModelsServer::whereTeamId($teamId)->whereUuid($uuid)->first();
And the fallback path of domains_by_server (when no ?uuid= param) correctly scopes:
$projects = Project::where('team_id', $teamId)->get();
Impact
Any authenticated API user can discover the configured domain names (FQDNs) of applications belonging to other teams. This leaks:
- Internal/private domain names (e.g.,
internal-tool.company.com)
- Application URL structure and naming conventions
- Which applications exist and their deployment domains
The {server_uuid} path parameter can be any valid server UUID (even the attacker's own), since it's not used when ?uuid= is provided.
Suggested Fix
Add team scoping to the ?uuid= code path:
if ($uuid) {
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $uuid)->first();
if ($application) {
return response()->json(serializeApiResponse($application->fqdns));
}
return response()->json(serializeApiResponse([]));
}
Or scope getDomainsByUuid to accept a teamId parameter.
Summary
GET /api/v1/servers/{server_uuid}/domains?uuid={app_uuid}bypasses team scoping when the optionaluuidquery parameter is provided. Any authenticated API user can enumerate domain names (FQDNs) of applications belonging to other teams.This is a separate finding from GHSA-5p5w-h58c-2h5m (cross-team deployment info disclosure via
deployment_by_uuid), affecting a different controller and different data.Details
File:
app/Http/Controllers/Api/ServersController.php, lines 285-296Application::getDomainsByUuid()inapp/Models/Application.php, lines 1962-1971:When the
?uuid=query parameter is provided, the endpoint short-circuits toApplication::getDomainsByUuid()which performs a global query without team scoping. The{server_uuid}path parameter is not even validated against the caller's team in this code path — the function returns before reaching the team-scoped fallback logic.Correct Pattern (same controller, other endpoints)
All other
ServersControllermethods correctly scope by team:And the fallback path of
domains_by_server(when no?uuid=param) correctly scopes:Impact
Any authenticated API user can discover the configured domain names (FQDNs) of applications belonging to other teams. This leaks:
internal-tool.company.com)The
{server_uuid}path parameter can be any valid server UUID (even the attacker's own), since it's not used when?uuid=is provided.Suggested Fix
Add team scoping to the
?uuid=code path:Or scope
getDomainsByUuidto accept a teamId parameter.