feat(security): fleet-replicated CVE suppression list (#650)

Operators can accept known-benign findings once and have Sencho filter
them out of scan drawers, comparison views, and other read surfaces.
Suppressions replicate from the control instance to every remote node.

* New cve_suppressions table with a COALESCE-based unique index so NULL
  scope slots collide the way users expect
* Admin + paid-tier CRUD routes; writes are rejected on replicas
* Read-time filter enriches vulnerability details and compare payloads
  without mutating stored counts
* Settings > Security panel for managing rules, per-CVE suppress action
  in the scan drawer, dimmed rows with a shield-off indicator
* Vitest unit tests for the filter (glob, expiry, specificity) and
  route tests (auth, tier, replica, UNIQUE conflict)
This commit is contained in:
Anso
2026-04-17 05:16:34 -04:00
committed by GitHub
parent 708d15b2b3
commit 732fc95415
16 changed files with 1568 additions and 41 deletions
+24 -13
View File
@@ -19,6 +19,7 @@ import {
MinusCircle,
PlusCircle,
ShieldCheck,
ShieldOff,
Equal,
AlertTriangle,
} from 'lucide-react';
@@ -282,27 +283,37 @@ export function ScanComparisonSheet({
</TableHeader>
<TableBody>
{pageItems.map((v, idx) => {
const rowClass =
const baseRowClass =
filter === 'added'
? 'bg-destructive/5'
: filter === 'removed'
? 'bg-success/5'
: 'opacity-70';
const rowClass = cn(baseRowClass, v.suppressed && 'opacity-60');
return (
<TableRow key={`${v.vulnerability_id}-${v.pkg_name}-${idx}`} className={rowClass}>
<TableCell className="font-mono text-xs">
{v.primary_url ? (
<a
href={v.primary_url}
target="_blank"
rel="noreferrer noopener"
className="hover:underline"
>
{v.vulnerability_id}
</a>
) : (
v.vulnerability_id
)}
<span className="inline-flex items-center gap-1.5">
{v.suppressed && (
<ShieldOff
className="w-3 h-3 text-muted-foreground"
strokeWidth={1.5}
aria-label="Suppressed"
/>
)}
{v.primary_url ? (
<a
href={v.primary_url}
target="_blank"
rel="noreferrer noopener"
className="hover:underline"
>
{v.vulnerability_id}
</a>
) : (
v.vulnerability_id
)}
</span>
</TableCell>
<TableCell className="font-mono text-xs truncate max-w-[180px]" title={v.pkg_name}>
{v.pkg_name}