UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

81 lines 11.6 kB
import { spawn } from 'child_process'; import { RegexValidator } from './regexValidator.js'; const ALLOWED_COMMANDS = { // SECURITY FIX: Added 'clone' command to git allowlist // Previously: npm installation feature would fail with "Argument not allowed: clone" // Now: git clone command is allowed for npm installation functionality git: ['pull', 'status', 'log', 'rev-parse', 'branch', 'checkout', 'fetch', 'clone', '--abbrev-ref', 'HEAD', '--porcelain'], npm: ['install', 'run', 'audit', 'ci', '--version', 'build'], node: ['--version'], npx: ['--version'] }; export class CommandValidator { static sanitizeCommand(cmd, args) { if (!ALLOWED_COMMANDS[cmd]) { throw new Error(`Command not allowed: ${cmd}`); } const allowedArgs = ALLOWED_COMMANDS[cmd]; for (const arg of args) { // Check if it's in allowed list or matches safe pattern if (!allowedArgs.includes(arg) && !this.isSafeArgument(arg)) { throw new Error(`Argument not allowed: ${arg}`); } } } static isSafeArgument(arg) { // Allow alphanumeric, dash, underscore, dot, and forward slash return RegexValidator.validate(arg, /^[a-zA-Z0-9\-_.\/]+$/, { maxLength: 1000 }); } static async secureExec(command, args, options) { this.sanitizeCommand(command, args); const safeOptions = { ...options, stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env, PATH: '/usr/bin:/bin:/usr/local/bin' // Restrict PATH }, cwd: options?.cwd || process.cwd(), timeout: options?.timeout || 30000 // 30 second default }; return new Promise((resolve, reject) => { const proc = spawn(command, args, safeOptions); let stdout = ''; let stderr = ''; let timeoutHandle; let isCompleted = false; // Helper to safely resolve/reject only once const complete = (fn) => { if (!isCompleted) { isCompleted = true; if (timeoutHandle) { clearTimeout(timeoutHandle); } fn(); } }; // Handle timeout if (options?.timeout) { timeoutHandle = setTimeout(() => { proc.kill('SIGTERM'); complete(() => reject(new Error(`Command timed out after ${options.timeout}ms`))); }, options.timeout); timeoutHandle.unref(); } proc.stdout?.on('data', (data) => stdout += data); proc.stderr?.on('data', (data) => stderr += data); proc.on('exit', (code) => { if (code === 0) { complete(() => resolve(stdout.trim())); } else { complete(() => reject(new Error(`Command failed (${code}): ${stderr}`))); } }); proc.on('error', (error) => { complete(() => reject(error)); }); }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tbWFuZFZhbGlkYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZWN1cml0eS9jb21tYW5kVmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLEVBQWdCLE1BQU0sZUFBZSxDQUFDO0FBRXBELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUVyRCxNQUFNLGdCQUFnQixHQUE2QjtJQUNqRCx1REFBdUQ7SUFDdkQscUZBQXFGO0lBQ3JGLHVFQUF1RTtJQUN2RSxHQUFHLEVBQUUsQ0FBQyxNQUFNLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLEVBQUUsYUFBYSxDQUFDO0lBQzFILEdBQUcsRUFBRSxDQUFDLFNBQVMsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsT0FBTyxDQUFDO0lBQzVELElBQUksRUFBRSxDQUFDLFdBQVcsQ0FBQztJQUNuQixHQUFHLEVBQUUsQ0FBQyxXQUFXLENBQUM7Q0FDbkIsQ0FBQztBQUVGLE1BQU0sT0FBTyxnQkFBZ0I7SUFDM0IsTUFBTSxDQUFDLGVBQWUsQ0FBQyxHQUFXLEVBQUUsSUFBYztRQUNoRCxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMzQixNQUFNLElBQUksS0FBSyxDQUFDLHdCQUF3QixHQUFHLEVBQUUsQ0FBQyxDQUFDO1FBQ2pELENBQUM7UUFFRCxNQUFNLFdBQVcsR0FBRyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMxQyxLQUFLLE1BQU0sR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO1lBQ3ZCLHdEQUF3RDtZQUN4RCxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElB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Q0FBQyxDQUFDO2dCQUN6QyxDQUFDO3FCQUFNLENBQUM7b0JBQ04sUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxtQkFBbUIsSUFBSSxNQUFNLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUMzRSxDQUFDO1lBQ0gsQ0FBQyxDQUFDLENBQUM7WUFFSCxJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFO2dCQUN6QixRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7WUFDaEMsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHNwYXduLCBTcGF3bk9wdGlvbnMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgUmVnZXhWYWxpZGF0b3IgfSBmcm9tICcuL3JlZ2V4VmFsaWRhdG9yLmpzJztcblxuY29uc3QgQUxMT1dFRF9DT01NQU5EUzogUmVjb3JkPHN0cmluZywgc3RyaW5nW10+ID0ge1xuICAvLyBTRUNVUklUWSBGSVg6IEFkZGVkICdjbG9uZScgY29tbWFuZCB0byBnaXQgYWxsb3dsaXN0XG4gIC8vIFByZXZpb3VzbHk6IG5wbSBpbnN0YWxsYXRpb24gZmVhdHVyZSB3b3VsZCBmYWlsIHdpdGggXCJBcmd1bWVudCBub3QgYWxsb3dlZDogY2xvbmVcIlxuICAvLyBOb3c6IGdpdCBjbG9uZSBjb21tYW5kIGlzIGFsbG93ZWQgZm9yIG5wbSBpbnN0YWxsYXRpb24gZnVuY3Rpb25hbGl0eVxuICBnaXQ6IFsncHVsbCcsICdzdGF0dXMnLCAnbG9nJywgJ3Jldi1wYXJzZScsICdicmFuY2gnLCAnY2hlY2tvdXQnLCAnZmV0Y2gnLCAnY2xvbmUnLCAnLS1hYmJyZXYtcmVmJywgJ0hFQUQnLCAnLS1wb3JjZWxhaW4nXSxcbiAgbnBtOiBbJ2luc3RhbGwnLCAncnVuJywgJ2F1ZGl0JywgJ2NpJywgJy0tdmVyc2lvbicsICdidWlsZCddLFxuICBub2RlOiBbJy0tdmVyc2lvbiddLFxuICBucHg6IFsnLS12ZXJzaW9uJ11cbn07XG5cbmV4cG9ydCBjbGFzcyBDb21tYW5kVmFsaWRhdG9yIHtcbiAgc3RhdGljIHNhbml0aXplQ29tbWFuZChjbWQ6IHN0cmluZywgYXJnczogc3RyaW5nW10pOiB2b2lkIHtcbiAgICBpZiAoIUFMTE9XRURfQ09NTUFORFNbY21kXSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBDb21tYW5kIG5vdCBhbGxvd2VkOiAke2NtZH1gKTtcbiAgICB9XG4gICAgXG4gICAgY29uc3QgYWxsb3dlZEFyZ3MgPSBBTExPV0VEX0NPTU1BTkRTW2NtZF07XG4gICAgZm9yIChjb25zdCBhcmcgb2YgYXJncykge1xuICAgICAgLy8gQ2hlY2sgaWYgaXQncyBpbiBhbGxvd2VkIGxpc3Qgb3IgbWF0Y2hlcyBzYWZlIHBhdHRlcm5cbiAgICAgIGlmICghYWxsb3dlZEFyZ3MuaW5jbHVkZXMoYXJnKSAmJiAhdGhpcy5pc1NhZmVBcmd1bWVudChhcmcpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgQXJndW1lbnQgbm90IGFsbG93ZWQ6ICR7YXJnfWApO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGlzU2FmZUFyZ3VtZW50KGFyZzogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgLy8gQWxsb3cgYWxwaGFudW1lcmljLCBkYXNoLCB1bmRlcnNjb3JlLCBkb3QsIGFuZCBmb3J3YXJkIHNsYXNoXG4gICAgcmV0dXJuIFJlZ2V4VmFsaWRhdG9yLnZhbGlkYXRlKGFyZywgL15bYS16QS1aMC05XFwtXy5cXC9dKyQvLCB7IG1heExlbmd0aDogMTAwMCB9KTtcbiAgfVxuXG4gIHN0YXRpYyBhc3luYyBzZWN1cmVFeGVjKGNvbW1hbmQ6IHN0cmluZywgYXJnczogc3RyaW5nW10sIG9wdGlvbnM/OiBTcGF3bk9wdGlvbnMpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHRoaXMuc2FuaXRpemVDb21tYW5kKGNvbW1hbmQsIGFyZ3MpO1xuICAgIFxuICAgIGNvbnN0IHNhZmVPcHRpb25zOiBTcGF3bk9wdGlvbnMgPSB7XG4gICAgICAuLi5vcHRpb25zLFxuICAgICAgc3RkaW86IFsnaWdub3JlJywgJ3BpcGUnLCAncGlwZSddLFxuICAgICAgZW52OiB7XG4gICAgICAgIC4uLnByb2Nlc3MuZW52LFxuICAgICAgICBQQVRIOiAnL3Vzci9iaW46L2JpbjovdXNyL2xvY2FsL2JpbicgLy8gUmVzdHJpY3QgUEFUSFxuICAgICAgfSxcbiAgICAgIGN3ZDogb3B0aW9ucz8uY3dkIHx8IHByb2Nlc3MuY3dkKCksXG4gICAgICB0aW1lb3V0OiBvcHRpb25zPy50aW1lb3V0IHx8IDMwMDAwIC8vIDMwIHNlY29uZCBkZWZhdWx0XG4gICAgfTtcbiAgICBcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgcHJvYyA9IHNwYXduKGNvbW1hbmQsIGFyZ3MsIHNhZmVPcHRpb25zKTtcbiAgICAgIFxuICAgICAgbGV0IHN0ZG91dCA9ICcnO1xuICAgICAgbGV0IHN0ZGVyciA9ICcnO1xuICAgICAgbGV0IHRpbWVvdXRIYW5kbGU6IE5vZGVKUy5UaW1lb3V0IHwgdW5kZWZpbmVkO1xuICAgICAgbGV0IGlzQ29tcGxldGVkID0gZmFsc2U7XG4gICAgICBcbiAgICAgIC8vIEhlbHBlciB0byBzYWZlbHkgcmVzb2x2ZS9yZWplY3Qgb25seSBvbmNlXG4gICAgICBjb25zdCBjb21wbGV0ZSA9IChmbjogKCkgPT4gdm9pZCkgPT4ge1xuICAgICAgICBpZiAoIWlzQ29tcGxldGVkKSB7XG4gICAgICAgICAgaXNDb21wbGV0ZWQgPSB0cnVlO1xuICAgICAgICAgIGlmICh0aW1lb3V0SGFuZGxlKSB7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dEhhbmRsZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGZuKCk7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBcbiAgICAgIC8vIEhhbmRsZSB0aW1lb3V0XG4gICAgICBpZiAob3B0aW9ucz8udGltZW91dCkge1xuICAgICAgICB0aW1lb3V0SGFuZGxlID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgcHJvYy5raWxsKCdTSUdURVJNJyk7XG4gICAgICAgICAgY29tcGxldGUoKCkgPT4gcmVqZWN0KG5ldyBFcnJvcihgQ29tbWFuZCB0aW1lZCBvdXQgYWZ0ZXIgJHtvcHRpb25zLnRpbWVvdXR9bXNgKSkpO1xuICAgICAgICB9LCBvcHRpb25zLnRpbWVvdXQpO1xuICAgICAgICB0aW1lb3V0SGFuZGxlLnVucmVmKCk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIHByb2Muc3Rkb3V0Py5vbignZGF0YScsIChkYXRhKSA9PiBzdGRvdXQgKz0gZGF0YSk7XG4gICAgICBwcm9jLnN0ZGVycj8ub24oJ2RhdGEnLCAoZGF0YSkgPT4gc3RkZXJyICs9IGRhdGEpO1xuICAgICAgXG4gICAgICBwcm9jLm9uKCdleGl0JywgKGNvZGUpID0+IHtcbiAgICAgICAgaWYgKGNvZGUgPT09IDApIHtcbiAgICAgICAgICBjb21wbGV0ZSgoKSA9PiByZXNvbHZlKHN0ZG91dC50cmltKCkpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb21wbGV0ZSgoKSA9PiByZWplY3QobmV3IEVycm9yKGBDb21tYW5kIGZhaWxlZCAoJHtjb2RlfSk6ICR7c3RkZXJyfWApKSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgXG4gICAgICBwcm9jLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xuICAgICAgICBjb21wbGV0ZSgoKSA9PiByZWplY3QoZXJyb3IpKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG59Il19