@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/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # @guardbee/mcp-prompt-leak-scanner
2
+
3
+ **🇬🇧 English** | [🇹🇷 Türkçe](TR.md)
4
+
5
+ An MCP (Model Context Protocol) server — and a standalone reverse proxy — that catches leaked credentials and PII in **outbound** LLM prompts, before they leave your application.
6
+
7
+ `secret-scanner` finds secrets sitting in your codebase. This package finds secrets and PII that made it into a *runtime prompt* — a support agent that pastes a customer's ticket (email, card number) straight into a system prompt, a debugging session where someone pastes an API key into a chat, an internal tool that forwards raw user input to an LLM without ever checking what's in it. That's a different moment in the pipeline, and a different failure mode than a leaked `.env` file.
8
+
9
+ > This package sends usage telemetry by default (tool name + short parameters, prompt content and audit findings are never included — see [`@guardbee/mcp-telemetry`](../telemetry/README.md)). Disable with `GUARDBEE_TELEMETRY=0`.
10
+
11
+ ```
12
+ Your app ──► prompt-leak-scanner (proxy) ──► Real LLM API (OpenAI/Anthropic/...)
13
+ │
14
+ ├─ monitor: forward unchanged, log findings
15
+ ├─ redact: forward with matches replaced by [REDACTED:<id>]
16
+ └─ block: reject the request before it ever reaches the model
17
+ ```
18
+
19
+ ---
20
+
21
+ ## Two ways to use it
22
+
23
+ **1. On-demand scanning (MCP tools)** — `scan_text`/`scan_messages`/`scan_file`/`scan_directory`. Ask Claude to check a prompt, a chat-request-body fixture, or a directory of prompt logs before you ship something that builds prompts from user input.
24
+
25
+ **2. A live reverse proxy (`proxy` CLI command)** — sits between your application and the real LLM API. It buffers and inspects only the **outbound request body** (message/system text), then streams the upstream response straight back unmodified — so SSE/streaming completions pass through untouched; only the prompt going *out* is ever parsed. Point your app's `baseURL` at the proxy instead of the real API and nothing else in your integration needs to change.
26
+
27
+ ```bash
28
+ guardbee-prompt-leak-scanner proxy --upstream=https://api.openai.com --port=8788 --mode=redact
29
+ # your app: baseURL = http://localhost:8788 instead of https://api.openai.com
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Detection quality: checksums, not just regex
35
+
36
+ A bare regex for "11 digits" or "16 digits" would flag order numbers, phone extensions, and timestamps constantly. Every PII pattern that has a real checksum algorithm uses it:
37
+
38
+ - **TC Kimlik No** — the actual 11-digit Turkish national ID checksum algorithm (not just a digit-count regex)
39
+ - **Credit card numbers** — Luhn checksum
40
+ - **IBAN** — ISO 7064 MOD97-10 checksum
41
+
42
+ A random 11-digit number has roughly a 1-in-10 chance of passing the TC Kimlik checksum by coincidence — regex alone would be far noisier. Credential patterns (API keys, JWTs, private keys) are matched against real provider-specific prefixes/structure (`sk-`, `AKIA`, `ghp_`, `-----BEGIN...PRIVATE KEY-----`, JWT's three-segment base64url shape), the same low-false-positive approach `secret-scanner` uses.
43
+
44
+ **Findings never echo the real value.** A finding's `maskedMatch` shows only the first 3 and last 2 characters (`sk-…wx`) — logging or displaying the very thing you just caught would defeat the point. The proxy's audit events go further: they record which pattern fired and where, never the matched text itself.
45
+
46
+ ---
47
+
48
+ ## Features
49
+
50
+ - **13 patterns, 4 categories**: credential (9), financial-pii (2, checksum-validated), national-id (1, checksum-validated), contact-pii (2)
51
+ - **Chat-body aware** — understands OpenAI/Anthropic-style `messages[].content` (string or content-block array) and `system` fields, not just flat text
52
+ - **Live reverse-proxy mode** with monitor/redact/block policies — request-only inspection, response streamed through untouched
53
+ - Masked findings and credential-free audit events — the tool never becomes a second copy of the leak
54
+ - SARIF 2.1.0 output — CI/CD integration (GitHub Code Scanning, etc.)
55
+ - Config file support via `guardbee.yml`
56
+ - 29 unit tests — checksum validators verified against known-good/known-bad test vectors, proxy tested against a real local HTTP client/server round trip (not just in-process mocks) in all three modes
57
+
58
+ ---
59
+
60
+ ## Quick Start
61
+
62
+ ### Claude Desktop / MCP Client
63
+
64
+ ```json
65
+ {
66
+ "mcpServers": {
67
+ "guardbee-prompt-leak-scanner": {
68
+ "command": "npx",
69
+ "args": ["-y", "@guardbee/mcp-prompt-leak-scanner"]
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ ### CLI (CI/CD)
76
+
77
+ ```bash
78
+ npx @guardbee/mcp-prompt-leak-scanner scan ./prompt-fixtures --fail-on=high --format=sarif > results.sarif
79
+ ```
80
+
81
+ ### CLI (live proxy)
82
+
83
+ ```bash
84
+ npx @guardbee/mcp-prompt-leak-scanner proxy --upstream=https://api.openai.com --mode=block --fail-on-severity=critical
85
+ ```
86
+
87
+ ---
88
+
89
+ ## MCP Tools
90
+
91
+ | Tool | Description |
92
+ |------|----------|
93
+ | `scan_text` | Scans a raw text string |
94
+ | `scan_messages` | Scans an OpenAI/Anthropic-style chat request body object |
95
+ | `scan_file` | Scans a single file (plain text, or a JSON request-body fixture — auto-detected) |
96
+ | `scan_directory` | Recursively scans a directory of prompt logs/fixtures |
97
+ | `list_patterns` | Lists all supported patterns by category |
98
+
99
+ ---
100
+
101
+ ## Detected Patterns
102
+
103
+ | Category | Pattern | Severity |
104
+ |---|---|---|
105
+ | credential | OpenAI / Anthropic / Google / Stripe API key, AWS access key ID, GitHub PAT, Slack token, PEM private key block | critical |
106
+ | credential | JWT | high |
107
+ | financial-pii | Credit card number (Luhn-validated) | high |
108
+ | financial-pii | IBAN (mod-97 validated) | high |
109
+ | national-id | TC Kimlik No (checksum-validated) | high |
110
+ | contact-pii | Email address | medium |
111
+ | contact-pii | Turkish phone number | medium |
112
+
113
+ ---
114
+
115
+ ## Configuration (`guardbee.yml`)
116
+
117
+ ```yaml
118
+ prompt-leak-scanner:
119
+ fail-on: high # any | critical | high | medium | low | none
120
+ max-files: 5000
121
+ exclude:
122
+ - "fixtures/known-safe/"
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Limitations (by design)
128
+
129
+ - The proxy only inspects the **request** body; it does not parse or modify streaming SSE responses — those are piped through untouched.
130
+ - Only `messages[].content` (string or `{type:"text"}` content blocks) and top-level `system` are extracted from a chat body; provider-specific fields outside that shape aren't scanned.
131
+ - This is pattern/checksum-based detection, not a general-purpose NER/PII model — it won't catch PII that doesn't match a known structural pattern (e.g. a name or street address in free text).
132
+
133
+ ---
134
+
135
+ ## Development
136
+
137
+ ```bash
138
+ npm run build
139
+ npm test # 29 unit tests
140
+ ```
141
+
142
+ ---
143
+
144
+ ## License
145
+
146
+ MIT — [GuardBee](https://guardbee.ai)
package/TR.md ADDED
@@ -0,0 +1,146 @@
1
+ # @guardbee/mcp-prompt-leak-scanner
2
+
3
+ [🇬🇧 English](README.md) | **🇹🇷 Türkçe**
4
+
5
+ MCP (Model Context Protocol) sunucusu — ve bağımsız bir reverse proxy — **outbound** LLM prompt'larındaki sızmış credential ve PII'yi, uygulamanızdan çıkmadan önce yakalar.
6
+
7
+ `secret-scanner` kod tabanınızda duran sırları bulur. Bu paket ise bir *runtime prompt*'a giren sır ve PII'yi bulur — bir müşteri destek agent'ının bir ticket'ı (email, kart no) doğrudan system prompt'a yapıştırması, birinin debug sırasında bir chat'e API key yapıştırması, ham kullanıcı girdisini hiç kontrol etmeden LLM'e ileten bir internal tool. Bu, pipeline'da farklı bir an ve sızmış bir `.env` dosyasından farklı bir hata modu.
8
+
9
+ > Bu paket varsayılan olarak kullanım telemetrisi gönderir (tool adı + kısa parametreler, prompt içeriği ve audit bulguları hiçbir zaman dahil değil — bkz. [`@guardbee/mcp-telemetry`](../telemetry/TR.md)). Kapatmak için `GUARDBEE_TELEMETRY=0`.
10
+
11
+ ```
12
+ Uygulamanız ──► prompt-leak-scanner (proxy) ──► Gerçek LLM API (OpenAI/Anthropic/...)
13
+ │
14
+ ├─ monitor: değiştirmeden ilet, bulguları logla
15
+ ├─ redact: eşleşmeleri [REDACTED:<id>] ile değiştirip ilet
16
+ └─ block: isteği modele hiç ulaşmadan reddet
17
+ ```
18
+
19
+ ---
20
+
21
+ ## İki kullanım şekli
22
+
23
+ **1. Talep üzerine tarama (MCP tool'ları)** — `scan_text`/`scan_messages`/`scan_file`/`scan_directory`. Kullanıcı girdisinden prompt oluşturan bir şeyi shipping'lemeden önce Claude'a bir prompt'u, bir chat-request-body fixture'ını ya da bir prompt log dizinini kontrol ettirin.
24
+
25
+ **2. Canlı bir reverse proxy (`proxy` CLI komutu)** — uygulamanızla gerçek LLM API arasına oturur. Sadece **outbound request body**'yi (message/system metni) buffer'layıp inceler, sonra upstream response'u değiştirmeden doğrudan geri stream eder — yani SSE/streaming completion'lar dokunulmadan geçer; sadece dışarı giden prompt hiç parse edilir. Uygulamanızın `baseURL`'ini gerçek API yerine proxy'ye yönlendirin, entegrasyonunuzda başka hiçbir şeyin değişmesi gerekmiyor.
26
+
27
+ ```bash
28
+ guardbee-prompt-leak-scanner proxy --upstream=https://api.openai.com --port=8788 --mode=redact
29
+ # uygulamanız: baseURL = http://localhost:8788 (https://api.openai.com yerine)
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Tespit kalitesi: sadece regex deÄŸil, checksum
35
+
36
+ "11 hane" ya da "16 hane" için çıplak bir regex sipariş numaralarını, telefon dahili hatlarını, timestamp'leri sürekli işaretlerdi. Gerçek bir checksum algoritması olan her PII kalıbı onu kullanıyor:
37
+
38
+ - **TC Kimlik No** — gerçek 11 haneli Türkiye kimlik checksum algoritması (sadece hane-sayısı regex'i değil)
39
+ - **Kredi kartı numaraları** — Luhn checksum
40
+ - **IBAN** — ISO 7064 MOD97-10 checksum
41
+
42
+ Rastgele 11 haneli bir sayının TC Kimlik checksum'ını tesadüfen geçme ihtimali kabaca 10'da 1 — tek başına regex çok daha gürültülü olurdu. Credential kalıpları (API key, JWT, private key) gerçek sağlayıcıya-özgü prefix/yapılara karşı eşleştiriliyor (`sk-`, `AKIA`, `ghp_`, `-----BEGIN...PRIVATE KEY-----`, JWT'nin üç-segmentli base64url şekli) — `secret-scanner`'ın kullandığı aynı düşük-yanlış-pozitif yaklaşım.
43
+
44
+ **Bulgular gerçek değeri asla tam göstermez.** Bir bulgunun `maskedMatch`'i sadece ilk 3 ve son 2 karakteri gösterir (`sk-…wx`) — az önce yakaladığınız şeyi loglamak ya da göstermek amacı boşa çıkarırdı. Proxy'nin audit event'leri daha da ileri gider: hangi kalıbın tetiklendiğini ve nerede olduğunu kaydeder, eşleşen metni asla değil.
45
+
46
+ ---
47
+
48
+ ## Özellikler
49
+
50
+ - **13 kalıp, 4 kategori**: credential (9), financial-pii (2, checksum-validated), national-id (1, checksum-validated), contact-pii (2)
51
+ - **Chat-body farkında** — OpenAI/Anthropic tarzı `messages[].content`'i (string ya da content-block array) ve `system` alanlarını anlıyor, sadece düz metni değil
52
+ - monitor/redact/block politikalarıyla **canlı reverse-proxy modu** — sadece request inceleniyor, response dokunulmadan stream ediliyor
53
+ - Maskelenmiş bulgular ve credential-içermeyen audit event'leri — araç sızıntının ikinci bir kopyası haline gelmiyor
54
+ - SARIF 2.1.0 çıktısı — CI/CD entegrasyonu (GitHub Code Scanning vb.)
55
+ - `guardbee.yml` ile config dosyası desteği
56
+ - 29 unit test — checksum doğrulayıcıları bilinen-geçerli/bilinen-geçersiz test vektörlerine karşı doğrulandı, proxy her üç modda gerçek bir lokal HTTP client/server round trip'ine karşı (sadece in-process mock değil) test edildi
57
+
58
+ ---
59
+
60
+ ## Hızlı Başlangıç
61
+
62
+ ### Claude Desktop / MCP Client
63
+
64
+ ```json
65
+ {
66
+ "mcpServers": {
67
+ "guardbee-prompt-leak-scanner": {
68
+ "command": "npx",
69
+ "args": ["-y", "@guardbee/mcp-prompt-leak-scanner"]
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ ### CLI (CI/CD)
76
+
77
+ ```bash
78
+ npx @guardbee/mcp-prompt-leak-scanner scan ./prompt-fixtures --fail-on=high --format=sarif > results.sarif
79
+ ```
80
+
81
+ ### CLI (canlı proxy)
82
+
83
+ ```bash
84
+ npx @guardbee/mcp-prompt-leak-scanner proxy --upstream=https://api.openai.com --mode=block --fail-on-severity=critical
85
+ ```
86
+
87
+ ---
88
+
89
+ ## MCP Tools
90
+
91
+ | Tool | Açıklama |
92
+ |------|----------|
93
+ | `scan_text` | Ham bir metin string'ini tarar |
94
+ | `scan_messages` | OpenAI/Anthropic tarzı bir chat request body objesini tarar |
95
+ | `scan_file` | Tek bir dosyayı tarar (düz metin, ya da bir JSON request-body fixture'ı — otomatik tespit edilir) |
96
+ | `scan_directory` | Bir prompt log/fixture dizinini recursive tarar |
97
+ | `list_patterns` | Desteklenen tüm kalıpları kategoriye göre listeler |
98
+
99
+ ---
100
+
101
+ ## Tespit Edilen Kalıplar
102
+
103
+ | Kategori | Kalıp | Önem |
104
+ |---|---|---|
105
+ | credential | OpenAI / Anthropic / Google / Stripe API key, AWS access key ID, GitHub PAT, Slack token, PEM private key block | critical |
106
+ | credential | JWT | high |
107
+ | financial-pii | Kredi kartı numarası (Luhn-validated) | high |
108
+ | financial-pii | IBAN (mod-97 validated) | high |
109
+ | national-id | TC Kimlik No (checksum-validated) | high |
110
+ | contact-pii | Email adresi | medium |
111
+ | contact-pii | Türkiye telefon numarası | medium |
112
+
113
+ ---
114
+
115
+ ## Yapılandırma (`guardbee.yml`)
116
+
117
+ ```yaml
118
+ prompt-leak-scanner:
119
+ fail-on: high # any | critical | high | medium | low | none
120
+ max-files: 5000
121
+ exclude:
122
+ - "fixtures/known-safe/"
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Kısıtlamalar (bilinçli tasarım)
128
+
129
+ - Proxy sadece **request** body'sini inceler; streaming SSE response'ları parse etmez ya da değiştirmez — bunlar dokunulmadan geçer.
130
+ - Bir chat body'sinden sadece `messages[].content` (string ya da `{type:"text"}` content block'ları) ve üst-seviye `system` çıkarılır; bu şeklin dışındaki sağlayıcıya-özgü alanlar taranmaz.
131
+ - Bu kalıp/checksum tabanlı bir tespit, genel amaçlı bir NER/PII modeli değil — bilinen yapısal bir kalıpla eşleşmeyen PII'yi (örn. serbest metindeki bir isim ya da adres) yakalamaz.
132
+
133
+ ---
134
+
135
+ ## GeliÅŸtirme
136
+
137
+ ```bash
138
+ npm run build
139
+ npm test # 29 unit test
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Lisans
145
+
146
+ MIT — [GuardBee](https://guardbee.ai)
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=proxy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/proxy.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,107 @@
1
+ import { describe, it, expect, afterEach } from "vitest";
2
+ import { createServer } from "http";
3
+ import { startProxy } from "../proxy.js";
4
+ let proxyServer;
5
+ let upstreamServer;
6
+ afterEach(async () => {
7
+ if (proxyServer)
8
+ await new Promise((r) => proxyServer.close(r));
9
+ if (upstreamServer)
10
+ await new Promise((r) => upstreamServer.close(r));
11
+ proxyServer = undefined;
12
+ upstreamServer = undefined;
13
+ });
14
+ function startMockUpstream() {
15
+ const receivedBodies = [];
16
+ return new Promise((resolve) => {
17
+ upstreamServer = createServer((req, res) => {
18
+ const chunks = [];
19
+ req.on("data", (c) => chunks.push(c));
20
+ req.on("end", () => {
21
+ receivedBodies.push(Buffer.concat(chunks).toString("utf8"));
22
+ res.writeHead(200, { "content-type": "application/json" });
23
+ res.end(JSON.stringify({ ok: true, echoPath: req.url }));
24
+ });
25
+ });
26
+ upstreamServer.listen(0, "127.0.0.1", () => resolve({ port: upstreamServer.address().port, receivedBodies }));
27
+ });
28
+ }
29
+ function postJson(port, path, body) {
30
+ return fetch(`http://127.0.0.1:${port}${path}`, {
31
+ method: "POST",
32
+ headers: { "content-type": "application/json" },
33
+ body: JSON.stringify(body),
34
+ }).then(async (res) => ({ status: res.status, json: await res.json() }));
35
+ }
36
+ function freePort() {
37
+ return new Promise((resolve) => {
38
+ const probe = createServer();
39
+ probe.listen(0, "127.0.0.1", () => {
40
+ const port = probe.address().port;
41
+ probe.close(() => resolve(port));
42
+ });
43
+ });
44
+ }
45
+ describe("startProxy — monitor mode", () => {
46
+ it("temiz bir body'yi deÄŸiÅŸtirmeden upstream'e iletir", async () => {
47
+ const { port: upstreamPort, receivedBodies } = await startMockUpstream();
48
+ const proxyPort = await freePort();
49
+ const audits = [];
50
+ proxyServer = startProxy({ port: proxyPort, upstream: `http://127.0.0.1:${upstreamPort}`, mode: "monitor", onAudit: (e) => audits.push(e) });
51
+ const body = { messages: [{ role: "user", content: "hello there" }] };
52
+ const { status } = await postJson(proxyPort, "/v1/chat/completions", body);
53
+ expect(status).toBe(200);
54
+ expect(JSON.parse(receivedBodies[0])).toEqual(body);
55
+ expect(audits[0]).toMatchObject({ blocked: false, findingCount: 0 });
56
+ });
57
+ it("secret içeren bir body'yi DEĞİŞTİRMEDEN iletir (monitor sadece loglar)", async () => {
58
+ const { port: upstreamPort, receivedBodies } = await startMockUpstream();
59
+ const proxyPort = await freePort();
60
+ const audits = [];
61
+ proxyServer = startProxy({ port: proxyPort, upstream: `http://127.0.0.1:${upstreamPort}`, mode: "monitor", onAudit: (e) => audits.push(e) });
62
+ const body = { messages: [{ role: "user", content: "my key is sk-abcdefghijklmnopqrstuvwx" }] };
63
+ await postJson(proxyPort, "/v1/chat/completions", body);
64
+ expect(JSON.parse(receivedBodies[0])).toEqual(body);
65
+ expect(audits[0]).toMatchObject({ findingCount: 1 });
66
+ expect(audits[0].findingCodes).toContain("openai_api_key");
67
+ });
68
+ it("audit event ham secret değerini içermez", async () => {
69
+ const { port: upstreamPort } = await startMockUpstream();
70
+ const proxyPort = await freePort();
71
+ const audits = [];
72
+ proxyServer = startProxy({ port: proxyPort, upstream: `http://127.0.0.1:${upstreamPort}`, mode: "monitor", onAudit: (e) => audits.push(e) });
73
+ await postJson(proxyPort, "/v1/chat/completions", { messages: [{ role: "user", content: "my key is sk-abcdefghijklmnopqrstuvwx" }] });
74
+ const serialized = JSON.stringify(audits[0]);
75
+ expect(serialized).not.toContain("sk-abcdefghijklmnopqrstuvwx");
76
+ });
77
+ });
78
+ describe("startProxy — redact mode", () => {
79
+ it("secret içeren body'yi redakte edilmiş haliyle upstream'e iletir", async () => {
80
+ const { port: upstreamPort, receivedBodies } = await startMockUpstream();
81
+ const proxyPort = await freePort();
82
+ proxyServer = startProxy({ port: proxyPort, upstream: `http://127.0.0.1:${upstreamPort}`, mode: "redact" });
83
+ await postJson(proxyPort, "/v1/chat/completions", { messages: [{ role: "user", content: "my key is sk-abcdefghijklmnopqrstuvwx" }] });
84
+ const forwarded = JSON.parse(receivedBodies[0]);
85
+ expect(forwarded.messages[0].content).toBe("my key is [REDACTED:openai_api_key]");
86
+ });
87
+ });
88
+ describe("startProxy — block mode", () => {
89
+ it("critical bulgu varsa isteği upstream'e hiç iletmeden 400 döner", async () => {
90
+ const { port: upstreamPort, receivedBodies } = await startMockUpstream();
91
+ const proxyPort = await freePort();
92
+ proxyServer = startProxy({ port: proxyPort, upstream: `http://127.0.0.1:${upstreamPort}`, mode: "block", failOnSeverity: "high" });
93
+ const { status, json } = await postJson(proxyPort, "/v1/chat/completions", { messages: [{ role: "user", content: "my key is sk-abcdefghijklmnopqrstuvwx" }] });
94
+ expect(status).toBe(400);
95
+ expect(json.error).toBe("blocked_by_guardbee_prompt_leak_scanner");
96
+ expect(receivedBodies).toHaveLength(0);
97
+ });
98
+ it("temiz bir body'yi normal ÅŸekilde iletir", async () => {
99
+ const { port: upstreamPort, receivedBodies } = await startMockUpstream();
100
+ const proxyPort = await freePort();
101
+ proxyServer = startProxy({ port: proxyPort, upstream: `http://127.0.0.1:${upstreamPort}`, mode: "block" });
102
+ const { status } = await postJson(proxyPort, "/v1/chat/completions", { messages: [{ role: "user", content: "hello" }] });
103
+ expect(status).toBe(200);
104
+ expect(receivedBodies).toHaveLength(1);
105
+ });
106
+ });
107
+ //# sourceMappingURL=proxy.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.test.js","sourceRoot":"","sources":["../../src/__tests__/proxy.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,YAAY,EAAe,MAAM,MAAM,CAAC;AACjD,OAAO,EAAE,UAAU,EAAmB,MAAM,aAAa,CAAC;AAE1D,IAAI,WAA+B,CAAC;AACpC,IAAI,cAAkC,CAAC;AAEvC,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,IAAI,WAAW;QAAE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,IAAI,cAAc;QAAE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,WAAW,GAAG,SAAS,CAAC;IACxB,cAAc,GAAG,SAAS,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,SAAS,iBAAiB;IACxB,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,cAAc,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACzC,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACtC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC5D,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YAC3D,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAG,cAAe,CAAC,OAAO,EAAuB,CAAC,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;IACvI,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,IAAY,EAAE,IAAa;IACzD,OAAO,KAAK,CAAC,oBAAoB,IAAI,GAAG,IAAI,EAAE,EAAE;QAC9C,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,SAAS,QAAQ;IACf,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;QAC7B,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YAChC,MAAM,IAAI,GAAI,KAAK,CAAC,OAAO,EAAuB,CAAC,IAAI,CAAC;YACxD,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACzE,MAAM,SAAS,GAAG,MAAM,QAAQ,EAAE,CAAC;QACnC,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,WAAW,GAAG,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAE7I,MAAM,IAAI,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;QACtE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,sBAAsB,EAAE,IAAI,CAAC,CAAC;QAE3E,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACzE,MAAM,SAAS,GAAG,MAAM,QAAQ,EAAE,CAAC;QACnC,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,WAAW,GAAG,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAE7I,MAAM,IAAI,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uCAAuC,EAAE,CAAC,EAAE,CAAC;QAChG,MAAM,QAAQ,CAAC,SAAS,EAAE,sBAAsB,EAAE,IAAI,CAAC,CAAC;QAExD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACzD,MAAM,SAAS,GAAG,MAAM,QAAQ,EAAE,CAAC;QACnC,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,WAAW,GAAG,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAE7I,MAAM,QAAQ,CAAC,SAAS,EAAE,sBAAsB,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uCAAuC,EAAE,CAAC,EAAE,CAAC,CAAC;QAEtI,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACzE,MAAM,SAAS,GAAG,MAAM,QAAQ,EAAE,CAAC;QACnC,WAAW,GAAG,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAE5G,MAAM,QAAQ,CAAC,SAAS,EAAE,sBAAsB,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uCAAuC,EAAE,CAAC,EAAE,CAAC,CAAC;QAEtI,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;IACpF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACzE,MAAM,SAAS,GAAG,MAAM,QAAQ,EAAE,CAAC;QACnC,WAAW,GAAG,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,YAAY,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC;QAEnI,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,sBAAsB,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uCAAuC,EAAE,CAAC,EAAE,CAAC,CAAC;QAE/J,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,MAAM,CAAE,IAA0B,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QAC1F,MAAM,CAAC,cAAc,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACzE,MAAM,SAAS,GAAG,MAAM,QAAQ,EAAE,CAAC;QACnC,WAAW,GAAG,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,oBAAoB,YAAY,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAE3G,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,sBAAsB,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAEzH,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,MAAM,CAAC,cAAc,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scanner.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scanner.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,111 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { scanText, scanAndRedactText, scanBody, redactBody } from "../scanner.js";
3
+ function idsOf(text) {
4
+ return scanText(text).map((f) => f.patternId);
5
+ }
6
+ describe("scanText — credentials", () => {
7
+ it("yakalar: OpenAI API key", () => {
8
+ expect(idsOf(`My key is sk-abcdefghijklmnopqrstuvwx and here is the prompt`)).toContain("openai_api_key");
9
+ });
10
+ it("yakalar: Anthropic API key", () => {
11
+ expect(idsOf(`key: sk-ant-abcdefghijklmnopqrstuvwx`)).toContain("anthropic_api_key");
12
+ });
13
+ it("yakalar: AWS access key ID", () => {
14
+ expect(idsOf(`AKIAIOSFODNN7EXAMPLE is the access key`)).toContain("aws_access_key_id");
15
+ });
16
+ it("yakalar: PEM private key block", () => {
17
+ expect(idsOf(`-----BEGIN RSA PRIVATE KEY-----\nMIIExyz...`)).toContain("private_key_block");
18
+ });
19
+ it("yakalar: JWT", () => {
20
+ const jwt = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dQw4w9WgXcQ_abcdefghij";
21
+ expect(idsOf(`Authorization: Bearer ${jwt}`)).toContain("jwt_token");
22
+ });
23
+ it("normal metinde credential yakalamaz", () => {
24
+ expect(idsOf(`Please summarize this document for me.`)).toHaveLength(0);
25
+ });
26
+ });
27
+ describe("scanText — tc_kimlik_no (checksum-validated)", () => {
28
+ it("geçerli bir TC Kimlik No'yu yakalar", () => {
29
+ // 10000000146 gerçek algoritmayı sağlayan bilinen bir test değeri
30
+ expect(idsOf(`Müşteri TC: 10000000146`)).toContain("tc_kimlik_no");
31
+ });
32
+ it("checksum'ı geçersiz 11 haneli bir sayıyı yakalamaz", () => {
33
+ expect(idsOf(`Sipariş numarası: 12345678901`)).not.toContain("tc_kimlik_no");
34
+ });
35
+ it("0 ile başlayan 11 haneli bir sayıyı yakalamaz", () => {
36
+ expect(idsOf(`Kod: 01234567890`)).not.toContain("tc_kimlik_no");
37
+ });
38
+ });
39
+ describe("scanText — credit_card_number (Luhn-validated)", () => {
40
+ it("geçerli (Luhn) bir kart numarasını yakalar", () => {
41
+ expect(idsOf(`Card: 4532015112830366`)).toContain("credit_card_number");
42
+ });
43
+ it("Luhn'u geçmeyen 16 haneli bir sayıyı yakalamaz", () => {
44
+ expect(idsOf(`Reference: 1234567890123456`)).not.toContain("credit_card_number");
45
+ });
46
+ });
47
+ describe("scanText — iban (mod-97 validated)", () => {
48
+ it("geçerli bir IBAN'ı yakalar", () => {
49
+ expect(idsOf(`IBAN: TR330006100519786457841326`)).toContain("iban");
50
+ });
51
+ it("checksum'ı bozuk bir IBAN benzeri stringi yakalamaz", () => {
52
+ expect(idsOf(`Code: TR330006100519786457841327`)).not.toContain("iban");
53
+ });
54
+ });
55
+ describe("scanText — contact PII", () => {
56
+ it("yakalar: email", () => {
57
+ expect(idsOf(`Contact me at jane.doe@example.com`)).toContain("email_address");
58
+ });
59
+ it("yakalar: Türkiye telefon numarası", () => {
60
+ expect(idsOf(`Beni ara: 0532 123 45 67`)).toContain("turkish_phone_number");
61
+ });
62
+ });
63
+ describe("scanText — masking", () => {
64
+ it("maskedMatch gerçek değeri asla tam göstermez", () => {
65
+ const findings = scanText(`sk-abcdefghijklmnopqrstuvwx`);
66
+ expect(findings[0].maskedMatch).not.toBe("sk-abcdefghijklmnopqrstuvwx");
67
+ expect(findings[0].maskedMatch).toContain("…");
68
+ });
69
+ });
70
+ describe("scanAndRedactText", () => {
71
+ it("bulunan deÄŸeri [REDACTED:id] ile deÄŸiÅŸtirir", () => {
72
+ const { findings, redactedText } = scanAndRedactText(`My key is sk-abcdefghijklmnopqrstuvwx, thanks`);
73
+ expect(findings).toHaveLength(1);
74
+ expect(redactedText).toBe(`My key is [REDACTED:openai_api_key], thanks`);
75
+ });
76
+ it("birden fazla bulguyu doğru sırayla redakte eder", () => {
77
+ const { redactedText } = scanAndRedactText(`email jane@example.com and key sk-abcdefghijklmnopqrstuvwx`);
78
+ expect(redactedText).toBe(`email [REDACTED:email_address] and key [REDACTED:openai_api_key]`);
79
+ });
80
+ });
81
+ describe("scanBody — chat message extraction", () => {
82
+ it("messages[].content string alanındaki bulguyu location ile birlikte döner", () => {
83
+ const body = { messages: [{ role: "user", content: "my key is sk-abcdefghijklmnopqrstuvwx" }] };
84
+ const { findings } = scanBody(body);
85
+ expect(findings[0]).toMatchObject({ patternId: "openai_api_key", location: "messages[0].content" });
86
+ });
87
+ it("system alanındaki bulguyu yakalar", () => {
88
+ const body = { system: "user email is jane@example.com", messages: [] };
89
+ const { findings } = scanBody(body);
90
+ expect(findings[0]).toMatchObject({ patternId: "email_address", location: "system" });
91
+ });
92
+ it("content-block array formundaki text alanını tarar", () => {
93
+ const body = { messages: [{ role: "user", content: [{ type: "text", text: "key sk-abcdefghijklmnopqrstuvwx" }] }] };
94
+ const { findings } = scanBody(body);
95
+ expect(findings[0]).toMatchObject({ location: "messages[0].content[0].text" });
96
+ });
97
+ it("temiz bir body'de bulgu döndürmez", () => {
98
+ const body = { messages: [{ role: "user", content: "What's the weather like today?" }] };
99
+ expect(scanBody(body).findings).toHaveLength(0);
100
+ });
101
+ });
102
+ describe("redactBody", () => {
103
+ it("body'yi in-place redakte eder ve orijinal referansı döner", () => {
104
+ const body = { messages: [{ role: "user", content: "my key is sk-abcdefghijklmnopqrstuvwx" }] };
105
+ const { findings, redactedBody } = redactBody(body);
106
+ expect(findings).toHaveLength(1);
107
+ expect(redactedBody).toBe(body); // aynı obje referansı, in-place mutasyon
108
+ expect(body.messages[0].content).toBe("my key is [REDACTED:openai_api_key]");
109
+ });
110
+ });
111
+ //# sourceMappingURL=scanner.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.test.js","sourceRoot":"","sources":["../../src/__tests__/scanner.test.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}