mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-20 07:13:05 +00:00
feat(images): Trivy-powered vulnerability scanning (#635)
* feat(images): Trivy-powered vulnerability scanning Scan container images for known CVEs via Trivy. On-demand scanning and severity badges are available on every tier; scheduled scans, scan policies, SBOM generation, and scan history are gated to Skipper+. - New TrivyService (binary detection, per-image scan, SBOM, digest cache) - Three new tables: vulnerability_scans, vulnerability_details, scan_policies - 12 routes under /api/security (scan, results, summaries, SBOM, policies, compare) - Post-deploy async scans wired into all five deploy paths, with a per-deploy opt-out toggle in the App Store deploy sheet - "scan" action type added to SchedulerService for fleet-wide recurring scans - Frontend: severity badges in Resources Hub with animated cursor detail, scan results drawer with vulnerability table and filters, and a new Security section in Settings for scan policy CRUD - Policy threshold violations dispatch a warning or critical alert based on the policy's block_on_deploy flag; deploys themselves are never blocked * fix(security): compute scan age in useEffect to satisfy react-hooks/purity
This commit is contained in:
@@ -6,7 +6,8 @@ import { Label } from "@/components/ui/label";
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter } from "@/components/ui/sheet";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Search, Rocket, Loader2, Info, ExternalLink, Star } from "lucide-react";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Search, Rocket, Loader2, Info, ExternalLink, Star, ShieldCheck } from "lucide-react";
|
||||
import { toast } from "@/components/ui/toast-store";
|
||||
import { cn } from '@/lib/utils';
|
||||
import { apiFetch } from '@/lib/api';
|
||||
@@ -78,9 +79,17 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) {
|
||||
const [newEnvKey, setNewEnvKey] = useState('');
|
||||
const [portsInUse, setPortsInUse] = useState<Record<string, PortInUseInfo>>({});
|
||||
const [newEnvVal, setNewEnvVal] = useState('');
|
||||
const [autoScan, setAutoScan] = useState(true);
|
||||
const [trivyAvailable, setTrivyAvailable] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTemplates();
|
||||
apiFetch('/security/trivy-status')
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(d => { if (d) setTrivyAvailable(!!d.available); })
|
||||
.catch((err) => {
|
||||
console.error('Failed to fetch Trivy status:', err);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const fetchTemplates = async () => {
|
||||
@@ -215,7 +224,8 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) {
|
||||
body: JSON.stringify({
|
||||
stackName: stackName.trim(),
|
||||
template: modifiedTemplate,
|
||||
envVars: finalEnvVars
|
||||
envVars: finalEnvVars,
|
||||
skip_scan: !autoScan
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
@@ -546,7 +556,23 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) {
|
||||
</ScrollArea>
|
||||
|
||||
<SheetFooter className="pt-4 mt-auto border-t sm:justify-start">
|
||||
<div className="flex flex-col w-full gap-2">
|
||||
<div className="flex flex-col w-full gap-3">
|
||||
{trivyAvailable && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id="auto-scan"
|
||||
checked={autoScan}
|
||||
onCheckedChange={(checked) => setAutoScan(!!checked)}
|
||||
/>
|
||||
<Label
|
||||
htmlFor="auto-scan"
|
||||
className="text-sm text-muted-foreground cursor-pointer flex items-center gap-1.5"
|
||||
>
|
||||
<ShieldCheck className="w-3.5 h-3.5" strokeWidth={1.5} />
|
||||
Scan images for vulnerabilities after deploy
|
||||
</Label>
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
onClick={handleDeploy}
|
||||
disabled={isDeploying || !stackName.trim() || !can('stack:create')}
|
||||
|
||||
Reference in New Issue
Block a user