cortex_run_analyzer_file
Submit a file to any analyzer for analysis by providing a file path or base64-encoded content, enabling automated security investigations.
Instructions
Submit a file to a specific analyzer for analysis. Provide a file path or base64-encoded content.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| analyzerId | Yes | The analyzer ID to run | |
| filePath | No | Path to the file to analyze (local filesystem) | |
| fileBase64 | No | Base64-encoded file content (alternative to filePath) | |
| filename | No | Filename (required with fileBase64, auto-detected from filePath) | |
| contentType | No | MIME type of the file (default: application/octet-stream) | application/octet-stream |
| tlp | No | Traffic Light Protocol level (0=WHITE, 1=GREEN, 2=AMBER, 3=RED) | |
| pap | No | Permissible Actions Protocol level (0-3) | |
| message | No | Optional context message for the analysis |
Implementation Reference
- src/tools/analyzers.ts:295-366 (handler)The main handler function for the 'cortex_run_analyzer_file' tool. It reads a file from a local path or decodes base64 content, then calls client.runAnalyzerWithFile() to submit the file for analysis. Returns the job details including jobId for polling results.
async ({ analyzerId, filePath, fileBase64, filename, contentType, tlp, pap, message }) => { try { let content: Buffer; let name: string; if (filePath) { content = await readFile(filePath); name = filename ?? basename(filePath); } else if (fileBase64) { if (!filename) { return { content: [ { type: "text" as const, text: "filename is required when using fileBase64.", }, ], isError: true, }; } content = Buffer.from(fileBase64, "base64"); name = filename; } else { return { content: [ { type: "text" as const, text: "Provide either filePath or fileBase64 to submit a file for analysis.", }, ], isError: true, }; } const job = await client.runAnalyzerWithFile( analyzerId, { content, filename: name, contentType }, { tlp, pap, message }, ); return { content: [ { type: "text" as const, text: JSON.stringify( { jobId: job.id, status: job.status, analyzerId: job.analyzerId, filename: name, fileSize: content.length, message: `File analysis job submitted. Use cortex_wait_and_get_report with jobId "${job.id}" to get results.`, }, null, 2, ), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error running file analysis: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); - src/tools/analyzers.ts:258-294 (schema)Input schema for the cortex_run_analyzer_file tool using Zod validation. Defines parameters: analyzerId (required string), filePath (optional), fileBase64 (optional), filename (optional), contentType (default: application/octet-stream), tlp (0-3, default: 2), pap (0-3, default: 2), and message (optional context).
{ analyzerId: z.string().describe("The analyzer ID to run"), filePath: z .string() .optional() .describe("Path to the file to analyze (local filesystem)"), fileBase64: z .string() .optional() .describe("Base64-encoded file content (alternative to filePath)"), filename: z .string() .optional() .describe("Filename (required with fileBase64, auto-detected from filePath)"), contentType: z .string() .default("application/octet-stream") .describe("MIME type of the file (default: application/octet-stream)"), tlp: z .number() .int() .min(0) .max(3) .default(2) .describe("Traffic Light Protocol level (0=WHITE, 1=GREEN, 2=AMBER, 3=RED)"), pap: z .number() .int() .min(0) .max(3) .default(2) .describe("Permissible Actions Protocol level (0-3)"), message: z .string() .optional() .describe("Optional context message for the analysis"), }, - src/tools/analyzers.ts:255-366 (registration)Registration of the 'cortex_run_analyzer_file' tool via server.tool() inside the registerAnalyzerTools() function, which is called from src/index.ts line 34.
server.tool( "cortex_run_analyzer_file", "Submit a file to a specific analyzer for analysis. Provide a file path or base64-encoded content.", { analyzerId: z.string().describe("The analyzer ID to run"), filePath: z .string() .optional() .describe("Path to the file to analyze (local filesystem)"), fileBase64: z .string() .optional() .describe("Base64-encoded file content (alternative to filePath)"), filename: z .string() .optional() .describe("Filename (required with fileBase64, auto-detected from filePath)"), contentType: z .string() .default("application/octet-stream") .describe("MIME type of the file (default: application/octet-stream)"), tlp: z .number() .int() .min(0) .max(3) .default(2) .describe("Traffic Light Protocol level (0=WHITE, 1=GREEN, 2=AMBER, 3=RED)"), pap: z .number() .int() .min(0) .max(3) .default(2) .describe("Permissible Actions Protocol level (0-3)"), message: z .string() .optional() .describe("Optional context message for the analysis"), }, async ({ analyzerId, filePath, fileBase64, filename, contentType, tlp, pap, message }) => { try { let content: Buffer; let name: string; if (filePath) { content = await readFile(filePath); name = filename ?? basename(filePath); } else if (fileBase64) { if (!filename) { return { content: [ { type: "text" as const, text: "filename is required when using fileBase64.", }, ], isError: true, }; } content = Buffer.from(fileBase64, "base64"); name = filename; } else { return { content: [ { type: "text" as const, text: "Provide either filePath or fileBase64 to submit a file for analysis.", }, ], isError: true, }; } const job = await client.runAnalyzerWithFile( analyzerId, { content, filename: name, contentType }, { tlp, pap, message }, ); return { content: [ { type: "text" as const, text: JSON.stringify( { jobId: job.id, status: job.status, analyzerId: job.analyzerId, filename: name, fileSize: content.length, message: `File analysis job submitted. Use cortex_wait_and_get_report with jobId "${job.id}" to get results.`, }, null, 2, ), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error running file analysis: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); - src/client.ts:165-220 (helper)The runAnalyzerWithFile() helper method on the CortexClient. It builds a multipart form request with _json metadata and file attachment, sends it to the Cortex API, and returns the resulting job.
async runAnalyzerWithFile( analyzerId: string, file: { content: Buffer; filename: string; contentType: string; }, options: { tlp: number; pap: number; message?: string; }, ): Promise<Job> { const url = `${this.baseUrl}/analyzer/${encodeURIComponent(analyzerId)}/run`; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); try { const formData = new FormData(); // Cortex expects _json field with metadata and attachment field with file const jsonData = JSON.stringify({ dataType: "file", tlp: options.tlp, pap: options.pap, message: options.message ?? "", }); formData.append("_json", new Blob([jsonData], { type: "application/json" })); const fileBuffer = Uint8Array.from(file.content).buffer; const fileBlob = new Blob([fileBuffer], { type: file.contentType }); formData.append("attachment", fileBlob, file.filename); const response = await fetch(url, { method: "POST", headers: { Authorization: `Bearer ${this.config.apiKey}`, }, body: formData, signal: controller.signal, }); if (!response.ok) { const body = await response.text().catch(() => ""); throw new Error(`Cortex API error: HTTP ${response.status}${body ? ` - ${body}` : ""}`); } return response.json() as Promise<Job>; } catch (error) { if (error instanceof Error && error.name === "AbortError") { throw new Error(`Cortex API timeout after ${this.timeout}ms`); } throw error; } finally { clearTimeout(timeoutId); }