@zcode-apps/mcp-sentinel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,144 @@
1
+ import { BaseDetector, Vulnerability, DetectorOptions, VulnerabilityDetector } from '../index';
2
+
3
+ /**
4
+ * RCE Detector - Checks for Remote Code Execution vulnerabilities
5
+ * Focuses on CVE-2026-26029 and CVE-2026-23744 patterns
6
+ */
7
+ export class RCEDetector extends BaseDetector implements VulnerabilityDetector {
8
+ name = 'RCE Detector';
9
+ category: Vulnerability['category'] = 'rce';
10
+
11
+ private readonly patterns = [
12
+ // Command injection patterns
13
+ { pattern: /;\s*(cat|ls|pwd|whoami|id|curl|wget)\b/, severity: 'critical' },
14
+ { pattern: /&&\s*(cat|ls|pwd|whoami|id)\b/, severity: 'critical' },
15
+ { pattern: /\|\|\s*(cat|ls|pwd|whoami|id)\b/, severity: 'critical' },
16
+ { pattern: /`\$\{[^}]+\}/, severity: 'high' },
17
+ { pattern: /\$\([^)]+\)/, severity: 'high' },
18
+ { pattern: /eval\s*\(/, severity: 'critical' },
19
+ { pattern: /exec\s*\(/, severity: 'high' },
20
+ { pattern: /system\s*\(/, severity: 'critical' },
21
+ { pattern: /passthru\s*\(/, severity: 'high' },
22
+ { pattern: /shell_exec\s*\(/, severity: 'critical' },
23
+
24
+ // Path traversal combined with code execution
25
+ { pattern: /\.\.\/\.\.\/etc\/passwd/, severity: 'critical' },
26
+ { pattern: /\.\.\/\.\.\/etc\/shadow/, severity: 'critical' },
27
+ ];
28
+
29
+ async applies(url: string, options?: DetectorOptions): Promise<boolean> {
30
+ this.log(`Checking if ${url} is vulnerable to RCE...`, 'info');
31
+ return true;
32
+ }
33
+
34
+ async scan(url: string, options?: DetectorOptions): Promise<Vulnerability[]> {
35
+ const vulnerabilities: Vulnerability[] = [];
36
+
37
+ // Test 1: Check for command injection in URL parameters
38
+ const testUrls = [
39
+ `${url}?cmd=ls`,
40
+ `${url}?exec=cat /etc/passwd`,
41
+ `${url}?command=whoami`,
42
+ `${url}?q=;cat /etc/passwd`,
43
+ `${url}?input=$(whoami)`,
44
+ ];
45
+
46
+ for (const testUrl of testUrls) {
47
+ try {
48
+ const vuln = await this.checkCommandInjection(testUrl, url);
49
+ if (vuln) vulnerabilities.push(vuln);
50
+ } catch (error) {
51
+ this.log(`Error testing ${testUrl}: ${error}`, 'warn');
52
+ }
53
+ }
54
+
55
+ // Test 2: Check for eval/exec patterns in responses
56
+ try {
57
+ const evalVuln = await this.checkEvalInjection(url);
58
+ if (evalVuln) vulnerabilities.push(evalVuln);
59
+ } catch (error) {
60
+ this.log(`Error checking eval injection: ${error}`, 'warn');
61
+ }
62
+
63
+ // Test 3: Check for PHP-specific vulnerabilities (common in MCP servers)
64
+ try {
65
+ const phpVulns = await this.checkPHPVulnerabilities(url);
66
+ vulnerabilities.push(...phpVulns);
67
+ } catch (error) {
68
+ this.log(`Error checking PHP vulnerabilities: ${error}`, 'warn');
69
+ }
70
+
71
+ this.log(`Found ${vulnerabilities.length} potential RCE vulnerabilities`,
72
+ vulnerabilities.length > 0 ? 'warn' : 'success');
73
+
74
+ return vulnerabilities;
75
+ }
76
+
77
+ private async checkCommandInjection(url: string, originalUrl: string): Promise<Vulnerability | null> {
78
+ // In a real implementation, this would make actual requests
79
+ // For now, we're setting up the framework
80
+
81
+ // Simulate checking (will be replaced with actual HTTP requests)
82
+ const riskPatterns = this.patterns.filter(p =>
83
+ url.toLowerCase().includes('cat') ||
84
+ url.toLowerCase().includes('ls') ||
85
+ url.toLowerCase().includes('whoami')
86
+ );
87
+
88
+ if (riskPatterns.length > 0) {
89
+ return {
90
+ id: 'RCE-001',
91
+ name: 'Potential Command Injection',
92
+ severity: 'critical',
93
+ description: 'The endpoint appears to accept user input that could be used for command injection attacks',
94
+ cve: 'CVE-2026-26029',
95
+ affectedEndpoint: originalUrl,
96
+ proof: `Test URL: ${url}`,
97
+ remediation: 'Implement input validation, use allowlists for allowed inputs, and escape special characters'
98
+ };
99
+ }
100
+
101
+ return null;
102
+ }
103
+
104
+ private async checkEvalInjection(url: string): Promise<Vulnerability | null> {
105
+ // Check for JavaScript eval() vulnerabilities
106
+ // This would make actual requests in production
107
+
108
+ return {
109
+ id: 'RCE-002',
110
+ name: 'Potential JavaScript Code Injection',
111
+ severity: 'critical',
112
+ description: 'The application may be vulnerable to JavaScript code injection through eval() or similar functions',
113
+ cve: 'CVE-2026-23744',
114
+ affectedEndpoint: url,
115
+ remediation: 'Avoid using eval(). Use JSON.parse() for JSON data and proper sanitization for user inputs'
116
+ };
117
+ }
118
+
119
+ private async checkPHPVulnerabilities(url: string): Promise<Vulnerability[]> {
120
+ const vulnerabilities: Vulnerability[] = [];
121
+
122
+ // Check for common PHP RCE vectors
123
+ const phpTestUrls = [
124
+ `${url}?file=php://filter/convert.base64-encode/resource=config.php`,
125
+ `${url}?page=php://input`,
126
+ `${url}?include=php://input`,
127
+ ];
128
+
129
+ for (const testUrl of phpTestUrls) {
130
+ vulnerabilities.push({
131
+ id: `RCE-PHP-${Date.now()}`,
132
+ name: 'PHP Wrappers Vulnerability',
133
+ severity: 'high',
134
+ description: 'PHP wrapper URLs detected - potential code execution vector',
135
+ affectedEndpoint: testUrl,
136
+ remediation: 'Validate and sanitize all file path inputs. Disable dangerous PHP wrappers if not needed.'
137
+ });
138
+ }
139
+
140
+ return vulnerabilities;
141
+ }
142
+ }
143
+
144
+ export default RCEDetector;
@@ -0,0 +1,85 @@
1
+ import chalk from 'chalk';
2
+
3
+ /**
4
+ * Scanner core module
5
+ * Provides the foundation for MCP security scanning
6
+ */
7
+
8
+ export type ScanResult = {
9
+ url: string;
10
+ timestamp: Date;
11
+ vulnerabilities: Vulnerability[];
12
+ score: number;
13
+ };
14
+
15
+ export type Vulnerability = {
16
+ id: string;
17
+ name: string;
18
+ severity: 'critical' | 'high' | 'medium' | 'low';
19
+ description: string;
20
+ cve?: string;
21
+ affectedEndpoint: string;
22
+ proof?: string;
23
+ remediation: string;
24
+ category: 'rce' | 'auth' | 'path-traversal' | 'data-leakage' | 'owasp';
25
+ };
26
+
27
+ export type DetectorOptions = {
28
+ timeout?: number;
29
+ verbose?: boolean;
30
+ };
31
+
32
+ export interface VulnerabilityDetector {
33
+ name: string;
34
+ category: Vulnerability['category'];
35
+
36
+ /**
37
+ * Check if this detector applies to the target
38
+ */
39
+ applies(url: string, options?: DetectorOptions): Promise<boolean>;
40
+
41
+ /**
42
+ * Run the vulnerability check
43
+ */
44
+ scan(url: string, options?: DetectorOptions): Promise<Vulnerability[]>;
45
+ }
46
+
47
+ /**
48
+ * Base class for all vulnerability detectors
49
+ */
50
+ export abstract class BaseDetector implements VulnerabilityDetector {
51
+ constructor(protected options: DetectorOptions = {}) {}
52
+
53
+ abstract name: string;
54
+ abstract category: Vulnerability['category'];
55
+
56
+ async applies(url: string, opts?: DetectorOptions): Promise<boolean> {
57
+ return true;
58
+ }
59
+
60
+ protected log(message: string, level: 'info' | 'warn' | 'error' | 'success' = 'info'): void {
61
+ const timestamp = new Date().toISOString();
62
+ let prefix = '';
63
+
64
+ switch (level) {
65
+ case 'info':
66
+ prefix = chalk.blue('[INFO]');
67
+ break;
68
+ case 'warn':
69
+ prefix = chalk.yellow('[WARN]');
70
+ break;
71
+ case 'error':
72
+ prefix = chalk.red('[ERROR]');
73
+ break;
74
+ case 'success':
75
+ prefix = chalk.green('[SUCCESS]');
76
+ break;
77
+ }
78
+
79
+ console.log(`${chalk.gray(timestamp)} ${prefix} ${message}`);
80
+ }
81
+
82
+ protected sleep(ms: number): Promise<void> {
83
+ return new Promise(resolve => setTimeout(resolve, ms));
84
+ }
85
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "commonjs",
5
+ "lib": ["ES2022"],
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "sourceMap": true,
9
+ "outDir": "./dist",
10
+ "rootDir": "./src",
11
+ "strict": true,
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "resolveJsonModule": true,
16
+ "moduleResolution": "node",
17
+ "types": ["node"]
18
+ },
19
+ "include": ["src/**/*"],
20
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
21
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { MCPSentinel } from './scanner.js';
4
+
5
+ const program = new Command();
6
+ program
7
+ .name('mcp-sentinel')
8
+ .description('MCP Security Scanner - Scans for vulnerabilities');
9
+
10
+ program
11
+ .command('scan <url>')
12
+ .description('Scan a URL for vulnerabilities')
13
+ .action(async (url) => {
14
+ const scanner = new MCPSentinel();
15
+ const results = await scanner.scan(url);
16
+ console.log(JSON.stringify(results, null, 2));
17
+ });
18
+
19
+ program.parse();
package/src/scanner.ts ADDED
@@ -0,0 +1,51 @@
1
+ import fetch, { RequestInit, Response } from 'node-fetch';
2
+
3
+ export class MCPSentinel {
4
+ async scan(url: string): Promise<any[]> {
5
+ const results = [];
6
+
7
+ // RCE Detection Check
8
+ const rceResult = await this.checkRCE(url);
9
+ if (rceResult) {
10
+ results.push(rceResult);
11
+ }
12
+
13
+ return results;
14
+ }
15
+
16
+ private async checkRCE(url: string): Promise<any | null> {
17
+ const controller = new AbortController();
18
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
19
+
20
+ try {
21
+ const response: Response = await fetch(url, {
22
+ signal: controller.signal,
23
+ timeout: 5000
24
+ } as RequestInit);
25
+
26
+ // Anzeichen für RCE-Schwachstellen
27
+ const headers = Object.fromEntries(response.headers.entries());
28
+
29
+ // Verdächtige Header-Anzeichen
30
+ if (headers['x-php-version'] || headers['server']?.includes('nginx')) {
31
+ return {
32
+ type: 'potential_rce',
33
+ severity: 'medium',
34
+ description: 'Verdächtige Server-Header könnten auf RCE-Angriffe hinweisen',
35
+ evidence: headers
36
+ };
37
+ }
38
+
39
+ return null;
40
+ } catch (error: any) {
41
+ return {
42
+ type: 'scan_error',
43
+ severity: 'low',
44
+ description: `Scan-Fehler: ${error.message}`,
45
+ url: url
46
+ };
47
+ } finally {
48
+ clearTimeout(timeoutId);
49
+ }
50
+ }
51
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }