mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-12 11:47:11 +00:00
feat: add an on-demand node-wide security scan with live progress (#1367)
Add a "Scan this node" action on the Security overview that scans, in one pass, any combination of three types: image vulnerabilities, image secrets, and compose misconfigurations. Progress streams live into the deploy-feedback modal. - TrivyService.scanNode runs the selected scanners across the node's images and, for misconfig, every stack's compose file, behind a per-node lock and tolerant of per-item failures. The existing scanAllNodeImages becomes a thin vuln-only wrapper over the shared image loop, so scheduled scans are unchanged. - POST /api/security/scan-node (admin, scanner-gated) streams sanitized progress to the deploy terminal and returns a combined summary. Secret scans stream counts only, never matched values. - Frontend adds a "scan" action verb and a ScanNodeLauncher wired into the overview; the scan stays bound to the node it started on even if the active node changes mid-run.
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
TopExposedImagesChart,
|
||||
FindingsByTypeChart,
|
||||
} from './SecurityCharts';
|
||||
import { ScanNodeLauncher } from './ScanNodeLauncher';
|
||||
|
||||
interface OverviewTabProps {
|
||||
overview: SecurityOverview | null;
|
||||
@@ -20,6 +21,10 @@ interface OverviewTabProps {
|
||||
trend: SecurityRiskTrendPoint[];
|
||||
onNavigate: (tab: SecurityTab) => void;
|
||||
onInspect: (scanId: number) => void;
|
||||
/** Admin on a node with a ready scanner; enables the node-scan launcher. */
|
||||
canScan: boolean;
|
||||
/** Refresh the overview after a node-wide scan completes. */
|
||||
onScanComplete: () => void;
|
||||
}
|
||||
|
||||
const STATUS_ROW_TONE: Record<'value' | 'warn' | 'subtitle', string> = {
|
||||
@@ -47,7 +52,7 @@ function ChartCard({ title, className, children }: { title: string; className?:
|
||||
);
|
||||
}
|
||||
|
||||
export function OverviewTab({ overview, loadError, summaries, trend, onNavigate, onInspect }: OverviewTabProps) {
|
||||
export function OverviewTab({ overview, loadError, summaries, trend, onNavigate, onInspect, canScan, onScanComplete }: OverviewTabProps) {
|
||||
if (loadError === 'unsupported') {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-20 text-center">
|
||||
@@ -98,6 +103,17 @@ export function OverviewTab({ overview, loadError, summaries, trend, onNavigate,
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{canScan && (
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<p className="text-sm text-stat-subtitle">
|
||||
{overview.scannedImages === 0
|
||||
? 'No images scanned on this node yet.'
|
||||
: `${overview.scannedImages} image${overview.scannedImages === 1 ? '' : 's'} scanned.`}
|
||||
</p>
|
||||
<ScanNodeLauncher canScan={canScan} onComplete={onScanComplete} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Charts lead the dashboard. */}
|
||||
<div className="grid gap-4 lg:grid-cols-3">
|
||||
<ChartCard title="Risk trend · 30 days · critical + high" className="lg:col-span-2">
|
||||
|
||||
Reference in New Issue
Block a user