Skip to content

Commit 1b4458d

Browse files
committed
feat(satellite): add env var sanitization to prevent code injection attacks
Block dangerous environment variables (LD_PRELOAD, NODE_OPTIONS, PYTHONSTARTUP, etc.) from being passed to nsjail-spawned MCP server processes. This prevents malicious MCP servers from exploiting library injection or code execution vulnerabilities. - Add BLOCKED_ENV_VARS set with ~30 dangerous vars for Linux/Node/Python/Shell - Add sanitizeEnvVars() method to filter and log blocked vars - Log blocked vars at WARN level for security auditing
1 parent 12703ac commit 1b4458d

File tree

2 files changed

+87
-3
lines changed

2 files changed

+87
-3
lines changed

services/satellite/src/config/nsjail.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,54 @@ export const nsjailConfig = {
3636
* Falls back to /opt/deploystack if HOME is not set
3737
*/
3838
export const mcpCacheBaseDir = process.env.HOME || '/opt/deploystack';
39+
40+
/**
41+
* Blocked Environment Variables
42+
* These env vars are stripped from user-provided config before passing to nsjail.
43+
* They can be exploited for code injection, library hijacking, or privilege escalation.
44+
*/
45+
export const BLOCKED_ENV_VARS = new Set([
46+
// Linux dynamic linker (affects ALL processes)
47+
'LD_PRELOAD', // Shared library injection - most dangerous
48+
'LD_LIBRARY_PATH', // Library search path hijacking
49+
'LD_AUDIT', // Audit library injection
50+
'LD_DEBUG', // Debug output manipulation
51+
'LD_DEBUG_OUTPUT', // Debug output file
52+
'LD_PROFILE', // Profiling library injection
53+
'LD_SHOW_AUXV', // Auxiliary vector exposure
54+
'LD_DYNAMIC_WEAK', // Weak symbol manipulation
55+
56+
// Node.js specific
57+
'NODE_OPTIONS', // Can inject --require, --inspect, etc.
58+
'NODE_REPL_EXTERNAL_MODULE', // External module loading
59+
'NODE_EXTRA_CA_CERTS', // Could point to malicious CA cert
60+
'NODE_PATH', // Module resolution hijacking
61+
'NODE_REDIRECT_WARNINGS', // Warning output redirection
62+
63+
// Python specific
64+
'PYTHONSTARTUP', // Executes script on Python interpreter start
65+
'PYTHONPATH', // Module resolution hijacking
66+
'PYTHONHOME', // Python installation path hijacking
67+
'PYTHONWARNINGS', // Warning behavior manipulation
68+
'PYTHONDEBUG', // Debug mode enabling
69+
'PYTHONINSPECT', // Forces interactive mode after script
70+
'PYTHONUSERSITE', // User site-packages manipulation
71+
'PYTHONEXECUTABLE', // Executable path override
72+
'PYTHONHASHSEED', // Hash randomization control
73+
74+
// Shell injection (if any subprocess spawns shell)
75+
'BASH_ENV', // Executed on non-interactive bash start
76+
'ENV', // Executed on sh start
77+
'ZDOTDIR', // Zsh config directory override
78+
'SHELL', // Default shell override
79+
80+
// Path and temp manipulation (already set by nsjail)
81+
'PATH', // Already controlled by nsjail
82+
'HOME', // Already set by nsjail
83+
'TMPDIR', // Could redirect temp to attacker location
84+
'TMP', // Windows-style temp
85+
'TEMP', // Windows-style temp
86+
87+
// Misc dangerous
88+
'IFS', // Shell word splitting manipulation
89+
]);

services/satellite/src/process/manager.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { existsSync } from 'fs';
77
import { MCPServerConfig, ProcessInfo } from './types';
88
import type { EventBus } from '../services/event-bus';
99
import type { RuntimeState } from './runtime-state';
10-
import { nsjailConfig, mcpCacheBaseDir } from '../config/nsjail';
10+
import { nsjailConfig, mcpCacheBaseDir, BLOCKED_ENV_VARS } from '../config/nsjail';
1111

1212
/**
1313
* Process Manager for MCP server subprocesses
@@ -140,6 +140,39 @@ export class ProcessManager extends EventEmitter {
140140
this.backendStatusCallback = callback;
141141
}
142142

143+
/**
144+
* Sanitize environment variables by removing dangerous entries
145+
* Prevents library injection (LD_PRELOAD), code injection (NODE_OPTIONS, PYTHONSTARTUP), etc.
146+
* @param env - User-provided environment variables
147+
* @param installationName - For logging blocked vars
148+
* @returns Array of nsjail -E arguments with safe env vars only
149+
*/
150+
private sanitizeEnvVars(env: Record<string, string>, installationName: string): string[] {
151+
const sanitized: string[] = [];
152+
const blocked: string[] = [];
153+
154+
for (const [key, value] of Object.entries(env)) {
155+
// Check against blocklist (case-insensitive for safety)
156+
if (BLOCKED_ENV_VARS.has(key) || BLOCKED_ENV_VARS.has(key.toUpperCase())) {
157+
blocked.push(key);
158+
continue;
159+
}
160+
sanitized.push('-E', `${key}=${value}`);
161+
}
162+
163+
// Log blocked vars for security auditing
164+
if (blocked.length > 0) {
165+
this.logger.warn({
166+
operation: 'env_vars_blocked',
167+
installation_name: installationName,
168+
blocked_vars: blocked,
169+
blocked_count: blocked.length
170+
}, `Blocked ${blocked.length} dangerous env var(s) for security: ${blocked.join(', ')}`);
171+
}
172+
173+
return sanitized;
174+
}
175+
143176
/**
144177
* Resolve command to full path for nsjail execution
145178
* nsjail has limited PATH, so we need full paths for common commands
@@ -1271,8 +1304,8 @@ export class ProcessManager extends EventEmitter {
12711304
'-E', 'NPM_CONFIG_PREFIX=/home/npx/.npm-global', // npm global prefix
12721305
'-E', 'NPM_CONFIG_UPDATE_NOTIFIER=false', // Disable update notifier
12731306
'-E', 'NO_UPDATE_NOTIFIER=1', // Disable update notifier (alternative)
1274-
// Inject user-provided environment variables
1275-
...Object.entries(config.env).flatMap(([key, value]) => ['-E', `${key}=${value}`]),
1307+
// Inject user-provided environment variables (sanitized)
1308+
...this.sanitizeEnvVars(config.env, config.installation_name),
12761309
'--disable_clone_newnet', // Allow network access (required for npm downloads)
12771310
'--disable_clone_newcgroup', // Disable cgroup namespace (causes clone() errors on some kernels)
12781311
'--disable_no_new_privs', // May be needed for some packages

0 commit comments

Comments
 (0)