@guardbee/mcp-tool-poisoning-scanner 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 +124 -0
- package/TR.md +124 -0
- package/dist/__tests__/scanner.test.d.ts +2 -0
- package/dist/__tests__/scanner.test.d.ts.map +1 -0
- package/dist/__tests__/scanner.test.js +128 -0
- package/dist/__tests__/scanner.test.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +159 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +101 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/patterns.d.ts +37 -0
- package/dist/patterns.d.ts.map +1 -0
- package/dist/patterns.js +102 -0
- package/dist/patterns.js.map +1 -0
- package/dist/sarif.d.ts +3 -0
- package/dist/sarif.d.ts.map +1 -0
- package/dist/sarif.js +52 -0
- package/dist/sarif.js.map +1 -0
- package/dist/scanner.d.ts +51 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +192 -0
- package/dist/scanner.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +71 -0
- package/dist/server.js.map +1 -0
- package/package.json +49 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { instrumentServer } from "@guardbee/mcp-telemetry";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { scanText, scanFile, scanDirectory } from "./scanner.js";
|
|
6
|
+
import { DESCRIPTION_INJECTION_PATTERNS, MISMATCH_SINK_RULES } from "./patterns.js";
|
|
7
|
+
function formatFindings(findings, scannedFiles, durationMs) {
|
|
8
|
+
if (findings.length === 0) {
|
|
9
|
+
return `✅ No tool-poisoning or confused-deputy findings. Scanned ${scannedFiles} file(s) in ${durationMs}ms.`;
|
|
10
|
+
}
|
|
11
|
+
const bySeverity = { critical: 0, high: 0, medium: 0 };
|
|
12
|
+
for (const f of findings)
|
|
13
|
+
bySeverity[f.severity]++;
|
|
14
|
+
const lines = [
|
|
15
|
+
`⚠️ Found ${findings.length} finding(s) in ${scannedFiles} file(s) (${durationMs}ms)`,
|
|
16
|
+
` Critical: ${bySeverity.critical} High: ${bySeverity.high} Medium: ${bySeverity.medium}`,
|
|
17
|
+
"",
|
|
18
|
+
];
|
|
19
|
+
for (const f of findings) {
|
|
20
|
+
const loc = f.file ? `${f.file}:${f.line}:${f.column}` : `line ${f.line}:${f.column}`;
|
|
21
|
+
lines.push(`[${f.severity.toUpperCase()}] ${f.patternName} (${f.category})`);
|
|
22
|
+
lines.push(` Location : ${loc}`);
|
|
23
|
+
lines.push(` Match : ${f.match}`);
|
|
24
|
+
lines.push(` Recommendation : ${f.recommendation}`);
|
|
25
|
+
lines.push("");
|
|
26
|
+
}
|
|
27
|
+
return lines.join("\n");
|
|
28
|
+
}
|
|
29
|
+
export async function startServer() {
|
|
30
|
+
const server = new McpServer({
|
|
31
|
+
name: "guardbee-tool-poisoning-scanner",
|
|
32
|
+
version: "0.1.0",
|
|
33
|
+
});
|
|
34
|
+
instrumentServer(server, "tool-poisoning-scanner");
|
|
35
|
+
server.tool("scan_text", "Scan MCP server source for tool poisoning (hidden instructions embedded in a tool's `description` string, read by the calling LLM as trusted context) and confused-deputy tools (a name/description promising read-only behavior while the handler has a shell/eval/file-write/env-dump sink).", {
|
|
36
|
+
content: z.string().describe("The source code to scan"),
|
|
37
|
+
label: z.string().optional().describe("Optional label shown in findings (e.g. filename)"),
|
|
38
|
+
}, async ({ content, label }) => {
|
|
39
|
+
const findings = scanText(content, label);
|
|
40
|
+
return { content: [{ type: "text", text: formatFindings(findings, 1, 0) }] };
|
|
41
|
+
});
|
|
42
|
+
server.tool("scan_file", "Scan a single file for tool-poisoning and confused-deputy patterns in MCP tool registrations", { path: z.string().describe("Absolute or relative path to the file to scan") }, async ({ path: filePath }) => {
|
|
43
|
+
const { findings, skipped } = scanFile(filePath);
|
|
44
|
+
if (skipped) {
|
|
45
|
+
return { content: [{ type: "text", text: `⏭️ File skipped (binary, too large, or unreadable): ${filePath}` }] };
|
|
46
|
+
}
|
|
47
|
+
return { content: [{ type: "text", text: formatFindings(findings, 1, 0) }] };
|
|
48
|
+
});
|
|
49
|
+
server.tool("scan_directory", "Recursively scan a directory of MCP server source for tool-poisoning and confused-deputy patterns", {
|
|
50
|
+
path: z.string().describe("Absolute or relative path to the directory to scan"),
|
|
51
|
+
maxFiles: z.number().optional().describe("Maximum number of files to scan (default: 5000)"),
|
|
52
|
+
include: z.array(z.string()).optional().describe("Only scan files whose path contains one of these strings"),
|
|
53
|
+
exclude: z.array(z.string()).optional().describe("Skip files/dirs whose path contains one of these strings"),
|
|
54
|
+
}, async ({ path: dirPath, maxFiles, include, exclude }) => {
|
|
55
|
+
const result = scanDirectory(dirPath, { maxFiles, include, exclude });
|
|
56
|
+
return { content: [{ type: "text", text: formatFindings(result.findings, result.scannedFiles, result.durationMs) }] };
|
|
57
|
+
});
|
|
58
|
+
server.tool("list_patterns", "List every description-injection phrase and confused-deputy sink rule this scanner detects", {}, async () => {
|
|
59
|
+
const lines = ["[description-injection]"];
|
|
60
|
+
for (const p of DESCRIPTION_INJECTION_PATTERNS)
|
|
61
|
+
lines.push(` • ${p.name} (${p.id}, ${p.severity})`);
|
|
62
|
+
lines.push("", "[confused-deputy]", " A tool whose name/description implies read-only/informational behavior, but whose handler contains:");
|
|
63
|
+
for (const r of MISMATCH_SINK_RULES)
|
|
64
|
+
lines.push(` • ${r.category} (${r.id}, ${r.severity})`);
|
|
65
|
+
lines.push("", "Scope: the MCP TypeScript SDK's `server.tool(name, description, schema, handler)` convenience form. Raw setRequestHandler()-style tool lists and non-JS/TS servers are not covered — see README.");
|
|
66
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
67
|
+
});
|
|
68
|
+
const transport = new StdioServerTransport();
|
|
69
|
+
await server.connect(transport);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@guardbee/mcp-tool-poisoning-scanner",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server that scans other MCP servers' tool definitions for tool poisoning (hidden instructions embedded in a tool's description, read as trusted context by the calling LLM) and confused-deputy mismatches (a tool's name/description promises read-only behavior while its handler has a shell/eval/file-write/env-dump sink)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"guardbee-tool-poisoning-scanner": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"TR.md"
|
|
21
|
+
],
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
24
|
+
"zod": "^4.5.4",
|
|
25
|
+
"@guardbee/mcp-telemetry": "^0.1.2"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"mcp",
|
|
29
|
+
"security",
|
|
30
|
+
"ai-security",
|
|
31
|
+
"mcp-security",
|
|
32
|
+
"tool-poisoning",
|
|
33
|
+
"prompt-injection",
|
|
34
|
+
"guardbee",
|
|
35
|
+
"devtools"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/GuardBee/guardbee-mcp.git",
|
|
41
|
+
"directory": "packages/tool-poisoning-scanner"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc",
|
|
45
|
+
"dev": "tsc --watch",
|
|
46
|
+
"start": "node dist/cli.js",
|
|
47
|
+
"test": "vitest run"
|
|
48
|
+
}
|
|
49
|
+
}
|