feat: Add resource agent to supervisor (#8509)

Co-authored-by: Chandrapal Badshah <12944530+Chan9390@users.noreply.github.com>
This commit is contained in:
Chandrapal Badshah
2025-08-19 13:10:14 +05:30
committed by GitHub
parent bb07cf9147
commit efdeb431ba
6 changed files with 157 additions and 24 deletions
+2 -1
View File
@@ -8,6 +8,7 @@ All notable changes to the **Prowler UI** are documented in this file.
- `Cloud Provider` type filter to providers page [(#8473)](https://github.com/prowler-cloud/prowler/pull/8473)
- New menu item under Configuration section for quick access to the Mutelist [(#8444)](https://github.com/prowler-cloud/prowler/pull/8444)
- Resource agent to Lighthouse for querying resource information [(#8509)](https://github.com/prowler-cloud/prowler/pull/8509)
### 🔄 Changed
@@ -15,6 +16,7 @@ All notable changes to the **Prowler UI** are documented in this file.
- Provider connection filter now shows "Connected/Disconnected" instead of "true/false" for better UX [(#8520)](https://github.com/prowler-cloud/prowler/pull/8520)
### 🐞 Fixed
- DataTable column headers set to single-line [(#8480)](https://github.com/prowler-cloud/prowler/pull/8480)
### ❌ Removed
@@ -29,7 +31,6 @@ All notable changes to the **Prowler UI** are documented in this file.
- `GitHub` submenu to High Risk Findings [(#8488)](https://github.com/prowler-cloud/prowler/pull/8488)
- Improved Overview chart `Findings by Severity` spacing [(#8491)](https://github.com/prowler-cloud/prowler/pull/8491)
## [1.10.0] (Prowler v5.10.0)
### 🚀 Added
+75 -13
View File
@@ -1,12 +1,18 @@
import { apiBaseUrl, getAuthHeaders, parseStringify } from "@/lib/helper";
export async function getLighthouseResources(
page: number = 1,
query: string = "",
sort: string = "",
filters: any = {},
fields: string[] = [],
) {
export async function getLighthouseResources({
page = 1,
query = "",
sort = "",
filters = {},
fields = [],
}: {
page?: number;
query?: string;
sort?: string;
filters?: any;
fields?: string[];
}) {
const headers = await getAuthHeaders({ contentType: false });
const url = new URL(`${apiBaseUrl}/resources`);
@@ -29,7 +35,7 @@ export async function getLighthouseResources(
if (filters) {
for (const [key, value] of Object.entries(filters)) {
url.searchParams.append(`filter[${key}]`, value as string);
url.searchParams.append(`${key}`, value as string);
}
}
@@ -46,11 +52,67 @@ export async function getLighthouseResources(
}
}
export async function getLighthouseResourceById(
id: string,
fields: string[] = [],
include: string[] = [],
) {
export async function getLighthouseLatestResources({
page = 1,
query = "",
sort = "",
filters = {},
fields = [],
}: {
page?: number;
query?: string;
sort?: string;
filters?: any;
fields?: string[];
}) {
const headers = await getAuthHeaders({ contentType: false });
const url = new URL(`${apiBaseUrl}/resources/latest`);
if (page) {
url.searchParams.append("page[number]", page.toString());
}
if (sort) {
url.searchParams.append("sort", sort);
}
if (query) {
url.searchParams.append("filter[search]", query);
}
if (fields.length > 0) {
url.searchParams.append("fields[resources]", fields.join(","));
}
if (filters) {
for (const [key, value] of Object.entries(filters)) {
url.searchParams.append(`${key}`, value as string);
}
}
try {
const response = await fetch(url.toString(), {
headers,
});
const data = await response.json();
const parsedData = parseStringify(data);
return parsedData;
} catch (error) {
console.error("Error fetching resources:", error);
return undefined;
}
}
export async function getLighthouseResourceById({
id,
fields = [],
include = [],
}: {
id: string;
fields?: string[];
include?: string[];
}) {
const headers = await getAuthHeaders({ contentType: false });
const url = new URL(`${apiBaseUrl}/resources/${id}`);
+33
View File
@@ -136,6 +136,11 @@ You operate in an agent loop, iterating through these steps:
- Fetches available user roles in Prowler
- Can get detailed information about the role
### resources_agent
- Fetches information about resources found during Prowler scans
- Can get detailed information about a specific resource
## Interacting with Agents
- Don't invoke agents if you have the necessary information in your prompt.
@@ -469,11 +474,39 @@ const rolesAgentPrompt = `You are Prowler's Roles Agent, specializing in role an
- Mentioning all keys in the function call is mandatory. Don't skip any keys.
- Don't add empty filters in the function call.`;
const resourcesAgentPrompt = `You are Prowler's Resource Agent, specializing in fetching resource information within Prowler.
## Available Tools
- getResourcesTool: List available resource with filtering options
- getResourceTool: Get detailed information about a specific resource by its UUID
- getLatestResourcesTool: List available resources from the latest scans across all providers without scan UUID
## Response Guidelines
- Keep the response concise
- Only share information relevant to the query
- Answer directly without unnecessary introductions or conclusions
- Ensure all responses are based on tools' output and information available in the prompt
## Additional Guidelines
- Focus only on resource-related information
- Format resource IDs, permissions, and descriptions consistently
- When user asks for resources without a specific scan UUID, use getLatestResourcesTool tool to fetch the resources
- To get the resource UUID, use getResourcesTool if scan UUID is present. If scan UUID is not present, use getLatestResourcesTool.
## Tool Calling Guidelines
- Mentioning all keys in the function call is mandatory. Don't skip any keys.
- Don't add empty filters in the function call.`;
export {
complianceAgentPrompt,
findingsAgentPrompt,
overviewAgentPrompt,
providerAgentPrompt,
resourcesAgentPrompt,
rolesAgentPrompt,
scansAgentPrompt,
supervisorPrompt,
+25 -4
View File
@@ -1,6 +1,7 @@
import { tool } from "@langchain/core/tools";
import {
getLighthouseLatestResources,
getLighthouseResourceById,
getLighthouseResources,
} from "@/actions/lighthouse/resources";
@@ -8,22 +9,42 @@ import { getResourceSchema, getResourcesSchema } from "@/types/lighthouse";
export const getResourcesTool = tool(
async ({ page, query, sort, filters, fields }) => {
return await getLighthouseResources(page, query, sort, filters, fields);
return await getLighthouseResources({ page, query, sort, filters, fields });
},
{
name: "getResources",
description: "Fetches all resource information",
description:
"Retrieve a list of all resources found during scans with options for filtering by various criteria. Mandatory to pass in scan UUID.",
schema: getResourcesSchema,
},
);
export const getResourceTool = tool(
async ({ id, fields, include }) => {
return await getLighthouseResourceById(id, fields, include);
return await getLighthouseResourceById({ id, fields, include });
},
{
name: "getResource",
description: "Fetches information about a resource by its UUID.",
description:
"Fetch detailed information about a specific resource by their Prowler assigned UUID. A Resource is an object that is discovered by Prowler. It can be anything from a single host to a whole VPC.",
schema: getResourceSchema,
},
);
export const getLatestResourcesTool = tool(
async ({ page, query, sort, filters, fields }) => {
return await getLighthouseLatestResources({
page,
query,
sort,
filters,
fields,
});
},
{
name: "getLatestResources",
description:
"Retrieve a list of the latest resources from the latest scans across all providers with options for filtering by various criteria.",
schema: getResourcesSchema, // Schema is same as getResourcesSchema
},
);
+14
View File
@@ -8,6 +8,7 @@ import {
findingsAgentPrompt,
overviewAgentPrompt,
providerAgentPrompt,
resourcesAgentPrompt,
rolesAgentPrompt,
scansAgentPrompt,
supervisorPrompt,
@@ -35,6 +36,11 @@ import {
getProvidersTool,
getProviderTool,
} from "@/lib/lighthouse/tools/providers";
import {
getLatestResourcesTool,
getResourcesTool,
getResourceTool,
} from "@/lib/lighthouse/tools/resources";
import { getRolesTool, getRoleTool } from "@/lib/lighthouse/tools/roles";
import { getScansTool, getScanTool } from "@/lib/lighthouse/tools/scans";
import {
@@ -127,6 +133,13 @@ export async function initLighthouseWorkflow() {
prompt: rolesAgentPrompt,
});
const resourcesAgent = createReactAgent({
llm: llm,
tools: [getResourceTool, getResourcesTool, getLatestResourcesTool],
name: "resources_agent",
prompt: resourcesAgentPrompt,
});
const agents = [
userInfoAgent,
providerAgent,
@@ -135,6 +148,7 @@ export async function initLighthouseWorkflow() {
complianceAgent,
findingsAgent,
rolesAgent,
resourcesAgent,
];
// Create supervisor workflow
+8 -6
View File
@@ -11,6 +11,7 @@ const resourceFieldsEnum = z.enum([
"tags",
"provider",
"findings",
"failed_findings_count",
"url",
"type",
]);
@@ -129,6 +130,10 @@ export const getResourcesSchema = z.object({
.optional()
.describe("Filter by multiple tags separated by commas."),
"filter[type]": z.string().optional().describe("Filter by type."),
"filter[type__icontains]": z
.string()
.optional()
.describe("Filter by substring."),
"filter[type__in]": z
.string()
.optional()
@@ -138,18 +143,15 @@ export const getResourcesSchema = z.object({
.string()
.optional()
.describe("Filter by substring."),
"filter[updated_at]": z
.string()
.optional()
.describe("The uid to filter by."),
"filter[updated_at]": z.string().optional().describe("Filter by date."),
"filter[updated_at__gte]": z
.string()
.optional()
.describe("The uid to filter by."),
.describe("Filter by date greater than or equal to."),
"filter[updated_at__lte]": z
.string()
.optional()
.describe("The uid to filter by."),
.describe("Filter by date less than or equal to."),
})
.optional()
.describe("The filters to apply to the resources."),