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
JavaScript
;
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 });
}