@guardbee/mcp-security-proxy 0.3.1 → 1.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 (76) hide show
  1. package/README.md +98 -19
  2. package/TR.md +98 -19
  3. package/dist/audit/logger.d.ts +14 -2
  4. package/dist/audit/logger.d.ts.map +1 -1
  5. package/dist/audit/logger.js +72 -1
  6. package/dist/audit/logger.js.map +1 -1
  7. package/dist/cli.d.ts +1 -0
  8. package/dist/cli.js +65 -3
  9. package/dist/cli.js.map +1 -1
  10. package/dist/gateway/approval.d.ts +16 -0
  11. package/dist/gateway/approval.d.ts.map +1 -0
  12. package/dist/gateway/approval.js +36 -0
  13. package/dist/gateway/approval.js.map +1 -0
  14. package/dist/gateway/config.d.ts +36 -0
  15. package/dist/gateway/config.d.ts.map +1 -0
  16. package/dist/gateway/config.js +206 -0
  17. package/dist/gateway/config.js.map +1 -0
  18. package/dist/gateway/fields.d.ts +7 -0
  19. package/dist/gateway/fields.d.ts.map +1 -0
  20. package/dist/gateway/fields.js +43 -0
  21. package/dist/gateway/fields.js.map +1 -0
  22. package/dist/gateway/gateway.d.ts +59 -0
  23. package/dist/gateway/gateway.d.ts.map +1 -0
  24. package/dist/gateway/gateway.js +376 -0
  25. package/dist/gateway/gateway.js.map +1 -0
  26. package/dist/gateway/labels.d.ts +10 -0
  27. package/dist/gateway/labels.d.ts.map +1 -0
  28. package/dist/gateway/labels.js +17 -0
  29. package/dist/gateway/labels.js.map +1 -0
  30. package/dist/gateway/policy.d.ts +37 -0
  31. package/dist/gateway/policy.d.ts.map +1 -0
  32. package/dist/gateway/policy.js +57 -0
  33. package/dist/gateway/policy.js.map +1 -0
  34. package/dist/gateway/taint.d.ts +25 -0
  35. package/dist/gateway/taint.d.ts.map +1 -0
  36. package/dist/gateway/taint.js +43 -0
  37. package/dist/gateway/taint.js.map +1 -0
  38. package/dist/gateway/tokens.d.ts +18 -0
  39. package/dist/gateway/tokens.d.ts.map +1 -0
  40. package/dist/gateway/tokens.js +60 -0
  41. package/dist/gateway/tokens.js.map +1 -0
  42. package/dist/gateway/upstream.d.ts +20 -0
  43. package/dist/gateway/upstream.d.ts.map +1 -0
  44. package/dist/gateway/upstream.js +49 -0
  45. package/dist/gateway/upstream.js.map +1 -0
  46. package/dist/index.d.ts +21 -3
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +32 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/init.d.ts +34 -0
  51. package/dist/init.d.ts.map +1 -0
  52. package/dist/init.js +140 -0
  53. package/dist/init.js.map +1 -0
  54. package/dist/interceptors/definition-drift.d.ts +1 -0
  55. package/dist/interceptors/definition-drift.d.ts.map +1 -1
  56. package/dist/interceptors/definition-drift.js +1 -0
  57. package/dist/interceptors/definition-drift.js.map +1 -1
  58. package/dist/interceptors/pii-masker.d.ts +1 -2
  59. package/dist/interceptors/pii-masker.d.ts.map +1 -1
  60. package/dist/interceptors/pii-masker.js +5 -68
  61. package/dist/interceptors/pii-masker.js.map +1 -1
  62. package/dist/interceptors/prompt-injection.d.ts +1 -2
  63. package/dist/interceptors/prompt-injection.d.ts.map +1 -1
  64. package/dist/interceptors/prompt-injection.js +4 -40
  65. package/dist/interceptors/prompt-injection.js.map +1 -1
  66. package/dist/interceptors/tool-result.d.ts +1 -3
  67. package/dist/interceptors/tool-result.d.ts.map +1 -1
  68. package/dist/interceptors/tool-result.js +4 -12
  69. package/dist/interceptors/tool-result.js.map +1 -1
  70. package/dist/proxy.d.ts +18 -0
  71. package/dist/proxy.d.ts.map +1 -1
  72. package/dist/proxy.js +48 -226
  73. package/dist/proxy.js.map +1 -1
  74. package/dist/types.d.ts +36 -6
  75. package/dist/types.d.ts.map +1 -1
  76. package/package.json +4 -2
package/README.md CHANGED
@@ -13,22 +13,25 @@ A transparent security layer that sits in front of any MCP server. Blocks prompt
13
13
  ```
14
14
  Claude ──► MCP Security Proxy ──► Any MCP Server
15
15
  │
16
- ├─ Prompt injection detection (16 attack patterns)
17
- ├─ PII masking (national ID, IBAN, email, JWT, API key)
16
+ ├─ Prompt injection detection (27 rules, EN + TR)
17
+ ├─ PII + secret masking (TC, VKN, IBAN, card, phone, 22 key formats)
18
18
  ├─ Block or warn mode
19
- └─ Configurable audit log
19
+ ├─ Toxic-flow (lethal trifecta) blocking
20
+ └─ Hash-chained audit log
20
21
  ```
21
22
 
22
23
  ---
23
24
 
24
25
  ## Features
25
26
 
