get_file_info
Extract file type, cryptographic hashes, and basic metadata from files for malware analysis and security investigations.
Instructions
Get file type, hashes, and basic metadata
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file | Yes | File path relative to samples directory, or absolute path in local mode |
Implementation Reference
- src/handlers/get-file-info.ts:8-102 (handler)Main handler function handleGetFileInfo that executes file analysis commands (file, sha256sum, md5sum, sha1sum, stat) to gather metadata including file type, cryptographic hashes, and size. Includes path validation, error handling, and returns standardized MCP response.export async function handleGetFileInfo( deps: HandlerDeps, args: GetFileInfoArgs ) { const startTime = Date.now(); try { const { connector, config } = deps; // Validate file path (skip unless --sandbox) if (!config.noSandbox) { const validation = validateFilePath(args.file, config.samplesDir); if (!validation.safe) { return formatError("get_file_info", new REMnuxError( validation.error || "Invalid file path", "INVALID_PATH", "validation", "Use a relative path within the samples directory", ), startTime); } } const filePath = (config.mode === "local" && args.file.startsWith("/")) ? args.file : `${config.samplesDir}/${args.file}`; let fileType = ""; let sha256 = ""; let md5 = ""; let sizeBytes: number | null = null; // file command try { const result = await connector.execute(["file", filePath], { timeout: 30000 }); if (result.stdout) fileType = result.stdout.trim(); } catch (error) { const mapped = toREMnuxError(error, config.mode); if (mapped.code === "CONNECTION_FAILED") { return formatError("get_file_info", mapped, startTime); } // Non-connection errors: continue (file command failure is non-fatal) } // sha256sum try { const result = await connector.execute(["sha256sum", filePath], { timeout: 30000 }); if (result.stdout) sha256 = result.stdout.trim().split(/\s+/)[0] || ""; } catch { /* skip */ } // md5sum try { const result = await connector.execute(["md5sum", filePath], { timeout: 30000 }); if (result.stdout) md5 = result.stdout.trim().split(/\s+/)[0] || ""; } catch { /* skip */ } // sha1sum let sha1 = ""; try { const result = await connector.execute(["sha1sum", filePath], { timeout: 30000 }); if (result.stdout) sha1 = result.stdout.trim().split(/\s+/)[0] || ""; } catch { /* skip */ } // file size (stat -c works on Linux/REMnux; wc -c as fallback) try { const result = await connector.execute(["stat", "-c", "%s", filePath], { timeout: 30000 }); if (result.stdout && result.exitCode === 0) { sizeBytes = parseInt(result.stdout.trim(), 10); } } catch { /* skip */ } if (sizeBytes === null) { try { const result = await connector.execute(["wc", "-c", filePath], { timeout: 30000 }); if (result.stdout && result.exitCode === 0) { sizeBytes = parseInt(result.stdout.trim().split(/\s+/)[0] || "0", 10); } } catch { /* skip */ } } if (!fileType && !sha256 && !md5) { return formatError("get_file_info", new REMnuxError( "Could not get file info", "EMPTY_OUTPUT", "tool_failure", "Check that the file exists and is readable", ), startTime); } return formatResponse("get_file_info", { file: args.file, file_type: fileType, sha256, sha1, md5, ...(sizeBytes !== null ? { size_bytes: sizeBytes } : {}), }, startTime); } catch (error) { return formatError("get_file_info", toREMnuxError(error, deps.config.mode), startTime); } }
- src/schemas/tools.ts:10-13 (schema)Zod schema definition for getFileInfoSchema and its inferred type GetFileInfoArgs. Validates input with a single 'file' parameter that accepts a file path relative to samples directory or absolute path in local mode.export const getFileInfoSchema = z.object({ file: z.string().describe("File path relative to samples directory, or absolute path in local mode"), }); export type GetFileInfoArgs = z.infer<typeof getFileInfoSchema>;
- src/index.ts:104-109 (registration)MCP server tool registration for get_file_info. Registers the tool with its name 'get_file_info', description 'Get file type, hashes, and basic metadata', schema shape, and handler function bound with dependencies.server.tool( "get_file_info", "Get file type, hashes, and basic metadata", getFileInfoSchema.shape, (args) => handleGetFileInfo(deps, args) );
- src/response.ts:27-44 (helper)formatResponse helper function that creates standardized success response envelopes with tool name, data, and elapsed time metadata. Used by get_file_info handler to return results.export function formatResponse( tool: string, data: Record<string, unknown>, startTime: number, ): { content: Array<{ type: "text"; text: string }>; isError?: boolean } { const envelope: ToolResponse = { success: true, tool, data, metadata: { elapsed_ms: Date.now() - startTime }, }; const COMPACT_THRESHOLD = 50 * 1024; const compact = JSON.stringify(envelope); const text = compact.length > COMPACT_THRESHOLD ? compact : JSON.stringify(envelope, null, 2); return { content: [{ type: "text", text }], }; }
- src/response.ts:52-77 (helper)formatError helper function that creates standardized error response envelopes with structured error fields (code, category, remediation) when error is a REMnuxError instance. Used by get_file_info handler for error reporting.export function formatError( tool: string, error: string | REMnuxError, startTime: number, ): { content: Array<{ type: "text"; text: string }>; isError: true } { const envelope: ToolResponse = { success: false, tool, data: {}, error: error instanceof REMnuxError ? error.message : error, metadata: { elapsed_ms: Date.now() - startTime }, }; if (error instanceof REMnuxError) { envelope.error_code = error.code; envelope.error_category = error.category; if (error.remediation) { envelope.remediation = error.remediation; } } return { content: [{ type: "text", text: JSON.stringify(envelope, null, 2) }], isError: true, }; }