diff --git a/packages/components/nodes/tools/MCP/Supergateway/SupergatewayMCP.ts b/packages/components/nodes/tools/MCP/Supergateway/SupergatewayMCP.ts index b8e1d6285..d7be92235 100644 --- a/packages/components/nodes/tools/MCP/Supergateway/SupergatewayMCP.ts +++ b/packages/components/nodes/tools/MCP/Supergateway/SupergatewayMCP.ts @@ -3,6 +3,136 @@ import { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams } from import { getNodeModulesPackagePath } from '../../../../src/utils' import { MCPToolkit } from '../core' +// List of dangerous commands that are banned for security reasons +const BANNED_COMMANDS = [ + 'sh', + 'bash', + 'zsh', + 'fish', + 'csh', + 'tcsh', + 'ksh', + 'dash', + '/bin/sh', + '/bin/bash', + '/bin/zsh', + '/bin/fish', + '/bin/csh', + '/bin/tcsh', + '/bin/ksh', + '/bin/dash', + '/usr/bin/sh', + '/usr/bin/bash', + '/usr/bin/zsh', + '/usr/bin/fish', + '/usr/bin/csh', + '/usr/bin/tcsh', + '/usr/bin/ksh', + '/usr/bin/dash', + 'cmd', + 'cmd.exe', + 'powershell', + 'powershell.exe', + 'pwsh', + 'pwsh.exe', + 'perl', + 'ruby', + 'php', + 'eval', + 'exec', + 'system' +] + +// Additional dangerous command patterns to check +const DANGEROUS_PATTERNS = [ + /^\/bin\//, + /^\/usr\/bin\//, + /^\/usr\/local\/bin\//, + /^\/sbin\//, + /^\/usr\/sbin\//, + /\.exe$/i, + /\.bat$/i, + /\.cmd$/i, + /\.ps1$/i, + /\.sh$/i +] + +function validateCommand(command: string): void { + if (!command || typeof command !== 'string') { + return + } + + const normalizedCommand = command.toLowerCase().trim() + + // Check against banned commands list + for (const bannedCmd of BANNED_COMMANDS) { + if ( + normalizedCommand === bannedCmd.toLowerCase() || + normalizedCommand.endsWith(`/${bannedCmd.toLowerCase()}`) || + normalizedCommand.endsWith(`\\${bannedCmd.toLowerCase()}`) + ) { + throw new Error( + `Security Error: Command "${command}" is banned for security reasons. Shell access and executable commands are not allowed.` + ) + } + } + + // Check against dangerous patterns + for (const pattern of DANGEROUS_PATTERNS) { + if (pattern.test(normalizedCommand)) { + throw new Error(`Security Error: Command "${command}" matches a dangerous pattern and is not allowed for security reasons.`) + } + } + + // Additional checks for potential shell injection attempts + if ( + normalizedCommand.includes('&&') || + normalizedCommand.includes('||') || + normalizedCommand.includes(';') || + normalizedCommand.includes('|') || + normalizedCommand.includes('`') || + normalizedCommand.includes('$(') || + normalizedCommand.includes('${') + ) { + throw new Error(`Security Error: Command "${command}" contains potentially dangerous shell operators and is not allowed.`) + } +} + +function validateArguments(args: string[]): void { + if (!args || !Array.isArray(args)) { + return + } + + for (const arg of args) { + if (typeof arg === 'string') { + // Check if any argument looks like it's trying to execute a shell command + const suspiciousArg = arg.toLowerCase().trim() + if ( + suspiciousArg.startsWith('/bin/') || + suspiciousArg.startsWith('/usr/bin/') || + suspiciousArg.includes('sh') || + suspiciousArg.includes('bash') || + suspiciousArg.includes('cmd') || + suspiciousArg.includes('powershell') + ) { + throw new Error(`Security Error: Argument "${arg}" contains potentially dangerous command references and is not allowed.`) + } + + // Check for shell injection attempts in arguments + if ( + suspiciousArg.includes('&&') || + suspiciousArg.includes('||') || + suspiciousArg.includes(';') || + suspiciousArg.includes('`') || + suspiciousArg.includes('$(') || + suspiciousArg.includes('${') + ) { + throw new Error(`Security Error: Argument "${arg}" contains potentially dangerous shell operators and is not allowed.`) + } + } + } +} + class Supergateway_MCP implements INode { label: string name: string @@ -90,21 +220,32 @@ class Supergateway_MCP implements INode { const _args = nodeData.inputs?.arguments as string const packagePath = getNodeModulesPackagePath('supergateway/dist/index.js') + const processedArgs = _args + .trim() + .split(/\s+/) + .map((arg) => { + // Remove surrounding double or single quotes if they exist + if ((arg.startsWith('"') && arg.endsWith('"')) || (arg.startsWith("'") && arg.endsWith("'"))) { + return arg.slice(1, -1) + } + return arg + }) + + // Security validation: Check for dangerous arguments + // TODO: To be removed and only allow Remote MCP for Cloud + if (process.env.CUSTOM_MCP_SECURITY_CHECK === 'true') { + validateArguments(processedArgs) + } + const serverParams = { command: 'node', - args: [ - packagePath, - ..._args - .trim() - .split(/\s+/) - .map((arg) => { - // Remove surrounding double or single quotes if they exist - if ((arg.startsWith('"') && arg.endsWith('"')) || (arg.startsWith("'") && arg.endsWith("'"))) { - return arg.slice(1, -1) - } - return arg - }) - ] + args: [packagePath, ...processedArgs] + } + + // Security validation: Check for dangerous commands + // TODO: To be removed and only allow Remote MCP for Cloud + if (process.env.CUSTOM_MCP_SECURITY_CHECK === 'true') { + validateCommand(serverParams.command) } const toolkit = new MCPToolkit(serverParams, 'stdio')