mcp-security-scanner 1.0.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.ar.md +662 -0
- package/README.bn.md +662 -0
- package/README.bs.md +662 -0
- package/README.da.md +662 -0
- package/README.de.md +662 -0
- package/README.el.md +662 -0
- package/README.es.md +662 -0
- package/README.fr.md +663 -0
- package/README.hi.md +662 -0
- package/README.it.md +662 -0
- package/README.ja.md +663 -0
- package/README.ko.md +662 -0
- package/README.md +662 -0
- package/README.no.md +662 -0
- package/README.pl.md +662 -0
- package/README.pt-BR.md +662 -0
- package/README.ru.md +662 -0
- package/README.th.md +662 -0
- package/README.tr.md +662 -0
- package/README.uk.md +663 -0
- package/README.vi.md +662 -0
- package/README.zh-TW.md +661 -0
- package/README.zh.md +661 -0
- package/dist/config/env-scanner.d.ts +3 -0
- package/dist/config/env-scanner.d.ts.map +1 -0
- package/dist/config/env-scanner.js +85 -0
- package/dist/config/env-scanner.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +169 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/mcp-config-parser.d.ts +16 -0
- package/dist/config/mcp-config-parser.d.ts.map +1 -0
- package/dist/config/mcp-config-parser.js +86 -0
- package/dist/config/mcp-config-parser.js.map +1 -0
- package/dist/config/server-verification.d.ts +5 -0
- package/dist/config/server-verification.d.ts.map +1 -0
- package/dist/config/server-verification.js +221 -0
- package/dist/config/server-verification.js.map +1 -0
- package/dist/data/dangerous-sinks.d.ts +13 -0
- package/dist/data/dangerous-sinks.d.ts.map +1 -0
- package/dist/data/dangerous-sinks.js +45 -0
- package/dist/data/dangerous-sinks.js.map +1 -0
- package/dist/data/owasp-mcp-top10.d.ts +12 -0
- package/dist/data/owasp-mcp-top10.d.ts.map +1 -0
- package/dist/data/owasp-mcp-top10.js +95 -0
- package/dist/data/owasp-mcp-top10.js.map +1 -0
- package/dist/data/poisoning-patterns.d.ts +15 -0
- package/dist/data/poisoning-patterns.d.ts.map +1 -0
- package/dist/data/poisoning-patterns.js +146 -0
- package/dist/data/poisoning-patterns.js.map +1 -0
- package/dist/data/popular-packages.d.ts +2 -0
- package/dist/data/popular-packages.d.ts.map +1 -0
- package/dist/data/popular-packages.js +71 -0
- package/dist/data/popular-packages.js.map +1 -0
- package/dist/data/secret-patterns.d.ts +8 -0
- package/dist/data/secret-patterns.d.ts.map +1 -0
- package/dist/data/secret-patterns.js +129 -0
- package/dist/data/secret-patterns.js.map +1 -0
- package/dist/deps/index.d.ts +3 -0
- package/dist/deps/index.d.ts.map +1 -0
- package/dist/deps/index.js +308 -0
- package/dist/deps/index.js.map +1 -0
- package/dist/deps/install-script-detector.d.ts +9 -0
- package/dist/deps/install-script-detector.d.ts.map +1 -0
- package/dist/deps/install-script-detector.js +98 -0
- package/dist/deps/install-script-detector.js.map +1 -0
- package/dist/deps/lockfile-parser.d.ts +15 -0
- package/dist/deps/lockfile-parser.d.ts.map +1 -0
- package/dist/deps/lockfile-parser.js +123 -0
- package/dist/deps/lockfile-parser.js.map +1 -0
- package/dist/deps/typosquat-checker.d.ts +10 -0
- package/dist/deps/typosquat-checker.d.ts.map +1 -0
- package/dist/deps/typosquat-checker.js +84 -0
- package/dist/deps/typosquat-checker.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +315 -0
- package/dist/index.js.map +1 -0
- package/dist/meta/sources.d.ts +3 -0
- package/dist/meta/sources.d.ts.map +1 -0
- package/dist/meta/sources.js +43 -0
- package/dist/meta/sources.js.map +1 -0
- package/dist/protocol/mcp-server.d.ts +4 -0
- package/dist/protocol/mcp-server.d.ts.map +1 -0
- package/dist/protocol/mcp-server.js +32 -0
- package/dist/protocol/mcp-server.js.map +1 -0
- package/dist/protocol/tools.d.ts +3 -0
- package/dist/protocol/tools.d.ts.map +1 -0
- package/dist/protocol/tools.js +21 -0
- package/dist/protocol/tools.js.map +1 -0
- package/dist/report/index.d.ts +3 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +259 -0
- package/dist/report/index.js.map +1 -0
- package/dist/report/json-report.d.ts +4 -0
- package/dist/report/json-report.d.ts.map +1 -0
- package/dist/report/json-report.js +61 -0
- package/dist/report/json-report.js.map +1 -0
- package/dist/report/markdown.d.ts +3 -0
- package/dist/report/markdown.d.ts.map +1 -0
- package/dist/report/markdown.js +89 -0
- package/dist/report/markdown.js.map +1 -0
- package/dist/report/sarif.d.ts +3 -0
- package/dist/report/sarif.d.ts.map +1 -0
- package/dist/report/sarif.js +56 -0
- package/dist/report/sarif.js.map +1 -0
- package/dist/runtime/client.d.ts +31 -0
- package/dist/runtime/client.d.ts.map +1 -0
- package/dist/runtime/client.js +53 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +239 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/pinning.d.ts +21 -0
- package/dist/runtime/pinning.d.ts.map +1 -0
- package/dist/runtime/pinning.js +74 -0
- package/dist/runtime/pinning.js.map +1 -0
- package/dist/runtime/schema-analyzer.d.ts +14 -0
- package/dist/runtime/schema-analyzer.d.ts.map +1 -0
- package/dist/runtime/schema-analyzer.js +204 -0
- package/dist/runtime/schema-analyzer.js.map +1 -0
- package/dist/runtime/tool-analyzer.d.ts +6 -0
- package/dist/runtime/tool-analyzer.d.ts.map +1 -0
- package/dist/runtime/tool-analyzer.js +92 -0
- package/dist/runtime/tool-analyzer.js.map +1 -0
- package/dist/static/analyzers/code-execution.d.ts +4 -0
- package/dist/static/analyzers/code-execution.d.ts.map +1 -0
- package/dist/static/analyzers/code-execution.js +72 -0
- package/dist/static/analyzers/code-execution.js.map +1 -0
- package/dist/static/analyzers/command-injection.d.ts +4 -0
- package/dist/static/analyzers/command-injection.d.ts.map +1 -0
- package/dist/static/analyzers/command-injection.js +62 -0
- package/dist/static/analyzers/command-injection.js.map +1 -0
- package/dist/static/analyzers/info-disclosure.d.ts +4 -0
- package/dist/static/analyzers/info-disclosure.d.ts.map +1 -0
- package/dist/static/analyzers/info-disclosure.js +65 -0
- package/dist/static/analyzers/info-disclosure.js.map +1 -0
- package/dist/static/analyzers/insecure-crypto.d.ts +4 -0
- package/dist/static/analyzers/insecure-crypto.d.ts.map +1 -0
- package/dist/static/analyzers/insecure-crypto.js +65 -0
- package/dist/static/analyzers/insecure-crypto.js.map +1 -0
- package/dist/static/analyzers/logging-audit.d.ts +4 -0
- package/dist/static/analyzers/logging-audit.d.ts.map +1 -0
- package/dist/static/analyzers/logging-audit.js +81 -0
- package/dist/static/analyzers/logging-audit.js.map +1 -0
- package/dist/static/analyzers/path-traversal.d.ts +4 -0
- package/dist/static/analyzers/path-traversal.d.ts.map +1 -0
- package/dist/static/analyzers/path-traversal.js +42 -0
- package/dist/static/analyzers/path-traversal.js.map +1 -0
- package/dist/static/analyzers/prototype-pollution.d.ts +4 -0
- package/dist/static/analyzers/prototype-pollution.d.ts.map +1 -0
- package/dist/static/analyzers/prototype-pollution.js +80 -0
- package/dist/static/analyzers/prototype-pollution.js.map +1 -0
- package/dist/static/analyzers/regex-dos.d.ts +4 -0
- package/dist/static/analyzers/regex-dos.d.ts.map +1 -0
- package/dist/static/analyzers/regex-dos.js +78 -0
- package/dist/static/analyzers/regex-dos.js.map +1 -0
- package/dist/static/analyzers/secret-hardcoded.d.ts +4 -0
- package/dist/static/analyzers/secret-hardcoded.d.ts.map +1 -0
- package/dist/static/analyzers/secret-hardcoded.js +70 -0
- package/dist/static/analyzers/secret-hardcoded.js.map +1 -0
- package/dist/static/analyzers/ssrf.d.ts +4 -0
- package/dist/static/analyzers/ssrf.d.ts.map +1 -0
- package/dist/static/analyzers/ssrf.js +39 -0
- package/dist/static/analyzers/ssrf.js.map +1 -0
- package/dist/static/analyzers/unsafe-regex.d.ts +4 -0
- package/dist/static/analyzers/unsafe-regex.d.ts.map +1 -0
- package/dist/static/analyzers/unsafe-regex.js +36 -0
- package/dist/static/analyzers/unsafe-regex.js.map +1 -0
- package/dist/static/ast-engine.d.ts +22 -0
- package/dist/static/ast-engine.d.ts.map +1 -0
- package/dist/static/ast-engine.js +155 -0
- package/dist/static/ast-engine.js.map +1 -0
- package/dist/static/index.d.ts +3 -0
- package/dist/static/index.d.ts.map +1 -0
- package/dist/static/index.js +114 -0
- package/dist/static/index.js.map +1 -0
- package/dist/static/taint-tracker.d.ts +15 -0
- package/dist/static/taint-tracker.d.ts.map +1 -0
- package/dist/static/taint-tracker.js +70 -0
- package/dist/static/taint-tracker.js.map +1 -0
- package/dist/types/findings.d.ts +60 -0
- package/dist/types/findings.d.ts.map +1 -0
- package/dist/types/findings.js +9 -0
- package/dist/types/findings.js.map +1 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/crypto.d.ts +4 -0
- package/dist/utils/crypto.d.ts.map +1 -0
- package/dist/utils/crypto.js +12 -0
- package/dist/utils/crypto.js.map +1 -0
- package/dist/utils/fs-helpers.d.ts +7 -0
- package/dist/utils/fs-helpers.d.ts.map +1 -0
- package/dist/utils/fs-helpers.js +92 -0
- package/dist/utils/fs-helpers.js.map +1 -0
- package/dist/utils/levenshtein.d.ts +7 -0
- package/dist/utils/levenshtein.d.ts.map +1 -0
- package/dist/utils/levenshtein.js +89 -0
- package/dist/utils/levenshtein.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { text, json } from "../types/index.js";
|
|
3
|
+
import { connectAndInspect } from "./client.js";
|
|
4
|
+
import { analyzePoisoning, analyzeAnsiInjection, analyzeUnicodeSteganography } from "./tool-analyzer.js";
|
|
5
|
+
import { analyzeScope, analyzeToolShadowing, analyzeCrossOrigin, analyzeResourceExposure } from "./schema-analyzer.js";
|
|
6
|
+
import { savePin, loadPin, verifyAgainstPin } from "./pinning.js";
|
|
7
|
+
// Common schema for server connection
|
|
8
|
+
const serverSchema = {
|
|
9
|
+
command: z.string().describe("Server command to execute (e.g. 'node', 'bun', 'npx')"),
|
|
10
|
+
args: z.array(z.string()).optional().describe("Command arguments (e.g. ['run', 'server.js'])"),
|
|
11
|
+
env: z.record(z.string()).optional().describe("Additional environment variables"),
|
|
12
|
+
timeout_ms: z.number().optional().describe("Connection timeout in milliseconds (default: 30000)"),
|
|
13
|
+
};
|
|
14
|
+
function formatFindings(findings) {
|
|
15
|
+
if (findings.length === 0)
|
|
16
|
+
return "No findings.";
|
|
17
|
+
const bySeverity = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
|
|
18
|
+
for (const f of findings)
|
|
19
|
+
bySeverity[f.severity]++;
|
|
20
|
+
let output = `${findings.length} finding(s): ${bySeverity.critical} critical, ${bySeverity.high} high, ${bySeverity.medium} medium, ${bySeverity.low} low, ${bySeverity.info} info\n\n`;
|
|
21
|
+
for (const f of findings) {
|
|
22
|
+
output += `[${f.severity.toUpperCase()}] ${f.id}: ${f.title}\n`;
|
|
23
|
+
output += ` OWASP: ${f.owasp_mcp} — ${f.owasp_mcp_title}\n`;
|
|
24
|
+
output += ` Evidence: ${f.evidence}\n`;
|
|
25
|
+
output += ` Remediation: ${f.remediation}\n\n`;
|
|
26
|
+
}
|
|
27
|
+
return output.trim();
|
|
28
|
+
}
|
|
29
|
+
const rtInspectServer = {
|
|
30
|
+
name: "rt_inspect_server",
|
|
31
|
+
description: "Connect to an MCP server via stdio, enumerate all tools with descriptions and schemas, list resources and prompts. Returns full server capability manifest.",
|
|
32
|
+
schema: serverSchema,
|
|
33
|
+
async execute(args) {
|
|
34
|
+
const manifest = await connectAndInspect(args.command, args.args, args.env, args.timeout_ms ?? 30_000);
|
|
35
|
+
return json({
|
|
36
|
+
tool_count: manifest.tools.length,
|
|
37
|
+
resource_count: manifest.resources.length,
|
|
38
|
+
prompt_count: manifest.prompts.length,
|
|
39
|
+
tools: manifest.tools,
|
|
40
|
+
resources: manifest.resources,
|
|
41
|
+
prompts: manifest.prompts,
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const rtCheckToolPoisoning = {
|
|
46
|
+
name: "rt_check_tool_poisoning",
|
|
47
|
+
description: "Analyze ALL tool descriptions for hidden prompt injection instructions. Checks for: file read instructions, exfiltration patterns, instruction override, system prompt extraction, social engineering. Returns findings with matched pattern and severity.",
|
|
48
|
+
schema: {
|
|
49
|
+
...serverSchema,
|
|
50
|
+
tool_name: z.string().optional().describe("Check only this tool (default: all tools)"),
|
|
51
|
+
},
|
|
52
|
+
async execute(args) {
|
|
53
|
+
const manifest = await connectAndInspect(args.command, args.args, args.env, args.timeout_ms ?? 30_000);
|
|
54
|
+
let tools = manifest.tools;
|
|
55
|
+
if (args.tool_name) {
|
|
56
|
+
tools = tools.filter((t) => t.name === args.tool_name);
|
|
57
|
+
if (tools.length === 0)
|
|
58
|
+
return text(`Tool "${args.tool_name}" not found in server.`);
|
|
59
|
+
}
|
|
60
|
+
const findings = analyzePoisoning(tools);
|
|
61
|
+
return text(formatFindings(findings));
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
const rtCheckAnsiInjection = {
|
|
65
|
+
name: "rt_check_ansi_injection",
|
|
66
|
+
description: "Scan all tool descriptions and schema field descriptions for ANSI escape sequences (CSI codes, cursor movement, color codes) used to hide malicious text in terminal display while LLM still reads it.",
|
|
67
|
+
schema: serverSchema,
|
|
68
|
+
async execute(args) {
|
|
69
|
+
const manifest = await connectAndInspect(args.command, args.args, args.env, args.timeout_ms ?? 30_000);
|
|
70
|
+
const findings = analyzeAnsiInjection(manifest.tools);
|
|
71
|
+
return text(formatFindings(findings));
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
const rtCheckUnicodeSteganography = {
|
|
75
|
+
name: "rt_check_unicode_steganography",
|
|
76
|
+
description: "Detect hidden Unicode characters in tool descriptions: zero-width spaces, zero-width joiners, word joiners, RTL/LTR override, BOM, invisible separators, homoglyph characters. These can hide instructions visible to LLM but invisible to humans.",
|
|
77
|
+
schema: serverSchema,
|
|
78
|
+
async execute(args) {
|
|
79
|
+
const manifest = await connectAndInspect(args.command, args.args, args.env, args.timeout_ms ?? 30_000);
|
|
80
|
+
const findings = analyzeUnicodeSteganography(manifest.tools);
|
|
81
|
+
return text(formatFindings(findings));
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
const rtCheckScopeCreep = {
|
|
85
|
+
name: "rt_check_scope_creep",
|
|
86
|
+
description: "Analyze tool schemas for over-permissive parameter types: arbitrary file paths, unrestricted URLs, shell commands, wildcard globs, any-type schemas. Also flags excessive tool count (>50).",
|
|
87
|
+
schema: serverSchema,
|
|
88
|
+
async execute(args) {
|
|
89
|
+
const manifest = await connectAndInspect(args.command, args.args, args.env, args.timeout_ms ?? 30_000);
|
|
90
|
+
const findings = analyzeScope(manifest.tools);
|
|
91
|
+
return text(formatFindings(findings));
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
const rtCheckToolShadowing = {
|
|
95
|
+
name: "rt_check_tool_shadowing",
|
|
96
|
+
description: "Detect tools with names that shadow common MCP tool names from well-known servers (read_file, write_file, execute_command, bash, etc.). A rogue server registering these names could intercept calls intended for legitimate servers.",
|
|
97
|
+
schema: {
|
|
98
|
+
...serverSchema,
|
|
99
|
+
known_tools: z.array(z.string()).optional().describe("Custom list of known tool names to check against"),
|
|
100
|
+
},
|
|
101
|
+
async execute(args) {
|
|
102
|
+
const manifest = await connectAndInspect(args.command, args.args, args.env, args.timeout_ms ?? 30_000);
|
|
103
|
+
const findings = analyzeToolShadowing(manifest.tools, args.known_tools);
|
|
104
|
+
return text(formatFindings(findings));
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
const rtCheckCrossOrigin = {
|
|
108
|
+
name: "rt_check_cross_origin",
|
|
109
|
+
description: "Scan tool descriptions for references to tools from OTHER servers — patterns like 'when using the email tool', 'before calling read_file'. These cross-origin instructions enable tool shadowing attacks.",
|
|
110
|
+
schema: serverSchema,
|
|
111
|
+
async execute(args) {
|
|
112
|
+
const manifest = await connectAndInspect(args.command, args.args, args.env, args.timeout_ms ?? 30_000);
|
|
113
|
+
const findings = analyzeCrossOrigin(manifest.tools);
|
|
114
|
+
return text(formatFindings(findings));
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
const rtPinTools = {
|
|
118
|
+
name: "rt_pin_tools",
|
|
119
|
+
description: "Connect to server, SHA-256 hash every tool definition (name + description + schema), store as a pin file. Use rt_verify_pins later to detect tool definition changes (rug pull detection).",
|
|
120
|
+
schema: {
|
|
121
|
+
...serverSchema,
|
|
122
|
+
pin_name: z.string().describe("Name for this pin (used as filename, e.g. 'my-mcp-server')"),
|
|
123
|
+
},
|
|
124
|
+
async execute(args, ctx) {
|
|
125
|
+
const manifest = await connectAndInspect(args.command, args.args, args.env, args.timeout_ms ?? 30_000);
|
|
126
|
+
const pinFile = await savePin(ctx.config.pinDir, args.pin_name, args.command, args.args, manifest.tools);
|
|
127
|
+
return text(`Pin saved: ${pinFile.pin_name}\n` +
|
|
128
|
+
`Tools: ${pinFile.tool_count}\n` +
|
|
129
|
+
`Manifest hash: ${pinFile.manifest_hash}\n` +
|
|
130
|
+
`Timestamp: ${pinFile.timestamp}\n\n` +
|
|
131
|
+
pinFile.tools.map((t) => ` ${t.name}: ${t.hash.substring(0, 16)}...`).join("\n"));
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
const rtVerifyPins = {
|
|
135
|
+
name: "rt_verify_pins",
|
|
136
|
+
description: "Connect to server, hash current tool definitions, compare against stored pin. Reports: added tools, removed tools, modified tools (hash changed — potential rug pull), unchanged tools.",
|
|
137
|
+
schema: {
|
|
138
|
+
...serverSchema,
|
|
139
|
+
pin_name: z.string().describe("Pin name to verify against"),
|
|
140
|
+
},
|
|
141
|
+
async execute(args, ctx) {
|
|
142
|
+
const pin = await loadPin(ctx.config.pinDir, args.pin_name);
|
|
143
|
+
if (!pin)
|
|
144
|
+
return text(`Pin "${args.pin_name}" not found. Run rt_pin_tools first.`);
|
|
145
|
+
const manifest = await connectAndInspect(args.command, args.args, args.env, args.timeout_ms ?? 30_000);
|
|
146
|
+
const verification = verifyAgainstPin(pin, manifest.tools);
|
|
147
|
+
if (verification.matched) {
|
|
148
|
+
return text(`PIN VERIFIED: All ${verification.unchanged.length} tools match the pin "${args.pin_name}".\n` +
|
|
149
|
+
`Original pin: ${pin.timestamp}`);
|
|
150
|
+
}
|
|
151
|
+
let output = `PIN MISMATCH: Tool definitions have changed since pin "${args.pin_name}" (${pin.timestamp})\n\n`;
|
|
152
|
+
if (verification.added.length > 0) {
|
|
153
|
+
output += `ADDED (${verification.added.length}):\n`;
|
|
154
|
+
for (const t of verification.added)
|
|
155
|
+
output += ` + ${t.name}: ${t.description_preview}\n`;
|
|
156
|
+
output += "\n";
|
|
157
|
+
}
|
|
158
|
+
if (verification.removed.length > 0) {
|
|
159
|
+
output += `REMOVED (${verification.removed.length}):\n`;
|
|
160
|
+
for (const t of verification.removed)
|
|
161
|
+
output += ` - ${t.name}: ${t.description_preview}\n`;
|
|
162
|
+
output += "\n";
|
|
163
|
+
}
|
|
164
|
+
if (verification.modified.length > 0) {
|
|
165
|
+
output += `MODIFIED (${verification.modified.length}) — POTENTIAL RUG PULL:\n`;
|
|
166
|
+
for (const m of verification.modified)
|
|
167
|
+
output += ` ~ ${m.name}: ${m.oldHash.substring(0, 16)}... → ${m.newHash.substring(0, 16)}...\n`;
|
|
168
|
+
output += "\n";
|
|
169
|
+
}
|
|
170
|
+
output += `Unchanged: ${verification.unchanged.length}\n`;
|
|
171
|
+
return text(output.trim());
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
const rtCheckAuth = {
|
|
175
|
+
name: "rt_check_auth",
|
|
176
|
+
description: "Test if MCP server requires authentication. Connects without credentials and checks if tools are accessible. Flags servers that accept unauthenticated connections.",
|
|
177
|
+
schema: serverSchema,
|
|
178
|
+
async execute(args) {
|
|
179
|
+
// For stdio servers, there's no auth layer — we check if the server
|
|
180
|
+
// starts without any auth env vars and still exposes tools
|
|
181
|
+
const manifest = await connectAndInspect(args.command, args.args, {}, // empty env — no API keys
|
|
182
|
+
args.timeout_ms ?? 30_000);
|
|
183
|
+
const findings = [];
|
|
184
|
+
if (manifest.tools.length > 0) {
|
|
185
|
+
findings.push({
|
|
186
|
+
id: "RT-AUTH-001",
|
|
187
|
+
title: "Server Accepts Unauthenticated Connections",
|
|
188
|
+
severity: "high",
|
|
189
|
+
owasp_mcp: "MCP07",
|
|
190
|
+
owasp_mcp_title: "Insufficient Authentication & Transport Security",
|
|
191
|
+
category: "runtime",
|
|
192
|
+
evidence: `Server exposes ${manifest.tools.length} tools without authentication. Connected with empty environment (no API keys).`,
|
|
193
|
+
remediation: "Implement authentication for the MCP server. For stdio servers, validate caller identity. For remote servers, require OAuth 2.1 tokens.",
|
|
194
|
+
cwe: "CWE-287",
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return text(formatFindings(findings));
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
const rtCheckResourceExposure = {
|
|
201
|
+
name: "rt_check_resource_exposure",
|
|
202
|
+
description: "Enumerate all MCP resources and prompts exposed by the server. Flag resources with broad URI patterns (file://*, https://*), resources exposing sensitive paths, and prompts that could be used for social engineering.",
|
|
203
|
+
schema: serverSchema,
|
|
204
|
+
async execute(args) {
|
|
205
|
+
const manifest = await connectAndInspect(args.command, args.args, args.env, args.timeout_ms ?? 30_000);
|
|
206
|
+
const findings = analyzeResourceExposure(manifest.resources, manifest.prompts);
|
|
207
|
+
let output = `Resources: ${manifest.resources.length}, Prompts: ${manifest.prompts.length}\n\n`;
|
|
208
|
+
if (manifest.resources.length > 0) {
|
|
209
|
+
output += "Resources:\n";
|
|
210
|
+
for (const r of manifest.resources) {
|
|
211
|
+
output += ` ${r.uri} — ${r.name ?? "unnamed"}\n`;
|
|
212
|
+
}
|
|
213
|
+
output += "\n";
|
|
214
|
+
}
|
|
215
|
+
if (manifest.prompts.length > 0) {
|
|
216
|
+
output += "Prompts:\n";
|
|
217
|
+
for (const p of manifest.prompts) {
|
|
218
|
+
output += ` ${p.name} — ${p.description ?? "no description"}\n`;
|
|
219
|
+
}
|
|
220
|
+
output += "\n";
|
|
221
|
+
}
|
|
222
|
+
output += formatFindings(findings);
|
|
223
|
+
return text(output.trim());
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
export const runtimeTools = [
|
|
227
|
+
rtInspectServer,
|
|
228
|
+
rtCheckToolPoisoning,
|
|
229
|
+
rtCheckAnsiInjection,
|
|
230
|
+
rtCheckUnicodeSteganography,
|
|
231
|
+
rtCheckScopeCreep,
|
|
232
|
+
rtCheckToolShadowing,
|
|
233
|
+
rtCheckCrossOrigin,
|
|
234
|
+
rtPinTools,
|
|
235
|
+
rtVerifyPins,
|
|
236
|
+
rtCheckAuth,
|
|
237
|
+
rtCheckResourceExposure,
|
|
238
|
+
];
|
|
239
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PinFile, ToolPin } from "../types/findings.js";
|
|
2
|
+
import type { ServerTool } from "./client.js";
|
|
3
|
+
export declare function hashTools(tools: ServerTool[]): {
|
|
4
|
+
tools: ToolPin[];
|
|
5
|
+
manifestHash: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function savePin(pinDir: string, pinName: string, command: string, args: string[] | undefined, tools: ServerTool[]): Promise<PinFile>;
|
|
8
|
+
export declare function loadPin(pinDir: string, pinName: string): Promise<PinFile | null>;
|
|
9
|
+
export interface PinVerification {
|
|
10
|
+
matched: boolean;
|
|
11
|
+
added: ToolPin[];
|
|
12
|
+
removed: ToolPin[];
|
|
13
|
+
modified: {
|
|
14
|
+
name: string;
|
|
15
|
+
oldHash: string;
|
|
16
|
+
newHash: string;
|
|
17
|
+
}[];
|
|
18
|
+
unchanged: string[];
|
|
19
|
+
}
|
|
20
|
+
export declare function verifyAgainstPin(pin: PinFile, currentTools: ServerTool[]): PinVerification;
|
|
21
|
+
//# sourceMappingURL=pinning.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinning.d.ts","sourceRoot":"","sources":["../../src/runtime/pinning.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG;IAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CASzF;AAED,wBAAsB,OAAO,CAC3B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,EAC1B,KAAK,EAAE,UAAU,EAAE,GAClB,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAQtF;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/D,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,eAAe,CAqC1F"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { hashToolDefinition, hashManifest } from "../utils/crypto.js";
|
|
4
|
+
export function hashTools(tools) {
|
|
5
|
+
const toolPins = tools.map((t) => ({
|
|
6
|
+
name: t.name,
|
|
7
|
+
hash: hashToolDefinition(t.name, t.description ?? "", t.inputSchema ?? {}),
|
|
8
|
+
description_preview: (t.description ?? "").substring(0, 100),
|
|
9
|
+
}));
|
|
10
|
+
const manifestHash = hashManifest(toolPins.map((t) => t.hash));
|
|
11
|
+
return { tools: toolPins, manifestHash };
|
|
12
|
+
}
|
|
13
|
+
export async function savePin(pinDir, pinName, command, args, tools) {
|
|
14
|
+
const { tools: toolPins, manifestHash } = hashTools(tools);
|
|
15
|
+
const pinFile = {
|
|
16
|
+
pin_name: pinName,
|
|
17
|
+
server_command: command,
|
|
18
|
+
server_args: args,
|
|
19
|
+
timestamp: new Date().toISOString(),
|
|
20
|
+
tool_count: tools.length,
|
|
21
|
+
manifest_hash: manifestHash,
|
|
22
|
+
tools: toolPins,
|
|
23
|
+
};
|
|
24
|
+
await mkdir(pinDir, { recursive: true });
|
|
25
|
+
const filePath = join(pinDir, `${pinName}.json`);
|
|
26
|
+
await writeFile(filePath, JSON.stringify(pinFile, null, 2), "utf8");
|
|
27
|
+
return pinFile;
|
|
28
|
+
}
|
|
29
|
+
export async function loadPin(pinDir, pinName) {
|
|
30
|
+
try {
|
|
31
|
+
const filePath = join(pinDir, `${pinName}.json`);
|
|
32
|
+
const content = await readFile(filePath, "utf8");
|
|
33
|
+
return JSON.parse(content);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function verifyAgainstPin(pin, currentTools) {
|
|
40
|
+
const { tools: currentPins } = hashTools(currentTools);
|
|
41
|
+
const pinMap = new Map(pin.tools.map((t) => [t.name, t]));
|
|
42
|
+
const currentMap = new Map(currentPins.map((t) => [t.name, t]));
|
|
43
|
+
const added = [];
|
|
44
|
+
const removed = [];
|
|
45
|
+
const modified = [];
|
|
46
|
+
const unchanged = [];
|
|
47
|
+
// Find added and modified
|
|
48
|
+
for (const [name, current] of currentMap) {
|
|
49
|
+
const pinned = pinMap.get(name);
|
|
50
|
+
if (!pinned) {
|
|
51
|
+
added.push(current);
|
|
52
|
+
}
|
|
53
|
+
else if (pinned.hash !== current.hash) {
|
|
54
|
+
modified.push({ name, oldHash: pinned.hash, newHash: current.hash });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
unchanged.push(name);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Find removed
|
|
61
|
+
for (const [name, pinned] of pinMap) {
|
|
62
|
+
if (!currentMap.has(name)) {
|
|
63
|
+
removed.push(pinned);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
matched: added.length === 0 && removed.length === 0 && modified.length === 0,
|
|
68
|
+
added,
|
|
69
|
+
removed,
|
|
70
|
+
modified,
|
|
71
|
+
unchanged,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=pinning.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinning.js","sourceRoot":"","sources":["../../src/runtime/pinning.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Finding } from "../types/findings.js";
|
|
2
|
+
import type { ServerTool } from "./client.js";
|
|
3
|
+
export declare function analyzeScope(tools: ServerTool[]): Finding[];
|
|
4
|
+
export declare function analyzeToolShadowing(tools: ServerTool[], knownTools?: string[]): Finding[];
|
|
5
|
+
export declare function analyzeCrossOrigin(tools: ServerTool[]): Finding[];
|
|
6
|
+
export declare function analyzeResourceExposure(resources: {
|
|
7
|
+
uri: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
}[], prompts: {
|
|
11
|
+
name: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
}[]): Finding[];
|
|
14
|
+
//# sourceMappingURL=schema-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-analyzer.d.ts","sourceRoot":"","sources":["../../src/runtime/schema-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA6B9C,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAoE3D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAuB1F;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAqCjE;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,EACjE,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAChD,OAAO,EAAE,CA8DX"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
const COMMON_TOOL_NAMES = new Set([
|
|
2
|
+
// File operations (Claude Code, filesystem servers)
|
|
3
|
+
"read_file", "write_file", "edit_file", "list_directory", "create_directory",
|
|
4
|
+
"move_file", "search_files", "get_file_info", "read_multiple_files",
|
|
5
|
+
// Shell / execution
|
|
6
|
+
"bash", "execute_command", "run_command", "shell", "terminal",
|
|
7
|
+
"run_script", "exec", "execute",
|
|
8
|
+
// Search
|
|
9
|
+
"search", "grep", "find", "glob", "ripgrep",
|
|
10
|
+
// Web
|
|
11
|
+
"fetch", "web_search", "browse", "http_request", "curl",
|
|
12
|
+
// Git
|
|
13
|
+
"git", "git_status", "git_diff", "git_log", "git_commit",
|
|
14
|
+
// Database
|
|
15
|
+
"query", "sql", "execute_query", "read_query",
|
|
16
|
+
// Memory / knowledge
|
|
17
|
+
"remember", "recall", "store", "retrieve",
|
|
18
|
+
]);
|
|
19
|
+
const DANGEROUS_PARAM_PATTERNS = [
|
|
20
|
+
{ pattern: /^(command|cmd|shell|exec|script)$/i, severity: "critical", desc: "Shell command parameter" },
|
|
21
|
+
{ pattern: /^(path|file|filepath|file_path|filename|directory|dir|folder)$/i, severity: "high", desc: "Unrestricted file path" },
|
|
22
|
+
{ pattern: /^(url|uri|endpoint|href|link|target)$/i, severity: "high", desc: "Unrestricted URL parameter" },
|
|
23
|
+
{ pattern: /^(query|sql|expression|filter|regex|pattern|code)$/i, severity: "medium", desc: "Query/code injection surface" },
|
|
24
|
+
{ pattern: /^(glob|wildcard)$/i, severity: "medium", desc: "Wildcard pattern parameter" },
|
|
25
|
+
];
|
|
26
|
+
export function analyzeScope(tools) {
|
|
27
|
+
const findings = [];
|
|
28
|
+
let counter = 1;
|
|
29
|
+
// Check excessive tool count
|
|
30
|
+
if (tools.length > 50) {
|
|
31
|
+
findings.push({
|
|
32
|
+
id: `RT-SCOPE-${String(counter++).padStart(3, "0")}`,
|
|
33
|
+
title: "Excessive Tool Count",
|
|
34
|
+
severity: "medium",
|
|
35
|
+
owasp_mcp: "MCP02",
|
|
36
|
+
owasp_mcp_title: "Tool & Scope Mismanagement",
|
|
37
|
+
category: "runtime",
|
|
38
|
+
evidence: `Server exposes ${tools.length} tools (threshold: 50). High tool count increases attack surface and prompt complexity.`,
|
|
39
|
+
remediation: "Reduce tool count. Split into multiple focused servers. Remove unused or redundant tools.",
|
|
40
|
+
cwe: "CWE-250",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
for (const tool of tools) {
|
|
44
|
+
const schema = tool.inputSchema;
|
|
45
|
+
if (!schema || typeof schema !== "object")
|
|
46
|
+
continue;
|
|
47
|
+
const properties = schema.properties;
|
|
48
|
+
if (!properties || typeof properties !== "object")
|
|
49
|
+
continue;
|
|
50
|
+
for (const [paramName, paramSchema] of Object.entries(properties)) {
|
|
51
|
+
const ps = paramSchema;
|
|
52
|
+
// Check for any/object type without constraints
|
|
53
|
+
if (ps.type === "object" && !ps.properties) {
|
|
54
|
+
findings.push({
|
|
55
|
+
id: `RT-SCOPE-${String(counter++).padStart(3, "0")}`,
|
|
56
|
+
title: `Over-permissive Parameter: ${tool.name}.${paramName}`,
|
|
57
|
+
severity: "medium",
|
|
58
|
+
owasp_mcp: "MCP02",
|
|
59
|
+
owasp_mcp_title: "Tool & Scope Mismanagement",
|
|
60
|
+
category: "runtime",
|
|
61
|
+
evidence: `Tool "${tool.name}" parameter "${paramName}" accepts arbitrary object without property constraints`,
|
|
62
|
+
remediation: "Define explicit properties and types for object parameters. Use Zod schemas with strict validation.",
|
|
63
|
+
cwe: "CWE-732",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Check dangerous parameter names
|
|
67
|
+
for (const dp of DANGEROUS_PARAM_PATTERNS) {
|
|
68
|
+
if (dp.pattern.test(paramName)) {
|
|
69
|
+
// Only flag if there's no enum constraint
|
|
70
|
+
if (!ps.enum && !ps.const) {
|
|
71
|
+
findings.push({
|
|
72
|
+
id: `RT-SCOPE-${String(counter++).padStart(3, "0")}`,
|
|
73
|
+
title: `Dangerous Parameter: ${tool.name}.${paramName}`,
|
|
74
|
+
severity: dp.severity,
|
|
75
|
+
owasp_mcp: "MCP02",
|
|
76
|
+
owasp_mcp_title: "Tool & Scope Mismanagement",
|
|
77
|
+
category: "runtime",
|
|
78
|
+
evidence: `Tool "${tool.name}" has ${dp.desc} "${paramName}" without enum/allowlist constraint`,
|
|
79
|
+
remediation: "Add enum constraints, allowlists, or strict validation for sensitive parameters.",
|
|
80
|
+
cwe: "CWE-732",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return findings;
|
|
89
|
+
}
|
|
90
|
+
export function analyzeToolShadowing(tools, knownTools) {
|
|
91
|
+
const findings = [];
|
|
92
|
+
let counter = 1;
|
|
93
|
+
const knownSet = new Set(knownTools ?? COMMON_TOOL_NAMES);
|
|
94
|
+
for (const tool of tools) {
|
|
95
|
+
if (knownSet.has(tool.name)) {
|
|
96
|
+
findings.push({
|
|
97
|
+
id: `RT-SHADOW-${String(counter++).padStart(3, "0")}`,
|
|
98
|
+
title: `Tool Name Shadows Common Tool: "${tool.name}"`,
|
|
99
|
+
severity: "high",
|
|
100
|
+
owasp_mcp: "MCP06",
|
|
101
|
+
owasp_mcp_title: "Context & Tool Shadowing",
|
|
102
|
+
category: "runtime",
|
|
103
|
+
evidence: `Tool "${tool.name}" matches a common MCP tool name from well-known servers. A rogue server registering this name could intercept calls intended for a legitimate server.`,
|
|
104
|
+
remediation: "Use namespaced tool names (e.g. prefix with server name). Audit tool name collisions across configured servers.",
|
|
105
|
+
cwe: "CWE-346",
|
|
106
|
+
references: ["https://blog.trailofbits.com/2025/04/22/mcp-security-research/"],
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return findings;
|
|
111
|
+
}
|
|
112
|
+
export function analyzeCrossOrigin(tools) {
|
|
113
|
+
const findings = [];
|
|
114
|
+
let counter = 1;
|
|
115
|
+
const crossOriginPatterns = [
|
|
116
|
+
/when\s+using\s+(?:the\s+)?(\w+)\s+tool/i,
|
|
117
|
+
/before\s+calling\s+(?:the\s+)?(\w+)/i,
|
|
118
|
+
/after\s+calling\s+(?:the\s+)?(\w+)/i,
|
|
119
|
+
/always\s+.*?with\s+(?:the\s+)?(\w+)\s+tool/i,
|
|
120
|
+
/use\s+(?:the\s+)?(\w+)\s+tool\s+(?:to|for)/i,
|
|
121
|
+
/tell\s+(?:the\s+)?(\w+)\s+server/i,
|
|
122
|
+
/pass\s+(?:to|this\s+to)\s+(?:the\s+)?(\w+)/i,
|
|
123
|
+
];
|
|
124
|
+
for (const tool of tools) {
|
|
125
|
+
const desc = tool.description ?? "";
|
|
126
|
+
for (const pattern of crossOriginPatterns) {
|
|
127
|
+
const match = desc.match(pattern);
|
|
128
|
+
if (match) {
|
|
129
|
+
findings.push({
|
|
130
|
+
id: `RT-XORIGIN-${String(counter++).padStart(3, "0")}`,
|
|
131
|
+
title: `Cross-Origin Tool Reference in "${tool.name}"`,
|
|
132
|
+
severity: "high",
|
|
133
|
+
owasp_mcp: "MCP06",
|
|
134
|
+
owasp_mcp_title: "Context & Tool Shadowing",
|
|
135
|
+
category: "runtime",
|
|
136
|
+
evidence: `Tool "${tool.name}" description references external tool/server: "${match[0]}"`,
|
|
137
|
+
remediation: "Remove cross-origin references from tool descriptions. Each server should only describe its own tools.",
|
|
138
|
+
cwe: "CWE-346",
|
|
139
|
+
});
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return findings;
|
|
145
|
+
}
|
|
146
|
+
export function analyzeResourceExposure(resources, prompts) {
|
|
147
|
+
const findings = [];
|
|
148
|
+
let counter = 1;
|
|
149
|
+
const sensitivePathPatterns = [
|
|
150
|
+
/\/etc\//i,
|
|
151
|
+
/~\/\.ssh/i,
|
|
152
|
+
/\.env/i,
|
|
153
|
+
/\.aws/i,
|
|
154
|
+
/\.kube/i,
|
|
155
|
+
/credentials/i,
|
|
156
|
+
/\.gnupg/i,
|
|
157
|
+
/id_rsa/i,
|
|
158
|
+
/private.?key/i,
|
|
159
|
+
];
|
|
160
|
+
const broadUriPatterns = [
|
|
161
|
+
/^file:\/\/\*$/,
|
|
162
|
+
/^file:\/\/\//,
|
|
163
|
+
/^https?:\/\/\*$/,
|
|
164
|
+
/\*\*$/,
|
|
165
|
+
];
|
|
166
|
+
for (const resource of resources) {
|
|
167
|
+
// Check broad URI patterns
|
|
168
|
+
for (const pattern of broadUriPatterns) {
|
|
169
|
+
if (pattern.test(resource.uri)) {
|
|
170
|
+
findings.push({
|
|
171
|
+
id: `RT-RESOURCE-${String(counter++).padStart(3, "0")}`,
|
|
172
|
+
title: `Overly Broad Resource URI: ${resource.uri}`,
|
|
173
|
+
severity: "high",
|
|
174
|
+
owasp_mcp: "MCP10",
|
|
175
|
+
owasp_mcp_title: "Context Over-sharing & Data Exposure",
|
|
176
|
+
category: "runtime",
|
|
177
|
+
evidence: `Resource "${resource.name ?? resource.uri}" has broad URI pattern "${resource.uri}" allowing wide file/network access`,
|
|
178
|
+
remediation: "Restrict resource URI patterns to specific directories or endpoints. Avoid wildcards.",
|
|
179
|
+
cwe: "CWE-200",
|
|
180
|
+
});
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Check sensitive paths
|
|
185
|
+
for (const pattern of sensitivePathPatterns) {
|
|
186
|
+
if (pattern.test(resource.uri)) {
|
|
187
|
+
findings.push({
|
|
188
|
+
id: `RT-RESOURCE-${String(counter++).padStart(3, "0")}`,
|
|
189
|
+
title: `Resource Exposes Sensitive Path: ${resource.uri}`,
|
|
190
|
+
severity: "critical",
|
|
191
|
+
owasp_mcp: "MCP10",
|
|
192
|
+
owasp_mcp_title: "Context Over-sharing & Data Exposure",
|
|
193
|
+
category: "runtime",
|
|
194
|
+
evidence: `Resource "${resource.name ?? resource.uri}" exposes sensitive path: ${resource.uri}`,
|
|
195
|
+
remediation: "Remove access to sensitive paths. Use allowlists for permitted resource URIs.",
|
|
196
|
+
cwe: "CWE-200",
|
|
197
|
+
});
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return findings;
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=schema-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-analyzer.js","sourceRoot":"","sources":["../../src/runtime/schema-analyzer.ts"],"names":[],"mappings":"AAGA,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,oDAAoD;IACpD,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,kBAAkB;IAC5E,WAAW,EAAE,cAAc,EAAE,eAAe,EAAE,qBAAqB;IACnE,oBAAoB;IACpB,MAAM,EAAE,iBAAiB,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU;IAC7D,YAAY,EAAE,MAAM,EAAE,SAAS;IAC/B,SAAS;IACT,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS;IAC3C,MAAM;IACN,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM;IACvD,MAAM;IACN,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY;IACxD,WAAW;IACX,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,YAAY;IAC7C,qBAAqB;IACrB,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU;CAC1C,CAAC,CAAC;AAEH,MAAM,wBAAwB,GAAG;IAC/B,EAAE,OAAO,EAAE,oCAAoC,EAAE,QAAQ,EAAE,UAAmB,EAAE,IAAI,EAAE,yBAAyB,EAAE;IACjH,EAAE,OAAO,EAAE,iEAAiE,EAAE,QAAQ,EAAE,MAAe,EAAE,IAAI,EAAE,wBAAwB,EAAE;IACzI,EAAE,OAAO,EAAE,wCAAwC,EAAE,QAAQ,EAAE,MAAe,EAAE,IAAI,EAAE,4BAA4B,EAAE;IACpH,EAAE,OAAO,EAAE,qDAAqD,EAAE,QAAQ,EAAE,QAAiB,EAAE,IAAI,EAAE,8BAA8B,EAAE;IACrI,EAAE,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,QAAiB,EAAE,IAAI,EAAE,4BAA4B,EAAE;CACnG,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,KAAmB;IAC9C,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,6BAA6B;IAC7B,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACtB,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,YAAY,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;YACpD,KAAK,EAAE,sBAAsB;YAC7B,QAAQ,EAAE,QAAQ;YAClB,SAAS,EAAE,OAAO;YAClB,eAAe,EAAE,4BAA4B;YAC7C,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,kBAAkB,KAAK,CAAC,MAAM,yFAAyF;YACjI,WAAW,EAAE,2FAA2F;YACxG,GAAG,EAAE,SAAS;SACf,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC;QAChC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,SAAS;QAEpD,MAAM,UAAU,GAAI,MAAc,CAAC,UAAU,CAAC;QAC9C,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ;YAAE,SAAS;QAE5D,KAAK,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YAClE,MAAM,EAAE,GAAG,WAAkB,CAAC;YAE9B,gDAAgD;YAChD,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC;gBAC3C,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,YAAY,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;oBACpD,KAAK,EAAE,8BAA8B,IAAI,CAAC,IAAI,IAAI,SAAS,EAAE;oBAC7D,QAAQ,EAAE,QAAQ;oBAClB,SAAS,EAAE,OAAO;oBAClB,eAAe,EAAE,4BAA4B;oBAC7C,QAAQ,EAAE,SAAS;oBACnB,QAAQ,EAAE,SAAS,IAAI,CAAC,IAAI,gBAAgB,SAAS,yDAAyD;oBAC9G,WAAW,EAAE,qGAAqG;oBAClH,GAAG,EAAE,SAAS;iBACf,CAAC,CAAC;YACL,CAAC;YAED,kCAAkC;YAClC,KAAK,MAAM,EAAE,IAAI,wBAAwB,EAAE,CAAC;gBAC1C,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC/B,0CAA0C;oBAC1C,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;wBAC1B,QAAQ,CAAC,IAAI,CAAC;4BACZ,EAAE,EAAE,YAAY,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;4BACpD,KAAK,EAAE,wBAAwB,IAAI,CAAC,IAAI,IAAI,SAAS,EAAE;4BACvD,QAAQ,EAAE,EAAE,CAAC,QAAQ;4BACrB,SAAS,EAAE,OAAO;4BAClB,eAAe,EAAE,4BAA4B;4BAC7C,QAAQ,EAAE,SAAS;4BACnB,QAAQ,EAAE,SAAS,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC,IAAI,KAAK,SAAS,qCAAqC;4BAC/F,WAAW,EAAE,kFAAkF;4BAC/F,GAAG,EAAE,SAAS;yBACf,CAAC,CAAC;oBACL,CAAC;oBACD,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAmB,EAAE,UAAqB;IAC7E,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,UAAU,IAAI,iBAAiB,CAAC,CAAC;IAE1D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,aAAa,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;gBACrD,KAAK,EAAE,mCAAmC,IAAI,CAAC,IAAI,GAAG;gBACtD,QAAQ,EAAE,MAAM;gBAChB,SAAS,EAAE,OAAO;gBAClB,eAAe,EAAE,0BAA0B;gBAC3C,QAAQ,EAAE,SAAS;gBACnB,QAAQ,EAAE,SAAS,IAAI,CAAC,IAAI,wJAAwJ;gBACpL,WAAW,EAAE,iHAAiH;gBAC9H,GAAG,EAAE,SAAS;gBACd,UAAU,EAAE,CAAC,gEAAgE,CAAC;aAC/E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,MAAM,mBAAmB,GAAG;QAC1B,yCAAyC;QACzC,sCAAsC;QACtC,qCAAqC;QACrC,6CAA6C;QAC7C,6CAA6C;QAC7C,mCAAmC;QACnC,6CAA6C;KAC9C,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;QAEpC,KAAK,MAAM,OAAO,IAAI,mBAAmB,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,KAAK,EAAE,CAAC;gBACV,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,cAAc,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;oBACtD,KAAK,EAAE,mCAAmC,IAAI,CAAC,IAAI,GAAG;oBACtD,QAAQ,EAAE,MAAM;oBAChB,SAAS,EAAE,OAAO;oBAClB,eAAe,EAAE,0BAA0B;oBAC3C,QAAQ,EAAE,SAAS;oBACnB,QAAQ,EAAE,SAAS,IAAI,CAAC,IAAI,mDAAmD,KAAK,CAAC,CAAC,CAAC,GAAG;oBAC1F,WAAW,EAAE,wGAAwG;oBACrH,GAAG,EAAE,SAAS;iBACf,CAAC,CAAC;gBACH,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,SAAiE,EACjE,OAAiD;IAEjD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,MAAM,qBAAqB,GAAG;QAC5B,UAAU;QACV,WAAW;QACX,QAAQ;QACR,QAAQ;QACR,SAAS;QACT,cAAc;QACd,UAAU;QACV,SAAS;QACT,eAAe;KAChB,CAAC;IAEF,MAAM,gBAAgB,GAAG;QACvB,eAAe;QACf,cAAc;QACd,iBAAiB;QACjB,OAAO;KACR,CAAC;IAEF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,2BAA2B;QAC3B,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;YACvC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,eAAe,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;oBACvD,KAAK,EAAE,8BAA8B,QAAQ,CAAC,GAAG,EAAE;oBACnD,QAAQ,EAAE,MAAM;oBAChB,SAAS,EAAE,OAAO;oBAClB,eAAe,EAAE,sCAAsC;oBACvD,QAAQ,EAAE,SAAS;oBACnB,QAAQ,EAAE,aAAa,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,GAAG,4BAA4B,QAAQ,CAAC,GAAG,qCAAqC;oBACjI,WAAW,EAAE,uFAAuF;oBACpG,GAAG,EAAE,SAAS;iBACf,CAAC,CAAC;gBACH,MAAM;YACR,CAAC;QACH,CAAC;QAED,wBAAwB;QACxB,KAAK,MAAM,OAAO,IAAI,qBAAqB,EAAE,CAAC;YAC5C,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,eAAe,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;oBACvD,KAAK,EAAE,oCAAoC,QAAQ,CAAC,GAAG,EAAE;oBACzD,QAAQ,EAAE,UAAU;oBACpB,SAAS,EAAE,OAAO;oBAClB,eAAe,EAAE,sCAAsC;oBACvD,QAAQ,EAAE,SAAS;oBACnB,QAAQ,EAAE,aAAa,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,GAAG,6BAA6B,QAAQ,CAAC,GAAG,EAAE;oBAC/F,WAAW,EAAE,+EAA+E;oBAC5F,GAAG,EAAE,SAAS;iBACf,CAAC,CAAC;gBACH,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Finding } from "../types/findings.js";
|
|
2
|
+
import type { ServerTool } from "./client.js";
|
|
3
|
+
export declare function analyzePoisoning(tools: ServerTool[]): Finding[];
|
|
4
|
+
export declare function analyzeAnsiInjection(tools: ServerTool[]): Finding[];
|
|
5
|
+
export declare function analyzeUnicodeSteganography(tools: ServerTool[]): Finding[];
|
|
6
|
+
//# sourceMappingURL=tool-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-analyzer.d.ts","sourceRoot":"","sources":["../../src/runtime/tool-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CA0C/D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAyBnE;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CA8B1E"}
|