26
- - **Prompt Injection Protection** — 16 attack patterns block requests attempting to override system prompts
27
+ - **Prompt Injection Protection** — 27 rules in English and Turkish; precise ones block, generic phrases only warn
27
28
  - **Tool-result injection** — the same patterns applied to tool results, tagged MCP06:2025. Block mode replaces the result; warn mode prepends a warning
28
29
  - **Session tool pin** — the first `tools/list` is pinned. A later description or schema change is a rug pull (MCP03:2025). Block mode keeps serving the pinned definition and refuses the drifted call
29
- - **PII Masking** — national ID numbers, IBAN, email, phone, JWT tokens, API keys are automatically masked in responses
30
+ - **PII Masking** — TC Kimlik No, VKN, IBAN, card and phone numbers (checksum-validated), email, 22 provider key formats, private keys and connection strings are masked in responses
30
31
  - **Block / Warn Mode** — each interceptor can independently run in blocking or warning mode
31
32
  - **Audit Log** — configurable log written to console or a file
33
+ - **Gateway mode** — several MCP servers behind one proxy, one YAML policy (allow / deny / mask / warn)
34
+ - **Toxic-flow blocking** — tracks the lethal trifecta across servers and blocks the egress call that would complete it
32
35
  - **Zero Code Changes** — attaches in front of any existing MCP server
33
36
 
34
37
  ---
@@ -60,6 +63,75 @@ Add to `claude_desktop_config.json`:
60
63
 
61
64
  ---
62
65
 
