refactor(api): return stable generic error messages for 5xx responses (#9669)
This commit is contained in:
commit
ea639dab8f
4 changed files with 79 additions and 8 deletions
|
|
@ -2332,7 +2332,7 @@ public function delete_backup_by_uuid(Request $request)
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
|
|
||||||
return response()->json(['message' => 'Failed to delete backup: '.$e->getMessage()], 500);
|
return response()->json(['message' => 'Failed to delete backup.'], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2452,7 +2452,7 @@ public function delete_execution_by_uuid(Request $request)
|
||||||
'message' => 'Backup execution deleted.',
|
'message' => 'Backup execution deleted.',
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return response()->json(['message' => 'Failed to delete backup execution: '.$e->getMessage()], 500);
|
return response()->json(['message' => 'Failed to delete backup execution.'], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ public function locations(Request $request)
|
||||||
|
|
||||||
return response()->json($locations);
|
return response()->json($locations);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return response()->json(['message' => 'Failed to fetch locations: '.$e->getMessage()], 500);
|
return response()->json(['message' => 'Failed to fetch Hetzner locations.'], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,7 +242,7 @@ public function serverTypes(Request $request)
|
||||||
|
|
||||||
return response()->json($serverTypes);
|
return response()->json($serverTypes);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return response()->json(['message' => 'Failed to fetch server types: '.$e->getMessage()], 500);
|
return response()->json(['message' => 'Failed to fetch Hetzner server types.'], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,7 +354,7 @@ public function images(Request $request)
|
||||||
|
|
||||||
return response()->json(array_values($filtered));
|
return response()->json(array_values($filtered));
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return response()->json(['message' => 'Failed to fetch images: '.$e->getMessage()], 500);
|
return response()->json(['message' => 'Failed to fetch Hetzner images.'], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -450,7 +450,7 @@ public function sshKeys(Request $request)
|
||||||
|
|
||||||
return response()->json($sshKeys);
|
return response()->json($sshKeys);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return response()->json(['message' => 'Failed to fetch SSH keys: '.$e->getMessage()], 500);
|
return response()->json(['message' => 'Failed to fetch Hetzner SSH keys.'], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -733,7 +733,7 @@ public function createServer(Request $request)
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return response()->json(['message' => 'Failed to create server: '.$e->getMessage()], 500);
|
return response()->json(['message' => 'Failed to create Hetzner server.'], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -391,7 +391,7 @@
|
||||||
'Content-Disposition' => 'attachment; filename="'.basename($filename).'"',
|
'Content-Disposition' => 'attachment; filename="'.basename($filename).'"',
|
||||||
]);
|
]);
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
return response()->json(['message' => $e->getMessage()], 500);
|
return response()->json(['message' => 'Failed to download backup.'], 500);
|
||||||
}
|
}
|
||||||
})->name('download.backup');
|
})->name('download.backup');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -446,3 +446,74 @@
|
||||||
$response->assertStatus(401);
|
$response->assertStatus(401);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('GHSA-m8wx-q63q-3w6c — error responses do not leak exception details', function () {
|
||||||
|
test('locations endpoint returns generic 500 message on upstream failure', function () {
|
||||||
|
Http::fake([
|
||||||
|
'https://api.hetzner.cloud/v1/locations*' => Http::response([
|
||||||
|
'error' => ['message' => 'INTERNAL_LEAK_TOKEN_abc /var/secret/path'],
|
||||||
|
], 500),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
])->getJson('/api/v1/hetzner/locations?cloud_provider_token_id='.$this->hetznerToken->uuid);
|
||||||
|
|
||||||
|
$response->assertStatus(500);
|
||||||
|
$response->assertExactJson(['message' => 'Failed to fetch Hetzner locations.']);
|
||||||
|
expect($response->getContent())->not->toContain('INTERNAL_LEAK_TOKEN_abc');
|
||||||
|
expect($response->getContent())->not->toContain('/var/secret/path');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('server-types endpoint returns generic 500 message on upstream failure', function () {
|
||||||
|
Http::fake([
|
||||||
|
'https://api.hetzner.cloud/v1/server_types*' => Http::response([
|
||||||
|
'error' => ['message' => 'INTERNAL_LEAK_TOKEN_abc'],
|
||||||
|
], 500),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
])->getJson('/api/v1/hetzner/server-types?cloud_provider_token_id='.$this->hetznerToken->uuid);
|
||||||
|
|
||||||
|
$response->assertStatus(500);
|
||||||
|
$response->assertExactJson(['message' => 'Failed to fetch Hetzner server types.']);
|
||||||
|
expect($response->getContent())->not->toContain('INTERNAL_LEAK_TOKEN_abc');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('images endpoint returns generic 500 message on upstream failure', function () {
|
||||||
|
Http::fake([
|
||||||
|
'https://api.hetzner.cloud/v1/images*' => Http::response([
|
||||||
|
'error' => ['message' => 'INTERNAL_LEAK_TOKEN_abc'],
|
||||||
|
], 500),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
])->getJson('/api/v1/hetzner/images?cloud_provider_token_id='.$this->hetznerToken->uuid);
|
||||||
|
|
||||||
|
$response->assertStatus(500);
|
||||||
|
$response->assertExactJson(['message' => 'Failed to fetch Hetzner images.']);
|
||||||
|
expect($response->getContent())->not->toContain('INTERNAL_LEAK_TOKEN_abc');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ssh-keys endpoint returns generic 500 message on upstream failure', function () {
|
||||||
|
Http::fake([
|
||||||
|
'https://api.hetzner.cloud/v1/ssh_keys*' => Http::response([
|
||||||
|
'error' => ['message' => 'INTERNAL_LEAK_TOKEN_abc'],
|
||||||
|
], 500),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
])->getJson('/api/v1/hetzner/ssh-keys?cloud_provider_token_id='.$this->hetznerToken->uuid);
|
||||||
|
|
||||||
|
$response->assertStatus(500);
|
||||||
|
$response->assertExactJson(['message' => 'Failed to fetch Hetzner SSH keys.']);
|
||||||
|
expect($response->getContent())->not->toContain('INTERNAL_LEAK_TOKEN_abc');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue