mcp-repl
Version:
MCP REPL with code execution, semantic code search, and comprehensive ast-grep integration
133 lines (113 loc) • 4.67 kB
JavaScript
// Bash executor for MCP REPL project
// Provides secure bash command execution with batching support and comprehensive error handling
import { validateWorkingDirectory } from './validation-utils.js';
import { createErrorResponse, createSuccessResponse, validateRequiredParams } from './common-errors.js';
import { executeBashCommands } from './process-executor.js';
/**
* Execute a single bash command or batch of commands
* @param {string|Array} commands - Single command string or array of commands for batching
* @param {number} timeout - Command timeout in milliseconds
* @param {string} workingDirectory - Required working directory for command execution
* @param {string} defaultWorkingDir - Server's default working directory
* @returns {Promise<Object>} Execution result with stdout, stderr, and metadata
*/
export async function executeBashCommand(commands, timeout = 120000, workingDirectory, defaultWorkingDir) {
const startTime = Date.now();
// Validate required parameters
const paramError = validateRequiredParams({ workingDirectory }, ['workingDirectory'], startTime);
if (paramError) return paramError;
// Validate and resolve working directory
const dirValidation = validateWorkingDirectory(workingDirectory, defaultWorkingDir);
if (!dirValidation.valid) {
return createErrorResponse(dirValidation.error, startTime);
}
const effectiveWorkingDir = dirValidation.effectiveDir;
// Handle both single commands and batched commands
const commandArray = Array.isArray(commands) ? commands : [commands];
// Validate commands
const validationResult = validateBashCommands(commandArray);
if (!validationResult.valid) {
return createErrorResponse(validationResult.error, startTime);
}
// Use consolidated process executor
const result = await executeBashCommands(commands, {
workingDirectory: dirValidation.effectiveDir,
timeout
});
return result;
}
/**
* Validate bash commands for security and structure
* @param {Array} commands - Array of command strings
* @returns {Object} Validation result
*/
export function validateBashCommands(commands) {
if (!Array.isArray(commands) || commands.length === 0) {
return {
valid: false,
error: "Commands must be a non-empty array"
};
}
// Check each command
for (let i = 0; i < commands.length; i++) {
const command = commands[i];
if (typeof command !== 'string') {
return {
valid: false,
error: `Command ${i}: Must be a string`
};
}
if (command.trim().length === 0) {
return {
valid: false,
error: `Command ${i}: Cannot be empty or whitespace only`
};
}
// Basic security validation - prevent obviously dangerous commands
const dangerous = ['rm -rf /', 'sudo rm', 'format', 'mkfs', ':(){ :|:& };:', 'dd if=/dev/zero'];
const lowerCommand = command.toLowerCase();
for (const pattern of dangerous) {
if (lowerCommand.includes(pattern)) {
return {
valid: false,
error: `Command ${i}: Contains potentially dangerous pattern '${pattern}'`
};
}
}
}
return { valid: true };
}
/**
* Create a bash script from command array with error handling and progress tracking
* @param {Array} commands - Array of commands to execute
* @returns {string} Bash script string
*/
function createBashScript(commands) {
const scriptLines = [
'#!/bin/bash',
'set -e # Exit on error',
'set -o pipefail # Exit on pipe failure',
'',
'# Bash command execution with progress tracking',
'echo "=== BASH EXECUTION START ==="',
`echo "Commands to execute: ${commands.length}"`,
'echo "Working directory: $(pwd)"',
'echo "Timestamp: $(date)"',
'echo ""'
];
// Add each command with progress tracking
commands.forEach((command, index) => {
scriptLines.push(`echo "--- Command ${index + 1}/${commands.length} ---"`);
scriptLines.push(`echo "$ ${command}"`);
scriptLines.push(command);
scriptLines.push('CMD_EXIT_CODE=$?');
scriptLines.push('if [ $CMD_EXIT_CODE -ne 0 ]; then');
scriptLines.push(` echo "Command ${index + 1} failed with exit code $CMD_EXIT_CODE" >&2`);
scriptLines.push(' exit $CMD_EXIT_CODE');
scriptLines.push('fi');
scriptLines.push('echo ""');
});
scriptLines.push('echo "=== BASH EXECUTION COMPLETE ==="');
scriptLines.push('echo "All commands completed successfully"');
return scriptLines.join('\n');
}