66
+ ## Gateway Mode: Several Servers, One Policy
67
+
68
+ Put every MCP server behind one proxy with a `guardbee-proxy.yaml`. `init` does the move for you:
69
+
70
+ ```bash
71
+ npx -y @guardbee/mcp-security-proxy@^1 init --client claude-desktop # or cursor, claude-code, --file ./mcp.json
72
+ ```
73
+
74
+ It moves each stdio server into `~/.guardbee/guardbee-proxy.yaml` (mode 0600), backs up the client config and points it at the proxy. HTTP servers stay as they are and are reported, because they would bypass the policy. `--dry-run` prints both files without writing. Or by hand:
75
+
76
+ ```json
77
+ {
78
+ "mcpServers": {
79
+ "guardbee": {
80
+ "command": "npx",
81
+ "args": ["-y", "@guardbee/mcp-security-proxy@^1", "--config", "/path/to/guardbee-proxy.yaml"]
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ```yaml
88
+ version: 1
89
+ upstreams:
90
+ github:
91
+ command: npx
92
+ args: ["-y", "@modelcontextprotocol/server-github"]
93
+ env: { GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}" } # read from the environment
94
+ postgres:
95
+ command: npx
96
+ args: ["-y", "@guardbee/mcp-db-gateway"]
97
+
98
+ labels: # override the heuristic labels
99
+ github__get_issue: [untrusted]
100
+ github__create_pull_request: [egress]
101
+
102
+ rules: # first match wins
103
+ - id: no-deletes
104
+ match: { tool: "postgres__delete_*" }
105
+ action: deny # allow | deny | mask | warn | approve
106
+ - match: { tool: "postgres__query", args: { table: "salaries" } } # dotted path → value, strings are globs
107
+ action: mask
108
+ mask: { fields: [salary, iban] } # blank these JSON keys
109
+ - match: { label: destructive }
110
+ action: approve # ask the person first
111
+
112
+ taint:
113
+ mode: strict # strict | approve | warn | off
114
+
115
+ audit:
116
+ sink: file
117
+ filePath: ./guardbee-audit.jsonl
118
+ ```
119
+
120
+ - Tools and prompts appear as `<upstream>__<tool>`.
121
+ - **Toxic flow (lethal trifecta):** each tool is labeled `untrusted`, `sensitive`, `egress` or `destructive` (name/description heuristics, overridable under `labels`). Once a session has read untrusted content (an `untrusted` tool or any resource) and sensitive data (a `sensitive` tool or any PII in a result), an `egress` call is blocked in `strict` mode and only logged in `warn` mode. The check spans servers: an issue read from GitHub plus a customer record from a CRM blocks a webhook on a third server.
122
+ - **Rules** match on `tool` (glob), `upstream`, `label`, `session` (`clean` | `tainted`) and `args`. `mask` forces PII masking for that tool even when masking is off, and `mask.fields` also blanks the named JSON keys in text and `structuredContent`. A rule's `allow` does not skip the toxic-flow check; relabel the tool instead.
123
+ - **Approval:** `approve` (a rule action, or `taint.mode: approve` for toxic flows) shows the person a yes/no form through MCP elicitation, with the tool, the reason and the arguments. Anything but an explicit yes — decline, cancel, no answer within `approval.timeoutSeconds` (default 120) — blocks the call. A client without elicitation support cannot approve, so the call is blocked with a message saying so.
124
+ - **Tokenize:** with `interceptors.piiMasking.mode: tokenize` the model sees `<pii:tc_kimlik:7f3a9b21>` instead of the value and can still pass it to another tool: the proxy puts the real value back on the way to that server. Tokens live in memory for the session only, and are not turned back into values for an `egress` tool unless `piiMasking.detokenizeForEgress: true`.
125
+ - **Prompts** (`prompts/get`) get the same injection scan and PII masking as tool results.
126
+ - `interceptors.definitionDrift.recheck: on-change` skips the per-call `tools/list` and re-checks only after a server sends `tools/list_changed`, which the proxy also passes on to the agent.
127
+ - **Audit log** is hash-chained. By default it stores a SHA-256 of the arguments instead of the arguments, and no results; set `audit.includePayloads: true` to log them. Check a log with `guardbee-proxy verify-audit ./guardbee-audit.jsonl`.
128
+ - `guardbee-proxy validate --config guardbee-proxy.yaml` checks a config without starting any server.
129
+ - Without `--config`, the proxy also picks up `./guardbee-proxy.yaml` or `GUARDBEE_PROXY_CONFIG=<file>.yaml`.
130
+
131
+ The single-server `--` form above keeps its 0.x behavior: tool names are not prefixed, payloads are logged, and a toxic flow is only logged as a warning.
132
+
133
+ ---
134
+
63
135
  ## MCP Tools
64
136
 
65
137
  The proxy does not register tools of its own. It forwards the target server's tools and applies the interceptor chain on every call.
@@ -70,24 +142,31 @@ The proxy does not register tools of its own. It forwards the target server's to
70
142
 
71
143
  ### Prompt Injection Detector
72
144
 
73
- Looks for the following attack patterns in incoming messages:
145
+ 27 rules from [`@guardbee/guard-core`](../guard-core/README.md), applied to tool arguments, tool results, resources and prompts:
146
+
147
+ - **Block** (13 precise, high/critical): instruction overrides in English and Turkish ("ignore all previous instructions", "önceki talimatları yok say"), `DAN mode`, "bypass your safety guardrails", chat-template tokens (`<|im_start|>`, `[INST]`), `<system>` tags, text hidden in Unicode tag characters, instructions in CSS-hidden elements or HTML comments, markdown image exfiltration beacons, requests to reveal the system prompt (English and Turkish), "send the user's data to https://…".
148
+ - **Warn only** (14): generic phrases that also appear in ordinary text — "act as", "you are now", "developer mode", "jailbreak", "override policy", "sen artık" — and medium-severity signals such as zero-width or bidi control characters and a line-start `System:` label.
149
+ - **Base64:** runs that decode to readable text are decoded and checked against the block rules, so an instruction cannot hide in an encoded blob.
74
150
 
75
- - `ignore previous instructions`
76
- - `disregard your system prompt`
77
- - `you are now [DAN/jailbreak]`
78
- - commands hidden via ANSI escape sequences
79
- - base64-encoded instructions
80
- - and 11 more patterns
151
+ Since 1.1, a broad phrase alone no longer blocks: in 1.0 "use jailbreak mode" or "act as a reverse proxy" in a tool result was blocked; now it is logged and the result carries a warning.
81
152
 
82
153
  ### PII Masker
83
154
 
84
- | Data Type | Example Input | Output |
85
- |-----------|------------|-------|
86
- | National ID | `12345678901` | `[TC-REDACTED]` |
87
- | IBAN | `TR320006200...` | `TR32***` |
88
- | Email | `ahmet@example.com` | `ah***@example.com` |
89
- | JWT | `eyJhbGc...` | `[JWT-REDACTED]` |
90
- | API Key | `sk-abc123...` | `[KEY-REDACTED]` |
155
+ | Data Type | Check | Example Input | Output |
156
+ |-----------|-------|---------------|--------|
157
+ | National ID (TC Kimlik No) | official checksum | `10000000146` | `[TC-KİMLİK]` |
158
+ | IBAN (TR) | mod-97 | `TR330006100519786457841326` | `TR**[IBAN]` |
159
+ | Card number | Luhn | `4111 1111 1111 1111` | `****-****-****-[KART]` |
160
+ | Email | — | `ahmet@example.com` | `***@[EMAIL]` |
161
+ | Phone (TR) | mobile / landline / 850 prefix | `0532 123 45 67` | `+90-***-***-**[TELEFON]` |
162
+ | Tax number (VKN) | check digit, needs a `VKN` / `Vergi No` label | `VKN: 1234567890` | `VKN: [VKN]` |
163
+ | Provider keys | 22 key and token formats from secret-scanner (AWS, GitHub, Stripe, OpenAI, Anthropic, Slack, …) | `ghp_…`, `AKIA…` | `[API-KEY]` |
164
+ | Other API keys | `sk_`/`api_`/`token_`… prefix, letters and digits | `api_k3y9x8…` | `[API-KEY]` |
165
+ | Private key | whole BEGIN…END block | `-----BEGIN … PRIVATE KEY-----` | `[PRIVATE-KEY]` |
166
+ | Connection string | user:password in the URL (localhost excluded) | `postgres://app:pw@db/prod` | `[CONNECTION-STRING]` |
167
+ | JWT | — | `eyJhbGc...` | `[JWT-TOKEN]` |
168
+
169
+ A number that fails its checksum (an order number, a tracking code) is left as is.
91
170
 
92
171
  ---
93
172
 
package/TR.md CHANGED
@@ -13,22 +13,25 @@ Herhangi bir MCP sunucusunun önüne oturan şeffaf güvenlik katmanı. Prompt i
13
13
  ```
14
14
  Claude ──► MCP Security Proxy ──► Herhangi bir MCP Sunucu
15
15
  │
16
- ├─ Prompt injection tespiti (16 saldırı deseni)
17
- ├─ PII maskeleme (TC kimlik, IBAN, e-posta, JWT, API key)
16
+ ├─ Prompt injection tespiti (27 kural, EN + TR)
17
+ ├─ PII + secret maskeleme (TC, VKN, IBAN, kart, telefon, 22 anahtar formatı)
18
18
  ├─ Block veya warn modu
19
- └─ Yapılandırılabilir audit log
19
+ ├─ Toxic-flow (lethal trifecta) engelleme
20
+ └─ Hash zincirli audit log
20
21
  ```
