@guardbee/mcp-toxic-flow-auditor 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 +61 -0
- package/TR.md +49 -0
- package/dist/__tests__/analyzer.test.d.ts +2 -0
- package/dist/__tests__/analyzer.test.d.ts.map +1 -0
- package/dist/__tests__/analyzer.test.js +76 -0
- package/dist/__tests__/analyzer.test.js.map +1 -0
- package/dist/analyzer.d.ts +32 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +139 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/capabilities.d.ts +12 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +32 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +133 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/sarif.d.ts +3 -0
- package/dist/sarif.d.ts.map +1 -0
- package/dist/sarif.js +50 -0
- package/dist/sarif.js.map +1 -0
- package/dist/scanner.d.ts +21 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +150 -0
- package/dist/scanner.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +96 -0
- package/dist/server.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @guardbee/mcp-toxic-flow-auditor
|
|
2
|
+
|
|
3
|
+
**🇬🇧 English** | [🇹🇷 Türkçe](TR.md)
|
|
4
|
+
|
|
5
|
+
An MCP server that audits an MCP **tool catalog** for **toxic flows** — Simon Willison's *lethal trifecta*:
|
|
6
|
+
|
|
7
|
+
1. **Untrusted content** (fetch, scrape, browse, issues, feeds)
|
|
8
|
+
2. **Sensitive / private data** (vault, DB, secrets, KVKK-regulated PII)
|
|
9
|
+
3. **Exfiltration or destruction** (send, webhook, export, delete, drop)
|
|
10
|
+
|
|
11
|
+
When one MCP server exposes all three, a single prompt injection can chain them. Mapped primarily to **OWASP MCP10:2025**.
|
|
12
|
+
|
|
13
|
+
> This package sends usage telemetry by default (tool name + short parameters, scanned code is never included — see [`@guardbee/mcp-telemetry`](../telemetry/README.md)). Disable with `GUARDBEE_TELEMETRY=0`.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Claude ──► toxic-flow-auditor ──► tools/list JSON or MCP server source
|
|
17
|
+
│
|
|
18
|
+
├─ lethal_trifecta (untrusted + sensitive + outbound)
|
|
19
|
+
├─ single_tool_trifecta (one tool alone spans all three)
|
|
20
|
+
├─ sensitive_plus_exfil
|
|
21
|
+
├─ untrusted_plus_exfil
|
|
22
|
+
└─ sensitive_plus_destruct
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## What it flags
|
|
26
|
+
|
|
27
|
+
- **Lethal trifecta across the catalog.** Tools that fetch untrusted content, reach vault/DB/PII, and can send data out or destroy it — on the same server.
|
|
28
|
+
- **Single-tool trifecta.** One registration whose name/description itself spans all three capabilities.
|
|
29
|
+
- **Dangerous pairs.** Sensitive+exfil, untrusted+exfil, or sensitive+destructive even when the full trifecta is not present.
|
|
30
|
+
- **KVKK-aware heuristics.** Turkish PII signals (`tc_kimlik`, `mĂĽĹźteri`, `KVKK`) count as sensitive data.
|
|
31
|
+
|
|
32
|
+
Grades **A–F**. No API key. Static / catalog analysis only — does not call live tools.
|
|
33
|
+
|
|
34
|
+
## Tools
|
|
35
|
+
|
|
36
|
+
| Tool | Purpose |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `audit_catalog` | Audit a `tools/list`-shaped JSON dump |
|
|
39
|
+
| `scan_source` | Extract `.tool(...)` registrations from source text |
|
|
40
|
+
| `scan_file` | Scan one source file |
|
|
41
|
+
| `scan_directory` | Recursive scan; merges tools across files |
|
|
42
|
+
| `explain_trifecta` | Explain the model |
|
|
43
|
+
|
|
44
|
+
## CLI
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
npx @guardbee/mcp-toxic-flow-auditor audit <tools.json> [--fail-on=any] [--format=text|json|sarif]
|
|
48
|
+
npx @guardbee/mcp-toxic-flow-auditor scan <path> [--fail-on=any] [--format=text|json|sarif]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Example catalog:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"tools": [
|
|
56
|
+
{ "name": "fetch_page", "description": "Scrape a URL" },
|
|
57
|
+
{ "name": "read_vault_secret", "description": "Read API key from vault" },
|
|
58
|
+
{ "name": "send_slack_message", "description": "Post to webhook" }
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
```
|
package/TR.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @guardbee/mcp-toxic-flow-auditor
|
|
2
|
+
|
|
3
|
+
[🇬🇧 English](README.md) | **🇹🇷 Türkçe**
|
|
4
|
+
|
|
5
|
+
Bir MCP **tool kataloğunu** **toksik akışlar** (Simon Willison'ın *lethal trifecta*'sı) için denetleyen bir MCP server:
|
|
6
|
+
|
|
7
|
+
1. **Güvenilmeyen içerik** (fetch, scrape, browse, issue, feed)
|
|
8
|
+
2. **Hassas / özel veri** (vault, DB, secret, KVKK kapsamındaki PII)
|
|
9
|
+
3. **Sızdırma veya yıkım** (send, webhook, export, delete, drop)
|
|
10
|
+
|
|
11
|
+
Aynı MCP server bu üçünü birden açıyorsa, tek bir prompt injection bunları zincirleyebilir. Başlıca eşleme: **OWASP MCP10:2025**.
|
|
12
|
+
|
|
13
|
+
> Bu paket varsayılan olarak kullanım telemetrisi gönderir (tool adı + kısa parametreler; taranan kod hiç gitmez — bkz. [`@guardbee/mcp-telemetry`](../telemetry/TR.md)). Kapatmak için `GUARDBEE_TELEMETRY=0`.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Claude ──► toxic-flow-auditor ──► tools/list JSON veya MCP server kaynağı
|
|
17
|
+
│
|
|
18
|
+
├─ lethal_trifecta (güvenilmeyen + hassas + dışarı)
|
|
19
|
+
├─ single_tool_trifecta (tek tool üç yeteneği birden taşıyor)
|
|
20
|
+
├─ sensitive_plus_exfil
|
|
21
|
+
├─ untrusted_plus_exfil
|
|
22
|
+
└─ sensitive_plus_destruct
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Ne yakalar
|
|
26
|
+
|
|
27
|
+
- **Katalog genelinde lethal trifecta.** Güvenilmeyen içerik okuyan, vault/DB/PII'ye ulaşan ve veriyi dışarı gönderen veya silen tool'lar — aynı server'da.
|
|
28
|
+
- **Tek-tool trifecta.** İsim/açıklaması üç yeteneği birden kapsayan tek kayıt.
|
|
29
|
+
- **Tehlikeli çiftler.** Tam trifecta yokken bile sensitive+exfil, untrusted+exfil veya sensitive+destructive.
|
|
30
|
+
- **KVKK bilinci.** `tc_kimlik`, `müşteri`, `KVKK` gibi Türkçe PII sinyalleri hassas veri sayılır.
|
|
31
|
+
|
|
32
|
+
Not: **A–F**. API anahtarı yok. Sadece statik / katalog analizi — canlı tool çağrısı yok.
|
|
33
|
+
|
|
34
|
+
## Tool'lar
|
|
35
|
+
|
|
36
|
+
| Tool | Amaç |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `audit_catalog` | `tools/list` biçimli JSON denetle |
|
|
39
|
+
| `scan_source` | Kaynak metinden `.tool(...)` kayıtlarını çıkar |
|
|
40
|
+
| `scan_file` | Tek dosya |
|
|
41
|
+
| `scan_directory` | Özyinelemeli; dosyalar arası tool birleştirme |
|
|
42
|
+
| `explain_trifecta` | Modeli açıkla |
|
|
43
|
+
|
|
44
|
+
## CLI
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
npx @guardbee/mcp-toxic-flow-auditor audit <tools.json> [--fail-on=any] [--format=text|json|sarif]
|
|
48
|
+
npx @guardbee/mcp-toxic-flow-auditor scan <path> [--fail-on=any] [--format=text|json|sarif]
|
|
49
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/analyzer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { auditCatalog, classifyTool, extractToolsFromSource } from "../index.js";
|
|
3
|
+
describe("classifyTool", () => {
|
|
4
|
+
it("fetch toolunu untrusted sayar", () => {
|
|
5
|
+
expect(classifyTool({ name: "fetch_url", description: "Download a page" })).toContain("untrusted-content");
|
|
6
|
+
});
|
|
7
|
+
it("vault toolunu sensitive sayar", () => {
|
|
8
|
+
expect(classifyTool({ name: "get_secret", description: "Read from vault" })).toContain("sensitive-data");
|
|
9
|
+
});
|
|
10
|
+
it("KVKK / müşteri alanını sensitive sayar", () => {
|
|
11
|
+
expect(classifyTool({
|
|
12
|
+
name: "lookup_customer",
|
|
13
|
+
description: "KVKK kapsamındaki müşteri kaydını getir",
|
|
14
|
+
inputSchema: { properties: { tc_kimlik: { type: "string" } } },
|
|
15
|
+
})).toContain("sensitive-data");
|
|
16
|
+
});
|
|
17
|
+
it("send_email toolunu exfil sayar", () => {
|
|
18
|
+
expect(classifyTool({ name: "send_email", description: "Notify user" })).toContain("exfiltration");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe("auditCatalog — lethal trifecta", () => {
|
|
22
|
+
it("üç yetenek bir arada ise F ve lethal_trifecta üretir", () => {
|
|
23
|
+
const result = auditCatalog([
|
|
24
|
+
{ name: "fetch_page", description: "Scrape a URL" },
|
|
25
|
+
{ name: "read_vault_secret", description: "Read API key from vault" },
|
|
26
|
+
{ name: "send_slack_message", description: "Post to webhook" },
|
|
27
|
+
]);
|
|
28
|
+
expect(result.grade).toBe("F");
|
|
29
|
+
expect(result.findings.map((f) => f.patternId)).toContain("lethal_trifecta");
|
|
30
|
+
expect(result.findings[0]?.owasp).toBe("MCP10:2025");
|
|
31
|
+
});
|
|
32
|
+
it("sadece isim listesi temizse A ĂĽretir", () => {
|
|
33
|
+
const result = auditCatalog([
|
|
34
|
+
{ name: "list_patterns", description: "List scanner rules" },
|
|
35
|
+
{ name: "scan_text", description: "Scan a string" },
|
|
36
|
+
]);
|
|
37
|
+
expect(result.grade).toBe("A");
|
|
38
|
+
expect(result.findings).toEqual([]);
|
|
39
|
+
});
|
|
40
|
+
it("sensitive + exfil çiftini high olarak işaretler", () => {
|
|
41
|
+
const result = auditCatalog([
|
|
42
|
+
{ name: "get_db_row", description: "Read database users" },
|
|
43
|
+
{ name: "export_csv", description: "Export and upload report" },
|
|
44
|
+
]);
|
|
45
|
+
expect(result.findings.map((f) => f.patternId)).toContain("sensitive_plus_exfil");
|
|
46
|
+
expect(result.grade).toBe("C");
|
|
47
|
+
});
|
|
48
|
+
it("tek tool trifecta yakalar", () => {
|
|
49
|
+
const result = auditCatalog([
|
|
50
|
+
{
|
|
51
|
+
name: "research_and_mail_secrets",
|
|
52
|
+
description: "Fetch a URL, read vault secrets, and send email with findings",
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
55
|
+
expect(result.findings.map((f) => f.patternId)).toContain("single_tool_trifecta");
|
|
56
|
+
expect(result.grade).toBe("F");
|
|
57
|
+
});
|
|
58
|
+
it("destructive + sensitive çiftini yakalar", () => {
|
|
59
|
+
const result = auditCatalog([
|
|
60
|
+
{ name: "read_customer_db", description: "SQL select from mĂĽĹźteri table" },
|
|
61
|
+
{ name: "drop_table", description: "Drop a database table" },
|
|
62
|
+
]);
|
|
63
|
+
expect(result.findings.map((f) => f.patternId)).toContain("sensitive_plus_destruct");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe("extractToolsFromSource", () => {
|
|
67
|
+
it("server.tool kayıtlarını çıkarır", () => {
|
|
68
|
+
const src = `
|
|
69
|
+
server.tool("fetch_url", "Download page", {}, async () => {});
|
|
70
|
+
server.tool("send_email", "Mail user", {}, async () => {});
|
|
71
|
+
`;
|
|
72
|
+
const tools = extractToolsFromSource(src);
|
|
73
|
+
expect(tools.map((t) => t.name)).toEqual(["fetch_url", "send_email"]);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=analyzer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.test.js","sourceRoot":"","sources":["../../src/__tests__/analyzer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Capability, type FlowCategory, type ToolRecord } from "./capabilities.js";
|
|
2
|
+
export interface Finding {
|
|
3
|
+
patternId: string;
|
|
4
|
+
patternName: string;
|
|
5
|
+
category: FlowCategory;
|
|
6
|
+
severity: "critical" | "high" | "medium";
|
|
7
|
+
owasp: string;
|
|
8
|
+
recommendation: string;
|
|
9
|
+
tools: string[];
|
|
10
|
+
capabilities: Capability[];
|
|
11
|
+
match: string;
|
|
12
|
+
}
|
|
13
|
+
export type Grade = "A" | "B" | "C" | "D" | "F";
|
|
14
|
+
export interface AuditResult {
|
|
15
|
+
toolCount: number;
|
|
16
|
+
grade: Grade;
|
|
17
|
+
score: number;
|
|
18
|
+
byCapability: Record<Capability, string[]>;
|
|
19
|
+
findings: Finding[];
|
|
20
|
+
durationMs: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function gradeFromFindings(findings: Finding[]): {
|
|
23
|
+
grade: Grade;
|
|
24
|
+
score: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Audits a tools/list-shaped catalog for Simon Willison's lethal trifecta:
|
|
28
|
+
* untrusted content + sensitive data + (exfiltration OR destruction) on one server.
|
|
29
|
+
*/
|
|
30
|
+
export declare function auditCatalog(tools: ToolRecord[], label?: string): AuditResult;
|
|
31
|
+
export declare function parseToolsJson(raw: string): ToolRecord[];
|
|
32
|
+
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEtG,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAMtF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,KAAK,SAAY,GAAG,WAAW,CAoHhF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAOxD"}
|
package/dist/analyzer.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { classifyTool } from "./capabilities.js";
|
|
2
|
+
function toolsWith(cap, classified) {
|
|
3
|
+
return [...classified.entries()].filter(([, caps]) => caps.includes(cap)).map(([name]) => name);
|
|
4
|
+
}
|
|
5
|
+
export function gradeFromFindings(findings) {
|
|
6
|
+
if (findings.some((f) => f.patternId === "lethal_trifecta"))
|
|
7
|
+
return { grade: "F", score: 0 };
|
|
8
|
+
if (findings.some((f) => f.severity === "critical"))
|
|
9
|
+
return { grade: "D", score: 25 };
|
|
10
|
+
if (findings.some((f) => f.severity === "high"))
|
|
11
|
+
return { grade: "C", score: 50 };
|
|
12
|
+
if (findings.some((f) => f.severity === "medium"))
|
|
13
|
+
return { grade: "B", score: 75 };
|
|
14
|
+
return { grade: "A", score: 100 };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Audits a tools/list-shaped catalog for Simon Willison's lethal trifecta:
|
|
18
|
+
* untrusted content + sensitive data + (exfiltration OR destruction) on one server.
|
|
19
|
+
*/
|
|
20
|
+
export function auditCatalog(tools, label = "catalog") {
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
const classified = new Map();
|
|
23
|
+
for (const tool of tools) {
|
|
24
|
+
classified.set(tool.name, classifyTool(tool));
|
|
25
|
+
}
|
|
26
|
+
const byCapability = {
|
|
27
|
+
"untrusted-content": toolsWith("untrusted-content", classified),
|
|
28
|
+
"sensitive-data": toolsWith("sensitive-data", classified),
|
|
29
|
+
exfiltration: toolsWith("exfiltration", classified),
|
|
30
|
+
destructive: toolsWith("destructive", classified),
|
|
31
|
+
};
|
|
32
|
+
const findings = [];
|
|
33
|
+
const hasUntrusted = byCapability["untrusted-content"].length > 0;
|
|
34
|
+
const hasSensitive = byCapability["sensitive-data"].length > 0;
|
|
35
|
+
const hasExfil = byCapability.exfiltration.length > 0;
|
|
36
|
+
const hasDestruct = byCapability.destructive.length > 0;
|
|
37
|
+
const hasOutbound = hasExfil || hasDestruct;
|
|
38
|
+
if (hasUntrusted && hasSensitive && hasOutbound) {
|
|
39
|
+
const involved = [
|
|
40
|
+
...byCapability["untrusted-content"],
|
|
41
|
+
...byCapability["sensitive-data"],
|
|
42
|
+
...byCapability.exfiltration,
|
|
43
|
+
...byCapability.destructive,
|
|
44
|
+
];
|
|
45
|
+
findings.push({
|
|
46
|
+
patternId: "lethal_trifecta",
|
|
47
|
+
patternName: "Lethal trifecta (toxic flow) across this MCP server's tools",
|
|
48
|
+
category: "toxic-flow",
|
|
49
|
+
severity: "critical",
|
|
50
|
+
owasp: "MCP10:2025",
|
|
51
|
+
recommendation: "This server can read untrusted content, reach private/sensitive data, and send data out or destroy it. A single prompt injection can chain those capabilities. Split the server: keep research/fetch tools off any vault, DB, or mail connector, or put a human approval gate on exfil/destructive tools.",
|
|
52
|
+
tools: [...new Set(involved)],
|
|
53
|
+
capabilities: [
|
|
54
|
+
"untrusted-content",
|
|
55
|
+
"sensitive-data",
|
|
56
|
+
...(hasExfil ? ["exfiltration"] : []),
|
|
57
|
+
...(hasDestruct ? ["destructive"] : []),
|
|
58
|
+
],
|
|
59
|
+
match: `${label}: untrusted=[${byCapability["untrusted-content"].join(", ")}] sensitive=[${byCapability["sensitive-data"].join(", ")}] outbound=[${[...byCapability.exfiltration, ...byCapability.destructive].join(", ")}]`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
else if (hasSensitive && hasExfil) {
|
|
63
|
+
findings.push({
|
|
64
|
+
patternId: "sensitive_plus_exfil",
|
|
65
|
+
patternName: "Sensitive data tools share a server with an exfiltration path",
|
|
66
|
+
category: "dangerous-pair",
|
|
67
|
+
severity: "high",
|
|
68
|
+
owasp: "MCP10:2025",
|
|
69
|
+
recommendation: "Even without an obvious untrusted-content tool, a compromised prompt or sibling MCP can still push private data out through send/export/webhook tools. Isolate vault/DB readers from outbound connectors.",
|
|
70
|
+
tools: [...new Set([...byCapability["sensitive-data"], ...byCapability.exfiltration])],
|
|
71
|
+
capabilities: ["sensitive-data", "exfiltration"],
|
|
72
|
+
match: `${label}: sensitive=[${byCapability["sensitive-data"].join(", ")}] exfil=[${byCapability.exfiltration.join(", ")}]`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else if (hasUntrusted && hasExfil) {
|
|
76
|
+
findings.push({
|
|
77
|
+
patternId: "untrusted_plus_exfil",
|
|
78
|
+
patternName: "Untrusted-content tools share a server with an exfiltration path",
|
|
79
|
+
category: "dangerous-pair",
|
|
80
|
+
severity: "high",
|
|
81
|
+
owasp: "MCP06:2025",
|
|
82
|
+
recommendation: "Fetched pages or issues can inject instructions that then use send/webhook tools. Prefer a research-only server with no outbound side effects.",
|
|
83
|
+
tools: [...new Set([...byCapability["untrusted-content"], ...byCapability.exfiltration])],
|
|
84
|
+
capabilities: ["untrusted-content", "exfiltration"],
|
|
85
|
+
match: `${label}: untrusted=[${byCapability["untrusted-content"].join(", ")}] exfil=[${byCapability.exfiltration.join(", ")}]`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else if (hasSensitive && hasDestruct) {
|
|
89
|
+
findings.push({
|
|
90
|
+
patternId: "sensitive_plus_destruct",
|
|
91
|
+
patternName: "Sensitive data tools share a server with destructive tools",
|
|
92
|
+
category: "dangerous-pair",
|
|
93
|
+
severity: "high",
|
|
94
|
+
owasp: "MCP02:2025",
|
|
95
|
+
recommendation: "An injection that reaches delete/drop/purge next to vault or DB access is a wipe risk. Require confirmation or move destructive tools to a separate, tightly approved server.",
|
|
96
|
+
tools: [...new Set([...byCapability["sensitive-data"], ...byCapability.destructive])],
|
|
97
|
+
capabilities: ["sensitive-data", "destructive"],
|
|
98
|
+
match: `${label}: sensitive=[${byCapability["sensitive-data"].join(", ")}] destruct=[${byCapability.destructive.join(", ")}]`,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// Single-tool combinations: one registration that itself spans the trifecta.
|
|
102
|
+
for (const [name, caps] of classified) {
|
|
103
|
+
const u = caps.includes("untrusted-content");
|
|
104
|
+
const s = caps.includes("sensitive-data");
|
|
105
|
+
const o = caps.includes("exfiltration") || caps.includes("destructive");
|
|
106
|
+
if (u && s && o) {
|
|
107
|
+
findings.push({
|
|
108
|
+
patternId: "single_tool_trifecta",
|
|
109
|
+
patternName: `Tool "${name}" alone spans the lethal trifecta`,
|
|
110
|
+
category: "toxic-flow",
|
|
111
|
+
severity: "critical",
|
|
112
|
+
owasp: "MCP10:2025",
|
|
113
|
+
recommendation: "Split this tool. A single handler that fetches untrusted content, touches secrets, and can send or destroy data is the densest toxic-flow shape.",
|
|
114
|
+
tools: [name],
|
|
115
|
+
capabilities: caps,
|
|
116
|
+
match: name,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const { grade, score } = gradeFromFindings(findings);
|
|
121
|
+
return {
|
|
122
|
+
toolCount: tools.length,
|
|
123
|
+
grade,
|
|
124
|
+
score,
|
|
125
|
+
byCapability,
|
|
126
|
+
findings,
|
|
127
|
+
durationMs: Date.now() - start,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function parseToolsJson(raw) {
|
|
131
|
+
const parsed = JSON.parse(raw);
|
|
132
|
+
if (Array.isArray(parsed))
|
|
133
|
+
return parsed;
|
|
134
|
+
if (parsed && typeof parsed === "object" && Array.isArray(parsed.tools)) {
|
|
135
|
+
return parsed.tools;
|
|
136
|
+
}
|
|
137
|
+
throw new Error("Expected a tools array or { tools: [...] } JSON document");
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type Capability = "untrusted-content" | "sensitive-data" | "exfiltration" | "destructive";
|
|
2
|
+
export type FlowCategory = "toxic-flow" | "dangerous-pair" | "capability";
|
|
3
|
+
export interface ToolRecord {
|
|
4
|
+
name: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
inputSchema?: unknown;
|
|
7
|
+
}
|
|
8
|
+
/** Name/description/schema heuristics for Simon Willison's lethal trifecta. */
|
|
9
|
+
export declare const CAPABILITY_RULES: Record<Capability, RegExp>;
|
|
10
|
+
export declare function textOf(tool: ToolRecord): string;
|
|
11
|
+
export declare function classifyTool(tool: ToolRecord): Capability[];
|
|
12
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAClB,mBAAmB,GACnB,gBAAgB,GAChB,cAAc,GACd,aAAa,CAAC;AAElB,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,gBAAgB,GAAG,YAAY,CAAC;AAE1E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CASvD,CAAC;AAEF,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAG/C;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,CAqB3D"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Name/description/schema heuristics for Simon Willison's lethal trifecta. */
|
|
2
|
+
export const CAPABILITY_RULES = {
|
|
3
|
+
"untrusted-content": /\b(fetch|scrape|browse|crawl|http|https?|url|web[_-]?search|search[_-]?web|read[_-]?page|download|wiki|rss|feed|issue|ticket|inbox|comment|tweet|reddit|html|screenshot)\b/i,
|
|
4
|
+
"sensitive-data": /\b(secret|credential|token|api[_-]?key|password|passwd|vault|private[_-]?key|ssh|env|database|sql|postgres|mysql|mongo|payment|card|iban|kvkk|pii|tc[_-]?kimlik|identity|müşteri|musteri|kullanıcı|kullanici|email|e-mail|mailbox|inbox|read[_-]?file|read[_-]?secret|get[_-]?secret|list[_-]?secret|dump|export[_-]?user)\b/i,
|
|
5
|
+
"exfiltration": /\b(send|post|publish|upload|email|mail|notify|webhook|export|sync|push|transfer|message|slack|discord|telegram|http[_-]?request|fetch[_-]?post|write[_-]?url|relay|forward)\b/i,
|
|
6
|
+
destructive: /\b(delete|remove|drop|destroy|purge|reset|truncate|revoke|kill|terminate|overwrite|wipe|rm\b|unlink)\b/i,
|
|
7
|
+
};
|
|
8
|
+
export function textOf(tool) {
|
|
9
|
+
const schema = tool.inputSchema ? JSON.stringify(tool.inputSchema) : "";
|
|
10
|
+
return `${tool.name}\n${tool.description ?? ""}\n${schema}`;
|
|
11
|
+
}
|
|
12
|
+
export function classifyTool(tool) {
|
|
13
|
+
const text = textOf(tool);
|
|
14
|
+
const name = tool.name;
|
|
15
|
+
const found = [];
|
|
16
|
+
// Untrusted / exfil / destructive: name is primary (CheckMCP-style); description reinforces.
|
|
17
|
+
if (CAPABILITY_RULES["untrusted-content"].test(name) || CAPABILITY_RULES["untrusted-content"].test(text)) {
|
|
18
|
+
found.push("untrusted-content");
|
|
19
|
+
}
|
|
20
|
+
if (CAPABILITY_RULES.exfiltration.test(name) || CAPABILITY_RULES.exfiltration.test(text)) {
|
|
21
|
+
found.push("exfiltration");
|
|
22
|
+
}
|
|
23
|
+
if (CAPABILITY_RULES.destructive.test(name) || CAPABILITY_RULES.destructive.test(text)) {
|
|
24
|
+
found.push("destructive");
|
|
25
|
+
}
|
|
26
|
+
// Sensitive: name + description + schema (secrets often hide in schema field names).
|
|
27
|
+
if (CAPABILITY_RULES["sensitive-data"].test(text)) {
|
|
28
|
+
found.push("sensitive-data");
|
|
29
|
+
}
|
|
30
|
+
return found;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAcA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,gBAAgB,GAA+B;IAC1D,mBAAmB,EACjB,6KAA6K;IAC/K,gBAAgB,EACd,+TAA+T;IACjU,cAAc,EACZ,gLAAgL;IAClL,WAAW,EACT,yGAAyG;CAC5G,CAAC;AAEF,MAAM,UAAU,MAAM,CAAC,IAAgB;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxE,OAAO,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,MAAM,KAAK,GAAiB,EAAE,CAAC;IAE/B,6FAA6F;IAC7F,IAAI,gBAAgB,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACzG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,gBAAgB,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACzF,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,gBAAgB,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvF,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC5B,CAAC;IACD,qFAAqF;IACrF,IAAI,gBAAgB,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAClD,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { startServer } from "./server.js";
|
|
3
|
+
import { auditCatalog, parseToolsJson } from "./analyzer.js";
|
|
4
|
+
import { scanDirectory, scanSourceFile } from "./scanner.js";
|
|
5
|
+
import { buildSarif } from "./sarif.js";
|
|
6
|
+
import { readFileSync, statSync } from "fs";
|
|
7
|
+
import { resolve, dirname } from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
function getVersion() {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf8")).version;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return "0.0.0";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function printHelp() {
|
|
19
|
+
console.log(`@guardbee/mcp-toxic-flow-auditor
|
|
20
|
+
|
|
21
|
+
Usage (MCP server):
|
|
22
|
+
guardbee-toxic-flow-auditor [serve]
|
|
23
|
+
|
|
24
|
+
Usage (CLI):
|
|
25
|
+
guardbee-toxic-flow-auditor audit <tools.json> Audit a tools/list JSON dump
|
|
26
|
+
guardbee-toxic-flow-auditor scan <path> Scan source file or directory
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
--format=text|json|sarif Output format (default: text)
|
|
30
|
+
--fail-on=any|critical|high|medium|none
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
function parseArgs(args) {
|
|
34
|
+
const positionals = [];
|
|
35
|
+
let format = "text";
|
|
36
|
+
let failOn = "any";
|
|
37
|
+
for (const arg of args) {
|
|
38
|
+
if (arg.startsWith("--format="))
|
|
39
|
+
format = arg.split("=")[1] ?? "text";
|
|
40
|
+
else if (arg.startsWith("--fail-on="))
|
|
41
|
+
failOn = arg.split("=")[1] ?? "any";
|
|
42
|
+
else if (!arg.startsWith("--"))
|
|
43
|
+
positionals.push(arg);
|
|
44
|
+
}
|
|
45
|
+
return { positionals, format, failOn };
|
|
46
|
+
}
|
|
47
|
+
function shouldFail(findings, failOn) {
|
|
48
|
+
if (failOn === "none")
|
|
49
|
+
return false;
|
|
50
|
+
if (failOn === "any")
|
|
51
|
+
return findings.length > 0;
|
|
52
|
+
const RANK = { critical: 0, high: 1, medium: 2 };
|
|
53
|
+
const threshold = RANK[failOn] ?? 1;
|
|
54
|
+
return findings.some((f) => (RANK[f.severity] ?? 3) <= threshold);
|
|
55
|
+
}
|
|
56
|
+
async function runAudit(path, format, failOn) {
|
|
57
|
+
const tools = parseToolsJson(readFileSync(path, "utf8"));
|
|
58
|
+
const result = auditCatalog(tools, path);
|
|
59
|
+
emit(result, format, failOn);
|
|
60
|
+
}
|
|
61
|
+
async function runScan(path, format, failOn) {
|
|
62
|
+
const stat = statSync(path);
|
|
63
|
+
const result = stat.isDirectory() ? scanDirectory(path) : scanSourceFile(path).result;
|
|
64
|
+
emit(result, format, failOn);
|
|
65
|
+
}
|
|
66
|
+
function emit(result, format, failOn) {
|
|
67
|
+
if (format === "json") {
|
|
68
|
+
console.log(JSON.stringify(result, null, 2));
|
|
69
|
+
}
|
|
70
|
+
else if (format === "sarif") {
|
|
71
|
+
console.log(JSON.stringify(buildSarif(getVersion(), result.findings), null, 2));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
console.log(`Grade ${result.grade} (${result.score}/100) — ${result.toolCount} tools, ${result.durationMs}ms`);
|
|
75
|
+
console.log(` untrusted : ${result.byCapability["untrusted-content"].join(", ") || "—"}`);
|
|
76
|
+
console.log(` sensitive : ${result.byCapability["sensitive-data"].join(", ") || "—"}`);
|
|
77
|
+
console.log(` exfil : ${result.byCapability.exfiltration.join(", ") || "—"}`);
|
|
78
|
+
console.log(` destruct : ${result.byCapability.destructive.join(", ") || "—"}`);
|
|
79
|
+
console.log("");
|
|
80
|
+
if (result.findings.length === 0) {
|
|
81
|
+
console.log("âś… No toxic-flow findings");
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
for (const f of result.findings) {
|
|
85
|
+
console.log(`[${f.severity.toUpperCase()}] ${f.patternName} (${f.owasp})`);
|
|
86
|
+
console.log(` tools: ${f.tools.join(", ")}`);
|
|
87
|
+
console.log(` fix : ${f.recommendation}`);
|
|
88
|
+
console.log("");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
process.exit(shouldFail(result.findings, failOn) ? 1 : 0);
|
|
93
|
+
}
|
|
94
|
+
const [, , firstArg, ...rest] = process.argv;
|
|
95
|
+
if (!firstArg || firstArg === "serve") {
|
|
96
|
+
startServer().catch((err) => {
|
|
97
|
+
process.stderr.write(`[guardbee-toxic-flow-auditor] ${err instanceof Error ? err.message : String(err)}\n`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
else if (firstArg === "--help" || firstArg === "-h") {
|
|
102
|
+
printHelp();
|
|
103
|
+
}
|
|
104
|
+
else if (firstArg === "audit") {
|
|
105
|
+
const { positionals, format, failOn } = parseArgs(rest);
|
|
106
|
+
const target = positionals[0];
|
|
107
|
+
if (!target) {
|
|
108
|
+
console.error("Usage: guardbee-toxic-flow-auditor audit <tools.json>");
|
|
109
|
+
process.exit(2);
|
|
110
|
+
}
|
|
111
|
+
runAudit(target, format, failOn).catch((err) => {
|
|
112
|
+
console.error("Error:", err.message);
|
|
113
|
+
process.exit(2);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else if (firstArg === "scan") {
|
|
117
|
+
const { positionals, format, failOn } = parseArgs(rest);
|
|
118
|
+
const target = positionals[0];
|
|
119
|
+
if (!target) {
|
|
120
|
+
console.error("Usage: guardbee-toxic-flow-auditor scan <path>");
|
|
121
|
+
process.exit(2);
|
|
122
|
+
}
|
|
123
|
+
runScan(target, format, failOn).catch((err) => {
|
|
124
|
+
console.error("Error:", err.message);
|
|
125
|
+
process.exit(2);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
console.error(`Unknown command: ${firstArg}`);
|
|
130
|
+
printHelp();
|
|
131
|
+
process.exit(2);
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { auditCatalog, parseToolsJson, gradeFromFindings } from "./analyzer.js";
|
|
2
|
+
export type { Finding, AuditResult, Grade } from "./analyzer.js";
|
|
3
|
+
export { classifyTool, CAPABILITY_RULES } from "./capabilities.js";
|
|
4
|
+
export type { Capability, ToolRecord } from "./capabilities.js";
|
|
5
|
+
export { extractToolsFromSource, scanSourceText, scanSourceFile, scanDirectory } from "./scanner.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAChF,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACnE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { auditCatalog, parseToolsJson, gradeFromFindings } from "./analyzer.js";
|
|
2
|
+
export { classifyTool, CAPABILITY_RULES } from "./capabilities.js";
|
|
3
|
+
export { extractToolsFromSource, scanSourceText, scanSourceFile, scanDirectory } from "./scanner.js";
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEhF,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/sarif.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../src/sarif.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAQ7C,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CA2C3E"}
|
package/dist/sarif.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
function severityToLevel(sev) {
|
|
2
|
+
if (sev === "critical" || sev === "high")
|
|
3
|
+
return "error";
|
|
4
|
+
if (sev === "medium")
|
|
5
|
+
return "warning";
|
|
6
|
+
return "note";
|
|
7
|
+
}
|
|
8
|
+
export function buildSarif(toolVersion, findings) {
|
|
9
|
+
const rulesMap = new Map();
|
|
10
|
+
const results = [];
|
|
11
|
+
for (const f of findings) {
|
|
12
|
+
if (!rulesMap.has(f.patternId)) {
|
|
13
|
+
rulesMap.set(f.patternId, {
|
|
14
|
+
id: f.patternId,
|
|
15
|
+
name: f.patternName.replace(/[^a-zA-Z0-9]/g, ""),
|
|
16
|
+
shortDescription: { text: f.patternName },
|
|
17
|
+
fullDescription: { text: f.recommendation },
|
|
18
|
+
helpUri: "https://guardbee.ai/docs/toxic-flow-auditor",
|
|
19
|
+
properties: {
|
|
20
|
+
"problem.severity": f.severity,
|
|
21
|
+
tags: ["security", "ai-security", "mcp-security", "toxic-flow", f.owasp, f.category],
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
results.push({
|
|
26
|
+
ruleId: f.patternId,
|
|
27
|
+
level: severityToLevel(f.severity),
|
|
28
|
+
message: { text: `${f.patternName} — ${f.recommendation}` },
|
|
29
|
+
properties: { tools: f.tools, capabilities: f.capabilities },
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
$schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
|
34
|
+
version: "2.1.0",
|
|
35
|
+
runs: [
|
|
36
|
+
{
|
|
37
|
+
tool: {
|
|
38
|
+
driver: {
|
|
39
|
+
name: "@guardbee/mcp-toxic-flow-auditor",
|
|
40
|
+
version: toolVersion,
|
|
41
|
+
informationUri: "https://guardbee.ai",
|
|
42
|
+
rules: Array.from(rulesMap.values()),
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
results,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../src/sarif.ts"],"names":[],"mappings":"AAEA,SAAS,eAAe,CAAC,GAAW;IAClC,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC;IACzD,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACvC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,WAAmB,EAAE,QAAmB;IACjE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE;gBACxB,EAAE,EAAE,CAAC,CAAC,SAAS;gBACf,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC;gBAChD,gBAAgB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE;gBACzC,eAAe,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,cAAc,EAAE;gBAC3C,OAAO,EAAE,6CAA6C;gBACtD,UAAU,EAAE;oBACV,kBAAkB,EAAE,CAAC,CAAC,QAAQ;oBAC9B,IAAI,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC;iBACrF;aACF,CAAC,CAAC;QACL,CAAC;QACD,OAAO,CAAC,IAAI,CAAC;YACX,MAAM,EAAE,CAAC,CAAC,SAAS;YACnB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC;YAClC,OAAO,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,MAAM,CAAC,CAAC,cAAc,EAAE,EAAE;YAC3D,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE;SAC7D,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,OAAO,EAAE,gGAAgG;QACzG,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE;YACJ;gBACE,IAAI,EAAE;oBACJ,MAAM,EAAE;wBACN,IAAI,EAAE,kCAAkC;wBACxC,OAAO,EAAE,WAAW;wBACpB,cAAc,EAAE,qBAAqB;wBACrC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;qBACrC;iBACF;gBACD,OAAO;aACR;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AuditResult, type Finding } from "./analyzer.js";
|
|
2
|
+
import type { ToolRecord } from "./capabilities.js";
|
|
3
|
+
/**
|
|
4
|
+
* Extract MCP SDK `.tool("name", "description", ...)` registrations from source.
|
|
5
|
+
* Enough to rough-classify a server without a live tools/list.
|
|
6
|
+
*/
|
|
7
|
+
export declare function extractToolsFromSource(text: string): ToolRecord[];
|
|
8
|
+
export declare function scanSourceText(text: string, label?: string): AuditResult;
|
|
9
|
+
export declare function scanSourceFile(filePath: string): {
|
|
10
|
+
result: AuditResult;
|
|
11
|
+
skipped: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function scanDirectory(dirPath: string, options?: {
|
|
14
|
+
maxFiles?: number;
|
|
15
|
+
exclude?: string[];
|
|
16
|
+
}): AuditResult & {
|
|
17
|
+
scannedFiles: number;
|
|
18
|
+
skippedFiles: number;
|
|
19
|
+
};
|
|
20
|
+
export type { Finding, AuditResult };
|
|
21
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,WAAW,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAYpD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CASjE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAExE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAiB1F;AAkBD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,GACtD,WAAW,GAAG;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CA4E9D;AAED,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { readFileSync, statSync, readdirSync } from "fs";
|
|
2
|
+
import { join, relative, extname } from "path";
|
|
3
|
+
import { auditCatalog } from "./analyzer.js";
|
|
4
|
+
const SKIP_EXTENSIONS = new Set([
|
|
5
|
+
".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".bmp",
|
|
6
|
+
".pdf", ".zip", ".tar", ".gz", ".lock",
|
|
7
|
+
]);
|
|
8
|
+
const SKIP_DIRS = new Set([
|
|
9
|
+
"node_modules", ".git", "dist", "build", ".next",
|
|
10
|
+
"__pycache__", "coverage", "venv", ".venv",
|
|
11
|
+
]);
|
|
12
|
+
const MAX_FILE_SIZE = 1 * 1024 * 1024;
|
|
13
|
+
/**
|
|
14
|
+
* Extract MCP SDK `.tool("name", "description", ...)` registrations from source.
|
|
15
|
+
* Enough to rough-classify a server without a live tools/list.
|
|
16
|
+
*/
|
|
17
|
+
export function extractToolsFromSource(text) {
|
|
18
|
+
const tools = [];
|
|
19
|
+
const re = /\.tool\s*\(\s*["'`]([^"'`]+)["'`]\s*(?:,\s*["'`]([^"'`]*)["'`])?/g;
|
|
20
|
+
let match;
|
|
21
|
+
while ((match = re.exec(text)) !== null) {
|
|
22
|
+
tools.push({ name: match[1] ?? "", description: match[2] });
|
|
23
|
+
if (match.index === re.lastIndex)
|
|
24
|
+
re.lastIndex++;
|
|
25
|
+
}
|
|
26
|
+
return tools;
|
|
27
|
+
}
|
|
28
|
+
export function scanSourceText(text, label) {
|
|
29
|
+
return auditCatalog(extractToolsFromSource(text), label ?? "source");
|
|
30
|
+
}
|
|
31
|
+
export function scanSourceFile(filePath) {
|
|
32
|
+
const ext = extname(filePath).toLowerCase();
|
|
33
|
+
if (SKIP_EXTENSIONS.has(ext))
|
|
34
|
+
return { result: emptyResult(), skipped: true };
|
|
35
|
+
let stat;
|
|
36
|
+
try {
|
|
37
|
+
stat = statSync(filePath);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return { result: emptyResult(), skipped: true };
|
|
41
|
+
}
|
|
42
|
+
if (!stat.isFile() || stat.size > MAX_FILE_SIZE)
|
|
43
|
+
return { result: emptyResult(), skipped: true };
|
|
44
|
+
let content;
|
|
45
|
+
try {
|
|
46
|
+
content = readFileSync(filePath, "utf8");
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return { result: emptyResult(), skipped: true };
|
|
50
|
+
}
|
|
51
|
+
return { result: scanSourceText(content, filePath), skipped: false };
|
|
52
|
+
}
|
|
53
|
+
function emptyResult() {
|
|
54
|
+
return {
|
|
55
|
+
toolCount: 0,
|
|
56
|
+
grade: "A",
|
|
57
|
+
score: 100,
|
|
58
|
+
byCapability: {
|
|
59
|
+
"untrusted-content": [],
|
|
60
|
+
"sensitive-data": [],
|
|
61
|
+
exfiltration: [],
|
|
62
|
+
destructive: [],
|
|
63
|
+
},
|
|
64
|
+
findings: [],
|
|
65
|
+
durationMs: 0,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export function scanDirectory(dirPath, options = {}) {
|
|
69
|
+
const start = Date.now();
|
|
70
|
+
const { maxFiles = 5000, exclude } = options;
|
|
71
|
+
if (!Number.isInteger(maxFiles) || maxFiles < 1) {
|
|
72
|
+
throw new Error(`maxFiles must be a positive integer (got ${String(maxFiles)})`);
|
|
73
|
+
}
|
|
74
|
+
const allTools = [];
|
|
75
|
+
let scannedFiles = 0;
|
|
76
|
+
let skippedFiles = 0;
|
|
77
|
+
function walk(dir) {
|
|
78
|
+
if (scannedFiles + skippedFiles >= maxFiles)
|
|
79
|
+
return;
|
|
80
|
+
let entries;
|
|
81
|
+
try {
|
|
82
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
const fullPath = join(dir, entry.name);
|
|
89
|
+
if (entry.isDirectory()) {
|
|
90
|
+
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith("."))
|
|
91
|
+
continue;
|
|
92
|
+
if (exclude?.some((ex) => entry.name === ex || fullPath.includes(ex)))
|
|
93
|
+
continue;
|
|
94
|
+
walk(fullPath);
|
|
95
|
+
}
|
|
96
|
+
else if (entry.isFile()) {
|
|
97
|
+
const rel = relative(dirPath, fullPath);
|
|
98
|
+
if (exclude?.some((ex) => rel.includes(ex))) {
|
|
99
|
+
skippedFiles++;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const ext = extname(fullPath).toLowerCase();
|
|
103
|
+
if (SKIP_EXTENSIONS.has(ext)) {
|
|
104
|
+
skippedFiles++;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
let stat;
|
|
108
|
+
try {
|
|
109
|
+
stat = statSync(fullPath);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
skippedFiles++;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (!stat.isFile() || stat.size > MAX_FILE_SIZE) {
|
|
116
|
+
skippedFiles++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const content = readFileSync(fullPath, "utf8");
|
|
121
|
+
const tools = extractToolsFromSource(content);
|
|
122
|
+
scannedFiles++;
|
|
123
|
+
allTools.push(...tools);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
skippedFiles++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
walk(dirPath);
|
|
132
|
+
const byName = new Map();
|
|
133
|
+
for (const tool of allTools) {
|
|
134
|
+
if (!tool.name)
|
|
135
|
+
continue;
|
|
136
|
+
const prev = byName.get(tool.name);
|
|
137
|
+
if (!prev || (tool.description && !(prev.description ?? "")))
|
|
138
|
+
byName.set(tool.name, tool);
|
|
139
|
+
else if (!byName.has(tool.name))
|
|
140
|
+
byName.set(tool.name, tool);
|
|
141
|
+
}
|
|
142
|
+
const result = auditCatalog([...byName.values()], dirPath);
|
|
143
|
+
return {
|
|
144
|
+
...result,
|
|
145
|
+
scannedFiles,
|
|
146
|
+
skippedFiles,
|
|
147
|
+
durationMs: Date.now() - start,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkD,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AA+B7F,wBAAsB,WAAW,kBA6FhC;AAED,YAAY,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
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 { auditCatalog, parseToolsJson } from "./analyzer.js";
|
|
6
|
+
import { scanDirectory, scanSourceFile, scanSourceText } from "./scanner.js";
|
|
7
|
+
function formatAudit(result, extra = "") {
|
|
8
|
+
const lines = [
|
|
9
|
+
`Grade ${result.grade} (score ${result.score}/100) — ${result.toolCount} tool(s) in ${result.durationMs}ms`,
|
|
10
|
+
` untrusted-content : ${result.byCapability["untrusted-content"].join(", ") || "—"}`,
|
|
11
|
+
` sensitive-data : ${result.byCapability["sensitive-data"].join(", ") || "—"}`,
|
|
12
|
+
` exfiltration : ${result.byCapability.exfiltration.join(", ") || "—"}`,
|
|
13
|
+
` destructive : ${result.byCapability.destructive.join(", ") || "—"}`,
|
|
14
|
+
"",
|
|
15
|
+
];
|
|
16
|
+
if (extra)
|
|
17
|
+
lines.unshift(extra);
|
|
18
|
+
if (result.findings.length === 0) {
|
|
19
|
+
lines.push("âś… No toxic-flow or dangerous capability pairs found.");
|
|
20
|
+
return lines.join("\n");
|
|
21
|
+
}
|
|
22
|
+
lines.push(`⚠️ ${result.findings.length} finding(s):`);
|
|
23
|
+
lines.push("");
|
|
24
|
+
for (const f of result.findings) {
|
|
25
|
+
lines.push(`[${f.severity.toUpperCase()}] ${f.patternName} (${f.owasp})`);
|
|
26
|
+
lines.push(` Tools : ${f.tools.join(", ")}`);
|
|
27
|
+
lines.push(` Capabilities : ${f.capabilities.join(", ")}`);
|
|
28
|
+
lines.push(` Recommendation : ${f.recommendation}`);
|
|
29
|
+
lines.push("");
|
|
30
|
+
}
|
|
31
|
+
return lines.join("\n");
|
|
32
|
+
}
|
|
33
|
+
export async function startServer() {
|
|
34
|
+
const server = new McpServer({
|
|
35
|
+
name: "guardbee-toxic-flow-auditor",
|
|
36
|
+
version: "0.1.0",
|
|
37
|
+
});
|
|
38
|
+
instrumentServer(server, "toxic-flow-auditor");
|
|
39
|
+
server.tool("audit_catalog", "Audit a tools/list-shaped JSON catalog for the lethal trifecta (toxic flow): untrusted content + sensitive/private data + exfiltration or destruction on the same MCP server. Returns an A–F grade.", {
|
|
40
|
+
catalog: z.string().describe("JSON array of tools, or { tools: [...] } from tools/list"),
|
|
41
|
+
label: z.string().optional().describe("Optional label for the server/catalog"),
|
|
42
|
+
}, async ({ catalog, label }) => {
|
|
43
|
+
const tools = parseToolsJson(catalog);
|
|
44
|
+
const result = auditCatalog(tools, label ?? "catalog");
|
|
45
|
+
return { content: [{ type: "text", text: formatAudit(result) }] };
|
|
46
|
+
});
|
|
47
|
+
server.tool("scan_source", "Scan TypeScript/JavaScript MCP server source for .tool(...) registrations and audit them for toxic flows", {
|
|
48
|
+
content: z.string().describe("Source code to scan"),
|
|
49
|
+
label: z.string().optional(),
|
|
50
|
+
}, async ({ content, label }) => {
|
|
51
|
+
const result = scanSourceText(content, label);
|
|
52
|
+
return { content: [{ type: "text", text: formatAudit(result) }] };
|
|
53
|
+
});
|
|
54
|
+
server.tool("scan_file", "Scan a single source file for toxic flows", { path: z.string() }, async ({ path: filePath }) => {
|
|
55
|
+
const { result, skipped } = scanSourceFile(filePath);
|
|
56
|
+
if (skipped) {
|
|
57
|
+
return { content: [{ type: "text", text: `âŹď¸Ź File skipped: ${filePath}` }] };
|
|
58
|
+
}
|
|
59
|
+
return { content: [{ type: "text", text: formatAudit(result) }] };
|
|
60
|
+
});
|
|
61
|
+
server.tool("scan_directory", "Recursively scan a directory of MCP server source for toxic flows across all registered tools", {
|
|
62
|
+
path: z.string(),
|
|
63
|
+
maxFiles: z.number().int().positive().optional(),
|
|
64
|
+
exclude: z.array(z.string()).optional(),
|
|
65
|
+
}, async ({ path: dirPath, maxFiles, exclude }) => {
|
|
66
|
+
const result = scanDirectory(dirPath, { maxFiles, exclude });
|
|
67
|
+
return {
|
|
68
|
+
content: [
|
|
69
|
+
{
|
|
70
|
+
type: "text",
|
|
71
|
+
text: formatAudit(result, `Scanned ${result.scannedFiles} file(s), skipped ${result.skippedFiles}.`),
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
server.tool("explain_trifecta", "Explain the lethal trifecta / toxic-flow model this auditor uses", {}, async () => ({
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: "text",
|
|
80
|
+
text: [
|
|
81
|
+
"Lethal trifecta (Simon Willison) / toxic flow:",
|
|
82
|
+
"1. Untrusted content — fetch, scrape, browse, issues, feeds the model will read",
|
|
83
|
+
"2. Sensitive data — secrets, DB, vault, mailboxes, KVKK-regulated PII",
|
|
84
|
+
"3. Exfiltration or destruction — send, webhook, export, delete, drop",
|
|
85
|
+
"",
|
|
86
|
+
"When one MCP server exposes all three, a prompt injection can chain them.",
|
|
87
|
+
"Mapped primarily to OWASP MCP10:2025 (context over-sharing / toxic combination).",
|
|
88
|
+
"Also reports dangerous pairs (sensitive+exfil, untrusted+exfil, sensitive+destruct).",
|
|
89
|
+
].join("\n"),
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
}));
|
|
93
|
+
const transport = new StdioServerTransport();
|
|
94
|
+
await server.connect(transport);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAkC,MAAM,eAAe,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE7E,SAAS,WAAW,CAAC,MAAmB,EAAE,KAAK,GAAG,EAAE;IAClD,MAAM,KAAK,GAAa;QACtB,SAAS,MAAM,CAAC,KAAK,WAAW,MAAM,CAAC,KAAK,WAAW,MAAM,CAAC,SAAS,eAAe,MAAM,CAAC,UAAU,IAAI;QAC3G,yBAAyB,MAAM,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE;QACrF,yBAAyB,MAAM,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE;QAClF,yBAAyB,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE;QAC7E,yBAAyB,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE;QAC5E,EAAE;KACH,CAAC;IACF,IAAI,KAAK;QAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAEhC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;QACnE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,cAAc,CAAC,CAAC;IACxD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;QAC1E,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvD,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9D,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC;QACrD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,6BAA6B;QACnC,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IACH,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAE/C,MAAM,CAAC,IAAI,CACT,eAAe,EACf,qMAAqM,EACrM;QACE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0DAA0D,CAAC;QACxF,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;KAC/E,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3B,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,IAAI,SAAS,CAAC,CAAC;QACvD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IACpE,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,aAAa,EACb,0GAA0G,EAC1G;QACE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;QACnD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC7B,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC9C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IACpE,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,WAAW,EACX,2CAA2C,EAC3C,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,EACpB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC3B,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC;QAChF,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IACpE,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,+FAA+F,EAC/F;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;QAChD,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;KACxC,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;QAC7C,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAC7D,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,MAAM,CAAC,YAAY,qBAAqB,MAAM,CAAC,YAAY,GAAG,CAAC;iBACrG;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,kEAAkE,EAClE,EAAE,EACF,KAAK,IAAI,EAAE,CAAC,CAAC;QACX,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE;oBACJ,gDAAgD;oBAChD,iFAAiF;oBACjF,uEAAuE;oBACvE,sEAAsE;oBACtE,EAAE;oBACF,2EAA2E;oBAC3E,kFAAkF;oBAClF,sFAAsF;iBACvF,CAAC,IAAI,CAAC,IAAI,CAAC;aACb;SACF;KACF,CAAC,CACH,CAAC;IAEF,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@guardbee/mcp-toxic-flow-auditor",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"mcpName": "io.github.4hmetuyar/toxic-flow-auditor",
|
|
5
|
+
"description": "MCP server that detects toxic flows (lethal trifecta) across an MCP tool catalog — untrusted content + private/sensitive data + exfiltration or destruction on the same server",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"guardbee-toxic-flow-auditor": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md",
|
|
21
|
+
"TR.md"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
25
|
+
"zod": "^4.5.4",
|
|
26
|
+
"@guardbee/mcp-telemetry": "^0.1.3"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"mcp",
|
|
30
|
+
"security",
|
|
31
|
+
"ai-security",
|
|
32
|
+
"mcp-security",
|
|
33
|
+
"toxic-flow",
|
|
34
|
+
"lethal-trifecta",
|
|
35
|
+
"owasp-mcp-top-10",
|
|
36
|
+
"kvkk",
|
|
37
|
+
"guardbee",
|
|
38
|
+
"devtools"
|
|
39
|
+
],
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://github.com/GuardBee/guardbee-mcp.git",
|
|
47
|
+
"directory": "packages/toxic-flow-auditor"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc",
|
|
51
|
+
"dev": "tsc --watch",
|
|
52
|
+
"start": "node dist/cli.js",
|
|
53
|
+
"test": "vitest run"
|
|
54
|
+
}
|
|
55
|
+
}
|