diff --git a/ui/actions/lighthouse/findings.ts b/ui/actions/lighthouse/findings.ts new file mode 100644 index 0000000000..5e43177b64 --- /dev/null +++ b/ui/actions/lighthouse/findings.ts @@ -0,0 +1,78 @@ +"use server"; + +import { apiBaseUrl, getAuthHeaders, parseStringify } from "@/lib"; + +export const getLighthouseFindings = async ({ + page = 1, + pageSize = 10, + query = "", + sort = "", + filters = {}, +}) => { + const headers = await getAuthHeaders({ contentType: false }); + + // For lighthouse usage, handle invalid page numbers by defaulting to 1 + const validPage = isNaN(Number(page)) || page < 1 ? 1 : page; + + const url = new URL(`${apiBaseUrl}/findings`); + + if (validPage) url.searchParams.append("page[number]", validPage.toString()); + if (pageSize) url.searchParams.append("page[size]", pageSize.toString()); + + if (query) url.searchParams.append("filter[search]", query); + if (sort) url.searchParams.append("sort", sort); + + Object.entries(filters).forEach(([key, value]) => { + url.searchParams.append(key, String(value)); + }); + + try { + const findings = await fetch(url.toString(), { + headers, + }); + const data = await findings.json(); + const parsedData = parseStringify(data); + return parsedData; + } catch (error) { + // eslint-disable-next-line no-console + console.error("Error fetching lighthouse findings:", error); + return undefined; + } +}; + +export const getLighthouseLatestFindings = async ({ + page = 1, + pageSize = 10, + query = "", + sort = "", + filters = {}, +}) => { + const headers = await getAuthHeaders({ contentType: false }); + + const validPage = isNaN(Number(page)) || page < 1 ? 1 : page; + + const url = new URL(`${apiBaseUrl}/findings/latest`); + + if (validPage) url.searchParams.append("page[number]", validPage.toString()); + if (pageSize) url.searchParams.append("page[size]", pageSize.toString()); + + if (query) url.searchParams.append("filter[search]", query); + if (sort) url.searchParams.append("sort", sort); + + Object.entries(filters).forEach(([key, value]) => { + url.searchParams.append(key, String(value)); + }); + + try { + const findings = await fetch(url.toString(), { + headers, + }); + const data = await findings.json(); + const parsedData = parseStringify(data); + return parsedData; + } catch (error) { + // eslint-disable-next-line no-console + console.error("Error fetching lighthouse latest findings:", error); + return undefined; + } +}; diff --git a/ui/lib/lighthouse/tools/findings.ts b/ui/lib/lighthouse/tools/findings.ts index e81e9ca0b6..8ef65f69e4 100644 --- a/ui/lib/lighthouse/tools/findings.ts +++ b/ui/lib/lighthouse/tools/findings.ts @@ -1,11 +1,21 @@ import { tool } from "@langchain/core/tools"; -import { getFindings, getMetadataInfo } from "@/actions/findings"; +import { getMetadataInfo } from "@/actions/findings"; +import { + getLighthouseFindings, + getLighthouseLatestFindings, +} from "@/actions/lighthouse/findings"; import { getFindingsSchema, getMetadataInfoSchema } from "@/types/lighthouse"; export const getFindingsTool = tool( async ({ page, pageSize, query, sort, filters }) => { - return await getFindings({ page, pageSize, query, sort, filters }); + return await getLighthouseFindings({ + page, + pageSize, + query, + sort, + filters, + }); }, { name: "getFindings", @@ -15,6 +25,25 @@ export const getFindingsTool = tool( }, ); +export const getLatestFindingsTool = tool( + async ({ page, pageSize, query, sort, filters }) => { + return await getLighthouseLatestFindings({ + page, + pageSize, + query, + sort, + filters, + }); + }, + { + name: "getLatestFindings", + description: + "Retrieves a list of the latest findings from the latest scans of all providers with options for filtering by various criteria.", + // getLatestFindings uses the same schema as getFindings + schema: getFindingsSchema, + }, +); + export const getMetadataInfoTool = tool( async ({ query, sort, filters }) => { return await getMetadataInfo({ query, sort, filters }); diff --git a/ui/lib/lighthouse/workflow.ts b/ui/lib/lighthouse/workflow.ts index faa7f02f4c..bd61bc897c 100644 --- a/ui/lib/lighthouse/workflow.ts +++ b/ui/lib/lighthouse/workflow.ts @@ -24,6 +24,7 @@ import { } from "@/lib/lighthouse/tools/compliances"; import { getFindingsTool, + getLatestFindingsTool, getMetadataInfoTool, } from "@/lib/lighthouse/tools/findings"; import { @@ -101,6 +102,7 @@ export async function initLighthouseWorkflow() { llm: llm, tools: [ getFindingsTool, + getLatestFindingsTool, getMetadataInfoTool, getProviderChecksTool, getProviderCheckDetailsTool, diff --git a/ui/types/lighthouse/findings.ts b/ui/types/lighthouse/findings.ts index feca1ab9de..d889cb90fc 100644 --- a/ui/types/lighthouse/findings.ts +++ b/ui/types/lighthouse/findings.ts @@ -71,21 +71,12 @@ export const getFindingsSchema = z.object({ .optional() .describe("Comma-separated list of UUID values"), - // Impact and Severity filters + // Impact filters "filter[impact]": impactEnum.optional(), "filter[impact__in]": z .string() .optional() .describe("Comma-separated list of impact values"), - "filter[severity]": z - .enum(["critical", "high", "medium", "low", "informational"]) - .optional(), - "filter[severity__in]": z - .string() - .optional() - .describe( - "Comma-separated list of severity values. Do not use it with severity filter.", - ), // Date filters "filter[inserted_at]": z @@ -105,6 +96,9 @@ export const getFindingsSchema = z.object({ .optional() .describe("Date in format YYYY-MM-DD"), + // Muted filter + "filter[muted]": z.boolean().optional(), + // Provider filters "filter[provider]": z.string().optional().describe("Provider UUID"), "filter[provider__in]": z @@ -176,6 +170,17 @@ export const getFindingsSchema = z.object({ .optional() .describe("Comma-separated list of service values"), + // Severity filters + "filter[severity]": z + .enum(["critical", "high", "medium", "low", "informational"]) + .optional(), + "filter[severity__in]": z + .string() + .optional() + .describe( + "Comma-separated list of severity values. Do not use it with severity filter.", + ), + // Status filters "filter[status]": statusEnum.optional(), "filter[status__in]": z