Files
sencho/frontend/src/components/VulnerabilityScanSheet.export.ts
T
Anso e3b3c3b857 fix: export the full vulnerability list to CSV (#1472)
The scan detail sheet fetches a capped page of vulnerabilities for
display, then told operators to "Export CSV for the complete list".
The CSV writer only serialized the rows already in memory, so for a
scan with more findings than the page cap the CSV silently dropped
everything past the cap: the recovery path the notice promised did not
exist.

Export now pages past the API's per-request cap and serializes every
row when the loaded set is short of the total, falling back to the
in-memory rows when they are already complete. The CSV action shows a
spinner and disables while the export runs.
2026-06-26 15:34:57 -04:00

29 lines
1.2 KiB
TypeScript

import { apiFetch } from '@/lib/api';
import type { VulnerabilityDetail } from '@/types/security';
/**
* Every vulnerability row for a scan, paging past the API's per-request cap
* (the backend clamps a single page to 1000 rows). The detail table renders
* only a capped page, so the CSV export relies on this to make good on its
* "complete list" promise for scans with thousands of findings. Unfiltered by
* severity on purpose: the CSV is the full record, not the current view. A
* short page also ends the loop, so an over-reported `total` cannot spin it.
*/
export async function fetchAllScanVulnerabilities(
scanId: number,
): Promise<VulnerabilityDetail[]> {
const all: VulnerabilityDetail[] = [];
const pageSize = 1000;
for (let offset = 0; ; offset += pageSize) {
const res = await apiFetch(
`/security/scans/${scanId}/vulnerabilities?limit=${pageSize}&offset=${offset}`,
);
if (!res.ok) throw new Error('Failed to fetch the full vulnerability list');
const data = await res.json();
const items: VulnerabilityDetail[] = Array.isArray(data.items) ? data.items : [];
all.push(...items);
if (items.length < pageSize || all.length >= (data.total ?? all.length)) break;
}
return all;
}