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.

205 lines (175 loc) 5.47 kB
const path = require('path'); /** * Secure Command Sanitizer - WHITELIST ONLY APPROACH * * Prevents execution of dangerous commands by only allowing safe commands */ class CommandSanitizer { constructor(options = {}) { // WHITELIST ONLY - NO EXCEPTIONS this.whitelistedCommands = [ 'ls', 'cat', 'grep', 'find', 'ps', 'top', 'df', 'du', 'whoami', 'pwd', 'date', 'uptime', 'free', 'netstat', 'head', 'tail', 'wc', 'sort', 'uniq', 'cut', 'awk', 'sed', 'tr', 'tee', 'echo', 'printf', 'test', 'expr', 'bc', 'dc', 'cal', 'clear', 'history', 'alias', 'type', 'which', 'whereis', 'locate', 'updatedb', 'man', 'info', 'help', 'apropos', 'whatis', 'tty', 'stty', 'reset' ]; this.whitelistedArguments = [ '-l', '-a', '-h', '--help', '--version', '-v', '-V', '-r', '-R', '-f', '-F', '-i', '-n', '-c', '-w', '-x', '-d', '-s', '-t', '-u', '-g', '-o', '-p' ]; // Maximum limits for security this.maxCommandLength = 1000; this.maxArgumentLength = 100; this.maxArguments = 10; } /** * Sanitize and validate command * @param {string} command - Command to sanitize * @returns {string} - Sanitized command * @throws {Error} - If command is dangerous or invalid */ sanitizeCommand(command) { // Basic validation if (!command || typeof command !== 'string') { throw new Error('Command must be a non-empty string'); } // Length validation if (command.length > this.maxCommandLength) { throw new Error(`Command too long: ${command.length} > ${this.maxCommandLength}`); } // Null byte check if (command.includes('\x00')) { throw new Error('Command contains null bytes'); } // Control character check if (/[\x01-\x1F\x7F]/.test(command)) { throw new Error('Command contains control characters'); } // Unicode normalization const normalized = command.normalize('NFC'); if (normalized !== command) { throw new Error('Command contains non-normalized Unicode'); } // Parse command into executable and arguments const parts = command.trim().split(/\s+/); if (parts.length > this.maxArguments) { throw new Error(`Too many arguments: ${parts.length} > ${this.maxArguments}`); } const executable = parts[0]; const args = parts.slice(1); // Check if executable is whitelisted if (!this.whitelistedCommands.includes(executable)) { throw new Error(`Command '${executable}' not allowed`); } // Check if arguments are safe for (const arg of args) { if (!this.isArgumentSafe(arg)) { throw new Error(`Argument '${arg}' not allowed`); } } return command; } /** * Check if argument is safe * @param {string} arg - Argument to check * @returns {boolean} - True if safe */ isArgumentSafe(arg) { // Check if argument is whitelisted if (this.whitelistedArguments.includes(arg)) { return true; } // Check if argument contains only safe characters (including quotes and wildcards) if (!/^[a-zA-Z0-9\/\._\-"*?\[\]{}]+$/.test(arg)) { return false; } // Check length if (arg.length > this.maxArgumentLength) { return false; } // Block path traversal if (this.containsPathTraversal(arg)) { return false; } return true; } /** * Check for path traversal attempts * @param {string} input - Input to check * @returns {boolean} - True if traversal detected */ containsPathTraversal(input) { // Block ALL absolute paths if (input.includes('/') || input.includes('\\')) { return true; } // Block directory traversal if (input.includes('..')) { return true; } // Block encoded traversal const encodedPatterns = [ /%2e%2e%2f/i, // URL encoded ../ /%252e%252e%252f/i, // Double URL encoded ../ /%c0%ae%c0%ae%c0%af/i, // UTF-8 encoded ../ /\u2215/i, // Unicode slash /\u2216/i // Unicode backslash ]; for (const pattern of encodedPatterns) { if (pattern.test(input)) { return true; } } return false; } /** * Legacy method for backward compatibility * @deprecated Use sanitizeCommand instead */ isCommandSafe(command) { try { this.sanitizeCommand(command); return true; } catch (error) { return false; } } /** * Get list of allowed commands * @returns {string[]} - List of allowed commands */ getAllowedCommands() { return [...this.whitelistedCommands]; } /** * Get list of allowed arguments * @returns {string[]} - List of allowed arguments */ getAllowedArguments() { return [...this.whitelistedArguments]; } /** * Add custom allowed command (for admin use only) * @param {string} command - Command to add */ addAllowedCommand(command) { if (typeof command === 'string' && command.trim()) { this.whitelistedCommands.push(command.trim()); } } /** * Remove custom allowed command * @param {string} command - Command to remove */ removeAllowedCommand(command) { const index = this.whitelistedCommands.indexOf(command); if (index > -1) { this.whitelistedCommands.splice(index, 1); } } } module.exports = { CommandSanitizer };