UNPKG

safe-commander-mcp

Version:

A secure MCP server for executing whitelisted development commands with comprehensive security controls and resource limits

90 lines (89 loc) 3.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CONFIG = void 0; exports.loadConfig = loadConfig; exports.validateCriticalConfig = validateCriticalConfig; const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); const logger_1 = require("../utils/logger"); // Configuration object with resource limits exports.CONFIG = { MAX_COMMAND_LENGTH: 1000, MAX_OUTPUT_SIZE: 1024 * 1024, // 1MB COMMAND_TIMEOUT: 30000, MAX_CONCURRENT_COMMANDS: 3, ALLOWED_COMMANDS: [], ALLOWED_PATH: "" }; /** * Configuration loader with comprehensive validation * * @throws Error if configuration is invalid */ function loadConfig() { try { // Load and validate ALLOWED_COMMANDS const allowedCommandsEnv = process.env.ALLOWED_COMMANDS; if (!allowedCommandsEnv) { exports.CONFIG.ALLOWED_COMMANDS = ['ls', 'cat', 'pwd', 'echo']; (0, logger_1.log)('warn', 'ALLOWED_COMMANDS not set, using security-first defaults', { defaults: exports.CONFIG.ALLOWED_COMMANDS, securityNote: 'Only read-only commands included by default. Add other commands only if you understand the risks.' }); } else { const commands = allowedCommandsEnv.split(',').map(cmd => cmd.trim()).filter(cmd => cmd.length > 0); if (commands.length === 0) { throw new Error('ALLOWED_COMMANDS cannot be empty after parsing'); } exports.CONFIG.ALLOWED_COMMANDS = commands; // Security warning for dangerous commands const dangerousCommands = commands.filter(cmd => ['node', 'python', 'python3', 'bash', 'sh', 'zsh', 'npm', 'curl', 'wget'].includes(cmd)); if (dangerousCommands.length > 0) { (0, logger_1.log)('warn', 'SECURITY WARNING: Commands with elevated risk detected', { dangerousCommands, warning: 'These commands may allow LLMs to access sensitive data or execute arbitrary code. Ensure you trust the LLM and understand the implications.' }); } } // Load and validate ALLOWED_PATH const allowedPathEnv = process.env.ALLOWED_PATH; if (!allowedPathEnv) { throw new Error('ALLOWED_PATH environment variable must be set'); } const resolvedPath = (0, node_path_1.resolve)(allowedPathEnv); if (!(0, node_fs_1.existsSync)(resolvedPath)) { throw new Error(`ALLOWED_PATH directory does not exist: ${resolvedPath}`); } exports.CONFIG.ALLOWED_PATH = resolvedPath; (0, logger_1.log)('info', 'Configuration loaded successfully', { allowedCommands: exports.CONFIG.ALLOWED_COMMANDS, allowedPath: exports.CONFIG.ALLOWED_PATH, maxCommandLength: exports.CONFIG.MAX_COMMAND_LENGTH, commandTimeout: exports.CONFIG.COMMAND_TIMEOUT, maxConcurrentCommands: exports.CONFIG.MAX_CONCURRENT_COMMANDS }); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown configuration error'; (0, logger_1.log)('error', 'Failed to load configuration', { error: errorMessage }); throw error; } } /** * Validate critical configuration first * * @throws Process exits with code 1 if critical config is invalid */ function validateCriticalConfig() { const allowedPath = process.env.ALLOWED_PATH; if (!allowedPath) { (0, logger_1.log)('error', 'FATAL: ALLOWED_PATH environment variable must be set'); process.exit(1); } if (!(0, node_fs_1.existsSync)(allowedPath)) { (0, logger_1.log)('error', `FATAL: ALLOWED_PATH directory does not exist: ${allowedPath}`); process.exit(1); } (0, logger_1.log)('info', 'Configuration validated successfully', { allowedPath }); }