@guardbee/mcp-prompt-leak-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.
Files changed (43) hide show
  1. package/README.md +146 -0
  2. package/TR.md +146 -0
  3. package/dist/__tests__/proxy.test.d.ts +2 -0
  4. package/dist/__tests__/proxy.test.d.ts.map +1 -0
  5. package/dist/__tests__/proxy.test.js +107 -0
  6. package/dist/__tests__/proxy.test.js.map +1 -0
  7. package/dist/__tests__/scanner.test.d.ts +2 -0
  8. package/dist/__tests__/scanner.test.d.ts.map +1 -0
  9. package/dist/__tests__/scanner.test.js +111 -0
  10. package/dist/__tests__/scanner.test.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +205 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config.d.ts +8 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +101 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/index.d.ts +7 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +4 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/patterns.d.ts +13 -0
  24. package/dist/patterns.d.ts.map +1 -0
  25. package/dist/patterns.js +169 -0
  26. package/dist/patterns.js.map +1 -0
  27. package/dist/proxy.d.ts +34 -0
  28. package/dist/proxy.d.ts.map +1 -0
  29. package/dist/proxy.js +92 -0
  30. package/dist/proxy.js.map +1 -0
  31. package/dist/sarif.d.ts +3 -0
  32. package/dist/sarif.d.ts.map +1 -0
  33. package/dist/sarif.js +54 -0
  34. package/dist/sarif.js.map +1 -0
  35. package/dist/scanner.d.ts +44 -0
  36. package/dist/scanner.d.ts.map +1 -0
  37. package/dist/scanner.js +237 -0
  38. package/dist/scanner.js.map +1 -0
  39. package/dist/server.d.ts +2 -0
  40. package/dist/server.d.ts.map +1 -0
  41. package/dist/server.js +85 -0
  42. package/dist/server.js.map +1 -0
  43. package/package.json +50 -0
