UNPKG

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.

521 lines (453 loc) 17 kB
const logger = require('./utils/logger'); const { EnhancedSSHClient } = require('./ssh-enhanced'); /** * SSH Connection Validation and Testing Framework * Implements comprehensive testing strategies from the ssh_time_fix.md document */ class SSHConnectionTester { constructor(serverConfig) { this.serverConfig = serverConfig; this.testResults = {}; this.connection = null; } /** * Run comprehensive server capability tests */ async validateServerCapabilities() { logger.info('Starting comprehensive server capability validation'); const tests = [ { name: 'SSH Service', test: this.testSSHService.bind(this) }, { name: 'Authentication', test: this.testAuthentication.bind(this) }, { name: 'Shell Access', test: this.testShellAccess.bind(this) }, { name: 'Home Directory', test: this.testHomeDirectory.bind(this) }, { name: 'Permissions', test: this.testPermissions.bind(this) }, { name: 'Environment', test: this.testEnvironment.bind(this) }, { name: 'Network Stability', test: this.testNetworkStability.bind(this) }, { name: 'Command Execution', test: this.testCommandExecution.bind(this) } ]; const results = {}; for (const test of tests) { try { logger.info(`Running test: ${test.name}`); results[test.name] = await test.test(); logger.info(`Test ${test.name} completed: ${results[test.name].success ? 'PASS' : 'FAIL'}`); } catch (error) { logger.error(`Test ${test.name} failed with error`, { error: error.message }); results[test.name] = { success: false, error: error.message }; } } this.testResults = results; return results; } /** * Test SSH service availability */ async testSSHService() { try { // Basic connection test without authentication const testConnection = `${this.serverConfig.username}@${this.serverConfig.hostname}`; const ssh = new EnhancedSSHClient(testConnection, { port: this.serverConfig.port || 22, readyTimeout: 10000, // 10 seconds for service test keepalive: false }); // Try to connect (this will fail at authentication, but proves service is running) await ssh.connect(); return { success: true, message: 'SSH service is running and accessible' }; } catch (error) { if (error.message.includes('Authentication failed') || error.message.includes('Permission denied')) { return { success: true, message: 'SSH service is running (authentication failed as expected)' }; } else if (error.message.includes('Connection refused') || error.message.includes('ECONNREFUSED')) { return { success: false, error: 'SSH service is not running or not accessible' }; } else if (error.message.includes('timeout') || error.message.includes('ETIMEDOUT')) { return { success: false, error: 'SSH service connection timeout' }; } else { return { success: false, error: `SSH service test failed: ${error.message}` }; } } } /** * Test authentication methods */ async testAuthentication() { try { const testConnection = `${this.serverConfig.username}@${this.serverConfig.hostname}`; const ssh = new EnhancedSSHClient(testConnection, { port: this.serverConfig.port || 22, password: this.serverConfig.password, key: this.serverConfig.privateKey, readyTimeout: 30000 }); await ssh.connect(); return { success: true, message: 'Authentication successful', method: this.serverConfig.password ? 'password' : 'private_key' }; } catch (error) { return { success: false, error: `Authentication failed: ${error.message}`, method: this.serverConfig.password ? 'password' : 'private_key' }; } } /** * Test shell access and functionality */ async testShellAccess() { try { const testConnection = `${this.serverConfig.username}@${this.serverConfig.hostname}`; const ssh = new EnhancedSSHClient(testConnection, { port: this.serverConfig.port || 22, password: this.serverConfig.password, key: this.serverConfig.privateKey, progressiveFallback: true }); await ssh.connect(); // Test basic shell commands const testCommands = [ 'echo "shell_test"', 'whoami', 'pwd', 'uname -a' ]; const results = {}; for (const command of testCommands) { try { const result = await ssh.exec(command); results[command] = { success: true, output: result.stdout.trim() }; } catch (error) { results[command] = { success: false, error: error.message }; } } const successfulCommands = Object.values(results).filter(r => r.success).length; const totalCommands = testCommands.length; return { success: successfulCommands > 0, message: `Shell access test: ${successfulCommands}/${totalCommands} commands successful`, details: results, shellType: await this.detectShellType(ssh) }; } catch (error) { return { success: false, error: `Shell access test failed: ${error.message}` }; } } /** * Test home directory accessibility */ async testHomeDirectory() { try { const testConnection = `${this.serverConfig.username}@${this.serverConfig.hostname}`; const ssh = new EnhancedSSHClient(testConnection, { port: this.serverConfig.port || 22, password: this.serverConfig.password, key: this.serverConfig.privateKey }); await ssh.connect(); // Test home directory access const homeTest = await ssh.exec('echo $HOME && ls -la $HOME 2>/dev/null || echo "HOME_DIR_ACCESS_DENIED"'); if (homeTest.stdout.includes('HOME_DIR_ACCESS_DENIED')) { return { success: false, error: 'Home directory exists but is not accessible', homePath: homeTest.stdout.split('\n')[0], accessible: false }; } const homePath = homeTest.stdout.split('\n')[0]; const homeContents = homeTest.stdout.split('\n').slice(1).join('\n'); return { success: true, message: 'Home directory is accessible', homePath: homePath, accessible: true, contents: homeContents, hasFiles: homeContents.length > 0 }; } catch (error) { return { success: false, error: `Home directory test failed: ${error.message}` }; } } /** * Test user permissions and access rights */ async testPermissions() { try { const testConnection = `${this.serverConfig.username}@${this.serverConfig.hostname}`; const ssh = new EnhancedSSHClient(testConnection, { port: this.serverConfig.port || 22, password: this.serverConfig.password, key: this.serverConfig.privateKey }); await ssh.connect(); // Test various permission levels const permissionTests = [ { command: 'id', description: 'User identity' }, { command: 'groups', description: 'User groups' }, { command: 'ls -la /tmp', description: 'Temp directory access' }, { command: 'ls -la /var/tmp', description: 'Var temp access' }, { command: 'ls -la /home', description: 'Home directory listing' }, { command: 'ls -la /usr/local', description: 'Local bin access' } ]; const results = {}; for (const test of permissionTests) { try { const result = await ssh.exec(test.command); results[test.description] = { success: true, output: result.stdout.trim() }; } catch (error) { results[test.description] = { success: false, error: error.message }; } } const successfulTests = Object.values(results).filter(r => r.success).length; const totalTests = permissionTests.length; return { success: successfulTests > 0, message: `Permission test: ${successfulTests}/${totalTests} tests successful`, details: results, userInfo: results['User identity']?.output || 'Unknown' }; } catch (error) { return { success: false, error: `Permission test failed: ${error.message}` }; } } /** * Test environment variables and shell environment */ async testEnvironment() { try { const testConnection = `${this.serverConfig.username}@${this.serverConfig.hostname}`; const ssh = new EnhancedSSHClient(testConnection, { port: this.serverConfig.port || 22, password: this.serverConfig.password, key: this.serverConfig.privateKey }); await ssh.connect(); // Test environment variables const envTests = [ 'echo $SHELL', 'echo $PATH', 'echo $HOME', 'echo $USER', 'echo $TERM', 'echo $LANG' ]; const results = {}; for (const test of envTests) { try { const result = await ssh.exec(test); const varName = test.split('$')[1]; results[varName] = { success: true, value: result.stdout.trim() }; } catch (error) { const varName = test.split('$')[1]; results[varName] = { success: false, error: error.message }; } } return { success: true, message: 'Environment test completed', details: results, shell: results.SHELL?.value || 'Unknown', path: results.PATH?.value || 'Unknown', home: results.HOME?.value || 'Unknown' }; } catch (error) { return { success: false, error: `Environment test failed: ${error.message}` }; } } /** * Test network stability and connection quality */ async testNetworkStability() { try { const testConnection = `${this.serverConfig.username}@${this.serverConfig.hostname}`; const ssh = new EnhancedSSHClient(testConnection, { port: this.serverConfig.port || 22, password: this.serverConfig.password, key: this.serverConfig.privateKey, keepalive: true, keepaliveInterval: 10 }); await ssh.connect(); // Test connection stability over time const stabilityTest = await ssh.testConnectionStability(30000); // 30 seconds return { success: stabilityTest.success, message: stabilityTest.success ? 'Network connection is stable' : 'Network connection is unstable', duration: stabilityTest.duration, tests: stabilityTest.tests, averageResponseTime: stabilityTest.averageResponseTime, failureReason: stabilityTest.failureReason }; } catch (error) { return { success: false, error: `Network stability test failed: ${error.message}` }; } } /** * Test command execution capabilities */ async testCommandExecution() { try { const testConnection = `${this.serverConfig.username}@${this.serverConfig.hostname}`; const ssh = new EnhancedSSHClient(testConnection, { port: this.serverConfig.port || 22, password: this.serverConfig.password, key: this.serverConfig.privateKey }); await ssh.connect(); // Test various command types const commandTests = [ { command: 'echo "test"', type: 'Basic output' }, { command: 'date', type: 'System command' }, { command: 'ls -la', type: 'File listing' }, { command: 'ps aux | head -5', type: 'Process listing' }, { command: 'df -h', type: 'Disk usage' }, { command: 'free -h', type: 'Memory usage' } ]; const results = {}; for (const test of commandTests) { try { const result = await ssh.exec(test.command); results[test.type] = { success: true, output: result.stdout.trim() }; } catch (error) { results[test.type] = { success: false, error: error.message }; } } const successfulCommands = Object.values(results).filter(r => r.success).length; const totalCommands = commandTests.length; return { success: successfulCommands > 0, message: `Command execution test: ${successfulCommands}/${totalCommands} commands successful`, details: results, executionRate: (successfulCommands / totalCommands) * 100 }; } catch (error) { return { success: false, error: `Command execution test failed: ${error.message}` }; } } /** * Detect the type of shell available */ async detectShellType(ssh) { try { const shellResult = await ssh.exec('echo $SHELL'); const shellPath = shellResult.stdout.trim(); if (shellPath.includes('bash')) return 'bash'; if (shellPath.includes('zsh')) return 'zsh'; if (shellPath.includes('ksh')) return 'ksh'; if (shellPath.includes('dash')) return 'dash'; if (shellPath.includes('sh')) return 'sh'; return 'unknown'; } catch (error) { return 'unknown'; } } /** * Generate comprehensive test report */ generateTestReport() { const totalTests = Object.keys(this.testResults).length; const passedTests = Object.values(this.testResults).filter(r => r.success).length; const failedTests = totalTests - passedTests; const successRate = (passedTests / totalTests) * 100; const report = { summary: { totalTests, passedTests, failedTests, successRate: `${successRate.toFixed(1)}%` }, results: this.testResults, recommendations: this.generateRecommendations(), timestamp: new Date().toISOString() }; return report; } /** * Generate recommendations based on test results */ generateRecommendations() { const recommendations = []; if (!this.testResults['SSH Service']?.success) { recommendations.push('SSH service is not accessible. Check if SSH daemon is running and firewall rules.'); } if (!this.testResults['Authentication']?.success) { recommendations.push('Authentication failed. Verify username, password, or SSH key.'); } if (!this.testResults['Shell Access']?.success) { recommendations.push('Shell access is limited. Try alternative shell strategies.'); } if (!this.testResults['Home Directory']?.success) { recommendations.push('Home directory issues detected. Use temporary directory workaround.'); } if (!this.testResults['Permissions']?.success) { recommendations.push('Limited permissions detected. Check user account restrictions.'); } if (!this.testResults['Network Stability']?.success) { recommendations.push('Network connection is unstable. Enable keepalive and increase timeouts.'); } if (recommendations.length === 0) { recommendations.push('All tests passed. Server appears to be fully compatible.'); } return recommendations; } /** * Run a quick connectivity test */ async quickConnectivityTest() { try { const testConnection = `${this.serverConfig.username}@${this.serverConfig.hostname}`; const ssh = new EnhancedSSHClient(testConnection, { port: this.serverConfig.port || 22, password: this.serverConfig.password, key: this.serverConfig.privateKey, readyTimeout: 15000, progressiveFallback: false }); await ssh.connect(); const result = await ssh.exec('echo "connectivity_test"'); await ssh.dispose(); return { success: true, message: 'Quick connectivity test passed', response: result.stdout.trim() }; } catch (error) { return { success: false, error: `Quick connectivity test failed: ${error.message}` }; } } /** * Test specific command execution */ async testSpecificCommand(command, options = {}) { try { const testConnection = `${this.serverConfig.username}@${this.serverConfig.hostname}`; const ssh = new EnhancedSSHClient(testConnection, { port: this.serverConfig.port || 22, password: this.serverConfig.password, key: this.serverConfig.privateKey, ...options }); await ssh.connect(); const result = await ssh.exec(command); await ssh.dispose(); return { success: true, command: command, stdout: result.stdout, stderr: result.stderr, exitCode: result.exitCode }; } catch (error) { return { success: false, command: command, error: error.message }; } } } module.exports = { SSHConnectionTester };