Skip to content

Commit 9081438

Browse files
committed
feat(all): implement comprehensive security validation for MCP installations
Add defense-in-depth security validation across backend, satellite, and frontend: Backend: - Add security validation library (args, env vars, HTTP headers, query params, stdio) - Implement build script validation for Node.js and Python deployments - Add deployment information tracking for GitHub installations - Enhance validation in installation routes (create, update, updateArgs, etc.) - Update API spec with detailed error descriptions and input constraints Satellite: - Add security validation for commands, arguments, and environment variables - Implement nsjail support for sandboxed build command execution - Enhance GitHub deployment handler with Python support and security checks - Add temporary directory cleanup in process manager - Refine nsjail configuration for build isolation Frontend: - Add DeploymentInfoCard component for displaying deployment details - Update GeneralTab to show deployment information - Extend installation types with deployment metadata This implements multi-layer security validation to protect against command injection, path traversal, and other security vulnerabilities in MCP server installations.
1 parent 619b967 commit 9081438

27 files changed

+3335
-128
lines changed

services/backend/api-spec.json

Lines changed: 323 additions & 22 deletions
Large diffs are not rendered by default.

services/backend/api-spec.yaml

Lines changed: 252 additions & 7 deletions
Large diffs are not rendered by default.
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/**
2+
* Build script validation for GitHub deployments
3+
*
4+
* Validates build scripts for security risks before accepting deployments.
5+
* Works for Node.js (package.json scripts) and Python (pyproject.toml).
6+
*
7+
* This is the first layer of defense - backend validation before accepting deployment.
8+
* The satellite re-validates as defense-in-depth before execution.
9+
*/
10+
11+
/**
12+
* Dangerous patterns that indicate potential malicious activity in build scripts.
13+
* These patterns block common attack vectors:
14+
* - Network exfiltration (curl, wget, netcat)
15+
* - Shell execution (bash -c, sh -c)
16+
* - Code execution from strings (eval, exec, python -c)
17+
* - Encoding for obfuscation (base64)
18+
* - Environment variable exfiltration
19+
* - Dangerous system operations
20+
*/
21+
const BLOCKED_PATTERNS: Array<{ pattern: RegExp; reason: string }> = [
22+
// Network exfiltration
23+
{ pattern: /\bcurl\b/i, reason: 'curl command (network exfiltration risk)' },
24+
{ pattern: /\bwget\b/i, reason: 'wget command (network exfiltration risk)' },
25+
{ pattern: /\bnc\b/, reason: 'netcat (network exfiltration risk)' },
26+
{ pattern: /\bnetcat\b/i, reason: 'netcat (network exfiltration risk)' },
27+
{ pattern: /\btelnet\b/i, reason: 'telnet (network exfiltration risk)' },
28+
{ pattern: />\s*\/dev\/tcp/, reason: 'bash network redirect (exfiltration risk)' },
29+
30+
// Shell execution with inline commands
31+
{ pattern: /\bbash\s+-c\b/i, reason: 'bash -c (arbitrary shell execution)' },
32+
{ pattern: /\bsh\s+-c\b/i, reason: 'sh -c (arbitrary shell execution)' },
33+
{ pattern: /\bzsh\s+-c\b/i, reason: 'zsh -c (arbitrary shell execution)' },
34+
35+
// Code execution from string
36+
{ pattern: /\bpython\s+-c\b/i, reason: 'python -c (code execution from string)' },
37+
{ pattern: /\bpython3\s+-c\b/i, reason: 'python3 -c (code execution from string)' },
38+
{ pattern: /\bnode\s+-e\b/i, reason: 'node -e (code execution from string)' },
39+
{ pattern: /\bnode\s+--eval\b/i, reason: 'node --eval (code execution from string)' },
40+
{ pattern: /\bperl\s+-e\b/i, reason: 'perl -e (code execution from string)' },
41+
{ pattern: /\bruby\s+-e\b/i, reason: 'ruby -e (code execution from string)' },
42+
{ pattern: /\beval\s*\(/, reason: 'eval() call (code execution from string)' },
43+
44+
// Encoding (often used for obfuscation)
45+
{ pattern: /\|.*\bbase64\b/, reason: 'piped base64 (obfuscation technique)' },
46+
{ pattern: /\bbase64\s+-d\b/, reason: 'base64 decode (obfuscation technique)' },
47+
48+
// Environment variable piping (exfiltration)
49+
{ pattern: /\$[A-Z_]+.*\|/, reason: 'env var piped to command (exfiltration risk)' },
50+
{ pattern: /echo\s+\$[A-Z_]+.*[|>]/, reason: 'echo env var redirected (exfiltration risk)' },
51+
52+
// Dangerous system operations
53+
{ pattern: /\brm\s+-rf\s+\//, reason: 'rm -rf / (destructive operation)' },
54+
{ pattern: /\bchmod\s+777/, reason: 'chmod 777 (insecure permissions)' },
55+
56+
// Python-specific dangerous patterns
57+
{ pattern: /os\.system\s*\(/, reason: 'os.system() (arbitrary command execution)' },
58+
{ pattern: /subprocess\.(call|run|Popen)\s*\(/, reason: 'subprocess execution' },
59+
{ pattern: /\bexec\s*\(/, reason: 'exec() (code execution from string)' },
60+
{ pattern: /__import__\s*\(/, reason: '__import__() (dynamic import)' },
61+
];
62+
63+
export interface ScriptValidationResult {
64+
valid: boolean;
65+
error?: string;
66+
blockedPatterns?: Array<{ location: string; pattern: string; reason: string }>;
67+
}
68+
69+
/**
70+
* Validate Node.js package.json scripts for security risks
71+
*
72+
* @param scripts - The scripts object from package.json
73+
* @returns Validation result with blocked patterns if any
74+
*/
75+
export function validateNodeScripts(
76+
scripts: Record<string, string> | undefined
77+
): ScriptValidationResult {
78+
if (!scripts) {
79+
return { valid: true };
80+
}
81+
82+
const blockedPatterns: Array<{ location: string; pattern: string; reason: string }> = [];
83+
84+
for (const [name, content] of Object.entries(scripts)) {
85+
if (!content || typeof content !== 'string') continue;
86+
87+
for (const { pattern, reason } of BLOCKED_PATTERNS) {
88+
if (pattern.test(content)) {
89+
blockedPatterns.push({
90+
location: `scripts.${name}`,
91+
pattern: pattern.source,
92+
reason
93+
});
94+
}
95+
}
96+
}
97+
98+
if (blockedPatterns.length > 0) {
99+
const locations = blockedPatterns.map(p => p.location).join(', ');
100+
return {
101+
valid: false,
102+
error: `Dangerous patterns detected in npm scripts: ${locations}. Build scripts cannot contain network commands, shell execution, or code evaluation.`,
103+
blockedPatterns
104+
};
105+
}
106+
107+
return { valid: true };
108+
}
109+
110+
/**
111+
* Validate Python project files for security risks
112+
*
113+
* Note: Python has many ways to run code during install, so this is best-effort.
114+
* The main risks are:
115+
* - setup.py: Can run arbitrary Python code during pip install
116+
* - Build hooks: Can execute code during package building
117+
*
118+
* @param pyprojectContent - Content of pyproject.toml (if exists)
119+
* @param hasSetupPy - Whether setup.py exists in the repository
120+
* @returns Validation result with blocked patterns if any
121+
*/
122+
export function validatePythonProject(
123+
pyprojectContent: string | undefined,
124+
hasSetupPy: boolean
125+
): ScriptValidationResult {
126+
const blockedPatterns: Array<{ location: string; pattern: string; reason: string }> = [];
127+
128+
// setup.py is inherently dangerous - it runs arbitrary Python during install
129+
if (hasSetupPy) {
130+
blockedPatterns.push({
131+
location: 'setup.py',
132+
pattern: 'setup.py exists',
133+
reason: 'setup.py can run arbitrary code during pip install. Use pyproject.toml instead.'
134+
});
135+
}
136+
137+
if (pyprojectContent) {
138+
// Check for build hooks that can run arbitrary code
139+
if (/\[tool\.hatch\.build\.hooks/.test(pyprojectContent)) {
140+
blockedPatterns.push({
141+
location: 'pyproject.toml',
142+
pattern: '[tool.hatch.build.hooks]',
143+
reason: 'hatch build hooks can execute arbitrary code during build'
144+
});
145+
}
146+
147+
// Check for setuptools build hooks
148+
if (/\[tool\.setuptools\.cmdclass\]/.test(pyprojectContent)) {
149+
blockedPatterns.push({
150+
location: 'pyproject.toml',
151+
pattern: '[tool.setuptools.cmdclass]',
152+
reason: 'setuptools cmdclass can execute arbitrary code during build'
153+
});
154+
}
155+
156+
// Check for dangerous patterns in the content
157+
for (const { pattern, reason } of BLOCKED_PATTERNS) {
158+
if (pattern.test(pyprojectContent)) {
159+
blockedPatterns.push({
160+
location: 'pyproject.toml',
161+
pattern: pattern.source,
162+
reason
163+
});
164+
}
165+
}
166+
}
167+
168+
if (blockedPatterns.length > 0) {
169+
const locations = [...new Set(blockedPatterns.map(p => p.location))].join(', ');
170+
return {
171+
valid: false,
172+
error: `Dangerous patterns detected in Python project: ${locations}. Projects with setup.py or build hooks are not allowed for security reasons.`,
173+
blockedPatterns
174+
};
175+
}
176+
177+
return { valid: true };
178+
}
179+
180+
/**
181+
* Get the list of blocked patterns (for documentation/testing)
182+
*/
183+
export function getBlockedPatterns(): Array<{ pattern: RegExp; reason: string }> {
184+
return [...BLOCKED_PATTERNS];
185+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Common types and utilities for security validation
3+
*/
4+
5+
/**
6+
* Result of a security validation operation
7+
*/
8+
export interface ValidationResult {
9+
valid: boolean;
10+
error?: string;
11+
details?: ValidationErrorDetails;
12+
}
13+
14+
/**
15+
* Detailed information about validation errors for debugging/logging
16+
*/
17+
export interface ValidationErrorDetails {
18+
type: 'BLOCKED_PATTERN' | 'INVALID_FORMAT' | 'BLOCKED_KEY' | 'BLOCKED_VALUE' | 'INVALID_COMMAND';
19+
index?: number;
20+
key?: string;
21+
value?: string;
22+
reason: string;
23+
}
24+
25+
/**
26+
* Creates a successful validation result
27+
*/
28+
export function validResult(): ValidationResult {
29+
return { valid: true };
30+
}
31+
32+
/**
33+
* Creates a failed validation result
34+
*/
35+
export function invalidResult(
36+
error: string,
37+
details?: ValidationErrorDetails
38+
): ValidationResult {
39+
return {
40+
valid: false,
41+
error,
42+
details
43+
};
44+
}

0 commit comments

Comments
 (0)