21
22
 
22
23
  ---
23
24
 
24
25
  ## Özellikler
25
26
 
26
- - **Prompt Injection Koruması** — 16 saldırı deseni ile sistem prompt'larını geçersiz kılmaya çalışan istekler engellenir
27
+ - **Prompt Injection Koruması** — İngilizce ve Türkçe 27 kural; kesin kurallar engeller, genel ifadeler sadece uyarır
27
28
  - **Tool sonucu enjeksiyonu** — aynı kalıplar tool sonucuna uygulanır (MCP06:2025). Block modu sonucu değiştirir; warn modu uyarı ekler
28
29
  - **Oturum tool sabitlemesi** — ilk `tools/list` sabitlenir. Sonraki description veya şema değişikliği rug pull'dur (MCP03:2025). Block modu sabit tanımı sunmaya devam eder ve kaymış çağrıyı reddeder
29
- - **PII Maskeleme** — TC kimlik no, IBAN, e-posta, telefon, JWT token, API key yanıtlarda otomatik maskelenir
30
+ - **PII Maskeleme** — TC kimlik no, VKN, IBAN, kart ve telefon numaraları (checksum doğrulamalı), e-posta, 22 sağlayıcı anahtar formatı, özel anahtarlar ve bağlantı dizeleri yanıtlarda maskelenir
30
31
  - **Block / Warn Modu** — Her interceptor bağımsız olarak engelleyici veya uyarı modunda çalışabilir
31
32
  - **Audit Log** — Console veya dosyaya yazılan yapılandırılabilir log
33
+ - **Gateway modu** — birden fazla MCP sunucusu tek proxy'nin arkasında, tek YAML politikası (allow / deny / mask / warn)
34
+ - **Toxic-flow engelleme** — lethal trifecta'yı sunucular arasında izler ve onu tamamlayacak egress çağrısını engeller
32
35
  - **Sıfır Kod Değişikliği** — Mevcut herhangi bir MCP sunucusunun önüne takılır
33
36
 
34
37
  ---
@@ -60,6 +63,75 @@ npm install -g @guardbee/mcp-security-proxy
60
63
 
61
64
  ---
62
65
 
