ssh-bridge-ai
Version:
One Command Magic SSH with Invisible Analytics - Connect to any server instantly with 'sshbridge user@server'. Zero setup, zero friction, pure magic. Industry-standard security with behind-the-scenes business intelligence.
488 lines (406 loc) ⢠14.8 kB
JavaScript
const SandboxManager = require('./sandbox-manager');
const logger = require('../utils/logger');
const chalk = require('chalk');
/**
* Sandbox Executor - Manages the workflow of testing commands in sandbox
* before executing them on real servers, similar to Cursor's approach
*/
class SandboxExecutor {
constructor(options = {}) {
this.sandboxManager = new SandboxManager(options);
this.alwaysSandbox = options.alwaysSandbox !== false; // Default to true
this.dryRun = options.dryRun || false;
this.sandboxResults = new Map();
// Bind event handlers
this.sandboxManager.on('sandbox:execute', this.onSandboxExecute.bind(this));
}
/**
* Execute a command with sandbox safety and user confirmation
*/
async executeWithSandbox(command, options = {}) {
const commandId = this.generateCommandId();
const startTime = Date.now();
logger.info(`š Testing command in sandbox: ${chalk.cyan(command)}`);
try {
// Step 1: Create sandbox environment
const sandbox = await this.sandboxManager.createSandbox();
// Step 2: Test command in sandbox
const sandboxResult = await this.sandboxManager.executeInSandbox(command, sandbox);
// Step 3: Validate command safety
const safetyCheck = this.sandboxManager.isCommandSafe(command, sandboxResult);
// Store sandbox results
this.sandboxResults.set(commandId, {
command,
sandboxResult,
safetyCheck,
timestamp: startTime,
duration: Date.now() - startTime
});
// Step 4: Handle safety validation
if (!safetyCheck.safe) {
logger.error(`šØ Command blocked: ${safetyCheck.reason}`);
if (safetyCheck.sandboxOutput) {
logger.error(`Sandbox output: ${safetyCheck.sandboxOutput}`);
}
await this.sandboxManager.cleanupSandbox(sandbox);
return {
success: false,
blocked: true,
reason: safetyCheck.reason,
sandboxResult,
commandId
};
}
// Step 5: Check if this is a dry run
if (this.dryRun || options.dryRun) {
logger.info(`š Dry run mode - command would execute: ${chalk.green(command)}`);
logger.info(`Sandbox result: ${chalk.gray(sandboxResult.stdout)}`);
await this.sandboxManager.cleanupSandbox(sandbox);
return {
success: true,
dryRun: true,
sandboxResult,
commandId,
message: 'Command validated in sandbox (dry run mode)'
};
}
// Step 6: Show preview and ask for confirmation (Cursor-like workflow)
if (options.target && options.executeOnTarget) {
const preview = this.generateCommandPreview(command, sandboxResult, safetyCheck);
// Always require confirmation unless explicitly auto-approved
if (!options.autoApprove) {
const confirmed = await this.requestUserConfirmation(preview);
if (!confirmed) {
logger.info(`ā Command cancelled by user`);
await this.sandboxManager.cleanupSandbox(sandbox);
return {
success: false,
cancelled: true,
sandboxResult,
commandId,
message: 'Command cancelled by user'
};
}
}
// Step 7: Execute on real target after confirmation
logger.info(`š User confirmed, executing on target: ${chalk.cyan(command)}`);
try {
const realResult = await this.executeOnTarget(command, options.target);
await this.sandboxManager.cleanupSandbox(sandbox);
return {
success: true,
sandboxResult,
realResult,
commandId,
message: 'Command executed successfully on target'
};
} catch (error) {
logger.error(`ā Target execution failed: ${error.message}`);
await this.sandboxManager.cleanupSandbox(sandbox);
return {
success: false,
sandboxResult,
error: error.message,
commandId,
message: 'Command passed sandbox but failed on target'
};
}
}
// Step 8: Clean up and return sandbox results
await this.sandboxManager.cleanupSandbox(sandbox);
logger.info(`ā
Command validated in sandbox: ${chalk.green(command)}`);
return {
success: true,
sandboxResult,
commandId,
message: 'Command validated in sandbox'
};
} catch (error) {
logger.error(`ā Sandbox execution failed: ${error.message}`);
return {
success: false,
error: error.message,
commandId,
message: 'Sandbox execution failed'
};
}
}
/**
* Execute command on real target (SSH server, etc.)
*/
async executeOnTarget(command, target) {
// This would integrate with your existing SSH system
// For now, we'll return a placeholder
logger.info(`šÆ Executing on target: ${target.type || 'unknown'}`);
if (target.ssh) {
// Use existing SSH connection
return await target.ssh.execCommand(command);
} else if (target.execute) {
// Use custom execute function
return await target.execute(command);
} else {
throw new Error('No execution method provided for target');
}
}
/**
* Batch execute multiple commands with sandbox safety
*/
async executeBatch(commands, options = {}) {
const results = [];
const sandbox = await this.sandboxManager.createSandbox();
try {
for (const command of commands) {
logger.info(`š Testing command in sandbox: ${chalk.cyan(command)}`);
const sandboxResult = await this.sandboxManager.executeInSandbox(command, sandbox);
const safetyCheck = this.sandboxManager.isCommandSafe(command, sandboxResult);
results.push({
command,
sandboxResult,
safetyCheck,
timestamp: Date.now()
});
// Stop if any command is unsafe
if (!safetyCheck.safe) {
logger.error(`šØ Batch stopped: ${safetyCheck.reason}`);
break;
}
}
// If all commands are safe and not dry run, execute on target
if (options.target && !this.dryRun && results.every(r => r.safetyCheck.safe)) {
logger.info(`š Executing ${results.length} validated commands on target`);
for (const result of results) {
const realResult = await this.executeOnTarget(result.command, options.target);
result.realResult = realResult;
}
}
} finally {
await this.sandboxManager.cleanupSandbox(sandbox);
}
return results;
}
/**
* Get sandbox statistics
*/
getStats() {
const totalCommands = this.sandboxResults.size;
const blockedCommands = Array.from(this.sandboxResults.values())
.filter(r => r.safetyCheck && !r.safetyCheck.safe).length;
const successfulCommands = totalCommands - blockedCommands;
return {
totalCommands,
blockedCommands,
successfulCommands,
blockRate: totalCommands > 0 ? (blockedCommands / totalCommands * 100).toFixed(2) : 0,
sandboxStatus: this.sandboxManager.getStatus()
};
}
/**
* Get detailed results for a specific command
*/
getCommandResult(commandId) {
return this.sandboxResults.get(commandId);
}
/**
* Clear sandbox results history
*/
clearHistory() {
this.sandboxResults.clear();
logger.info('šļø Sandbox execution history cleared');
}
/**
* Generate unique command ID
*/
generateCommandId() {
return `cmd_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
/**
* Event handler for sandbox execution
*/
onSandboxExecute(data) {
logger.debug(`š Sandbox execution: ${data.command.substring(0, 50)}...`);
}
/**
* Enable/disable sandbox mode
*/
setSandboxMode(enabled) {
this.alwaysSandbox = enabled;
logger.info(`š Sandbox mode ${enabled ? 'enabled' : 'disabled'}`);
}
/**
* Set dry run mode
*/
setDryRunMode(enabled) {
this.dryRun = enabled;
logger.info(`š Dry run mode ${enabled ? 'enabled' : 'disabled'}`);
}
/**
* Generate a comprehensive command preview (Cursor-like)
*/
generateCommandPreview(command, sandboxResult, safetyCheck) {
const preview = {
command: command,
sandboxOutput: sandboxResult.stdout || '',
sandboxError: sandboxResult.stderr || '',
exitCode: sandboxResult.exitCode,
duration: sandboxResult.duration || 0,
safetyLevel: safetyCheck.safe ? 'SAFE' : 'BLOCKED',
warnings: safetyCheck.warnings || [],
impact: this.assessCommandImpact(command, sandboxResult),
summary: this.generateCommandSummary(command, sandboxResult)
};
return preview;
}
/**
* Assess the potential impact of a command
*/
assessCommandImpact(command, sandboxResult) {
const impact = {
risk: 'LOW',
scope: 'LOCAL',
reversibility: 'REVERSIBLE',
systemAffected: false
};
// Analyze command for potential impact
if (command.includes('rm -rf') || command.includes('rm -r')) {
impact.risk = 'HIGH';
impact.scope = 'FILESYSTEM';
impact.reversibility = 'IRREVERSIBLE';
}
if (command.includes('chmod') || command.includes('chown')) {
impact.risk = 'MEDIUM';
impact.scope = 'PERMISSIONS';
}
if (command.includes('systemctl') || command.includes('service')) {
impact.risk = 'MEDIUM';
impact.scope = 'SYSTEM';
impact.systemAffected = true;
}
if (command.includes('reboot') || command.includes('shutdown')) {
impact.risk = 'CRITICAL';
impact.scope = 'SYSTEM';
impact.systemAffected = true;
impact.reversibility = 'IRREVERSIBLE';
}
return impact;
}
/**
* Generate a human-readable command summary
*/
generateCommandSummary(command, sandboxResult) {
const parts = command.split(' ');
const mainCommand = parts[0];
let summary = `Execute ${mainCommand}`;
if (parts.length > 1) {
const args = parts.slice(1).join(' ');
summary += ` with arguments: ${args}`;
}
if (sandboxResult.stdout) {
summary += `\nExpected output: ${sandboxResult.stdout.substring(0, 100)}${sandboxResult.stdout.length > 100 ? '...' : ''}`;
}
return summary;
}
/**
* Request user confirmation for command execution (Cursor-like)
*/
async requestUserConfirmation(preview) {
const inquirer = require('inquirer');
console.log('\n' + '='.repeat(80));
console.log('š COMMAND PREVIEW & CONFIRMATION REQUIRED');
console.log('='.repeat(80));
// Display command details
console.log(`\nš Command: ${chalk.cyan(preview.command)}`);
console.log(`šÆ Target: SSH Server`);
console.log(`ā±ļø Sandbox Duration: ${preview.duration}ms`);
console.log(`š Exit Code: ${preview.exitCode}`);
// Display impact assessment
console.log(`\nā ļø Impact Assessment:`);
console.log(` Risk Level: ${this.getRiskColor(preview.impact.risk)}`);
console.log(` Scope: ${chalk.yellow(preview.impact.scope)}`);
console.log(` Reversibility: ${chalk.yellow(preview.impact.reversibility)}`);
console.log(` System Affected: ${preview.impact.systemAffected ? chalk.red('YES') : chalk.green('NO')}`);
// Display sandbox output preview
if (preview.sandboxOutput) {
console.log(`\nš¤ Expected Output:`);
console.log(chalk.gray(preview.sandboxOutput.substring(0, 200)));
if (preview.sandboxOutput.length > 200) {
console.log(chalk.gray('... (truncated)'));
}
}
if (preview.sandboxError) {
console.log(`\nā ļø Sandbox Warnings:`);
console.log(chalk.yellow(preview.sandboxError.substring(0, 200)));
if (preview.sandboxError.length > 200) {
console.log(chalk.yellow('... (truncated)'));
}
}
// Display command summary
console.log(`\nš Summary: ${preview.summary}`);
console.log('\n' + '='.repeat(80));
// Ask for confirmation
const answers = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: 'š Do you want to execute this command on the real server?',
default: false
},
{
type: 'list',
name: 'action',
message: 'What would you like to do?',
choices: [
{ name: 'ā
Execute command', value: 'execute' },
{ name: 'š Modify command', value: 'modify' },
{ name: 'ā Cancel execution', value: 'cancel' },
{ name: 'š Show full sandbox output', value: 'show_output' }
],
when: (answers) => answers.confirm
}
]);
if (answers.action === 'show_output') {
console.log('\nš Full Sandbox Output:');
console.log('='.repeat(80));
console.log(preview.sandboxOutput || 'No output');
console.log('='.repeat(80));
// Ask again after showing output
const finalAnswer = await inquirer.prompt([
{
type: 'confirm',
name: 'finalConfirm',
message: 'š Now do you want to execute this command?',
default: false
}
]);
return finalAnswer.finalConfirm;
}
if (answers.action === 'modify') {
const modifiedCommand = await inquirer.prompt([
{
type: 'input',
name: 'newCommand',
message: 'Enter the modified command:',
default: preview.command
}
]);
console.log(`\nš Command modified to: ${chalk.cyan(modifiedCommand.newCommand)}`);
console.log('š Re-testing modified command in sandbox...');
// This would trigger a re-test of the modified command
// For now, we'll just return false to indicate the original command shouldn't execute
return false;
}
return answers.confirm;
}
/**
* Get colored risk level display
*/
getRiskColor(risk) {
switch (risk) {
case 'LOW': return chalk.green('LOW');
case 'MEDIUM': return chalk.yellow('MEDIUM');
case 'HIGH': return chalk.red('HIGH');
case 'CRITICAL': return chalk.red.bold('CRITICAL');
default: return chalk.gray(risk);
}
}
}
module.exports = SandboxExecutor;