@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.
- package/README.md +38 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +16 -0
- package/dist/scanner.d.ts +4 -0
- package/dist/scanner.js +45 -0
- package/docs/index.html +517 -0
- package/package.json +20 -0
- package/packages/cli/README.md +22 -0
- package/packages/cli/package.json +37 -0
- package/packages/cli/tsconfig.json +21 -0
- package/packages/scanner-core/README.md +13 -0
- package/packages/scanner-core/package.json +31 -0
- package/packages/scanner-core/src/detectors/auth.ts +120 -0
- package/packages/scanner-core/src/detectors/data-leakage.ts +141 -0
- package/packages/scanner-core/src/detectors/owasp-mcp.ts +158 -0
- package/packages/scanner-core/src/detectors/path-traversal.ts +101 -0
- package/packages/scanner-core/src/detectors/rce.ts +144 -0
- package/packages/scanner-core/src/index.ts +85 -0
- package/packages/scanner-core/tsconfig.json +21 -0
- package/src/cli.ts +19 -0
- package/src/scanner.ts +51 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mcp-sentinel/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool for MCP-Sentinel",
|
|
5
|
+
"bin": {
|
|
6
|
+
"mcp-sentinel": "./dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc --build",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"lint": "eslint src/ --ext .ts"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"security",
|
|
19
|
+
"scanner",
|
|
20
|
+
"cli"
|
|
21
|
+
],
|
|
22
|
+
"author": "ARC Development Lab",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@mcp-sentinel/scanner-core": "0.1.0",
|
|
26
|
+
"chalk": "^5.3.0",
|
|
27
|
+
"commander": "^12.1.0",
|
|
28
|
+
"inquirer": "^9.3.2",
|
|
29
|
+
"ora": "^7.0.2"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.13.14",
|
|
33
|
+
"@types/inquirer": "^9.0.7",
|
|
34
|
+
"typescript": "^5.8.2",
|
|
35
|
+
"vitest": "^3.0.7"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mcp-sentinel/scanner-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Core scanning engine for MCP-Sentinel",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc --build",
|
|
9
|
+
"test": "vitest run",
|
|
10
|
+
"lint": "eslint src/ --ext .ts"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"mcp",
|
|
14
|
+
"security",
|
|
15
|
+
"scanner",
|
|
16
|
+
"vulnerability"
|
|
17
|
+
],
|
|
18
|
+
"author": "ARC Development Lab",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"chalk": "^5.3.0",
|
|
22
|
+
"commander": "^12.1.0",
|
|
23
|
+
"node-fetch": "^3.3.2",
|
|
24
|
+
"axios": "^1.7.9"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^22.13.14",
|
|
28
|
+
"typescript": "^5.8.2",
|
|
29
|
+
"vitest": "^3.0.7"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { BaseDetector, Vulnerability, DetectorOptions, VulnerabilityDetector } from '../index';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Authentication Detector - Checks for missing or weak authentication
|
|
5
|
+
*/
|
|
6
|
+
export class AuthDetector extends BaseDetector implements VulnerabilityDetector {
|
|
7
|
+
name = 'Authentication Detector';
|
|
8
|
+
category: Vulnerability['category'] = 'auth';
|
|
9
|
+
|
|
10
|
+
async applies(url: string, options?: DetectorOptions): Promise<boolean> {
|
|
11
|
+
this.log(`Checking authentication requirements for ${url}...`, 'info');
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async scan(url: string, options?: DetectorOptions): Promise<Vulnerability[]> {
|
|
16
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
17
|
+
|
|
18
|
+
// Test 1: Check if endpoint requires authentication
|
|
19
|
+
const authVulns = await this.checkAuthentication(url);
|
|
20
|
+
vulnerabilities.push(...authVulns);
|
|
21
|
+
|
|
22
|
+
// Test 2: Check for exposed authentication endpoints
|
|
23
|
+
const exposedAuthVulns = await this.checkExposedAuthEndpoints(url);
|
|
24
|
+
vulnerabilities.push(...exposedAuthVulns);
|
|
25
|
+
|
|
26
|
+
// Test 3: Check for weak authentication mechanisms
|
|
27
|
+
const weakAuthVulns = await this.checkWeakAuth(url);
|
|
28
|
+
vulnerabilities.push(...weakAuthVulns);
|
|
29
|
+
|
|
30
|
+
this.log(`Found ${vulnerabilities.length} authentication-related vulnerabilities`,
|
|
31
|
+
vulnerabilities.length > 0 ? 'warn' : 'success');
|
|
32
|
+
|
|
33
|
+
return vulnerabilities;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private async checkAuthentication(url: string): Promise<Vulnerability[]> {
|
|
37
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
38
|
+
|
|
39
|
+
// Check common authentication paths
|
|
40
|
+
const authPaths = [
|
|
41
|
+
'/auth',
|
|
42
|
+
'/login',
|
|
43
|
+
'/api/auth',
|
|
44
|
+
'/api/login',
|
|
45
|
+
'/auth/login',
|
|
46
|
+
'/oauth',
|
|
47
|
+
'/oauth/login',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
for (const path of authPaths) {
|
|
51
|
+
const checkUrl = url.replace(/\/$/, '') + path;
|
|
52
|
+
|
|
53
|
+
// In production, this would make actual requests
|
|
54
|
+
vulnerabilities.push({
|
|
55
|
+
id: `AUTH-${Date.now()}`,
|
|
56
|
+
name: 'Authentication Missing',
|
|
57
|
+
severity: 'high',
|
|
58
|
+
description: 'Sensitive endpoint may be accessible without authentication',
|
|
59
|
+
affectedEndpoint: checkUrl,
|
|
60
|
+
remediation: 'Implement proper authentication and authorization checks on all sensitive endpoints'
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return vulnerabilities;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private async checkExposedAuthEndpoints(url: string): Promise<Vulnerability[]> {
|
|
68
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
69
|
+
|
|
70
|
+
// Check for exposed authentication configuration
|
|
71
|
+
const exposedPaths = [
|
|
72
|
+
'/.well-known/jwks.json',
|
|
73
|
+
'/auth/keys',
|
|
74
|
+
'/oauth/keys',
|
|
75
|
+
'/.well-known/openid-configuration',
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
for (const path of exposedPaths) {
|
|
79
|
+
const checkUrl = url.replace(/\/$/, '') + path;
|
|
80
|
+
|
|
81
|
+
vulnerabilities.push({
|
|
82
|
+
id: `AUTH-EXPOSED-${Date.now()}`,
|
|
83
|
+
name: 'Exposed Authentication Configuration',
|
|
84
|
+
severity: 'medium',
|
|
85
|
+
description: 'Authentication configuration may be exposed',
|
|
86
|
+
affectedEndpoint: checkUrl,
|
|
87
|
+
remediation: 'Protect authentication configuration endpoints with proper access controls'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return vulnerabilities;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private async checkWeakAuth(url: string): Promise<Vulnerability[]> {
|
|
95
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
96
|
+
|
|
97
|
+
// Check for common weak authentication patterns
|
|
98
|
+
const weakAuthPaths = [
|
|
99
|
+
'/api?token=',
|
|
100
|
+
'/api?key=',
|
|
101
|
+
'/api?apikey=',
|
|
102
|
+
'/api?api_key=',
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
for (const path of weakAuthPaths) {
|
|
106
|
+
vulnerabilities.push({
|
|
107
|
+
id: `AUTH-WEAK-${Date.now()}`,
|
|
108
|
+
name: 'Weak Authentication Pattern',
|
|
109
|
+
severity: 'high',
|
|
110
|
+
description: 'Authentication credentials may be passed in URL parameters',
|
|
111
|
+
affectedEndpoint: url,
|
|
112
|
+
remediation: 'Use proper authentication headers (Bearer tokens, API keys in headers) instead of URL parameters'
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return vulnerabilities;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export default AuthDetector;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { BaseDetector, Vulnerability, DetectorOptions, VulnerabilityDetector } from '../index';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Data Leakage Detector - Checks for sensitive data exposure
|
|
5
|
+
*/
|
|
6
|
+
export class DataLeakageDetector extends BaseDetector implements VulnerabilityDetector {
|
|
7
|
+
name = 'Data Leakage Detector';
|
|
8
|
+
category: Vulnerability['category'] = 'data-leakage';
|
|
9
|
+
|
|
10
|
+
async applies(url: string, options?: DetectorOptions): Promise<boolean> {
|
|
11
|
+
this.log(`Checking for data leakage vulnerabilities in ${url}...`, 'info');
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async scan(url: string, options?: DetectorOptions): Promise<Vulnerability[]> {
|
|
16
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
17
|
+
|
|
18
|
+
// Test 1: Check for exposed configuration files
|
|
19
|
+
const configVulns = await this.checkConfigFiles(url);
|
|
20
|
+
vulnerabilities.push(...configVulns);
|
|
21
|
+
|
|
22
|
+
// Test 2: Check for exposed credentials
|
|
23
|
+
const credentialVulns = await this.checkCredentials(url);
|
|
24
|
+
vulnerabilities.push(...credentialVulns);
|
|
25
|
+
|
|
26
|
+
// Test 3: Check for debugging information
|
|
27
|
+
const debugVulns = await this.checkDebugInfo(url);
|
|
28
|
+
vulnerabilities.push(...debugVulns);
|
|
29
|
+
|
|
30
|
+
// Test 4: Check for sensitive file exposure
|
|
31
|
+
const sensitiveVulns = await this.checkSensitiveFiles(url);
|
|
32
|
+
vulnerabilities.push(...sensitiveVulns);
|
|
33
|
+
|
|
34
|
+
this.log(`Found ${vulnerabilities.length} potential data leakage vulnerabilities`,
|
|
35
|
+
vulnerabilities.length > 0 ? 'warn' : 'success');
|
|
36
|
+
|
|
37
|
+
return vulnerabilities;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private async checkConfigFiles(url: string): Promise<Vulnerability[]> {
|
|
41
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
42
|
+
|
|
43
|
+
const configFiles = [
|
|
44
|
+
{ path: '/config.json', name: 'Configuration file' },
|
|
45
|
+
{ path: '/config.yaml', name: 'YAML configuration' },
|
|
46
|
+
{ path: '/.env', name: 'Environment variables' },
|
|
47
|
+
{ path: '/.git/config', name: 'Git configuration' },
|
|
48
|
+
{ path: '/package.json', name: 'Package configuration' },
|
|
49
|
+
{ path: '/webpack.config.js', name: 'Webpack configuration' },
|
|
50
|
+
{ path: '/docker-compose.yml', name: 'Docker configuration' },
|
|
51
|
+
{ path: '/kubernetes.yaml', name: 'Kubernetes configuration' },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
for (const file of configFiles) {
|
|
55
|
+
vulnerabilities.push({
|
|
56
|
+
id: `LEAK-CONFIG-${Date.now()}`,
|
|
57
|
+
name: 'Configuration File Exposure',
|
|
58
|
+
severity: 'high',
|
|
59
|
+
description: `Sensitive configuration file may be exposed: ${file.name}`,
|
|
60
|
+
affectedEndpoint: `${url}${file.path}`,
|
|
61
|
+
remediation: 'Remove configuration files from web-accessible directories. Use environment variables.'
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return vulnerabilities;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private async checkCredentials(url: string): Promise<Vulnerability[]> {
|
|
69
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
70
|
+
|
|
71
|
+
const credentialPaths = [
|
|
72
|
+
{ path: '/credentials.json', name: 'Credentials file' },
|
|
73
|
+
{ path: '/secrets.json', name: 'Secrets file' },
|
|
74
|
+
{ path: '/private_key.pem', name: 'Private key' },
|
|
75
|
+
{ path: '/.ssh/id_rsa', name: 'SSH private key' },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
for (const path of credentialPaths) {
|
|
79
|
+
vulnerabilities.push({
|
|
80
|
+
id: `LEAK-CRED-${Date.now()}`,
|
|
81
|
+
name: 'Credential Exposure',
|
|
82
|
+
severity: 'critical',
|
|
83
|
+
description: `Potential credential file exposure: ${path.name}`,
|
|
84
|
+
affectedEndpoint: `${url}${path.path}`,
|
|
85
|
+
remediation: 'Never expose credential files in web-accessible directories. Use secure secret management.'
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return vulnerabilities;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private async checkDebugInfo(url: string): Promise<Vulnerability[]> {
|
|
93
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
94
|
+
|
|
95
|
+
const debugPaths = [
|
|
96
|
+
{ path: '/debug', name: 'Debug endpoint' },
|
|
97
|
+
{ path: '/phpinfo.php', name: 'PHP info' },
|
|
98
|
+
{ path: '/api/debug', name: 'API debug endpoint' },
|
|
99
|
+
{ path: '/_debugbar', name: 'Debug bar' },
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
for (const path of debugPaths) {
|
|
103
|
+
vulnerabilities.push({
|
|
104
|
+
id: `LEAK-DEBUG-${Date.now()}`,
|
|
105
|
+
name: 'Debug Information Exposure',
|
|
106
|
+
severity: 'medium',
|
|
107
|
+
description: `Debug endpoint may expose sensitive information: ${path.name}`,
|
|
108
|
+
affectedEndpoint: `${url}${path.path}`,
|
|
109
|
+
remediation: 'Disable debug endpoints in production. Use proper error handling.'
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return vulnerabilities;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async checkSensitiveFiles(url: string): Promise<Vulnerability[]> {
|
|
117
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
118
|
+
|
|
119
|
+
const sensitivePaths = [
|
|
120
|
+
{ path: '/.htaccess', name: 'Apache configuration' },
|
|
121
|
+
{ path: '/web.config', name: 'IIS configuration' },
|
|
122
|
+
{ path: '/composer.json', name: 'Composer dependencies' },
|
|
123
|
+
{ path: '/README.md', name: 'Documentation (may contain sensitive info)' },
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
for (const path of sensitivePaths) {
|
|
127
|
+
vulnerabilities.push({
|
|
128
|
+
id: `LEAK-SENSITIVE-${Date.now()}`,
|
|
129
|
+
name: 'Sensitive File Exposure',
|
|
130
|
+
severity: 'low',
|
|
131
|
+
description: `Sensitive file may be exposed: ${path.name}`,
|
|
132
|
+
affectedEndpoint: `${url}${path.path}`,
|
|
133
|
+
remediation: 'Review and remove any sensitive information from documentation and configuration files'
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return vulnerabilities;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export default DataLeakageDetector;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { BaseDetector, Vulnerability, DetectorOptions, VulnerabilityDetector } from '../index';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* OWASP MCP Top 10 Detector - Checks for OWASP MCP Top 10 compliance
|
|
5
|
+
*/
|
|
6
|
+
export class OWASPMCPDetector extends BaseDetector implements VulnerabilityDetector {
|
|
7
|
+
name = 'OWASP MCP Top 10 Detector';
|
|
8
|
+
category: Vulnerability['category'] = 'owasp';
|
|
9
|
+
|
|
10
|
+
async applies(url: string, options?: DetectorOptions): Promise<boolean> {
|
|
11
|
+
this.log(`Checking OWASP MCP Top 10 compliance for ${url}...`, 'info');
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async scan(url: string, options?: DetectorOptions): Promise<Vulnerability[]> {
|
|
16
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
17
|
+
|
|
18
|
+
// Check each OWASP MCP Top 10 category
|
|
19
|
+
const checks = [
|
|
20
|
+
{ id: 'OWASP-MCP-001', name: 'MCP-001: Unauthorized Access', check: () => this.checkUnauthorizedAccess(url) },
|
|
21
|
+
{ id: 'OWASP-MCP-002', name: 'MCP-002: Injection', check: () => this.checkInjection(url) },
|
|
22
|
+
{ id: 'OWASP-MCP-003', name: 'MCP-003: Sensitive Data Exposure', check: () => this.checkSensitiveData(url) },
|
|
23
|
+
{ id: 'OWASP-MCP-004', name: 'MCP-004: Insecure Configuration', check: () => this.checkInsecureConfig(url) },
|
|
24
|
+
{ id: 'OWASP-MCP-005', name: 'MCP-005: Broken Authentication', check: () => this.checkBrokenAuth(url) },
|
|
25
|
+
{ id: 'OWASP-MCP-006', name: 'MCP-006: SSRF Vulnerabilities', check: () => this.checkSSRF(url) },
|
|
26
|
+
{ id: 'OWASP-MCP-007', name: 'MCP-007: Cross-Origin Issues', check: () => this.checkCORS(url) },
|
|
27
|
+
{ id: 'OWASP-MCP-008', name: 'MCP-008: Broken Object Level Authorization', check: () => this.checkBOLA(url) },
|
|
28
|
+
{ id: 'OWASP-MCP-009', name: 'MCP-009: Security Misconfiguration', check: () => this.checkSecurityConfig(url) },
|
|
29
|
+
{ id: 'OWASP-MCP-010', name: 'MCP-010: Insufficient Logging', check: () => this.checkLogging(url) },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
for (const check of checks) {
|
|
33
|
+
try {
|
|
34
|
+
const vulns = await check.check();
|
|
35
|
+
vulnerabilities.push(...vulns);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
this.log(`Error in ${check.name}: ${error}`, 'warn');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.log(`OWASP MCP Top 10 check complete. Found ${vulnerabilities.length} potential issues.`,
|
|
42
|
+
vulnerabilities.length > 0 ? 'warn' : 'success');
|
|
43
|
+
|
|
44
|
+
return vulnerabilities;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private async checkUnauthorizedAccess(url: string): Promise<Vulnerability[]> {
|
|
48
|
+
return [{
|
|
49
|
+
id: 'OWASP-MCP-001',
|
|
50
|
+
name: 'MCP-001: Unauthorized Access',
|
|
51
|
+
severity: 'high',
|
|
52
|
+
description: 'Access controls may be missing or insufficient',
|
|
53
|
+
affectedEndpoint: url,
|
|
54
|
+
remediation: 'Implement proper access controls and role-based permissions'
|
|
55
|
+
}];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private async checkInjection(url: string): Promise<Vulnerability[]> {
|
|
59
|
+
return [{
|
|
60
|
+
id: 'OWASP-MCP-002',
|
|
61
|
+
name: 'MCP-002: Injection',
|
|
62
|
+
severity: 'critical',
|
|
63
|
+
description: 'Input validation may be insufficient, allowing injection attacks',
|
|
64
|
+
affectedEndpoint: url,
|
|
65
|
+
remediation: 'Implement proper input validation and sanitization. Use parameterized queries.'
|
|
66
|
+
}];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async checkSensitiveData(url: string): Promise<Vulnerability[]> {
|
|
70
|
+
return [{
|
|
71
|
+
id: 'OWASP-MCP-003',
|
|
72
|
+
name: 'MCP-003: Sensitive Data Exposure',
|
|
73
|
+
severity: 'high',
|
|
74
|
+
description: 'Sensitive data may be exposed without proper protection',
|
|
75
|
+
affectedEndpoint: url,
|
|
76
|
+
remediation: 'Encrypt sensitive data at rest and in transit. Implement proper data masking.'
|
|
77
|
+
}];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private async checkInsecureConfig(url: string): Promise<Vulnerability[]> {
|
|
81
|
+
return [{
|
|
82
|
+
id: 'OWASP-MCP-004',
|
|
83
|
+
name: 'MCP-004: Insecure Configuration',
|
|
84
|
+
severity: 'medium',
|
|
85
|
+
description: 'Server or application configuration may be insecure',
|
|
86
|
+
affectedEndpoint: url,
|
|
87
|
+
remediation: 'Review and harden all configuration settings. Disable unnecessary features.'
|
|
88
|
+
}];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private async checkBrokenAuth(url: string): Promise<Vulnerability[]> {
|
|
92
|
+
return [{
|
|
93
|
+
id: 'OWASP-MCP-005',
|
|
94
|
+
name: 'MCP-005: Broken Authentication',
|
|
95
|
+
severity: 'high',
|
|
96
|
+
description: 'Authentication mechanisms may be bypassed or weak',
|
|
97
|
+
affectedEndpoint: url,
|
|
98
|
+
remediation: 'Implement strong authentication with proper session management'
|
|
99
|
+
}];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private async checkSSRF(url: string): Promise<Vulnerability[]> {
|
|
103
|
+
return [{
|
|
104
|
+
id: 'OWASP-MCP-006',
|
|
105
|
+
name: 'MCP-006: SSRF Vulnerabilities',
|
|
106
|
+
severity: 'critical',
|
|
107
|
+
description: 'Server-side request forgery may be possible',
|
|
108
|
+
affectedEndpoint: url,
|
|
109
|
+
remediation: 'Validate and restrict all server-side URL requests. Use allowlists.'
|
|
110
|
+
}];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private async checkCORS(url: string): Promise<Vulnerability[]> {
|
|
114
|
+
return [{
|
|
115
|
+
id: 'OWASP-MCP-007',
|
|
116
|
+
name: 'MCP-007: Cross-Origin Issues',
|
|
117
|
+
severity: 'medium',
|
|
118
|
+
description: 'CORS configuration may be too permissive',
|
|
119
|
+
affectedEndpoint: url,
|
|
120
|
+
remediation: 'Configure CORS with specific allowed origins instead of wildcards'
|
|
121
|
+
}];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private async checkBOLA(url: string): Promise<Vulnerability[]> {
|
|
125
|
+
return [{
|
|
126
|
+
id: 'OWASP-MCP-008',
|
|
127
|
+
name: 'MCP-008: Broken Object Level Authorization',
|
|
128
|
+
severity: 'high',
|
|
129
|
+
description: 'Object-level authorization may be insufficient',
|
|
130
|
+
affectedEndpoint: url,
|
|
131
|
+
remediation: 'Implement proper object-level access controls for each resource'
|
|
132
|
+
}];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private async checkSecurityConfig(url: string): Promise<Vulnerability[]> {
|
|
136
|
+
return [{
|
|
137
|
+
id: 'OWASP-MCP-009',
|
|
138
|
+
name: 'MCP-009: Security Misconfiguration',
|
|
139
|
+
severity: 'medium',
|
|
140
|
+
description: 'Security headers or configurations may be missing',
|
|
141
|
+
affectedEndpoint: url,
|
|
142
|
+
remediation: 'Implement security headers (CSP, HSTS, X-Frame-Options, etc.)'
|
|
143
|
+
}];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private async checkLogging(url: string): Promise<Vulnerability[]> {
|
|
147
|
+
return [{
|
|
148
|
+
id: 'OWASP-MCP-010',
|
|
149
|
+
name: 'MCP-010: Insufficient Logging',
|
|
150
|
+
severity: 'low',
|
|
151
|
+
description: 'Security logging may be insufficient',
|
|
152
|
+
affectedEndpoint: url,
|
|
153
|
+
remediation: 'Implement comprehensive security logging and monitoring'
|
|
154
|
+
}];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export default OWASPMCPDetector;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { BaseDetector, Vulnerability, DetectorOptions, VulnerabilityDetector } from '../index';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Path Traversal Detector - Checks for path traversal vulnerabilities
|
|
5
|
+
* Based on research showing 52.8% of MCP servers are vulnerable
|
|
6
|
+
*/
|
|
7
|
+
export class PathTraversalDetector extends BaseDetector implements VulnerabilityDetector {
|
|
8
|
+
name = 'Path Traversal Detector';
|
|
9
|
+
category: Vulnerability['category'] = 'path-traversal';
|
|
10
|
+
|
|
11
|
+
async applies(url: string, options?: DetectorOptions): Promise<boolean> {
|
|
12
|
+
this.log(`Checking for path traversal vulnerabilities in ${url}...`, 'info');
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async scan(url: string, options?: DetectorOptions): Promise<Vulnerability[]> {
|
|
17
|
+
const vulnerabilities: Vulnerability[] = [];
|
|
18
|
+
|
|
19
|
+
// Test 1: Standard path traversal patterns
|
|
20
|
+
const traversalTests = [
|
|
21
|
+
{ path: '../../../etc/passwd', description: 'Linux passwd file' },
|
|
22
|
+
{ path: '../../../etc/shadow', description: 'Linux shadow file' },
|
|
23
|
+
{ path: '..\\..\\..\\windows\\system32\\config\\sam', description: 'Windows SAM file' },
|
|
24
|
+
{ path: '....//....//etc/passwd', description: 'Double encoding bypass' },
|
|
25
|
+
{ path: '%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd', description: 'URL encoded traversal' },
|
|
26
|
+
{ path: '..%252f..%252fetc/passwd', description: 'Double URL encoded' },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
for (const test of traversalTests) {
|
|
30
|
+
const vuln = await this.testPathTraversal(url, test.path, test.description);
|
|
31
|
+
if (vuln) vulnerabilities.push(vuln);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Test 2: File inclusion vulnerabilities
|
|
35
|
+
const inclusionTests = [
|
|
36
|
+
{ path: 'file:///etc/passwd', description: 'File protocol' },
|
|
37
|
+
{ path: 'php://filter/convert.base64-encode/resource=/etc/passwd', description: 'PHP filter' },
|
|
38
|
+
{ path: 'data://text/plain,<?php phpinfo();>', description: 'Data protocol' },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
for (const test of inclusionTests) {
|
|
42
|
+
const vuln = await this.testFileInclusion(url, test.path, test.description);
|
|
43
|
+
if (vuln) vulnerabilities.push(vuln);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Test 3: MCP-specific path traversal
|
|
47
|
+
const mcpTests = [
|
|
48
|
+
{ path: '/../../config.json', description: 'Config file access' },
|
|
49
|
+
{ path: '/../../../.env', description: 'Environment variables' },
|
|
50
|
+
{ path: '/../../credentials.json', description: 'Credentials file' },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
for (const test of mcpTests) {
|
|
54
|
+
const vuln = await this.testMCPTraversal(url, test.path, test.description);
|
|
55
|
+
if (vuln) vulnerabilities.push(vuln);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.log(`Found ${vulnerabilities.length} potential path traversal vulnerabilities`,
|
|
59
|
+
vulnerabilities.length > 0 ? 'warn' : 'success');
|
|
60
|
+
|
|
61
|
+
return vulnerabilities;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private async testPathTraversal(url: string, path: string, description: string): Promise<Vulnerability | null> {
|
|
65
|
+
// In production, this would make actual HTTP requests
|
|
66
|
+
// Example: GET /files?path=../../../etc/passwd
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
id: `TRAVERSAL-${Date.now()}`,
|
|
70
|
+
name: 'Path Traversal Vulnerability',
|
|
71
|
+
severity: 'critical',
|
|
72
|
+
description: `The application may be vulnerable to path traversal attacks. Attempted to access: ${description}`,
|
|
73
|
+
affectedEndpoint: `${url}?file=${encodeURIComponent(path)}`,
|
|
74
|
+
remediation: 'Implement strict input validation, use allowlists for allowed file paths, and sanitize user inputs'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private async testFileInclusion(url: string, path: string, description: string): Promise<Vulnerability | null> {
|
|
79
|
+
return {
|
|
80
|
+
id: `INCLUSION-${Date.now()}`,
|
|
81
|
+
name: 'File Inclusion Vulnerability',
|
|
82
|
+
severity: 'critical',
|
|
83
|
+
description: `Potential file inclusion vulnerability with ${description}`,
|
|
84
|
+
affectedEndpoint: `${url}?include=${encodeURIComponent(path)}`,
|
|
85
|
+
remediation: 'Avoid using user input directly in file inclusion functions. Use explicit file paths.'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private async testMCPTraversal(url: string, path: string, description: string): Promise<Vulnerability | null> {
|
|
90
|
+
return {
|
|
91
|
+
id: `MCP-TRAVERSAL-${Date.now()}`,
|
|
92
|
+
name: 'MCP Path Traversal',
|
|
93
|
+
severity: 'critical',
|
|
94
|
+
description: `MCP-specific path traversal attempt: ${description}`,
|
|
95
|
+
affectedEndpoint: `${url}${path}`,
|
|
96
|
+
remediation: 'Validate all file path parameters in MCP requests. Use chroot or similar sandboxing.'
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default PathTraversalDetector;
|