66
+ ## Gateway Modu: Birden Fazla Sunucu, Tek Politika
67
+
68
+ Tüm MCP sunucularını bir `guardbee-proxy.yaml` ile tek proxy'nin arkasına koyun. `init` taşımayı sizin için yapar:
69
+
70
+ ```bash
71
+ npx -y @guardbee/mcp-security-proxy@^1 init --client claude-desktop # veya cursor, claude-code, --file ./mcp.json
72
+ ```
73
+
74
+ Her stdio sunucusunu `~/.guardbee/guardbee-proxy.yaml` dosyasına (izin 0600) taşır, istemci config'inin yedeğini alır ve config'i proxy'ye yönlendirir. HTTP sunucuları olduğu gibi kalır ve raporlanır, çünkü politikayı atlarlar. `--dry-run` iki dosyayı da yazmadan gösterir. Elle yapmak için:
75
+
76
+ ```json
77
+ {
78
+ "mcpServers": {
79
+ "guardbee": {
80
+ "command": "npx",
81
+ "args": ["-y", "@guardbee/mcp-security-proxy@^1", "--config", "/path/to/guardbee-proxy.yaml"]
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ```yaml
88
+ version: 1
89
+ upstreams:
90
+ github:
91
+ command: npx
92
+ args: ["-y", "@modelcontextprotocol/server-github"]
93
+ env: { GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}" } # ortam değişkeninden okunur
94
+ postgres:
95
+ command: npx
96
+ args: ["-y", "@guardbee/mcp-db-gateway"]
97
+
98
+ labels: # sezgisel etiketleri ezer
99
+ github__get_issue: [untrusted]
100
+ github__create_pull_request: [egress]
101
+
102
+ rules: # ilk eşleşen kural kazanır
103
+ - id: no-deletes
104
+ match: { tool: "postgres__delete_*" }
105
+ action: deny # allow | deny | mask | warn | approve
106
+ - match: { tool: "postgres__query", args: { table: "salaries" } } # noktalı yol → değer, string'ler glob
107
+ action: mask
108
+ mask: { fields: [salary, iban] } # bu JSON anahtarlarını boşalt
109
+ - match: { label: destructive }
110
+ action: approve # önce kişiye sor
111
+
112
+ taint:
113
+ mode: strict # strict | approve | warn | off
114
+
115
+ audit:
116
+ sink: file
117
+ filePath: ./guardbee-audit.jsonl
118
+ ```
119
+
120
+ - Tool'lar ve prompt'lar `<upstream>__<tool>` adıyla görünür.
121
+ - **Toxic flow (lethal trifecta):** her tool `untrusted`, `sensitive`, `egress` veya `destructive` olarak etiketlenir (ad/açıklama sezgileriyle; `labels` altında ezilebilir). Oturum güvenilmeyen içerik (`untrusted` bir tool veya herhangi bir resource) ve hassas veri (`sensitive` bir tool veya bir sonuçtaki herhangi bir PII) gördükten sonra, `egress` çağrısı `strict` modda engellenir, `warn` modda sadece loglanır. Kontrol sunucular arasında çalışır: GitHub'dan okunan bir issue ile CRM'den gelen bir müşteri kaydı, üçüncü bir sunucudaki webhook çağrısını engeller.
122
+ - **Kurallar** `tool` (glob), `upstream`, `label`, `session` (`clean` | `tainted`) ve `args` alanlarıyla eşleşir. `mask`, maskeleme kapalı olsa bile o tool için PII maskelemeyi zorunlu kılar; `mask.fields` ayrıca adı verilen JSON anahtarlarını metinde ve `structuredContent`'te boşaltır. Bir kuraldaki `allow` toxic-flow kontrolünü atlatmaz; bunun yerine tool'un etiketini değiştirin.
123
+ - **Onay:** `approve` (kural aksiyonu olarak ya da toxic flow için `taint.mode: approve`) kişiye MCP elicitation üzerinden tool'u, gerekçeyi ve argümanları gösteren bir evet/hayır formu açar. Açık bir "evet" dışındaki her cevap (red, iptal, `approval.timeoutSeconds` içinde cevap gelmemesi; varsayılan 120) çağrıyı engeller. Elicitation desteklemeyen istemci onay veremez; çağrı bunu söyleyen bir mesajla engellenir.
124
+ - **Tokenize:** `interceptors.piiMasking.mode: tokenize` ile model değerin yerine `<pii:tc_kimlik:7f3a9b21>` görür ve bu token'ı başka bir tool'a yine verebilir; proxy gerçek değeri o sunucuya giderken geri koyar. Token'lar sadece oturum boyunca bellekte tutulur ve `piiMasking.detokenizeForEgress: true` olmadıkça `egress` tool'lar için geri çevrilmez.
125
+ - **Prompt'lar** (`prompts/get`) tool sonuçlarıyla aynı injection taramasından ve PII maskelemeden geçer.
126
+ - `interceptors.definitionDrift.recheck: on-change` her çağrıdaki `tools/list`'i atlar ve sadece sunucu `tools/list_changed` gönderdiğinde yeniden kontrol eder; proxy bu bildirimi ajana da iletir.
127
+ - **Audit log** hash zincirlidir. Varsayılan olarak argümanların kendisi yerine SHA-256'sını tutar, sonuçları hiç yazmaz; yazmak için `audit.includePayloads: true`. Bir log'u `guardbee-proxy verify-audit ./guardbee-audit.jsonl` ile doğrulayın.
128
+ - `guardbee-proxy validate --config guardbee-proxy.yaml` sunucu başlatmadan config'i kontrol eder.
129
+ - `--config` verilmezse proxy `./guardbee-proxy.yaml` dosyasını veya `GUARDBEE_PROXY_CONFIG=<dosya>.yaml` değişkenini de okur.
130
+
131
+ Yukarıdaki tek sunuculu `--` kullanımı 0.x davranışını korur: tool adlarına önek eklenmez, payload'lar loglanır ve toxic flow sadece uyarı olarak loglanır.
132
+
133
+ ---
134
+
63
135
  ## MCP Tools
64
136
 
65
137
  Proxy kendi tool'unu kaydetmez. Hedef sunucunun tool'larını iletir ve her çağrıda interceptor zincirini uygular.
@@ -70,24 +142,31 @@ Proxy kendi tool'unu kaydetmez. Hedef sunucunun tool'larını iletir ve her ça
70
142
 
71
143
  ### Prompt Injection Detector
72
144
 
73
- Gelen mesajlarda aşağıdaki saldırı desenlerini arar:
145
+ [`@guardbee/guard-core`](../guard-core/README.md) içindeki 27 kural; tool argümanlarına, tool sonuçlarına, resource'lara ve prompt'lara uygulanır:
146
+
147
+ - **Engeller** (13 kesin kural, high/critical): İngilizce ve Türkçe talimat geçersiz kılma ("ignore all previous instructions", "önceki talimatları yok say"), `DAN mode`, "bypass your safety guardrails", chat-template token'ları (`<|im_start|>`, `[INST]`), `<system>` etiketleri, Unicode tag karakterlerine gizlenmiş metin, CSS ile gizlenmiş öğelerde veya HTML yorumlarında talimat, markdown görsel exfiltration işaretçileri, sistem istemini isteme (İngilizce ve Türkçe), "send the user's data to https://…".
148
+ - **Sadece uyarır** (14): sıradan metinde de geçen genel ifadeler — "act as", "you are now", "developer mode", "jailbreak", "override policy", "sen artık" — ve zero-width veya bidi kontrol karakterleri, satır başındaki `System:` etiketi gibi orta önemdeki işaretler.
149
+ - **Base64:** okunabilir metne çözülen dizileri çözer ve engelleme kurallarıyla kontrol eder; talimat kodlanmış bir blob'a saklanamaz.
74
150
 
75
- - `ignore previous instructions`
76
- - `disregard your system prompt`
77
- - `you are now [DAN/jailbreak]`
78
- - ANSI escape dizileri ile gizlenmiş komutlar
79
- - Base64 kodlanmış talimatlar
80
- - ve 11 desen daha
151
+ 1.1'den itibaren genel bir ifade tek başına engellemez: 1.0'da bir tool sonucundaki "use jailbreak mode" veya "act as a reverse proxy" engelleniyordu; artık loglanır ve sonuca bir uyarı eklenir.
81
152
 
82
153
  ### PII Masker
83
154
 
84
- | Veri Tipi | Örnek Girdi | Çıktı |
85
- |-----------|------------|-------|
86
- | TC Kimlik | `12345678901` | `[TC-REDACTED]` |
87
- | IBAN | `TR320006200...` | `TR32***` |
88
- | E-posta | `ahmet@example.com` | `ah***@example.com` |
89
- | JWT | `eyJhbGc...` | `[JWT-REDACTED]` |
90
- | API Key | `sk-abc123...` | `[KEY-REDACTED]` |
155
+ | Veri Tipi | Kontrol | Örnek Girdi | Çıktı |
156
+ |-----------|---------|-------------|-------|
157
+ | TC Kimlik | resmi checksum | `10000000146` | `[TC-KİMLİK]` |
158
+ | IBAN (TR) | mod-97 | `TR330006100519786457841326` | `TR**[IBAN]` |
159
+ | Kart numarası | Luhn | `4111 1111 1111 1111` | `****-****-****-[KART]` |
160
+ | E-posta | — | `ahmet@example.com` | `***@[EMAIL]` |
161
+ | Telefon (TR) | cep / sabit hat / 850 öneki | `0532 123 45 67` | `+90-***-***-**[TELEFON]` |
162
+ | Vergi no (VKN) | kontrol hanesi, `VKN` / `Vergi No` etiketi gerekir | `VKN: 1234567890` | `VKN: [VKN]` |
163
+ | Sağlayıcı anahtarları | secret-scanner'dan 22 anahtar ve token formatı (AWS, GitHub, Stripe, OpenAI, Anthropic, Slack, …) | `ghp_…`, `AKIA…` | `[API-KEY]` |
164
+ | Diğer API key'ler | `sk_`/`api_`/`token_`… öneki, harf ve rakam | `api_k3y9x8…` | `[API-KEY]` |
165
+ | Özel anahtar | BEGIN…END bloğunun tamamı | `-----BEGIN … PRIVATE KEY-----` | `[PRIVATE-KEY]` |
166
+ | Bağlantı dizesi | URL'de kullanıcı:şifre (localhost hariç) | `postgres://app:pw@db/prod` | `[CONNECTION-STRING]` |
167
+ | JWT | — | `eyJhbGc...` | `[JWT-TOKEN]` |
168
+
169
+ Checksum'ı tutmayan sayılar (sipariş no, takip kodu) olduğu gibi bırakılır.
91
170
 
