mcpsec 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/LICENSE +21 -0
- package/README.md +75 -0
- package/package.json +48 -0
- package/src/cli/index.ts +158 -0
- package/src/lib/injection-patterns.ts +283 -0
- package/src/lib/mcp-client.ts +384 -0
- package/src/lib/types.ts +90 -0
- package/src/lib/url-validator.ts +130 -0
- package/src/scanner/config-scanner.ts +262 -0
- package/src/scanner/credential-scanner.ts +200 -0
- package/src/scanner/live-scanner.ts +375 -0
- package/src/scanner/report.ts +248 -0
- package/src/scanner/tool-scanner.ts +142 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel MCP - Tool Description Scanner
|
|
3
|
+
*
|
|
4
|
+
* Scans MCP tool descriptions, resource URIs, and prompt templates for:
|
|
5
|
+
* - Prompt injection patterns (tool poisoning)
|
|
6
|
+
* - Hidden instructions in tool descriptions
|
|
7
|
+
* - Tool shadowing attempts
|
|
8
|
+
* - Data exfiltration indicators
|
|
9
|
+
* - Command injection in default arguments
|
|
10
|
+
*
|
|
11
|
+
* Maps to OWASP MCP Top 10: Tool Poisoning, Excessive Agency
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { MCPConfigFile, Finding, Scanner } from '../lib/types';
|
|
15
|
+
import {
|
|
16
|
+
analyzeForInjection,
|
|
17
|
+
INJECTION_PATTERNS,
|
|
18
|
+
TOOL_POISONING_PATTERNS,
|
|
19
|
+
COMMAND_INJECTION_PATTERNS,
|
|
20
|
+
} from '../lib/injection-patterns';
|
|
21
|
+
import { validateURL } from '../lib/url-validator';
|
|
22
|
+
|
|
23
|
+
export const toolScanner: Scanner = {
|
|
24
|
+
name: 'Tool Description Scanner',
|
|
25
|
+
|
|
26
|
+
async scan(configs: MCPConfigFile[]): Promise<Finding[]> {
|
|
27
|
+
const findings: Finding[] = [];
|
|
28
|
+
let findingId = 0;
|
|
29
|
+
|
|
30
|
+
for (const config of configs) {
|
|
31
|
+
for (const [serverName, server] of Object.entries(config.servers)) {
|
|
32
|
+
|
|
33
|
+
// Scan command + args for injection patterns
|
|
34
|
+
const commandParts = [
|
|
35
|
+
server.command || '',
|
|
36
|
+
...(server.args || []),
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
for (const part of commandParts) {
|
|
40
|
+
if (!part || part.length < 5) continue;
|
|
41
|
+
|
|
42
|
+
// Check for command injection patterns
|
|
43
|
+
const cmdAnalysis = analyzeForInjection(part, [COMMAND_INJECTION_PATTERNS]);
|
|
44
|
+
if (cmdAnalysis.detected) {
|
|
45
|
+
for (const match of cmdAnalysis.matches) {
|
|
46
|
+
findings.push({
|
|
47
|
+
id: `TOOL-${++findingId}`,
|
|
48
|
+
severity: match.severity,
|
|
49
|
+
category: 'command-injection',
|
|
50
|
+
title: `${match.name} in server command`,
|
|
51
|
+
description: `Server "${serverName}" command/args contain a ${match.name.toLowerCase()} pattern.`,
|
|
52
|
+
server: serverName,
|
|
53
|
+
configFile: config.path,
|
|
54
|
+
evidence: part.length > 100 ? part.substring(0, 100) + '...' : part,
|
|
55
|
+
remediation: 'Review and sanitize the command arguments. Avoid shell metacharacters in MCP server configs.',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check for injection patterns in args (tool descriptions are sometimes embedded)
|
|
61
|
+
const injAnalysis = analyzeForInjection(part, [INJECTION_PATTERNS, TOOL_POISONING_PATTERNS]);
|
|
62
|
+
if (injAnalysis.detected) {
|
|
63
|
+
for (const match of injAnalysis.matches) {
|
|
64
|
+
findings.push({
|
|
65
|
+
id: `TOOL-${++findingId}`,
|
|
66
|
+
severity: match.severity,
|
|
67
|
+
category: match.category.includes('Poisoning') || match.category.includes('Shadow')
|
|
68
|
+
? 'tool-poisoning'
|
|
69
|
+
: 'prompt-injection',
|
|
70
|
+
title: `${match.name} in server arguments`,
|
|
71
|
+
description: `Server "${serverName}" arguments contain a ${match.name.toLowerCase()} pattern. This may indicate a tool poisoning attack.`,
|
|
72
|
+
server: serverName,
|
|
73
|
+
configFile: config.path,
|
|
74
|
+
evidence: part.length > 100 ? part.substring(0, 100) + '...' : part,
|
|
75
|
+
remediation: 'Inspect the MCP server source code and tool descriptions for hidden instructions.',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Validate server URLs for SSRF
|
|
82
|
+
if (server.url) {
|
|
83
|
+
const urlResult = validateURL(server.url);
|
|
84
|
+
if (!urlResult.valid) {
|
|
85
|
+
findings.push({
|
|
86
|
+
id: `TOOL-${++findingId}`,
|
|
87
|
+
severity: urlResult.severity || 'high',
|
|
88
|
+
category: 'ssrf',
|
|
89
|
+
title: `Unsafe server URL`,
|
|
90
|
+
description: `Server "${serverName}" URL failed validation: ${urlResult.reason}`,
|
|
91
|
+
server: serverName,
|
|
92
|
+
configFile: config.path,
|
|
93
|
+
evidence: `url: ${server.url}`,
|
|
94
|
+
remediation: 'Use a publicly routable HTTPS URL for remote MCP servers.',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const warning of urlResult.warnings) {
|
|
99
|
+
findings.push({
|
|
100
|
+
id: `TOOL-${++findingId}`,
|
|
101
|
+
severity: 'medium',
|
|
102
|
+
category: warning.includes('HTTP') ? 'insecure-transport' : 'configuration',
|
|
103
|
+
title: `URL warning: ${warning}`,
|
|
104
|
+
description: `Server "${serverName}" URL has a security concern: ${warning}`,
|
|
105
|
+
server: serverName,
|
|
106
|
+
configFile: config.path,
|
|
107
|
+
evidence: `url: ${server.url}`,
|
|
108
|
+
remediation: warning.includes('HTTP')
|
|
109
|
+
? 'Use HTTPS to encrypt data in transit.'
|
|
110
|
+
: 'Review and fix the URL configuration.',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check env vars for suspicious patterns
|
|
116
|
+
if (server.env) {
|
|
117
|
+
const envStr = JSON.stringify(server.env);
|
|
118
|
+
|
|
119
|
+
// Check for injection in env values
|
|
120
|
+
const envAnalysis = analyzeForInjection(envStr, [INJECTION_PATTERNS]);
|
|
121
|
+
if (envAnalysis.detected) {
|
|
122
|
+
for (const match of envAnalysis.matches) {
|
|
123
|
+
findings.push({
|
|
124
|
+
id: `TOOL-${++findingId}`,
|
|
125
|
+
severity: match.severity,
|
|
126
|
+
category: 'prompt-injection',
|
|
127
|
+
title: `${match.name} in environment variables`,
|
|
128
|
+
description: `Server "${serverName}" environment variables contain injection patterns.`,
|
|
129
|
+
server: serverName,
|
|
130
|
+
configFile: config.path,
|
|
131
|
+
evidence: 'Injection pattern detected in env configuration',
|
|
132
|
+
remediation: 'Review environment variable values for hidden instructions or injection payloads.',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return findings;
|
|
141
|
+
},
|
|
142
|
+
};
|