mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 15:51:26 +00:00
feat: security center page (/security)
Project-scoped security surface: scan trigger (dependency / configuration / comprehensive, optional service), security score and vulnerability metric cards, findings list with severity badges and resolve/ignore actions, and scan history. Fixed a latent bug in GET /projects/:id/security/metrics: it selected a non-existent `score` column (the score lives in the security_scans summary jsonb), so the endpoint 500'd on every project. Verified live: comprehensive scan produced 4 real findings (insecure public URL, curl|sh build, http source, unpinned tag) with score 47; resolve flow and metrics aggregation confirmed. Authored By: TDvorak <[email protected]> Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
d5c99acb8c
commit
d5e6bfc40d
6 files changed
+345
-9
No files matched your search
@@ -1602,3 +1602,59 @@ export async function listHealthResults(): Promise<HAHealthResult[]> {
|
||||
const payload = await requestJson<{ results?: HAHealthResult[] }>('/ha/health/results');
|
||||
return payload.results ?? [];
|
||||
}
|
||||
|
||||
export type SecurityScan = components['schemas']['SecurityScan'];
|
||||
export type Vulnerability = components['schemas']['Vulnerability'];
|
||||
|
||||
export type SecurityMetrics = {
|
||||
vulnerabilities?: {
|
||||
total?: number; critical?: number; high?: number; medium?: number;
|
||||
low?: number; open?: number; resolved?: number;
|
||||
};
|
||||
latest_scan?: { id?: string; score?: number; scanned_at?: string; status?: string };
|
||||
compliance?: { overall_status?: string; score?: number; last_assessed?: string };
|
||||
security_score?: number;
|
||||
};
|
||||
|
||||
export async function startSecurityScan(input: {
|
||||
project_id: string;
|
||||
service_id?: string;
|
||||
scan_type: 'dependency' | 'configuration' | 'comprehensive';
|
||||
}): Promise<SecurityScan> {
|
||||
return requestJson<SecurityScan>('/security/scans', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
}
|
||||
|
||||
export async function getSecurityScan(scanId: string): Promise<SecurityScan> {
|
||||
const payload = await requestJson<{ scan?: SecurityScan } | SecurityScan>(
|
||||
`/security/scans/${encodeURIComponent(scanId)}`,
|
||||
);
|
||||
return 'scan' in payload && payload.scan ? payload.scan : (payload as SecurityScan);
|
||||
}
|
||||
|
||||
export async function getSecurityHistory(projectId: string): Promise<SecurityScan[]> {
|
||||
const payload = await requestJson<{ scans?: SecurityScan[] }>(
|
||||
`/projects/${encodeURIComponent(projectId)}/security/history`,
|
||||
);
|
||||
return payload.scans ?? [];
|
||||
}
|
||||
|
||||
export async function listVulnerabilities(projectId: string): Promise<Vulnerability[]> {
|
||||
const payload = await requestJson<{ vulnerabilities?: Vulnerability[] }>(
|
||||
`/projects/${encodeURIComponent(projectId)}/vulnerabilities`,
|
||||
);
|
||||
return payload.vulnerabilities ?? [];
|
||||
}
|
||||
|
||||
export async function updateVulnerability(id: string, status: 'open' | 'resolved' | 'ignored'): Promise<void> {
|
||||
await requestJson(`/vulnerabilities/${encodeURIComponent(id)}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ status }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function getSecurityMetrics(projectId: string): Promise<SecurityMetrics> {
|
||||
return requestJson<SecurityMetrics>(`/projects/${encodeURIComponent(projectId)}/security/metrics`);
|
||||
}
|
||||
Reference in new issue
Block a user