92
171
  ---
93
172
 
@@ -1,9 +1,21 @@
1
- import type { AuditEvent, ProxyConfig } from "../types.js";
1
+ import type { AuditConfig, AuditEvent } from "../types.js";
2
2
  export declare class AuditLogger {
3
3
  private config;
4
4
  private stream;
5
- constructor(config: NonNullable<ProxyConfig["audit"]>);
5
+ private lastHash;
6
+ constructor(config: AuditConfig);
6
7
  log(event: AuditEvent): void;
7
8
  close(): void;
9
+ private redact;
8
10
  }
11
+ export type ChainVerification = {
12
+ ok: true;
13
+ events: number;
14
+ } | {
15
+ ok: false;
16
+ line: number;
17
+ reason: string;
18
+ };
19
+ /** Recompute the hash chain of a JSONL audit log. A deleted, inserted or edited line breaks it. */
20
+ export declare function verifyAuditChain(jsonl: string): ChainVerification;
9
21
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/audit/logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE3D,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAoC;IAClD,OAAO,CAAC,MAAM,CAA+B;gBAEjC,MAAM,EAAE,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAOrD,GAAG,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAU5B,KAAK,IAAI,IAAI;CAGd"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/audit/logger.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiC3D,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,QAAQ,CAAW;gBAEf,MAAM,EAAE,WAAW;IAQ/B,GAAG,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAa5B,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,MAAM;CAKf;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhD,mGAAmG;AACnG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAwBjE"}
@@ -4,19 +4,56 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.AuditLogger = void 0;
7
+ exports.verifyAuditChain = verifyAuditChain;
7
8
  const fs_1 = __importDefault(require("fs"));
