* 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.