Additional Improvements to MCP Server Config security (#5943)

This commit is contained in:
christopherholland-workday
2026-03-23 10:36:42 -07:00
committed by GitHub
parent d2acf4ced8
commit 7176dcb409
2 changed files with 127 additions and 4 deletions
@@ -33,6 +33,22 @@ describe('MCP Security Validations', () => {
}).toThrow("Argument '-y' is not allowed for command 'npx'")
})
it('should block --yes flag', () => {
expect(() => {
validateCommandFlags('npx', ['--yes', 'https://test-malicious-download.com'])
}).toThrow("Argument '--yes' is not allowed for command 'npx'")
})
it('should block --node-options flag', () => {
expect(() => {
validateCommandFlags('npx', ['--node-options', '--eval malicious'])
}).toThrow("Argument '--node-options' is not allowed for command 'npx'")
expect(() => {
validateCommandFlags('npx', ['--node-options=--eval malicious'])
}).toThrow("contains flag '--node-options'")
})
it('should block case variations', () => {
expect(() => {
validateCommandFlags('npx', ['-C', 'command'])
@@ -83,6 +99,42 @@ describe('MCP Security Validations', () => {
}).toThrow("Argument '--inspect-brk' is not allowed for command 'node'")
})
it('should block -r/--require flags', () => {
expect(() => {
validateCommandFlags('node', ['-r', 'malicious-module'])
}).toThrow("Argument '-r' is not allowed for command 'node'")
expect(() => {
validateCommandFlags('node', ['--require', 'malicious-module'])
}).toThrow("Argument '--require' is not allowed for command 'node'")
})
it('should block --loader/--experimental-loader flags', () => {
expect(() => {
validateCommandFlags('node', ['--loader', './malicious-loader.mjs'])
}).toThrow("Argument '--loader' is not allowed for command 'node'")
expect(() => {
validateCommandFlags('node', ['--experimental-loader', './malicious-loader.mjs'])
}).toThrow("Argument '--experimental-loader' is not allowed for command 'node'")
})
it('should block --import flag', () => {
expect(() => {
validateCommandFlags('node', ['--import', './malicious.mjs'])
}).toThrow("Argument '--import' is not allowed for command 'node'")
})
it('should block --env-file flag', () => {
expect(() => {
validateCommandFlags('node', ['--env-file', '.env'])
}).toThrow("Argument '--env-file' is not allowed for command 'node'")
expect(() => {
validateCommandFlags('node', ['--env-file=.env'])
}).toThrow("contains flag '--env-file'")
})
it('should allow legitimate node usage', () => {
expect(() => {
validateCommandFlags('node', ['server.js'])
@@ -189,6 +241,56 @@ describe('MCP Security Validations', () => {
}).toThrow("Argument '--ipc' is not allowed for command 'docker'")
})
it('should block --mount flag', () => {
expect(() => {
validateCommandFlags('docker', ['--mount', 'type=bind,source=/,target=/host'])
}).toThrow("Argument '--mount' is not allowed for command 'docker'")
expect(() => {
validateCommandFlags('docker', ['--mount=type=bind,source=/,target=/host'])
}).toThrow("contains flag '--mount'")
})
it('should block --device flag', () => {
expect(() => {
validateCommandFlags('docker', ['--device', '/dev/sda'])
}).toThrow("Argument '--device' is not allowed for command 'docker'")
})
it('should block --entrypoint flag', () => {
expect(() => {
validateCommandFlags('docker', ['--entrypoint', '/bin/sh'])
}).toThrow("Argument '--entrypoint' is not allowed for command 'docker'")
})
it('should block compose subcommand', () => {
expect(() => {
validateCommandFlags('docker', ['compose', 'up'])
}).toThrow("Argument 'compose' is not allowed for command 'docker'")
})
it('should block --volumes-from flag', () => {
expect(() => {
validateCommandFlags('docker', ['--volumes-from', 'other-container'])
}).toThrow("Argument '--volumes-from' is not allowed for command 'docker'")
})
it('should block --env-file flag', () => {
expect(() => {
validateCommandFlags('docker', ['--env-file', '/etc/secrets'])
}).toThrow("Argument '--env-file' is not allowed for command 'docker'")
expect(() => {
validateCommandFlags('docker', ['--env-file=/etc/secrets'])
}).toThrow("contains flag '--env-file'")
})
it('should block build subcommand', () => {
expect(() => {
validateCommandFlags('docker', ['build', 'https://evil.com/'])
}).toThrow("Argument 'build' is not allowed for command 'docker'")
})
it('should allow safe docker usage', () => {
expect(() => {
validateCommandFlags('docker', ['ps'])
@@ -271,6 +373,12 @@ describe('MCP Security Validations', () => {
}).toThrow('Argument contains potential local file access')
})
it('should block double-slash absolute paths', () => {
expect(() => {
validateArgsForLocalFileAccess(['//etc/passwd'])
}).toThrow('Argument contains potential local file access')
})
it('should block path traversal', () => {
expect(() => {
validateArgsForLocalFileAccess(['../../../etc/passwd'])
+19 -4
View File
@@ -190,7 +190,7 @@ function createSchemaModel(
export const validateArgsForLocalFileAccess = (args: string[]): void => {
const dangerousPatterns = [
// Absolute paths
/^\/[^/]/, // Unix absolute paths starting with /
/^\//, // Unix absolute paths starting with /
/^[a-zA-Z]:\\/, // Windows absolute paths like C:\
// Relative paths that could escape current directory
@@ -286,7 +286,9 @@ export const validateCommandFlags = (command: string, args: string[]): void => {
'-c', // Execute shell commands
'--call', // Execute shell commands
'--shell-auto-fallback', // Shell execution fallback
'-y' // Auto-confirms installation prompts
'-y', // Auto-confirms installation prompts
'--yes', // Auto-confirms installation prompts
'--node-options' // Passes arbitrary Node flags to underlying process, bypassing node flag blocklist
],
node: [
'-e', // Execute JavaScript code
@@ -295,7 +297,13 @@ export const validateCommandFlags = (command: string, args: string[]): void => {
'--print', // Evaluate and print JavaScript code
'--inspect', // Enable remote debugging (security risk)
'--inspect-brk', // Enable remote debugging with breakpoint (security risk)
'--experimental-policy' // Could load malicious policies
'--experimental-policy', // Could load malicious policies
'-r', // Short alias for --require
'--require', // Preload a CommonJS module before script runs
'--loader', // Custom ES module loader hook (code execution)
'--experimental-loader', // Same as --loader, older Node alias
'--import', // Preload ESM module before entry script (Node 18+)
'--env-file' // Read env vars from a local file (Node 20+, local file access)
],
python: [
'-c', // Execute Python code
@@ -307,15 +315,22 @@ export const validateCommandFlags = (command: string, args: string[]): void => {
],
docker: [
'run', // Run containers (too powerful)
'build', // Pulls a container and executes the run instructions
'exec', // Execute in containers
'compose', // Subcommand that starts containers (same risk as run)
'-v', // Mount host filesystems
'--volume', // Mount host filesystems
'--mount', // Alternative to -v/--volume for mounting host paths
'--volumes-from', // Mount volumes from another container (filesystem access)
'--privileged', // Privileged mode
'--cap-add', // Add capabilities
'--security-opt', // Modify security options
'--device', // Add host device files to container (privilege escalation)
'--entrypoint', // Override container entrypoint (arbitrary code execution)
'--network', // Host network access (catches --network=host and --network host)
'--pid', // Host PID namespace (catches --pid=host and --pid host)
'--ipc' // Host IPC namespace (catches --ipc=host and --ipc host)
'--ipc', // Host IPC namespace (catches --ipc=host and --ipc host)
'--env-file' // Read env vars from a local host file (local file access)
]
}