mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-10 10:49:35 +00:00
3d39d856a3
* feat: chart-led Security overview with sortable Images and History tables Refine the Security page around the existing design system and add the data the dashboard needs. - Overview leads with four charts (30-day risk trend, severity donut, top exposed images, findings by type); the signal-rail counts become a secondary summary, and the scanner and deploy-enforcement posture follow. - Images becomes a recessed table with search, a severity filter, sortable columns, a last-scan column, and inline scan actions; the findings cell is clickable into the scan sheet, and the per-row cursor tooltip is dropped where the columns already carry that information. - Policies puts deploy-enforcement first, collapses the policy packs into an accordion, and uses the standard primary button for Add policy. - Suppressions and acknowledgements move their titles and Add buttons outside the cards, matching the Fleet tab layout. - History switches from the detail sheet to an inline table (search, sortable columns, two-scan compare, pagination); the now-unreachable scan-history overlay is removed. - Add GET /api/security/overview/trend, a node-scoped daily critical/high rollup backing the risk-trend chart. - Extract the shared image-scan hook and the severity classifier, and harden the overview data fetch so a malformed non-critical response can never read as a clean security state. * fix: treat malformed Security responses as errors, not empty or clean states Address an independent review of the data-fetch paths so a 200 with an unexpected shape can never read as a benign "no findings" view. - SecurityView: validate that the image-summaries body is a scan-summary map; an unexpected shape now sets the error state instead of an empty map. Isolate the trend fetch in its own self-catching promise so a transport failure on the non-critical chart can no longer poison the overview or summaries error state. - useImageScan: only a "completed" poll counts as success (a malformed or unknown status now throws), and a failed post-scan summaries refresh is logged instead of silently dropped. - HistoryTab: a 200 whose body lacks an items array is treated as an error, not an empty "no completed scans" list.
43 lines
1.4 KiB
TypeScript
43 lines
1.4 KiB
TypeScript
/**
|
|
* getSeverityKey is the single classifier shared by the severity badge, the
|
|
* Images severity sort, and the Images severity filter. Lock its mapping so the
|
|
* three consumers can never disagree: a CVE severity wins, a secret/misconfig-only
|
|
* scan is FINDINGS (not a false "Clean"), and an all-zero scan is CLEAN.
|
|
*/
|
|
import { it, expect } from 'vitest';
|
|
import { getSeverityKey } from '../severityStyles';
|
|
import type { ScanSummary } from '@/types/security';
|
|
|
|
function summary(o: Partial<ScanSummary>): ScanSummary {
|
|
return {
|
|
image_ref: 'x:1',
|
|
highest_severity: null,
|
|
scanned_at: 1,
|
|
scan_id: 1,
|
|
total: 0,
|
|
critical: 0,
|
|
high: 0,
|
|
medium: 0,
|
|
low: 0,
|
|
unknown: 0,
|
|
fixable: 0,
|
|
secret_count: 0,
|
|
misconfig_count: 0,
|
|
...o,
|
|
};
|
|
}
|
|
|
|
it('returns the highest CVE severity when present', () => {
|
|
expect(getSeverityKey(summary({ highest_severity: 'CRITICAL' }))).toBe('CRITICAL');
|
|
expect(getSeverityKey(summary({ highest_severity: 'LOW' }))).toBe('LOW');
|
|
});
|
|
|
|
it('returns FINDINGS for a secret- or misconfig-only scan with no CVE severity', () => {
|
|
expect(getSeverityKey(summary({ highest_severity: null, secret_count: 1 }))).toBe('FINDINGS');
|
|
expect(getSeverityKey(summary({ highest_severity: null, misconfig_count: 2 }))).toBe('FINDINGS');
|
|
});
|
|
|
|
it('returns CLEAN when there are no findings of any kind', () => {
|
|
expect(getSeverityKey(summary({ highest_severity: null }))).toBe('CLEAN');
|
|
});
|