mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
106 lines
2.7 KiB
TypeScript
106 lines
2.7 KiB
TypeScript
interface ScanDateSource {
|
|
id: string;
|
|
attributes?: {
|
|
// Findings are written throughout the run, so the range spans both
|
|
// timestamps, not the scan's `inserted_at` (when the row was created).
|
|
started_at?: string;
|
|
completed_at?: string;
|
|
};
|
|
}
|
|
|
|
interface ResolveFindingScanDateFiltersOptions {
|
|
filters: Record<string, string>;
|
|
scans: ScanDateSource[];
|
|
loadScan: (scanId: string) => Promise<ScanDateSource | null | undefined>;
|
|
}
|
|
|
|
const INSERTED_AT_FILTER_KEYS = [
|
|
"filter[inserted_at]",
|
|
"filter[inserted_at__date]",
|
|
"filter[inserted_at__gte]",
|
|
"filter[inserted_at__lte]",
|
|
] as const;
|
|
|
|
function getScanFilterIds(filters: Record<string, string>): string[] {
|
|
const scanIds = filters["filter[scan__in]"] || filters["filter[scan]"] || "";
|
|
return Array.from(new Set(scanIds.split(",").filter(Boolean)));
|
|
}
|
|
|
|
function formatScanDate(dateTime?: string): string | null {
|
|
if (!dateTime) return null;
|
|
const [date] = dateTime.split("T");
|
|
return date?.trim() || null;
|
|
}
|
|
|
|
function hasInsertedAtFilter(filters: Record<string, string>): boolean {
|
|
return INSERTED_AT_FILTER_KEYS.some((key) => Boolean(filters[key]));
|
|
}
|
|
|
|
export function buildFindingScanDateFilters(
|
|
scanDateTimes: string[],
|
|
): Record<string, string> {
|
|
const dates = Array.from(
|
|
new Set(scanDateTimes.map(formatScanDate).filter(Boolean)),
|
|
).sort() as string[];
|
|
|
|
if (dates.length === 0) {
|
|
return {};
|
|
}
|
|
|
|
if (dates.length === 1) {
|
|
return {
|
|
"filter[inserted_at]": dates[0],
|
|
};
|
|
}
|
|
|
|
return {
|
|
"filter[inserted_at__gte]": dates[0],
|
|
"filter[inserted_at__lte]": dates[dates.length - 1],
|
|
};
|
|
}
|
|
|
|
export async function resolveFindingScanDateFilters({
|
|
filters,
|
|
scans,
|
|
loadScan,
|
|
}: ResolveFindingScanDateFiltersOptions): Promise<Record<string, string>> {
|
|
const scanIds = getScanFilterIds(filters);
|
|
|
|
if (scanIds.length === 0 || hasInsertedAtFilter(filters)) {
|
|
return filters;
|
|
}
|
|
|
|
const scansById = new Map(scans.map((scan) => [scan.id, scan]));
|
|
const missingScanIds = scanIds.filter((scanId) => !scansById.has(scanId));
|
|
|
|
if (missingScanIds.length > 0) {
|
|
const missingScans = await Promise.all(
|
|
missingScanIds.map((scanId) => loadScan(scanId)),
|
|
);
|
|
|
|
missingScans.forEach((scan) => {
|
|
if (scan) {
|
|
scansById.set(scan.id, scan);
|
|
}
|
|
});
|
|
}
|
|
|
|
const scanDateTimes = scanIds
|
|
.flatMap((scanId) => {
|
|
const attributes = scansById.get(scanId)?.attributes;
|
|
return [attributes?.started_at, attributes?.completed_at];
|
|
})
|
|
.filter((dateTime): dateTime is string => Boolean(dateTime));
|
|
|
|
const dateFilters = buildFindingScanDateFilters(scanDateTimes);
|
|
|
|
if (Object.keys(dateFilters).length === 0) {
|
|
return filters;
|
|
}
|
|
|
|
return {
|
|
...filters,
|
|
...dateFilters,
|
|
};
|
|
}
|