mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-01-25 02:08:11 +00:00
182 lines
5.0 KiB
TypeScript
182 lines
5.0 KiB
TypeScript
import { createAgent } from "langchain";
|
|
|
|
import {
|
|
getProviderCredentials,
|
|
getTenantConfig,
|
|
} from "@/actions/lighthouse/lighthouse";
|
|
import { TOOLS_UNAVAILABLE_MESSAGE } from "@/lib/lighthouse/constants";
|
|
import type { ProviderType } from "@/lib/lighthouse/llm-factory";
|
|
import { createLLM } from "@/lib/lighthouse/llm-factory";
|
|
import {
|
|
getMCPTools,
|
|
initializeMCPClient,
|
|
isMCPAvailable,
|
|
} from "@/lib/lighthouse/mcp-client";
|
|
import {
|
|
generateUserDataSection,
|
|
LIGHTHOUSE_SYSTEM_PROMPT_TEMPLATE,
|
|
} from "@/lib/lighthouse/system-prompt";
|
|
import { describeTool, executeTool } from "@/lib/lighthouse/tools/meta-tool";
|
|
import { getModelParams } from "@/lib/lighthouse/utils";
|
|
|
|
export interface RuntimeConfig {
|
|
model?: string;
|
|
provider?: string;
|
|
businessContext?: string;
|
|
currentData?: string;
|
|
}
|
|
|
|
/**
|
|
* Truncate description to specified length
|
|
*/
|
|
function truncateDescription(desc: string | undefined, maxLen: number): string {
|
|
if (!desc) return "No description available";
|
|
|
|
const cleaned = desc.replace(/\n/g, " ").replace(/\s+/g, " ").trim();
|
|
|
|
if (cleaned.length <= maxLen) return cleaned;
|
|
|
|
return cleaned.substring(0, maxLen) + "...";
|
|
}
|
|
|
|
/**
|
|
* Tools explicitly allowed for the LLM to list and execute.
|
|
* Follows the principle of least privilege - only these tools are accessible.
|
|
* All other tools are blocked by default.
|
|
*/
|
|
const ALLOWED_TOOLS = new Set([
|
|
// === Prowler Hub Tools - read-only ===
|
|
"prowler_hub_list_checks",
|
|
"prowler_hub_semantic_search_checks",
|
|
"prowler_hub_get_check_details",
|
|
"prowler_hub_get_check_code",
|
|
"prowler_hub_get_check_fixer",
|
|
"prowler_hub_list_compliances",
|
|
"prowler_hub_semantic_search_compliances",
|
|
"prowler_hub_get_compliance_details",
|
|
"prowler_hub_list_providers",
|
|
"prowler_hub_get_provider_services",
|
|
// === Prowler Docs Tools - read-only ===
|
|
"prowler_docs_search",
|
|
"prowler_docs_get_document",
|
|
// === Prowler App Tools - read-only ===
|
|
// Findings
|
|
"prowler_app_search_security_findings",
|
|
"prowler_app_get_finding_details",
|
|
"prowler_app_get_findings_overview",
|
|
// Providers
|
|
"prowler_app_search_providers",
|
|
// Scans
|
|
"prowler_app_list_scans",
|
|
"prowler_app_get_scan",
|
|
// Muting
|
|
"prowler_app_get_mutelist",
|
|
"prowler_app_list_mute_rules",
|
|
"prowler_app_get_mute_rule",
|
|
// Compliance
|
|
"prowler_app_get_compliance_overview",
|
|
"prowler_app_get_compliance_framework_state_details",
|
|
// Resources
|
|
"prowler_app_list_resources",
|
|
"prowler_app_get_resource",
|
|
"prowler_app_get_resources_overview",
|
|
]);
|
|
|
|
/**
|
|
* Check if a tool is allowed for LLM access.
|
|
* Returns true only if the tool is explicitly in the whitelist.
|
|
*/
|
|
export function isAllowedTool(toolName: string): boolean {
|
|
return ALLOWED_TOOLS.has(toolName);
|
|
}
|
|
|
|
/**
|
|
* Generate dynamic tool listing from MCP tools.
|
|
* Only includes tools that are explicitly whitelisted.
|
|
*/
|
|
function generateToolListing(): string {
|
|
if (!isMCPAvailable()) {
|
|
return TOOLS_UNAVAILABLE_MESSAGE;
|
|
}
|
|
|
|
const mcpTools = getMCPTools();
|
|
|
|
if (mcpTools.length === 0) {
|
|
return TOOLS_UNAVAILABLE_MESSAGE;
|
|
}
|
|
|
|
// Only include whitelisted tools
|
|
const safeTools = mcpTools.filter((tool) => isAllowedTool(tool.name));
|
|
|
|
let listing = "\n## Available Prowler Tools\n\n";
|
|
listing += `${safeTools.length} tools loaded from Prowler MCP\n\n`;
|
|
|
|
for (const tool of safeTools) {
|
|
const desc = truncateDescription(tool.description, 150);
|
|
listing += `- **${tool.name}**: ${desc}\n`;
|
|
}
|
|
|
|
listing +=
|
|
"\nUse describe_tool with exact tool name to see full schema and parameters.\n";
|
|
|
|
return listing;
|
|
}
|
|
|
|
export async function initLighthouseWorkflow(runtimeConfig?: RuntimeConfig) {
|
|
await initializeMCPClient();
|
|
|
|
const toolListing = generateToolListing();
|
|
|
|
let systemPrompt = LIGHTHOUSE_SYSTEM_PROMPT_TEMPLATE.replace(
|
|
"{{TOOL_LISTING}}",
|
|
toolListing,
|
|
);
|
|
|
|
// Add user-provided data section if available
|
|
const userDataSection = generateUserDataSection(
|
|
runtimeConfig?.businessContext,
|
|
runtimeConfig?.currentData,
|
|
);
|
|
|
|
if (userDataSection) {
|
|
systemPrompt += userDataSection;
|
|
}
|
|
|
|
const tenantConfigResult = await getTenantConfig();
|
|
const tenantConfig = tenantConfigResult?.data?.attributes;
|
|
|
|
const defaultProvider = tenantConfig?.default_provider || "openai";
|
|
const defaultModels = tenantConfig?.default_models || {};
|
|
const defaultModel = defaultModels[defaultProvider] || "gpt-5.2";
|
|
|
|
const providerType = (runtimeConfig?.provider ||
|
|
defaultProvider) as ProviderType;
|
|
const modelId = runtimeConfig?.model || defaultModel;
|
|
|
|
// Get credentials
|
|
const providerConfig = await getProviderCredentials(providerType);
|
|
const { credentials, base_url: baseUrl } = providerConfig;
|
|
|
|
// Get model params
|
|
const modelParams = getModelParams({ model: modelId });
|
|
|
|
// Initialize LLM
|
|
const llm = createLLM({
|
|
provider: providerType,
|
|
model: modelId,
|
|
credentials,
|
|
baseUrl,
|
|
streaming: true,
|
|
tags: ["lighthouse-agent"],
|
|
modelParams,
|
|
});
|
|
|
|
const agent = createAgent({
|
|
model: llm,
|
|
tools: [describeTool, executeTool],
|
|
systemPrompt,
|
|
});
|
|
|
|
return agent;
|
|
}
|