UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

460 lines (459 loc) 17.1 kB
import fs from 'fs-extra'; import path from 'path'; import yaml from 'js-yaml'; import logger from '../../../logger.js'; import { UnifiedSecurityEngine, createDefaultSecurityConfig } from '../core/unified-security-engine.js'; export class FileUtils { static MAX_FILE_SIZE = 10 * 1024 * 1024; static ALLOWED_EXTENSIONS = ['.json', '.yaml', '.yml', '.md', '.txt']; static securityEngine = null; static async getSecurityEngine() { if (!this.securityEngine) { const config = createDefaultSecurityConfig(); this.securityEngine = UnifiedSecurityEngine.getInstance(config); await this.securityEngine.initialize(); } return this.securityEngine; } static async readFile(filePath) { try { const validationResult = await this.validateFilePath(filePath); if (!validationResult.valid) { return { success: false, error: `Invalid file path: ${validationResult.error}`, metadata: { filePath, operation: 'read', timestamp: new Date() } }; } if (!await fs.pathExists(filePath)) { return { success: false, error: 'File does not exist', metadata: { filePath, operation: 'read', timestamp: new Date() } }; } const stats = await fs.stat(filePath); if (stats.size > this.MAX_FILE_SIZE) { return { success: false, error: `File too large: ${stats.size} bytes (max: ${this.MAX_FILE_SIZE})`, metadata: { filePath, operation: 'read', timestamp: new Date(), size: stats.size } }; } const content = await fs.readFile(filePath, 'utf-8'); logger.debug({ filePath, size: stats.size }, 'File read successfully'); return { success: true, data: content, metadata: { filePath, operation: 'read', timestamp: new Date(), size: stats.size } }; } catch (error) { logger.error({ err: error, filePath }, 'Failed to read file'); return { success: false, error: error instanceof Error ? error.message : String(error), metadata: { filePath, operation: 'read', timestamp: new Date() } }; } } static async writeFile(filePath, content) { try { const validationResult = await this.validateFilePath(filePath, 'write'); if (!validationResult.valid) { return { success: false, error: `Invalid file path: ${validationResult.error}`, metadata: { filePath, operation: 'write', timestamp: new Date() } }; } const dirPath = path.dirname(filePath); await fs.ensureDir(dirPath); const tempFilePath = `${filePath}.tmp.${Date.now()}.${Math.random().toString(36).substr(2, 9)}`; try { await fs.writeFile(tempFilePath, content, 'utf-8'); await fs.rename(tempFilePath, filePath); } catch (writeError) { try { await fs.remove(tempFilePath); } catch { } throw writeError; } const stats = await fs.stat(filePath); logger.debug({ filePath, size: stats.size }, 'File written successfully (atomic)'); return { success: true, metadata: { filePath, operation: 'write', timestamp: new Date(), size: stats.size } }; } catch (error) { logger.error({ err: error, filePath }, 'Failed to write file'); return { success: false, error: error instanceof Error ? error.message : String(error), metadata: { filePath, operation: 'write', timestamp: new Date() } }; } } static async readYamlFile(filePath, schema) { try { const readResult = await this.readFile(filePath); if (!readResult.success) { return readResult; } const parsedData = yaml.load(readResult.data); if (schema) { const validationResult = schema.safeParse(parsedData); if (!validationResult.success) { return { success: false, error: `YAML validation failed: ${validationResult.error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`, metadata: { filePath, operation: 'read_yaml', timestamp: new Date() } }; } return { success: true, data: validationResult.data, metadata: { filePath, operation: 'read_yaml', timestamp: new Date(), size: readResult.metadata?.size } }; } return { success: true, data: parsedData, metadata: { filePath, operation: 'read_yaml', timestamp: new Date(), size: readResult.metadata?.size } }; } catch (error) { logger.error({ err: error, filePath }, 'Failed to read YAML file'); return { success: false, error: error instanceof Error ? error.message : String(error), metadata: { filePath, operation: 'read_yaml', timestamp: new Date() } }; } } static async writeYamlFile(filePath, data, schema) { try { if (schema) { const validationResult = schema.safeParse(data); if (!validationResult.success) { return { success: false, error: `Data validation failed: ${validationResult.error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`, metadata: { filePath, operation: 'write_yaml', timestamp: new Date() } }; } } const yamlContent = yaml.dump(data, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: true }); return await this.writeFile(filePath, yamlContent); } catch (error) { logger.error({ err: error, filePath }, 'Failed to write YAML file'); return { success: false, error: error instanceof Error ? error.message : String(error), metadata: { filePath, operation: 'write_yaml', timestamp: new Date() } }; } } static async readJsonFile(filePath, schema) { try { const readResult = await this.readFile(filePath); if (!readResult.success) { return readResult; } const parsedData = JSON.parse(readResult.data); if (schema) { const validationResult = schema.safeParse(parsedData); if (!validationResult.success) { return { success: false, error: `JSON validation failed: ${validationResult.error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`, metadata: { filePath, operation: 'read_json', timestamp: new Date() } }; } return { success: true, data: validationResult.data, metadata: { filePath, operation: 'read_json', timestamp: new Date(), size: readResult.metadata?.size } }; } return { success: true, data: parsedData, metadata: { filePath, operation: 'read_json', timestamp: new Date(), size: readResult.metadata?.size } }; } catch (error) { logger.error({ err: error, filePath }, 'Failed to read JSON file'); return { success: false, error: error instanceof Error ? error.message : String(error), metadata: { filePath, operation: 'read_json', timestamp: new Date() } }; } } static async writeJsonFile(filePath, data, schema) { try { if (schema) { const validationResult = schema.safeParse(data); if (!validationResult.success) { return { success: false, error: `Data validation failed: ${validationResult.error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`, metadata: { filePath, operation: 'write_json', timestamp: new Date() } }; } } const jsonContent = JSON.stringify(data, null, 2); return await this.writeFile(filePath, jsonContent); } catch (error) { logger.error({ err: error, filePath }, 'Failed to write JSON file'); return { success: false, error: error instanceof Error ? error.message : String(error), metadata: { filePath, operation: 'write_json', timestamp: new Date() } }; } } static async validateFilePath(filePath, operation = 'read') { try { const securityEngine = await this.getSecurityEngine(); if (operation === 'write') { const dirPath = path.dirname(filePath); const fileName = path.basename(filePath); const dirValidationResult = await securityEngine.validatePath(dirPath, 'write'); if (!dirValidationResult.success || !dirValidationResult.data?.isValid) { const fullPathResult = await securityEngine.validatePath(filePath, 'write'); if (!fullPathResult.success) { return { valid: false, error: fullPathResult.error.message || 'Path validation failed' }; } if (!fullPathResult.data.isValid) { return { valid: false, error: fullPathResult.data.error || 'Path validation failed' }; } } const dangerousChars = /[<>"|?*]/; const controlChars = new RegExp('[' + String.fromCharCode(0) + '-' + String.fromCharCode(31) + ']'); if (dangerousChars.test(fileName) || controlChars.test(fileName)) { return { valid: false, error: 'Filename contains dangerous characters' }; } } else { const validationResult = await securityEngine.validatePath(filePath, 'read'); if (!validationResult.success) { return { valid: false, error: validationResult.error.message || 'Path validation failed' }; } if (!validationResult.data.isValid) { return { valid: false, error: validationResult.data.error || 'Path validation failed' }; } } const ext = path.extname(filePath).toLowerCase(); if (ext && !this.ALLOWED_EXTENSIONS.includes(ext)) { return { valid: false, error: `File extension ${ext} not allowed. Allowed extensions: ${this.ALLOWED_EXTENSIONS.join(', ')}` }; } return { valid: true }; } catch (error) { logger.error({ err: error, filePath, operation }, 'File path validation failed with exception'); return { valid: false, error: `Path validation error: ${error instanceof Error ? error.message : String(error)}` }; } } static async ensureDirectory(dirPath) { try { await fs.ensureDir(dirPath); return { success: true, metadata: { filePath: dirPath, operation: 'ensure_directory', timestamp: new Date() } }; } catch (error) { logger.error({ err: error, dirPath }, 'Failed to ensure directory'); return { success: false, error: error instanceof Error ? error.message : String(error), metadata: { filePath: dirPath, operation: 'ensure_directory', timestamp: new Date() } }; } } static async fileExists(filePath) { try { return await fs.pathExists(filePath); } catch { return false; } } static async deleteFile(filePath) { try { const validationResult = await this.validateFilePath(filePath, 'read'); if (!validationResult.valid) { return { success: false, error: `Invalid file path: ${validationResult.error}`, metadata: { filePath, operation: 'delete', timestamp: new Date() } }; } if (!await fs.pathExists(filePath)) { return { success: true, metadata: { filePath, operation: 'delete', timestamp: new Date() } }; } await fs.remove(filePath); logger.debug({ filePath }, 'File deleted successfully'); return { success: true, metadata: { filePath, operation: 'delete', timestamp: new Date() } }; } catch (error) { logger.error({ err: error, filePath }, 'Failed to delete file'); return { success: false, error: error instanceof Error ? error.message : String(error), metadata: { filePath, operation: 'delete', timestamp: new Date() } }; } } }