diff --git a/packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts b/packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts
index ff39a21fa..a3e9e3a80 100644
--- a/packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts
+++ b/packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts
@@ -72,7 +72,11 @@ class Custom_MCP implements INode {
label: 'How to use',
value: howToUseCode
},
- placeholder: mcpServerConfig
+ placeholder: mcpServerConfig,
+ warning:
+ process.env.CUSTOM_MCP_SECURITY_CHECK === 'true'
+ ? 'In next release, only Remote MCP with url is supported. Read more here'
+ : undefined
},
{
label: 'Available Actions',
diff --git a/packages/components/nodes/tools/MCP/Supergateway/SupergatewayMCP.ts b/packages/components/nodes/tools/MCP/Supergateway/SupergatewayMCP.ts
index 347a35773..610ef5b79 100644
--- a/packages/components/nodes/tools/MCP/Supergateway/SupergatewayMCP.ts
+++ b/packages/components/nodes/tools/MCP/Supergateway/SupergatewayMCP.ts
@@ -1,7 +1,7 @@
import { Tool } from '@langchain/core/tools'
import { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../../src/Interface'
import { getNodeModulesPackagePath } from '../../../../src/utils'
-import { MCPToolkit, validateMCPServerSecurity } from '../core'
+import { MCPToolkit, validateArgsForLocalFileAccess } from '../core'
class Supergateway_MCP implements INode {
label: string
@@ -108,7 +108,7 @@ class Supergateway_MCP implements INode {
if (process.env.CUSTOM_MCP_SECURITY_CHECK === 'true') {
try {
- validateMCPServerSecurity(serverParams)
+ validateArgsForLocalFileAccess(processedArgs)
} catch (error) {
throw new Error(`Security validation failed: ${error.message}`)
}
diff --git a/packages/components/nodes/tools/MCP/core.ts b/packages/components/nodes/tools/MCP/core.ts
index be9e65f56..a34d44efa 100644
--- a/packages/components/nodes/tools/MCP/core.ts
+++ b/packages/components/nodes/tools/MCP/core.ts
@@ -176,375 +176,18 @@ function createSchemaModel(
/**
* TODO: To be removed and only allow Remote MCP for Cloud
- * Validates MCP server configuration to prevent execution of dangerous commands
+ * Validates MCP server configuration to only allow whitelisted commands
*/
-export function validateMCPServerSecurity(serverParams: any): void {
- // Comprehensive list of dangerous commands that could compromise system security
- const dangerousCommands = [
- // Shell interpreters and command processors
- 'sh',
- 'bash',
- 'zsh',
- 'fish',
- 'csh',
- 'tcsh',
- 'ksh',
- 'ash',
- 'dash',
- 'cmd',
- 'command',
- 'powershell',
- 'pwsh',
- 'cmd.exe',
- 'powershell.exe',
- 'wsl',
- 'wsl.exe',
- 'ubuntu',
- 'debian',
-
- // File operations that could read/write sensitive files
- 'cat',
- 'more',
- 'less',
- 'head',
- 'tail',
- 'tee',
- 'cp',
- 'mv',
- 'rm',
- 'del',
- 'copy',
- 'move',
- 'type',
- 'ren',
- 'rename',
- 'ln',
- 'link',
- 'unlink',
- 'touch',
- 'mkdir',
- 'rmdir',
- 'rd',
- 'md',
- 'makedir',
-
- // Directory operations and file system navigation
- 'ls',
- 'dir',
- 'pwd',
- 'cd',
- 'find',
- 'locate',
- 'tree',
- 'du',
- 'df',
- 'pushd',
- 'popd',
- 'dirs',
- 'whereis',
- 'which',
- 'where',
- 'stat',
-
- // Network operations that could exfiltrate data or download malicious content
- 'curl',
- 'wget',
- 'nc',
- 'netcat',
- 'ping',
- 'nslookup',
- 'dig',
- 'host',
- 'telnet',
- 'ssh',
- 'scp',
- 'rsync',
- 'ftp',
- 'sftp',
- 'icat',
- 'socat',
-
- // System operations and process management
- 'ps',
- 'top',
- 'htop',
- 'kill',
- 'killall',
- 'pkill',
- 'pgrep',
- 'jobs',
- 'systemctl',
- 'service',
- 'chkconfig',
- 'update-rc.d',
- 'systemd',
- 'crontab',
- 'at',
- 'batch',
- 'nohup',
- 'screen',
- 'tmux',
-
- // Archive operations that could be used for data exfiltration
- 'tar',
- 'zip',
- 'unzip',
- 'gzip',
- 'gunzip',
- 'bzip2',
- 'bunzip2',
- 'xz',
- 'unxz',
- '7z',
- 'rar',
- 'unrar',
- 'compress',
- 'uncompress',
- 'cpio',
- 'ar',
-
- // Text processing tools that could read sensitive files
- 'grep',
- 'awk',
- 'sed',
- 'sort',
- 'uniq',
- 'cut',
- 'tr',
- 'wc',
- 'diff',
- 'patch',
- 'strings',
- 'hexdump',
- 'od',
- 'xxd',
- 'base64',
- 'base32',
-
- // File editors that could modify system files
- 'vi',
- 'vim',
- 'nano',
- 'emacs',
- 'gedit',
- 'notepad',
- 'notepad.exe',
- 'pico',
- 'joe',
- 'micro',
- 'code',
- 'subl',
- 'atom',
-
- // Process execution and evaluation commands
- 'exec',
- 'eval',
- 'system',
- 'spawn',
- 'fork',
- 'clone',
- 'source',
- 'sudo',
- 'su',
- 'runuser',
- 'doas',
- 'pfexec',
-
- // Package managers that could install malicious software
- 'npm',
- 'yarn',
- 'pnpm',
- 'pip',
- 'pip3',
- 'apt',
- 'apt-get',
- 'yum',
- 'dnf',
- 'pacman',
- 'brew',
- 'choco',
- 'winget',
- 'scoop',
- 'snap',
- 'flatpak',
-
- // Compilers and interpreters that could execute arbitrary code
- 'python',
- 'python3',
- 'nodejs',
- 'java',
- 'javac',
- 'gcc',
- 'g++',
- 'clang',
- 'clang++',
- 'ruby',
- 'perl',
- 'php',
- 'go',
- 'rust',
- 'cargo',
- 'dotnet',
- 'mono',
- 'scala',
- 'kotlin',
- 'swift',
-
- // Database clients that could access sensitive data
- 'mysql',
- 'psql',
- 'mongo',
- 'mongosh',
- 'redis-cli',
- 'sqlite3',
- 'sqlcmd',
- 'isql',
- 'osql',
- 'bcp',
-
- // Development and deployment tools
- 'git',
- 'docker',
- 'podman',
- 'kubectl',
- 'helm',
- 'terraform',
- 'ansible',
- 'vagrant',
- 'chef',
- 'puppet',
- 'saltstack',
- 'make',
- 'cmake',
-
- // System information and configuration tools
- 'uname',
- 'whoami',
- 'id',
- 'groups',
- 'env',
- 'set',
- 'printenv',
- 'export',
- 'mount',
- 'umount',
- 'lsblk',
- 'fdisk',
- 'parted',
- 'lsmod',
- 'modprobe',
-
- // File permissions and ownership commands
- 'chmod',
- 'chown',
- 'chgrp',
- 'umask',
- 'setfacl',
- 'getfacl',
- 'lsattr',
- 'chattr',
-
- // Network configuration and monitoring
- 'ifconfig',
- 'ip',
- 'route',
- 'netstat',
- 'ss',
- 'lsof',
- 'tcpdump',
- 'wireshark',
- 'iptables',
- 'ufw',
- 'firewall-cmd',
-
- // Boot and system control
- 'init',
- 'telinit',
- 'shutdown',
- 'reboot',
- 'halt',
- 'poweroff',
-
- // Hardware and kernel interaction
- 'dmesg',
- 'lspci',
- 'lsusb',
- 'dmidecode',
- 'hdparm',
- 'smartctl'
- ]
-
- /**
- * Checks a string for dangerous commands and patterns
- * @param str - The string to check
- * @param context - Context information for better error messages
- */
- function checkString(str: string, context: string = ''): void {
- if (typeof str !== 'string') return
-
- const lowerStr = str.toLowerCase().trim()
- const contextPrefix = context ? `${context}: ` : ''
-
- for (const cmd of dangerousCommands) {
- const cmdLower = cmd.toLowerCase()
- // Escape special regex characters in command name
- const escapedCmd = cmdLower.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
-
- if (
- lowerStr === cmdLower || // 1. Exact match: "cat" === "cat"
- lowerStr.startsWith(cmdLower + ' ') || // 2. Command with space args: "cat /etc/passwd"
- lowerStr.startsWith(cmdLower + '\t') || // 3. Command with tab args: "cat\t/etc/passwd"
- lowerStr.endsWith('/' + cmdLower) || // 4. Unix absolute path: "/bin/sh"
- lowerStr.includes('\\' + cmdLower + '.exe') || // 5. Windows executable: "C:\\Windows\\cmd.exe"
- lowerStr.includes('/' + cmdLower + ' ') || // 6. Unix path with args: "/bin/sh -c"
- lowerStr.includes('\\' + cmdLower + ' ') || // 7. Windows path with args: "C:\\bin\\sh.exe -c"
- new RegExp(`\\b${escapedCmd}\\b`).test(lowerStr)
- ) {
- // 8. Word boundary match: catches embedded commands
- throw new Error(`${contextPrefix}Dangerous command detected: "${cmd}" in "${str}"`)
- }
- }
-
- // 2. Check for null bytes (binary content or encoding attacks)
- if (str.includes('\0')) {
- throw new Error(`${contextPrefix}Null byte detected in string: "${str}"`)
- }
- }
-
- /**
- * Recursively validates an object for dangerous content
- * This function traverses the entire object tree to ensure no malicious content is hidden
- * @param obj - The object to validate (can be string, array, object, or primitive)
- * @param path - The current path in the object (for error reporting and debugging)
- */
- function validateObject(obj: any, path: string = ''): void {
- // Skip null/undefined values
- if (obj === null || obj === undefined) return
-
- if (typeof obj === 'string') {
- // Validate string content for dangerous commands and patterns
- checkString(obj, path)
- } else if (Array.isArray(obj)) {
- // Recursively validate each array element
- obj.forEach((item, index) => {
- validateObject(item, `${path}[${index}]`)
- })
- } else if (typeof obj === 'object') {
- // Recursively validate each object property
- for (const [key, value] of Object.entries(obj)) {
- const currentPath = path ? `${path}.${key}` : key
- // Validate only the property value
- validateObject(value, currentPath)
- }
- }
- }
-
- validateObject(serverParams, 'serverParams')
+export function validateMCPServerSecurity(serverParams: Record): void {
+ // Whitelist of allowed commands - only these are permitted
+ const allowedCommands = ['npx', 'node']
if (serverParams.command) {
const cmd = serverParams.command.toLowerCase()
- if (cmd.includes('sh') || cmd.includes('cmd') || cmd.includes('powershell') || cmd.includes('eval')) {
- throw new Error(`Command field contains dangerous interpreter: "${serverParams.command}"`)
+ const baseCmd = cmd
+
+ if (!allowedCommands.includes(baseCmd)) {
+ throw new Error(`Only allowed: ${allowedCommands.join(', ')}`)
}
}
@@ -555,18 +198,50 @@ export function validateMCPServerSecurity(serverParams: any): void {
}
}
}
+}
- if (serverParams.cwd) {
- checkString(serverParams.cwd, 'cwd')
- const cwd = serverParams.cwd.toLowerCase()
- if (
- cwd.startsWith('/bin') ||
- cwd.startsWith('/sbin') ||
- cwd.startsWith('/etc') ||
- cwd.includes('system32') ||
- cwd.includes('program files')
- ) {
- throw new Error(`Working directory points to sensitive system location: "${serverParams.cwd}"`)
+export const validateArgsForLocalFileAccess = (args: string[]): void => {
+ const dangerousPatterns = [
+ // Absolute paths
+ /^\/[^/]/, // Unix absolute paths starting with /
+ /^[a-zA-Z]:\\/, // Windows absolute paths like C:\
+
+ // Relative paths that could escape current directory
+ /\.\.\//, // Parent directory traversal with ../
+ /\.\.\\/, // Parent directory traversal with ..\
+ /^\.\./, // Starting with ..
+
+ // Local file access patterns
+ /^\.\//, // Current directory with ./
+ /^~\//, // Home directory with ~/
+ /^file:\/\//, // File protocol
+
+ // Common file extensions that shouldn't be accessed
+ /\.(exe|bat|cmd|sh|ps1|vbs|scr|com|pif|dll|sys)$/i,
+
+ // File flags and options that could access local files
+ /^--?(?:file|input|output|config|load|save|import|export|read|write)=/i,
+ /^--?(?:file|input|output|config|load|save|import|export|read|write)$/i
+ ]
+
+ for (const arg of args) {
+ if (typeof arg !== 'string') continue
+
+ // Check for dangerous patterns
+ for (const pattern of dangerousPatterns) {
+ if (pattern.test(arg)) {
+ throw new Error(`Argument contains potential local file access: "${arg}"`)
+ }
+ }
+
+ // Check for null bytes
+ if (arg.includes('\0')) {
+ throw new Error(`Argument contains null byte: "${arg}"`)
+ }
+
+ // Check for very long paths that might be used for buffer overflow attacks
+ if (arg.length > 1000) {
+ throw new Error(`Argument is suspiciously long (${arg.length} characters): "${arg.substring(0, 100)}..."`)
}
}
}
diff --git a/packages/ui/src/views/canvas/NodeInputHandler.jsx b/packages/ui/src/views/canvas/NodeInputHandler.jsx
index c7ed270ec..04175e087 100644
--- a/packages/ui/src/views/canvas/NodeInputHandler.jsx
+++ b/packages/ui/src/views/canvas/NodeInputHandler.jsx
@@ -4,6 +4,7 @@ import { useEffect, useRef, useState, useContext } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { cloneDeep } from 'lodash'
import showdown from 'showdown'
+import parser from 'html-react-parser'
// material-ui
import { useTheme, styled } from '@mui/material/styles'
@@ -966,7 +967,7 @@ const NodeInputHandler = ({
}}
>
- {inputParam.warning}
+ {parser(inputParam.warning)}
)}
{inputParam.type === 'credential' && (