mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
3554859a5c
Co-authored-by: Pablo F.G <pablo.fernandez@prowler.com> Co-authored-by: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
116 lines
3.1 KiB
TypeScript
116 lines
3.1 KiB
TypeScript
"use server";
|
|
|
|
import { z } from "zod";
|
|
|
|
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
|
import { handleApiResponse } from "@/lib/server-actions-helper";
|
|
import { AttackPathScan, AttackPathScansResponse } from "@/types/attack-paths";
|
|
|
|
import { adaptAttackPathScansResponse } from "./scans.adapter";
|
|
|
|
const UUIDSchema = z.uuid();
|
|
|
|
const ATTACK_PATH_SCANS_PAGE_SIZE = 100;
|
|
const ATTACK_PATH_SCANS_MAX_PAGES = 50;
|
|
|
|
/**
|
|
* Fetch list of attack path scans (latest scan for each provider).
|
|
*
|
|
* Iterates through every backend page so callers receive the complete
|
|
* dedup'd dataset along with an accurate total count. The underlying
|
|
* endpoint is paginated server-side (default page_size=10), so fetching
|
|
* only the first page would silently hide providers beyond that window.
|
|
*/
|
|
export const getAttackPathScans = async (): Promise<
|
|
{ data: AttackPathScan[] } | undefined
|
|
> => {
|
|
const headers = await getAuthHeaders({ contentType: false });
|
|
const allScans: AttackPathScan[] = [];
|
|
let currentPage = 1;
|
|
let lastResponse: AttackPathScansResponse | undefined;
|
|
let hasMorePages = true;
|
|
|
|
while (hasMorePages && currentPage <= ATTACK_PATH_SCANS_MAX_PAGES) {
|
|
try {
|
|
const url = new URL(`${apiBaseUrl}/attack-paths-scans`);
|
|
url.searchParams.append("page[number]", currentPage.toString());
|
|
url.searchParams.append(
|
|
"page[size]",
|
|
ATTACK_PATH_SCANS_PAGE_SIZE.toString(),
|
|
);
|
|
|
|
const response = await fetch(url.toString(), {
|
|
headers,
|
|
method: "GET",
|
|
});
|
|
|
|
const data = (await handleApiResponse(response)) as
|
|
| AttackPathScansResponse
|
|
| undefined;
|
|
|
|
if (!data?.data || data.data.length === 0) {
|
|
hasMorePages = false;
|
|
continue;
|
|
}
|
|
|
|
allScans.push(...data.data);
|
|
lastResponse = data;
|
|
|
|
const totalPages = data.meta?.pagination?.pages ?? 1;
|
|
if (currentPage >= totalPages) {
|
|
hasMorePages = false;
|
|
} else {
|
|
currentPage++;
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching attack path scans:", error);
|
|
if (allScans.length === 0) {
|
|
return undefined;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!lastResponse) {
|
|
return { data: [] };
|
|
}
|
|
|
|
const adapted = adaptAttackPathScansResponse({
|
|
...lastResponse,
|
|
data: allScans,
|
|
});
|
|
|
|
return { data: adapted.data };
|
|
};
|
|
|
|
/**
|
|
* Fetch detail of a specific attack path scan
|
|
*/
|
|
export const getAttackPathScanDetail = async (
|
|
scanId: string,
|
|
): Promise<{ data: AttackPathScan } | undefined> => {
|
|
// Validate scanId is a valid UUID format to prevent request forgery
|
|
const validatedScanId = UUIDSchema.safeParse(scanId);
|
|
if (!validatedScanId.success) {
|
|
console.error("Invalid scan ID format");
|
|
return undefined;
|
|
}
|
|
|
|
const headers = await getAuthHeaders({ contentType: false });
|
|
|
|
try {
|
|
const response = await fetch(
|
|
`${apiBaseUrl}/attack-paths-scans/${validatedScanId.data}`,
|
|
{
|
|
headers,
|
|
method: "GET",
|
|
},
|
|
);
|
|
|
|
return handleApiResponse(response);
|
|
} catch (error) {
|
|
console.error("Error fetching attack path scan detail:", error);
|
|
return undefined;
|
|
}
|
|
};
|