UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

405 lines 13.4 kB
/** * Config File Processor * * Processes configuration files (.env, .ini, .toml, .cfg, .conf, .properties). * Automatically detects format based on extension and redacts sensitive values * for security. * * Key features: * - Format detection (env, ini, toml, properties) * - Automatic secret redaction (passwords, tokens, API keys) * - Key-value extraction for structured access * - Security-first approach to config file handling * * Priority: 130 (lower priority - text-based config files) * * @module processors/code/ConfigProcessor * * @example * ```typescript * import { configProcessor, processConfig, isConfigFile } from "./code/index.js"; * * // Check if a file is a config file * if (isConfigFile("text/plain", ".env")) { * const result = await processConfig({ * id: "file-123", * name: ".env", * mimetype: "text/plain", * size: 512, * buffer: envBuffer, * }); * * if (result.success) { * console.log(`Format: ${result.data.format}`); * console.log(`Redacted keys: ${result.data.redactedKeys.join(", ")}`); * } * } * ``` */ import { basename as pathBasename } from "node:path"; import { BaseFileProcessor } from "../base/BaseFileProcessor.js"; import { SIZE_LIMITS } from "../config/index.js"; // ============================================================================= // TYPES // ============================================================================= // Re-import for local use within this file // ============================================================================= // CONSTANTS // ============================================================================= /** * Regex patterns for identifying keys that likely contain secrets. * These keys will have their values redacted in the output. */ const SECRET_KEY_PATTERNS = [ /password/i, /secret/i, /token/i, /key/i, /api[_-]?key/i, /auth/i, /credential/i, /private/i, /access[_-]?token/i, /bearer/i, /jwt/i, /session/i, /cookie/i, /encryption/i, /salt/i, /hash/i, /cert/i, /certificate/i, /pem/i, /rsa/i, /ssh/i, /aws[_-]?secret/i, /azure[_-]?key/i, /gcp[_-]?key/i, ]; /** * Supported MIME types for configuration files */ const SUPPORTED_CONFIG_MIME_TYPES = [ "text/plain", "application/x-env", "text/x-ini", "application/toml", ]; /** * Supported file extensions for configuration files */ const SUPPORTED_CONFIG_EXTENSIONS = [ ".env", ".ini", ".toml", ".cfg", ".conf", ".properties", ]; /** * Default timeout for config file processing (30 seconds) */ const CONFIG_TIMEOUT_MS = 30000; // ============================================================================= // CONFIG PROCESSOR CLASS // ============================================================================= /** * Config Processor - handles configuration files with security redaction. * * Automatically detects the format based on file extension and extracts * key-value pairs while redacting sensitive values for security. * * Priority: 130 (processed after binary/document formats) * * @example * ```typescript * const processor = new ConfigProcessor(); * * const result = await processor.processFile({ * id: "file-123", * name: ".env", * mimetype: "text/plain", * size: 512, * buffer: envBuffer, * }); * * if (result.success) { * console.log(`Detected format: ${result.data.format}`); * console.log(`Redacted ${result.data.redactedKeys.length} sensitive keys`); * } * ``` */ export class ConfigProcessor extends BaseFileProcessor { constructor() { super({ maxSizeMB: SIZE_LIMITS.TEXT_MAX_MB, timeoutMs: CONFIG_TIMEOUT_MS, supportedMimeTypes: SUPPORTED_CONFIG_MIME_TYPES, supportedExtensions: SUPPORTED_CONFIG_EXTENSIONS, fileTypeName: "Config", defaultFilename: "config.env", }); } /** * Override to check for exact filename matches like ".env" files. * * @param mimetype - MIME type of the file * @param filename - Filename for detection * @returns true if the file is a supported config file */ isFileSupported(mimetype, filename) { if (!filename) { return false; } // Check for exact filename matches (e.g., ".env", ".env.local") const basename = pathBasename(filename); // Special handling for dotenv files if (basename.startsWith(".env")) { return true; } // Check by extension const ext = this.getExtension(filename); if (ext && SUPPORTED_CONFIG_EXTENSIONS.includes(ext.toLowerCase())) { return true; } // Fall back to MIME type check return super.isFileSupported(mimetype, filename); } /** * Build the processed config result. * Detects format, extracts key-values, and redacts sensitive content. * * @param buffer - Raw file content * @param fileInfo - Original file information * @returns Processed config with redacted content and key-value pairs */ buildProcessedResult(buffer, fileInfo) { const content = buffer.toString("utf-8"); const ext = this.getExtension(fileInfo.name || ""); const format = this.detectFormat(ext, content); const { keyValues, redactedKeys } = this.parseAndRedact(content, format); return { content: this.redactContent(content, redactedKeys), format, keyValues, redactedKeys, buffer, mimetype: fileInfo.mimetype || "text/plain", size: fileInfo.size, filename: this.getFilename(fileInfo), }; } /** * Detect the configuration format based on extension and content. * * @param ext - File extension (with leading dot) * @param content - File content for format heuristics * @returns Detected format */ detectFormat(ext, content) { // First check extension if (ext) { const lowerExt = ext.toLowerCase(); if (lowerExt === ".env") { return "env"; } if (lowerExt === ".ini" || lowerExt === ".cfg" || lowerExt === ".conf") { return "ini"; } if (lowerExt === ".toml") { return "toml"; } if (lowerExt === ".properties") { return "properties"; } } // Try content-based heuristics const lines = content .split("\n") .filter((l) => l.trim() && !l.trim().startsWith("#")); // Check for TOML markers if (content.includes("[") && content.includes("]") && lines.some((l) => l.includes("="))) { // Could be INI or TOML - check for TOML-specific syntax if (content.includes("[[") || content.includes('"""') || content.includes("'''")) { return "toml"; } return "ini"; } // Check for properties-style (key: value or key = value with Java conventions) if (lines.some((l) => l.includes(":") && !l.includes("="))) { return "properties"; } // Default to env format for simple KEY=VALUE files if (lines.some((l) => /^[A-Z_][A-Z0-9_]*=/i.test(l.trim()))) { return "env"; } return "unknown"; } /** * Parse configuration content and redact sensitive values. * * @param content - Raw configuration content * @param format - Detected format * @returns Key-value pairs and list of redacted keys */ parseAndRedact(content, format) { const keyValues = {}; const redactedKeys = []; const lines = content.split("\n"); for (const line of lines) { const trimmedLine = line.trim(); // Skip comments and empty lines if (!trimmedLine || trimmedLine.startsWith("#") || trimmedLine.startsWith(";")) { continue; } // Skip section headers (INI/TOML style) if (trimmedLine.startsWith("[")) { continue; } // Try to extract key-value pair let key = ""; let value = ""; // Handle different formats if (format === "properties" && trimmedLine.includes(":") && !trimmedLine.includes("=")) { // Properties format: key: value or key : value const colonIndex = trimmedLine.indexOf(":"); key = trimmedLine.substring(0, colonIndex).trim(); value = trimmedLine.substring(colonIndex + 1).trim(); } else if (trimmedLine.includes("=")) { // Standard KEY=VALUE format const equalIndex = trimmedLine.indexOf("="); key = trimmedLine.substring(0, equalIndex).trim(); value = trimmedLine.substring(equalIndex + 1).trim(); // Remove quotes if present if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { value = value.slice(1, -1); } } if (key) { // Check if this key should be redacted if (this.isSensitiveKey(key)) { keyValues[key] = "[REDACTED]"; redactedKeys.push(key); } else { keyValues[key] = value; } } } return { keyValues, redactedKeys }; } /** * Check if a key likely contains sensitive data. * * @param key - Key name to check * @returns true if the key matches a sensitive pattern */ isSensitiveKey(key) { return SECRET_KEY_PATTERNS.some((pattern) => pattern.test(key)); } /** * Redact sensitive values in the original content. * * @param content - Original configuration content * @param redactedKeys - Keys to redact * @returns Content with redacted values */ redactContent(content, redactedKeys) { let result = content; for (const key of redactedKeys) { // Escape special regex characters in the key const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Match key=value or key: value patterns const patterns = [ // KEY=value (with optional quotes) new RegExp(`(${escapedKey}\\s*=\\s*)(["']?)[^"'\\n]*(["']?)`, "gm"), // KEY: value (properties style) new RegExp(`(${escapedKey}\\s*:\\s*)(.*)$`, "gm"), ]; for (const regex of patterns) { result = result.replace(regex, "$1[REDACTED]"); } } return result; } /** * Extract file extension from filename. * * @param filename - Filename to extract extension from * @returns Extension with leading dot or null */ getExtension(filename) { // Handle .env files specially if (filename.startsWith(".env")) { return ".env"; } const match = filename.toLowerCase().match(/\.[^.]+$/); return match ? match[0] : null; } } // ============================================================================= // SINGLETON INSTANCE // ============================================================================= /** * Singleton instance of the ConfigProcessor. * Use this for all configuration file processing to share configuration. */ export const configProcessor = new ConfigProcessor(); // ============================================================================= // HELPER FUNCTIONS // ============================================================================= /** * Check if a file is a configuration file. * * @param mimetype - MIME type of the file * @param filename - Filename for detection * @returns true if the file is a supported config file * * @example * ```typescript * if (isConfigFile("text/plain", ".env")) { * console.log("This is a config file"); * } * ``` */ export function isConfigFile(mimetype, filename) { return configProcessor.isFileSupported(mimetype, filename); } /** * Process a configuration file. * * @param fileInfo - File information (can include URL or buffer) * @param options - Optional processing options * @returns Processing result with success flag and either data or error * * @example * ```typescript * const result = await processConfig({ * id: "file-123", * name: ".env.production", * mimetype: "text/plain", * size: 1024, * buffer: configBuffer, * }); * * if (result.success) { * console.log(`Format: ${result.data.format}`); * console.log(`Keys: ${Object.keys(result.data.keyValues).length}`); * console.log(`Redacted: ${result.data.redactedKeys.join(", ")}`); * } * ``` */ export async function processConfig(fileInfo, options) { return configProcessor.processFile(fileInfo, options); } //# sourceMappingURL=ConfigProcessor.js.map