9
+ const crypto_1 = require("crypto");
10
+ const definition_drift_js_1 = require("../interceptors/definition-drift.js");
11
+ const GENESIS = "";
12
+ function sha256(text) {
13
+ return (0, crypto_1.createHash)("sha256").update(text).digest("hex");
14
+ }
15
+ /** Hash of an event line without its own `hash` field (key order as written). */
16
+ function chainHash(unhashed) {
17
+ return sha256(JSON.stringify(unhashed));
18
+ }
19
+ /** The last event hash in an existing JSONL file, so a restart continues the chain. */
20
+ function lastHashIn(filePath) {
21
+ let raw;
22
+ try {
23
+ raw = fs_1.default.readFileSync(filePath, "utf8");
24
+ }
25
+ catch {
26
+ return GENESIS;
27
+ }
28
+ const lines = raw.split("\n").filter((line) => line.trim() !== "");
29
+ const last = lines[lines.length - 1];
30
+ if (!last)
31
+ return GENESIS;
32
+ try {
33
+ const hash = JSON.parse(last).hash;
34
+ return typeof hash === "string" ? hash : GENESIS;
35
+ }
36
+ catch {
37
+ return GENESIS;
38
+ }
39
+ }
8
40
  class AuditLogger {
9
41
  constructor(config) {
10
42
  this.stream = null;
43
+ this.lastHash = GENESIS;
11
44
  this.config = config;
12
45
  if (config.sink === "file" && config.filePath) {
46
+ this.lastHash = lastHashIn(config.filePath);
13
47
  this.stream = fs_1.default.createWriteStream(config.filePath, { flags: "a" });
14
48
  }
15
49
  }
16
50
  log(event) {
17
51
  if (!this.config.enabled)
18
52
  return;
19
- const line = JSON.stringify(event);
53
+ const unhashed = { ...this.redact(event), prevHash: this.lastHash };
54
+ const hash = chainHash(unhashed);
55
+ this.lastHash = hash;
56
+ const line = JSON.stringify({ ...unhashed, hash });
20
57
  if (this.config.sink === "file" && this.stream) {
21
58
  this.stream.write(line + "\n");
22
59
  }
@@ -27,6 +64,40 @@ class AuditLogger {
27
64
  close() {
28
65
  this.stream?.end();
29
66
  }
67
+ redact(event) {
68
+ if (this.config.includePayloads !== false)
69
+ return event;
70
+ const { input, output: _output, ...rest } = event;
71
+ return input === undefined ? rest : { ...rest, inputHash: sha256((0, definition_drift_js_1.stable)(input)) };
72
+ }
30
73
  }
31
74
  exports.AuditLogger = AuditLogger;
75
+ /** Recompute the hash chain of a JSONL audit log. A deleted, inserted or edited line breaks it. */
76
+ function verifyAuditChain(jsonl) {
77
+ const lines = jsonl.split("\n");
78
+ let expectedPrev = GENESIS;
79
+ let events = 0;
80
+ for (const [index, line] of lines.entries()) {
81
+ if (line.trim() === "")
82
+ continue;
83
+ const lineNo = index + 1;
84
+ let event;
85
+ try {
86
+ event = JSON.parse(line);
87
+ }
88
+ catch {
89
+ return { ok: false, line: lineNo, reason: "not valid JSON" };
90
+ }
91
+ const { hash, ...unhashed } = event;
92
+ if (unhashed.prevHash !== expectedPrev) {
93
+ return { ok: false, line: lineNo, reason: "prevHash does not match the previous event (a line was removed or reordered)" };
94
+ }
95
+ if (hash !== chainHash(unhashed)) {
96
+ return { ok: false, line: lineNo, reason: "hash does not match the event contents (the line was edited)" };
97
+ }
98
+ expectedPrev = hash;
99
+ events++;
100
+ }
101
+ return { ok: true, events };
102
+ }
32
103
  //# sourceMappingURL=logger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/audit/logger.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AAGpB,MAAa,WAAW;IAItB,YAAY,MAAyC;QAF7C,WAAM,GAA0B,IAAI,CAAC;QAG3C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC9C,IAAI,CAAC,MAAM,GAAG,YAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED,GAAG,CAAC,KAAiB;QACnB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC/C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;IACrB,CAAC;CACF;AAxBD,kCAwBC"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/audit/logger.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts CHANGED
@@ -1,2 +1,3 @@
1
+ #!/usr/bin/env node
1
2
  export {};
2
3
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.js CHANGED
@@ -1,10 +1,72 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
2
6
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const config_js_1 = require("./config.js");
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const logger_js_1 = require("./audit/logger.js");
11
+ const config_js_1 = require("./gateway/config.js");
12
+ const init_js_1 = require("./init.js");
4
13
  const proxy_js_1 = require("./proxy.js");
14
+ const CLIENTS = ["claude-desktop", "cursor", "claude-code"];
15
+ function flag(args, name) {
16
+ const at = args.indexOf(name);
17
+ return at === -1 ? undefined : args[at + 1];
18
+ }
19
+ function verifyAudit(filePath) {
20
+ if (!filePath)
21
+ throw new Error("Usage: guardbee-proxy verify-audit <audit.jsonl>");
22
+ const result = (0, logger_js_1.verifyAuditChain)(fs_1.default.readFileSync(filePath, "utf8"));
23
+ if (result.ok) {
24
+ process.stdout.write(`OK: ${result.events} events, hash chain intact\n`);
25
+ return;
26
+ }
27
+ process.stdout.write(`BROKEN at line ${result.line}: ${result.reason}\n`);
28
+ process.exit(1);
29
+ }
30
+ function init(args) {
31
+ const client = flag(args, "--client");
32
+ const file = flag(args, "--file");
33
+ if ((!client && !file) || (client && !CLIENTS.includes(client))) {
34
+ throw new Error(`Usage: guardbee-proxy init (--client ${CLIENTS.join("|")} | --file <mcp.json>) [--out <guardbee-proxy.yaml>] [--dry-run] [--force]`);
35
+ }
36
+ const dryRun = args.includes("--dry-run");
37
+ const result = (0, init_js_1.runInit)({
38
+ clientConfigPath: file ?? (0, init_js_1.clientConfigPath)(client),
39
+ yamlPath: flag(args, "--out") ?? path_1.default.join(os_1.default.homedir(), ".guardbee", "guardbee-proxy.yaml"),
40
+ dryRun,
41
+ force: args.includes("--force"),
42
+ });
43
+ const out = process.stdout;
44
+ for (const [key, name] of Object.entries(result.migrated))
45
+ out.write(` moved ${key} → upstream "${name}"\n`);
46
+ for (const { name, reason } of result.kept)
47
+ out.write(` kept ${name} (${reason})\n`);
48
+ if (dryRun) {
49
+ out.write(`\n--- guardbee-proxy.yaml ---\n${result.yaml}\n--- client config ---\n${result.clientConfig}`);
50
+ return;
51
+ }
52
+ out.write(`\nWrote the proxy config and pointed the client at it. Backup: ${result.backupPath}\n`);
53
+ if (client === "claude-code" && !file) {
54
+ out.write("Claude Code rewrites ~/.claude.json while it runs: quit it before init, or check the file afterwards.\n");
55
+ }
56
+ out.write("Restart the client to load the proxy.\n");
57
+ }
5
58
  async function main() {
6
- const config = (0, config_js_1.loadProxyConfig)();
7
- await (0, proxy_js_1.startProxy)(config);
59
+ const [command, ...rest] = process.argv.slice(2);
60
+ if (command === "verify-audit")
61
+ return verifyAudit(rest[0]);
62
+ if (command === "init")
63
+ return init(rest);
64
+ if (command === "validate") {
65
+ const config = (0, config_js_1.loadGatewayConfig)();
66
+ process.stdout.write(`OK: ${Object.keys(config.upstreams).length} upstream(s), ${config.rules.length} rule(s), taint.mode=${config.taint.mode}\n`);
67
+ return;
68
+ }
69
+ await (0, proxy_js_1.startGateway)((0, config_js_1.loadGatewayConfig)());
8
70
  }
9
71
  main().catch((err) => {
10
72
  process.stderr.write(`[guardbee-proxy] Fatal error: ${err instanceof Error ? err.message : String(err)}\n`);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;AAAA,2CAA8C;AAC9C,yCAAwC;AAExC,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,IAAA,2BAAe,GAAE,CAAC;IACjC,MAAM,IAAA,qBAAU,EAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,iCAAiC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CACtF,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ export interface ApprovalRequest {
3
+ tool: string;
4
+ upstream: string;
5
+ args: Record<string, unknown>;
6
+ reason: string;
7
+ }
8
+ /** `unavailable`: the client cannot show a prompt, so nobody could approve. */
9
+ export type ApprovalOutcome = "approved" | "declined" | "timed-out" | "unavailable";
10
+ export type Approver = (request: ApprovalRequest) => Promise<ApprovalOutcome>;
11
+ /**
12
+ * Ask the person through MCP elicitation (a form the client shows). Any answer
13
+ * other than an explicit yes — decline, cancel, timeout — counts as no.
14
+ */
15
+ export declare function elicitationApprover(server: Server, timeoutSeconds: number): Approver;
16
+ //# sourceMappingURL=approval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../../src/gateway/approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAExE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;AAEpF,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;AAS9E;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,QAAQ,CAwBpF"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.elicitationApprover = elicitationApprover;
4
+ const MAX_ARGS_SHOWN = 600;
5
+ function describeArgs(args) {
6
+ const json = JSON.stringify(args, null, 2);
7
+ return json.length > MAX_ARGS_SHOWN ? `${json.slice(0, MAX_ARGS_SHOWN)}\n… (${json.length - MAX_ARGS_SHOWN} more characters)` : json;
8
+ }
9
+ /**
10
+ * Ask the person through MCP elicitation (a form the client shows). Any answer
11
+ * other than an explicit yes — decline, cancel, timeout — counts as no.
12
+ */
13
+ function elicitationApprover(server, timeoutSeconds) {
14
+ return async (request) => {
15
+ if (!server.getClientCapabilities()?.elicitation)
16
+ return "unavailable";
17
+ try {
18
+ const answer = await server.elicitInput({
19
+ message: `GuardBee: approve this tool call?\n\n` +
20
+ `Tool: ${request.tool}\nWhy approval is needed: ${request.reason}\n\nArguments:\n${describeArgs(request.args)}`,
21
+ requestedSchema: {
22
+ type: "object",
23
+ properties: {
24
+ approve: { type: "boolean", title: "Approve this call", default: false },
25
+ },
26
+ required: ["approve"],
27
+ },
28
+ }, { timeout: timeoutSeconds * 1000 });
29
+ return answer.action === "accept" && answer.content?.["approve"] === true ? "approved" : "declined";
30
+ }
31
+ catch (err) {
32
+ return err instanceof Error && /timed? ?out/i.test(err.message) ? "timed-out" : "declined";
33
+ }
34
+ };
35
+ }
36
+ //# sourceMappingURL=approval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval.js","sourceRoot":"","sources":["../../src/gateway/approval.ts"],"names":[],"mappings":";;AAyBA,kDAwBC;AAnCD,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,SAAS,YAAY,CAAC,IAA6B;IACjD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3C,OAAO,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,QAAQ,IAAI,CAAC,MAAM,GAAG,cAAc,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC;AACvI,CAAC;AAED;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,MAAc,EAAE,cAAsB;IACxE,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,CAAC,MAAM,CAAC,qBAAqB,EAAE,EAAE,WAAW;YAAE,OAAO,aAAa,CAAC;QACvE,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CACrC;gBACE,OAAO,EACL,uCAAuC;oBACvC,SAAS,OAAO,CAAC,IAAI,6BAA6B,OAAO,CAAC,MAAM,mBAAmB,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBACjH,eAAe,EAAE;oBACf,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,KAAK,EAAE;qBACzE;oBACD,QAAQ,EAAE,CAAC,SAAS,CAAC;iBACtB;aACF,EACD,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,EAAE,CACnC,CAAC;YACF,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;QACtG,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,GAAG,YAAY,KAAK,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC;QAC7F,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}