package/dist/proxy.js ADDED
@@ -0,0 +1,92 @@
1
+ import { createServer, request as httpRequest } from "http";
2
+ import { request as httpsRequest } from "https";
3
+ import { URL } from "url";
4
+ import { scanBody, redactBody } from "./scanner.js";
5
+ const SEV_RANK = { critical: 0, high: 1, medium: 2, low: 3 };
6
+ /**
7
+ * A reverse proxy that sits between your application and a real LLM API
8
+ * (OpenAI/Anthropic-compatible). It buffers and inspects only the outbound
9
+ * REQUEST body — message/system text fields — for leaked credentials and PII,
10
+ * then streams the upstream response straight back unmodified (so SSE/streaming
11
+ * completions pass through untouched; only the prompt going *out* is inspected).
12
+ *
13
+ * The audit trail never stores the actual matched text — only which pattern
14
+ * fired and where — so the proxy's own logs can't become a second copy of the
15
+ * leak it just caught.
16
+ */
17
+ export function startProxy(options) {
18
+ const upstreamUrl = new URL(options.upstream);
19
+ const isHttps = upstreamUrl.protocol === "https:";
20
+ const failOnRank = SEV_RANK[options.failOnSeverity ?? "high"] ?? 1;
21
+ const server = createServer((req, res) => {
22
+ const chunks = [];
23
+ req.on("data", (c) => chunks.push(c));
24
+ req.on("end", () => {
25
+ const rawBody = Buffer.concat(chunks);
26
+ let parsedBody = null;
27
+ let bodyToSend = rawBody;
28
+ let findings = [];
29
+ const contentType = String(req.headers["content-type"] ?? "");
30
+ if (contentType.includes("application/json") && rawBody.length > 0) {
31
+ try {
32
+ parsedBody = JSON.parse(rawBody.toString("utf8"));
33
+ }
34
+ catch {
35
+ parsedBody = null;
36
+ }
37
+ }
38
+ if (parsedBody) {
39
+ if (options.mode === "redact") {
40
+ const result = redactBody(parsedBody);
41
+ findings = result.findings;
42
+ bodyToSend = Buffer.from(JSON.stringify(result.redactedBody), "utf8");
43
+ }
44
+ else {
45
+ findings = scanBody(parsedBody).findings;
46
+ }
47
+ }
48
+ const blocked = options.mode === "block" && findings.some((f) => SEV_RANK[f.severity] <= failOnRank);
49
+ options.onAudit?.({
50
+ timestamp: new Date().toISOString(),
51
+ method: req.method ?? "GET",
52
+ path: req.url ?? "/",
53
+ mode: options.mode,
54
+ blocked,
55
+ findingCount: findings.length,
56
+ findingCodes: [...new Set(findings.map((f) => f.patternId))],
57
+ });
58
+ if (blocked) {
59
+ res.writeHead(400, { "content-type": "application/json" });
60
+ res.end(JSON.stringify({
61
+ error: "blocked_by_guardbee_prompt_leak_scanner",
62
+ message: `Request blocked: ${findings.length} finding(s) at or above '${options.failOnSeverity ?? "high"}' severity`,
63
+ findings: findings.map((f) => ({ patternId: f.patternId, severity: f.severity, location: f.location })),
64
+ }, null, 2));
65
+ return;
66
+ }
67
+ const forwardHeaders = { ...req.headers };
68
+ delete forwardHeaders.host;
69
+ forwardHeaders["content-length"] = String(bodyToSend.length);
70
+ const doRequest = isHttps ? httpsRequest : httpRequest;
71
+ const upstreamReq = doRequest({
72
+ protocol: upstreamUrl.protocol,
73
+ hostname: upstreamUrl.hostname,
74
+ port: upstreamUrl.port || (isHttps ? 443 : 80),
75
+ path: req.url,
76
+ method: req.method,
77
+ headers: forwardHeaders,
78
+ }, (upstreamRes) => {
79
+ res.writeHead(upstreamRes.statusCode ?? 502, upstreamRes.headers);
80
+ upstreamRes.pipe(res);
81
+ });
82
+ upstreamReq.on("error", (err) => {
83
+ res.writeHead(502, { "content-type": "application/json" });
84
+ res.end(JSON.stringify({ error: "upstream_request_failed", message: err.message }));
85
+ });
86
+ upstreamReq.end(bodyToSend);
87
+ });
88
+ });
89
+ server.listen(options.port);
90
+ return server;
91
+ }
92
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Finding } from "./scanner.js";
2
+ export declare function buildSarif(toolVersion: string, findings: Finding[]): object;
3
+ //# sourceMappingURL=sarif.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../src/sarif.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AA6B5C,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAgD3E"}
package/dist/sarif.js ADDED
@@ -0,0 +1,54 @@
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/prompt-leak-scanner",
19
+ properties: { "problem.severity": f.severity, tags: ["security", "ai-security", "data-leak", f.category] },
20
+ });
21
+ }
22
+ results.push({
23
+ ruleId: f.patternId,
24
+ level: severityToLevel(f.severity),
25
+ message: { text: `${f.patternName} (${f.maskedMatch}) — ${f.recommendation}` },
26
+ locations: [
27
+ {
28
+ physicalLocation: {
29
+ artifactLocation: { uri: f.location, uriBaseId: "%SRCROOT%" },
30
+ region: { startLine: f.line, startColumn: f.column },
31
+ },
32
+ },
33
+ ],
34
+ });
35
+ }
36
+ return {
37
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
38
+ version: "2.1.0",
39
+ runs: [
40
+ {
41
+ tool: {
42
+ driver: {
43
+ name: "@guardbee/mcp-prompt-leak-scanner",
44
+ version: toolVersion,
45
+ informationUri: "https://guardbee.ai",
46
+ rules: Array.from(rulesMap.values()),
47
+ },
48
+ },
49
+ results,
50
+ },
51
+ ],
52
+ };
53
+ }
54
+ //# sourceMappingURL=sarif.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif.js","sourceRoot":"","sources":["../src/sarif.ts"],"names":[],"mappings":"AAuBA,SAAS,eAAe,CAAC,GAAwB;IAC/C,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,EAAqB,CAAC;IAC9C,MAAM,OAAO,GAAkB,EAAE,CAAC;IAElC,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,8CAA8C;gBACvD,UAAU,EAAE,EAAE,kBAAkB,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE;aAC3G,CAAC,CAAC;QACL,CAAC;QAED,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,KAAK,CAAC,CAAC,WAAW,OAAO,CAAC,CAAC,cAAc,EAAE,EAAE;YAC9E,SAAS,EAAE;gBACT;oBACE,gBAAgB,EAAE;wBAChB,gBAAgB,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE;wBAC7D,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;qBACrD;iBACF;aACF;SACF,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,mCAAmC;wBACzC,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,44 @@
1
+ import { type LeakPattern } from "./patterns.js";
2
+ export interface Finding {
3
+ patternId: string;
4
+ patternName: string;
5
+ category: LeakPattern["category"];
6
+ severity: LeakPattern["severity"];
7
+ recommendation: string;
8
+ location: string;
9
+ line: number;
10
+ column: number;
11
+ /** The leaked value itself, masked — never echoed back in full (we'd otherwise be re-leaking the very thing we just found). */
12
+ maskedMatch: string;
13
+ }
14
+ export interface ScanResult {
15
+ scannedFiles: number;
16
+ skippedFiles: number;
17
+ totalFindings: number;
18
+ findings: Finding[];
19
+ durationMs: number;
20
+ }
21
+ export declare function scanText(text: string, location?: string): Finding[];
22
+ /** Same as scanText, but also returns the text with every valid match replaced by a redaction placeholder. */
23
+ export declare function scanAndRedactText(text: string, location?: string): {
24
+ findings: Finding[];
25
+ redactedText: string;
26
+ };
27
+ export declare function scanBody(body: unknown): {
28
+ findings: Finding[];
29
+ };
30
+ /** Redacts matches in place within `body`'s message/system text fields and returns the same (mutated) object. */
31
+ export declare function redactBody(body: unknown): {
32
+ findings: Finding[];
33
+ redactedBody: unknown;
34
+ };
35
+ export declare function scanFile(filePath: string): {
36
+ findings: Finding[];
37
+ skipped: boolean;
38
+ };
39
+ export declare function scanDirectory(dirPath: string, options?: {
40
+ maxFiles?: number;
41
+ include?: string[];
42
+ exclude?: string[];
43
+ }): ScanResult;
44
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAEhE,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAClC,QAAQ,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,+HAA+H;IAC/H,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAkBD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAU,GAAG,OAAO,EAAE,CAoCpE;AAED,8GAA8G;AAC9G,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAU,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CA+CjH;AAyCD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAA;CAAE,CAK/D;AAED,iHAAiH;AACjH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CASxF;AAID,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA+BpF;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,GAC1E,UAAU,CAoDZ"}
@@ -0,0 +1,237 @@
1
+ import { readFileSync, statSync, readdirSync } from "fs";
2
+ import { join, relative, extname } from "path";
3
+ import { LEAK_PATTERNS } from "./patterns.js";
4
+ const SKIP_EXTENSIONS = new Set([
5
+ ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".bmp",
6
+ ".pdf", ".zip", ".tar", ".gz", ".bz2", ".rar", ".7z",
7
+ ".exe", ".dll", ".so", ".dylib", ".bin", ".wasm",
8
+ ".mp3", ".mp4", ".avi", ".mov", ".wav",
9
+ ".ttf", ".woff", ".woff2", ".eot",
10
+ ".lock",
11
+ ]);
12
+ const SKIP_DIRS = new Set(["node_modules", ".git", ".svn", "dist", "build", ".next", "coverage"]);
13
+ const MAX_FILE_SIZE = 1 * 1024 * 1024; // 1MB
14
+ function maskMatch(raw) {
15
+ if (raw.length <= 6)
16
+ return "*".repeat(raw.length);
17
+ return `${raw.slice(0, 3)}…${raw.slice(-2)}`;
18
+ }
19
+ export function scanText(text, location = "input") {
20
+ const findings = [];
21
+ const lines = text.split("\n");
22
+ for (const p of LEAK_PATTERNS) {
23
+ const re = new RegExp(p.pattern.source, p.pattern.flags);
24
+ let m;
25
+ while ((m = re.exec(text)) !== null) {
26
+ const matchStr = m[0];
27
+ if (p.validate && !p.validate(matchStr)) {
28
+ if (m.index === re.lastIndex)
29
+ re.lastIndex++;
30
+ continue;
31
+ }
32
+ const before = text.slice(0, m.index);
33
+ const line = before.split("\n").length;
34
+ const lastNl = before.lastIndexOf("\n");
35
+ const column = m.index - (lastNl === -1 ? 0 : lastNl + 1) + 1;
36
+ findings.push({
37
+ patternId: p.id,
38
+ patternName: p.name,
39
+ category: p.category,
40
+ severity: p.severity,
41
+ recommendation: p.recommendation,
42
+ location,
43
+ line,
44
+ column,
45
+ maskedMatch: maskMatch(matchStr),
46
+ });
47
+ if (m.index === re.lastIndex)
48
+ re.lastIndex++;
49
+ }
50
+ }
51
+ return findings;
52
+ }
53
+ /** Same as scanText, but also returns the text with every valid match replaced by a redaction placeholder. */
54
+ export function scanAndRedactText(text, location = "input") {
55
+ const findings = [];
56
+ const replacements = [];
57
+ for (const p of LEAK_PATTERNS) {
58
+ const re = new RegExp(p.pattern.source, p.pattern.flags);
59
+ let m;
60
+ while ((m = re.exec(text)) !== null) {
61
+ const matchStr = m[0];
62
+ if (p.validate && !p.validate(matchStr)) {
63
+ if (m.index === re.lastIndex)
64
+ re.lastIndex++;
65
+ continue;
66
+ }
67
+ const before = text.slice(0, m.index);
68
+ const line = before.split("\n").length;
69
+ const lastNl = before.lastIndexOf("\n");
70
+ const column = m.index - (lastNl === -1 ? 0 : lastNl + 1) + 1;
71
+ findings.push({
72
+ patternId: p.id,
73
+ patternName: p.name,
74
+ category: p.category,
75
+ severity: p.severity,
76
+ recommendation: p.recommendation,
77
+ location,
78
+ line,
79
+ column,
80
+ maskedMatch: maskMatch(matchStr),
81
+ });
82
+ replacements.push({ start: m.index, end: m.index + matchStr.length, replacement: `[REDACTED:${p.id}]` });
83
+ if (m.index === re.lastIndex)
84
+ re.lastIndex++;
85
+ }
86
+ }
87
+ replacements.sort((a, b) => a.start - b.start);
88
+ let redactedText = "";
89
+ let cursor = 0;
90
+ for (const r of replacements) {
91
+ if (r.start < cursor)
92
+ continue; // overlapping match already redacted by an earlier pattern
93
+ redactedText += text.slice(cursor, r.start) + r.replacement;
94
+ cursor = r.end;
95
+ }
96
+ redactedText += text.slice(cursor);
97
+ return { findings, redactedText };
98
+ }
99
+ function extractTextFields(body) {
100
+ const fields = [];
101
+ if (typeof body !== "object" || body === null)
102
+ return fields;
103
+ const b = body;
104
+ if (typeof b.system === "string") {
105
+ const parentB = b;
106
+ fields.push({ location: "system", text: b.system, setText: (t) => { parentB.system = t; } });
107
+ }
108
+ if (Array.isArray(b.messages)) {
109
+ b.messages.forEach((msg, i) => {
110
+ if (typeof msg !== "object" || msg === null)
111
+ return;
112
+ const m = msg;
113
+ if (typeof m.content === "string") {
114
+ fields.push({ location: `messages[${i}].content`, text: m.content, setText: (t) => { m.content = t; } });
115
+ }
116
+ else if (Array.isArray(m.content)) {
117
+ m.content.forEach((block, j) => {
118
+ if (typeof block !== "object" || block === null)
119
+ return;
120
+ const bl = block;
121
+ if (typeof bl.text === "string") {
122
+ fields.push({ location: `messages[${i}].content[${j}].text`, text: bl.text, setText: (t) => { bl.text = t; } });
123
+ }
124
+ });
125
+ }
126
+ });
127
+ }
128
+ return fields;
129
+ }
130
+ export function scanBody(body) {
131
+ const fields = extractTextFields(body);
132
+ const findings = [];
133
+ for (const field of fields)
134
+ findings.push(...scanText(field.text, field.location));
135
+ return { findings };
136
+ }
137
+ /** Redacts matches in place within `body`'s message/system text fields and returns the same (mutated) object. */
138
+ export function redactBody(body) {
139
+ const fields = extractTextFields(body);
140
+ const findings = [];
141
+ for (const field of fields) {
142
+ const { findings: fFindings, redactedText } = scanAndRedactText(field.text, field.location);
143
+ findings.push(...fFindings);
144
+ if (fFindings.length > 0)
145
+ field.setText(redactedText);
146
+ }
147
+ return { findings, redactedBody: body };
148
+ }
149
+ // ── File / directory scanning (CI use — request-body fixtures, prompt logs) ───
150
+ export function scanFile(filePath) {
151
+ const ext = extname(filePath).toLowerCase();
152
+ if (SKIP_EXTENSIONS.has(ext))
153
+ return { findings: [], skipped: true };
154
+ let stat;
155
+ try {
156
+ stat = statSync(filePath);
157
+ }
158
+ catch {
159
+ return { findings: [], skipped: true };
160
+ }
161
+ if (!stat.isFile() || stat.size > MAX_FILE_SIZE)
162
+ return { findings: [], skipped: true };
163
+ let content;
164
+ try {
165
+ content = readFileSync(filePath, "utf8");
166
+ }
167
+ catch {
168
+ return { findings: [], skipped: true };
169
+ }
170
+ if (ext === ".json") {
171
+ try {
172
+ const parsed = JSON.parse(content);
173
+ if (parsed && typeof parsed === "object" && (Array.isArray(parsed.messages) || typeof parsed.system === "string")) {
174
+ return { findings: scanBody(parsed).findings.map((f) => ({ ...f, location: `${filePath}:${f.location}` })), skipped: false };
175
+ }
176
+ }
177
+ catch {
178
+ // fall through to plain-text scanning
179
+ }
180
+ }
181
+ return { findings: scanText(content, filePath), skipped: false };
182
+ }
183
+ export function scanDirectory(dirPath, options = {}) {
184
+ const start = Date.now();
185
+ const { maxFiles = 5000, include, exclude } = options;
186
+ const allFindings = [];
187
+ let scannedFiles = 0;
188
+ let skippedFiles = 0;
189
+ function walk(dir) {
190
+ if (scannedFiles + skippedFiles >= maxFiles)
191
+ return;
192
+ let entries;
193
+ try {
194
+ entries = readdirSync(dir, { withFileTypes: true });
195
+ }
196
+ catch {
197
+ return;
198
+ }
199
+ for (const entry of entries) {
200
+ const fullPath = join(dir, entry.name);
201
+ if (entry.isDirectory()) {
202
+ if (SKIP_DIRS.has(entry.name) || entry.name.startsWith("."))
203
+ continue;
204
+ if (exclude?.some((ex) => entry.name === ex || fullPath.includes(ex)))
205
+ continue;
206
+ walk(fullPath);
207
+ }
208
+ else if (entry.isFile()) {
209
+ const relPath = relative(dirPath, fullPath);
210
+ if (exclude?.some((ex) => relPath.includes(ex))) {
211
+ skippedFiles++;
212
+ continue;
213
+ }
214
+ if (include && !include.some((inc) => relPath.includes(inc))) {
215
+ skippedFiles++;
216
+ continue;
217
+ }
218
+ const { findings, skipped } = scanFile(fullPath);
219
+ if (skipped)
220
+ skippedFiles++;
221
+ else {
222
+ scannedFiles++;
223
+ allFindings.push(...findings);
224
+ }
225
+ }
226
+ }
227
+ }
228
+ walk(dirPath);
229
+ return {
230
+ scannedFiles,
231
+ skippedFiles,
232
+ totalFindings: allFindings.length,
233
+ findings: allFindings,
234
+ durationMs: Date.now() - start,
235
+ };
236
+ }
237
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function startServer(): Promise<void>;
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAiCA,wBAAsB,WAAW,kBAsFhC"}
package/dist/server.js ADDED
@@ -0,0 +1,85 @@
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, scanBody, scanFile, scanDirectory } from "./scanner.js";
6
+ import { LEAK_PATTERNS } from "./patterns.js";
7
+ function formatFindings(findings, scannedFiles, durationMs) {
8
+ if (findings.length === 0) {
9
+ return `✅ No leaked credentials or PII found. Scanned ${scannedFiles} item(s) in ${durationMs}ms.`;
10
+ }
11
+ const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
12
+ for (const f of findings)
13
+ bySeverity[f.severity]++;
14
+ const lines = [
15
+ `⚠️ Found ${findings.length} finding(s) in ${scannedFiles} item(s) (${durationMs}ms)`,
16
+ ` Critical: ${bySeverity.critical} High: ${bySeverity.high} Medium: ${bySeverity.medium} Low: ${bySeverity.low}`,
17
+ "",
18
+ ];
19
+ for (const f of findings) {
20
+ lines.push(`[${f.severity.toUpperCase()}] ${f.patternName} (${f.category})`);
21
+ lines.push(` Location : ${f.location}:${f.line}:${f.column}`);
22
+ lines.push(` Value : ${f.maskedMatch} (masked)`);
23
+ lines.push(` Recommendation : ${f.recommendation}`);
24
+ lines.push("");
25
+ }
26
+ return lines.join("\n");
27
+ }
28
+ export async function startServer() {
29
+ const server = new McpServer({
30
+ name: "guardbee-prompt-leak-scanner",
31
+ version: "0.1.0",
32
+ });
33
+ instrumentServer(server, "prompt-leak-scanner");
34
+ server.tool("scan_text", "Scan a raw text string (e.g. a prompt you're about to send to an LLM) for leaked credentials (API keys, private keys, JWTs) and PII (TC Kimlik No, credit card, IBAN, email, phone) — checksum-validated where applicable to keep false positives low.", {
35
+ content: z.string().describe("The text to scan"),
36
+ label: z.string().optional().describe("Optional label shown in findings"),
37
+ }, async ({ content, label }) => {
38
+ const findings = scanText(content, label);
39
+ return { content: [{ type: "text", text: formatFindings(findings, 1, 0) }] };
40
+ });
41
+ server.tool("scan_messages", "Scan an OpenAI/Anthropic-style chat request body (a JSON object with a `messages` array and/or a `system` field) for leaked credentials and PII in every message's text content, before it would be sent to the model.", {
42
+ body: z.record(z.string(), z.unknown()).describe("The chat request body object, e.g. { model, system, messages: [{ role, content }] }"),
43
+ }, async ({ body }) => {
44
+ const { findings } = scanBody(body);
45
+ return { content: [{ type: "text", text: formatFindings(findings, 1, 0) }] };
46
+ });
47
+ server.tool("scan_file", "Scan a single file — plain text, or a JSON chat-request-body fixture (auto-detected) — for leaked credentials and PII", {
48
+ path: z.string().describe("Absolute or relative path to the file to scan"),
49
+ }, async ({ path: filePath }) => {
50
+ const { findings, skipped } = scanFile(filePath);
51
+ if (skipped) {
52
+ return { content: [{ type: "text", text: `⏭️ File skipped (binary, too large, or unreadable): ${filePath}` }] };
53
+ }
54
+ return { content: [{ type: "text", text: formatFindings(findings, 1, 0) }] };
55
+ });
56
+ server.tool("scan_directory", "Recursively scan a directory of prompt logs or request-body fixtures for leaked credentials and PII", {
57
+ path: z.string().describe("Absolute or relative path to the directory to scan"),
58
+ maxFiles: z.number().optional().describe("Maximum number of files to scan (default: 5000)"),
59
+ include: z.array(z.string()).optional().describe("Only scan files whose path contains one of these strings"),
60
+ exclude: z.array(z.string()).optional().describe("Skip files/dirs whose path contains one of these strings"),
61
+ }, async ({ path: dirPath, maxFiles, include, exclude }) => {
62
+ const result = scanDirectory(dirPath, { maxFiles, include, exclude });
63
+ return { content: [{ type: "text", text: formatFindings(result.findings, result.scannedFiles, result.durationMs) }] };
64
+ });
65
+ server.tool("list_patterns", "List every credential/PII pattern this scanner detects, grouped by category", {}, async () => {
66
+ const lines = ["Detected patterns:\n"];
67
+ const byCategory = new Map();
68
+ for (const p of LEAK_PATTERNS) {
69
+ const list = byCategory.get(p.category) ?? [];
70
+ list.push(p);
71
+ byCategory.set(p.category, list);
72
+ }
73
+ for (const [category, patterns] of byCategory) {
74
+ lines.push(`[${category}]`);
75
+ for (const p of patterns)
76
+ lines.push(` • ${p.name} (${p.id}, ${p.severity})${p.validate ? " — checksum-validated" : ""}`);
77
+ lines.push("");
78
+ }
79
+ lines.push("Use the `proxy` CLI command to run this as a live reverse proxy in front of a real LLM API (monitor/redact/block modes).");
80
+ return { content: [{ type: "text", text: lines.join("\n") }] };
81
+ });
82
+ const transport = new StdioServerTransport();
83
+ await server.connect(transport);
84
+ }
85
+ //# 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,50 @@
1
+ {
2
+ "name": "@guardbee/mcp-prompt-leak-scanner",
3
+ "version": "0.1.0",
4
+ "description": "MCP server and reverse proxy that catches leaked credentials and PII (API keys, TC Kimlik No, credit cards, IBANs) in outbound LLM prompts before they leave your application",
5
+ "type": "module",
6
+ "bin": {
7
+ "guardbee-prompt-leak-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
+ "llm-security",
32
+ "data-leak-prevention",
33
+ "kvkk",
34
+ "gdpr",
35
+ "guardbee",
36
+ "devtools"
37
+ ],
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/GuardBee/guardbee-mcp.git",
42
+ "directory": "packages/prompt-leak-scanner"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc",
46
+ "dev": "tsc --watch",
47
+ "start": "node dist/cli.js",
48
+ "test": "vitest run"
49
+ }
50
+ }