fix(security): signal when scan comparison is truncated (#658)

Compare endpoint loads up to 1000 findings per scan. When a scan exceeds
this cap, the response now includes truncated=true and row_limit, and
the comparison sheet surfaces a banner so users understand the diff may
be incomplete. Also exposes total_vulnerabilities on scanA/scanB for UI
use and logs a warning when truncation occurs.
This commit is contained in:
Anso
2026-04-17 13:53:53 -04:00
committed by GitHub
parent f4e3c267cd
commit c211f655c3
6 changed files with 92 additions and 6 deletions
@@ -175,6 +175,18 @@ export function ScanComparisonSheet({
</div>
)}
{data.truncated && (
<div
role="alert"
className="flex items-start gap-2 rounded border border-warning/40 bg-warning/10 px-3 py-2 text-xs text-warning"
>
<AlertTriangle className="w-3.5 h-3.5 shrink-0 mt-[1px]" strokeWidth={1.5} />
<span>
Showing the first {data.row_limit ?? 1000} findings per scan. One or both scans exceed this limit, so the comparison may be incomplete.
</span>
</div>
)}
{/* Delta ribbon */}
{addedCounts && removedCounts && (
<div className="flex flex-wrap gap-2">
@@ -150,6 +150,27 @@ describe('ScanComparisonSheet', () => {
);
});
it('renders the truncation banner when the response flags truncated', async () => {
mockedFetch.mockResolvedValueOnce(
jsonResponse(200, result({ truncated: true, row_limit: 1000 })),
);
render(<ScanComparisonSheet baselineScanId={1} currentScanId={2} onClose={() => {}} />);
await waitFor(() =>
expect(screen.getByText(/first 1000 findings per scan/i)).toBeInTheDocument(),
);
});
it('hides the truncation banner when truncated is false', async () => {
mockedFetch.mockResolvedValueOnce(jsonResponse(200, result({ truncated: false })));
render(<ScanComparisonSheet baselineScanId={1} currentScanId={2} onClose={() => {}} />);
await waitFor(() => expect(screen.getByText(/Baseline/i)).toBeInTheDocument());
expect(screen.queryByText(/findings per scan/i)).toBeNull();
});
it('reloads when the scan ids change', async () => {
mockedFetch.mockResolvedValue(jsonResponse(200, result()));