@mayhemx87/mcpscan 0.3.1 → 0.4.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 +6 -3
- package/dist/discovery.js +5 -0
- package/dist/engine.js +19 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ the exact line of the offending server definition.
|
|
|
91
91
|
# .pre-commit-config.yaml
|
|
92
92
|
repos:
|
|
93
93
|
- repo: https://github.com/mayhemx87/mcpscan
|
|
94
|
-
rev: v0.
|
|
94
|
+
rev: v0.4.0
|
|
95
95
|
hooks:
|
|
96
96
|
- id: mcpscan
|
|
97
97
|
```
|
|
@@ -115,11 +115,14 @@ Dedicated MCP config files and editor settings files that embed MCP servers:
|
|
|
115
115
|
| `.amazonq/mcp.json` | Amazon Q |
|
|
116
116
|
| `.roo/mcp.json` | Roo Code |
|
|
117
117
|
| `.kiro/settings/mcp.json` | Kiro |
|
|
118
|
+
| `.trae/mcp.json` | Trae |
|
|
118
119
|
| `.claude/settings.json`, `.claude/settings.local.json` | Claude Code |
|
|
119
120
|
| `.gemini/settings.json` | Gemini CLI |
|
|
121
|
+
| `.zed/settings.json` (`context_servers`) | Zed |
|
|
120
122
|
|
|
121
|
-
Both `mcpServers` and VS Code's `servers` keys are understood
|
|
122
|
-
`
|
|
123
|
+
Both `mcpServers` and VS Code's `servers` keys are understood, as is Zed's
|
|
124
|
+
`context_servers` key; `command` and `args` are analyzed together, including
|
|
125
|
+
Zed's legacy nested command format; remote (`url`-based) servers are covered. Nested
|
|
123
126
|
occurrences of these paths are scanned too (any subdirectory can be opened as a
|
|
124
127
|
workspace root). Only `.git/` and `node_modules/` are skipped; `.gitignore` is
|
|
125
128
|
deliberately ignored — an injected config would be exactly the file that's
|
package/dist/discovery.js
CHANGED
|
@@ -11,6 +11,10 @@ const SKIP_DIRS = new Set(['.git', 'node_modules']);
|
|
|
11
11
|
*/
|
|
12
12
|
const MCP_CONFIG_SUFFIXES = [
|
|
13
13
|
'.mcp.json',
|
|
14
|
+
// A bare `mcp.json` in any directory. Besides Claude Code's own file this is
|
|
15
|
+
// the exact path several clients auto-load from their own dot-directory --
|
|
16
|
+
// Trae (`.trae/mcp.json`) among them -- so those need no entry of their own.
|
|
17
|
+
// Keep new clients out of this list unless their filename differs.
|
|
14
18
|
'mcp.json',
|
|
15
19
|
'.amazonq/mcp.json',
|
|
16
20
|
'.cursor/mcp.json',
|
|
@@ -24,6 +28,7 @@ const MCP_CONFIG_SUFFIXES = [
|
|
|
24
28
|
'.claude/settings.json',
|
|
25
29
|
'.claude/settings.local.json',
|
|
26
30
|
'.gemini/settings.json',
|
|
31
|
+
'.zed/settings.json',
|
|
27
32
|
];
|
|
28
33
|
function isMcpConfigPath(relPath) {
|
|
29
34
|
const p = relPath.replace(/\\/g, '/');
|
package/dist/engine.js
CHANGED
|
@@ -88,8 +88,9 @@ export function isEmbeddedHostFile(filePath) {
|
|
|
88
88
|
}
|
|
89
89
|
/** Extract the server map from any known config shape:
|
|
90
90
|
* - dedicated files: { mcpServers: {...} } or VS Code's { servers: {...} }
|
|
91
|
-
* - embedded hosts: { mcpServers: {...} } (Claude/Gemini settings)
|
|
92
|
-
* { mcp: { servers: {...} } } (VS Code settings.json)
|
|
91
|
+
* - embedded hosts: { mcpServers: {...} } (Claude/Gemini settings),
|
|
92
|
+
* { mcp: { servers: {...} } } (VS Code settings.json), or
|
|
93
|
+
* { context_servers: {...} } (Zed settings.json)
|
|
93
94
|
* Also returns the JSON path to the map so findings can carry line numbers. */
|
|
94
95
|
function extractServers(config) {
|
|
95
96
|
if (config.mcpServers && typeof config.mcpServers === 'object') {
|
|
@@ -102,6 +103,9 @@ function extractServers(config) {
|
|
|
102
103
|
if (mcp && typeof mcp === 'object' && mcp.servers && typeof mcp.servers === 'object') {
|
|
103
104
|
return { servers: mcp.servers, path: ['mcp', 'servers'] };
|
|
104
105
|
}
|
|
106
|
+
if (config.context_servers && typeof config.context_servers === 'object') {
|
|
107
|
+
return { servers: config.context_servers, path: ['context_servers'] };
|
|
108
|
+
}
|
|
105
109
|
return null;
|
|
106
110
|
}
|
|
107
111
|
/**
|
|
@@ -161,8 +165,18 @@ export function analyzeConfig(filePath, content, isGitTracked) {
|
|
|
161
165
|
// Analyze command AND args as one line -- the dominant real-world shape
|
|
162
166
|
// is {"command": "npx", "args": ["-y", "pkg"]}, invisible to any rule
|
|
163
167
|
// that inspects `command` alone (the v0.1.0 gap).
|
|
164
|
-
const
|
|
165
|
-
|
|
168
|
+
const command = typeof server.command === 'string' ? server.command : server.command?.path ?? '';
|
|
169
|
+
// Legacy Zed command.args and the top-level args are merged into one
|
|
170
|
+
// array -- MCP-004's absolute-path check scans `args` directly, so
|
|
171
|
+
// command.args must live there too or it silently evades detection.
|
|
172
|
+
const commandArgs = typeof server.command === 'object' && Array.isArray(server.command.args)
|
|
173
|
+
? server.command.args.filter(a => typeof a === 'string')
|
|
174
|
+
: [];
|
|
175
|
+
const args = [
|
|
176
|
+
...commandArgs,
|
|
177
|
+
...(Array.isArray(server.args) ? server.args.filter(a => typeof a === 'string') : []),
|
|
178
|
+
];
|
|
179
|
+
const line = [command, ...args].join(' ').trim();
|
|
166
180
|
const serverFindings = [];
|
|
167
181
|
// MCP-001: remote URL or network command execution
|
|
168
182
|
if (line && (/https?:\/\//.test(line) || /\b(curl|wget)\b/.test(line))) {
|
|
@@ -202,7 +216,7 @@ export function analyzeConfig(filePath, content, isGitTracked) {
|
|
|
202
216
|
}
|
|
203
217
|
}
|
|
204
218
|
// MCP-004: absolute path or path traversal outside repo
|
|
205
|
-
const cmd =
|
|
219
|
+
const cmd = command;
|
|
206
220
|
if (cmd.startsWith('/') ||
|
|
207
221
|
args.some(a => a.startsWith('/')) ||
|
|
208
222
|
/(?:^|[\s;|&])\.\.\//.test(line)) {
|