@guardbee/mcp-security-proxy 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -11
- package/TR.md +88 -11
- package/dist/audit/logger.d.ts +14 -2
- package/dist/audit/logger.d.ts.map +1 -1
- package/dist/audit/logger.js +72 -1
- package/dist/audit/logger.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +65 -3
- package/dist/cli.js.map +1 -1
- package/dist/gateway/approval.d.ts +16 -0
- package/dist/gateway/approval.d.ts.map +1 -0
- package/dist/gateway/approval.js +36 -0
- package/dist/gateway/approval.js.map +1 -0
- package/dist/gateway/config.d.ts +36 -0
- package/dist/gateway/config.d.ts.map +1 -0
- package/dist/gateway/config.js +206 -0
- package/dist/gateway/config.js.map +1 -0
- package/dist/gateway/fields.d.ts +7 -0
- package/dist/gateway/fields.d.ts.map +1 -0
- package/dist/gateway/fields.js +43 -0
- package/dist/gateway/fields.js.map +1 -0
- package/dist/gateway/gateway.d.ts +59 -0
- package/dist/gateway/gateway.d.ts.map +1 -0
- package/dist/gateway/gateway.js +376 -0
- package/dist/gateway/gateway.js.map +1 -0
- package/dist/gateway/labels.d.ts +10 -0
- package/dist/gateway/labels.d.ts.map +1 -0
- package/dist/gateway/labels.js +17 -0
- package/dist/gateway/labels.js.map +1 -0
- package/dist/gateway/policy.d.ts +37 -0
- package/dist/gateway/policy.d.ts.map +1 -0
- package/dist/gateway/policy.js +57 -0
- package/dist/gateway/policy.js.map +1 -0
- package/dist/gateway/taint.d.ts +25 -0
- package/dist/gateway/taint.d.ts.map +1 -0
- package/dist/gateway/taint.js +43 -0
- package/dist/gateway/taint.js.map +1 -0
- package/dist/gateway/tokens.d.ts +18 -0
- package/dist/gateway/tokens.d.ts.map +1 -0
- package/dist/gateway/tokens.js +60 -0
- package/dist/gateway/tokens.js.map +1 -0
- package/dist/gateway/upstream.d.ts +20 -0
- package/dist/gateway/upstream.d.ts.map +1 -0
- package/dist/gateway/upstream.js +49 -0
- package/dist/gateway/upstream.js.map +1 -0
- package/dist/index.d.ts +21 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +34 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +140 -0
- package/dist/init.js.map +1 -0
- package/dist/interceptors/definition-drift.d.ts +1 -0
- package/dist/interceptors/definition-drift.d.ts.map +1 -1
- package/dist/interceptors/definition-drift.js +1 -0
- package/dist/interceptors/definition-drift.js.map +1 -1
- package/dist/interceptors/pii-masker.d.ts +1 -2
- package/dist/interceptors/pii-masker.d.ts.map +1 -1
- package/dist/interceptors/pii-masker.js +5 -68
- package/dist/interceptors/pii-masker.js.map +1 -1
- package/dist/interceptors/prompt-injection.d.ts +1 -2
- package/dist/interceptors/prompt-injection.d.ts.map +1 -1
- package/dist/interceptors/prompt-injection.js +4 -40
- package/dist/interceptors/prompt-injection.js.map +1 -1
- package/dist/interceptors/tool-result.d.ts +1 -3
- package/dist/interceptors/tool-result.d.ts.map +1 -1
- package/dist/interceptors/tool-result.js +4 -12
- package/dist/interceptors/tool-result.js.map +1 -1
- package/dist/proxy.d.ts +18 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +48 -226
- package/dist/proxy.js.map +1 -1
- package/dist/types.d.ts +36 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -16,7 +16,8 @@ Claude ──► MCP Security Proxy ──► Any MCP Server
|
|
|
16
16
|
├─ Prompt injection detection (16 attack patterns)
|
|
17
17
|
├─ PII masking (national ID, IBAN, email, JWT, API key)
|
|
18
18
|
├─ Block or warn mode
|
|
19
|
-
|
|
19
|
+
├─ Toxic-flow (lethal trifecta) blocking
|
|
20
|
+
└─ Hash-chained audit log
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
---
|
|
@@ -29,6 +30,8 @@ Claude ──► MCP Security Proxy ──► Any MCP Server
|
|
|
29
30
|
- **PII Masking** — national ID numbers, IBAN, email, phone, JWT tokens, API keys are automatically 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.
|
|
@@ -75,19 +147,24 @@ Looks for the following attack patterns in incoming messages:
|
|
|
75
147
|
- `ignore previous instructions`
|
|
76
148
|
- `disregard your system prompt`
|
|
77
149
|
- `you are now [DAN/jailbreak]`
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
150
|
+
- `act as` / `pretend to be` role overrides
|
|
151
|
+
- `DAN mode`, `developer mode`, `jailbreak`
|
|
152
|
+
- fake `[SYSTEM]` / `<system>` tags
|
|
153
|
+
- and 6 more patterns
|
|
81
154
|
|
|
82
155
|
### PII Masker
|
|
83
156
|
|
|
84
|
-
| Data Type | Example Input | Output |
|
|
85
|
-
|
|
86
|
-
| National ID | `
|
|
87
|
-
| IBAN | `
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
157
|
+
| Data Type | Check | Example Input | Output |
|
|
158
|
+
|-----------|-------|---------------|--------|
|
|
159
|
+
| National ID (TC Kimlik No) | official checksum | `10000000146` | `[TC-KİMLİK]` |
|
|
160
|
+
| IBAN (TR) | mod-97 | `TR330006100519786457841326` | `TR**[IBAN]` |
|
|
161
|
+
| Card number | Luhn | `4111 1111 1111 1111` | `****-****-****-[KART]` |
|
|
162
|
+
| Email | — | `ahmet@example.com` | `***@[EMAIL]` |
|
|
163
|
+
| Phone (TR) | — | `0532 123 45 67` | `+90-***-***-**[TELEFON]` |
|
|
164
|
+
| JWT | — | `eyJhbGc...` | `[JWT-TOKEN]` |
|
|
165
|
+
| API Key | — | `sk_abc123...` | `[API-KEY]` |
|
|
166
|
+
|
|
167
|
+
A number that fails its checksum (an order number, a tracking code) is left as is.
|
|
91
168
|
|
|
92
169
|
---
|
|
93
170
|
|
package/TR.md
CHANGED
|
@@ -16,7 +16,8 @@ Claude ──► MCP Security Proxy ──► Herhangi bir MCP Sunucu
|
|
|
16
16
|
├─ Prompt injection tespiti (16 saldırı deseni)
|
|
17
17
|
├─ PII maskeleme (TC kimlik, IBAN, e-posta, JWT, API key)
|
|
18
18
|
├─ Block veya warn modu
|
|
19
|
-
|
|
19
|
+
├─ Toxic-flow (lethal trifecta) engelleme
|
|
20
|
+
└─ Hash zincirli audit log
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
---
|
|
@@ -29,6 +30,8 @@ Claude ──► MCP Security Proxy ──► Herhangi bir MCP Sunucu
|
|
|
29
30
|
- **PII Maskeleme** — TC kimlik no, IBAN, e-posta, telefon, JWT token, API key yanıtlarda otomatik 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.
|
|
@@ -75,19 +147,24 @@ Gelen mesajlarda aşağıdaki saldırı desenlerini arar:
|
|
|
75
147
|
- `ignore previous instructions`
|
|
76
148
|
- `disregard your system prompt`
|
|
77
149
|
- `you are now [DAN/jailbreak]`
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
150
|
+
- `act as` / `pretend to be` rol değiştirme
|
|
151
|
+
- `DAN mode`, `developer mode`, `jailbreak`
|
|
152
|
+
- sahte `[SYSTEM]` / `<system>` etiketleri
|
|
153
|
+
- ve 6 desen daha
|
|
81
154
|
|
|
82
155
|
### PII Masker
|
|
83
156
|
|
|
84
|
-
| Veri Tipi | Örnek Girdi | Çıktı |
|
|
85
|
-
|
|
86
|
-
| TC Kimlik | `
|
|
87
|
-
| IBAN | `
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
157
|
+
| Veri Tipi | Kontrol | Örnek Girdi | Çıktı |
|
|
158
|
+
|-----------|---------|-------------|-------|
|
|
159
|
+
| TC Kimlik | resmi checksum | `10000000146` | `[TC-KİMLİK]` |
|
|
160
|
+
| IBAN (TR) | mod-97 | `TR330006100519786457841326` | `TR**[IBAN]` |
|
|
161
|
+
| Kart numarası | Luhn | `4111 1111 1111 1111` | `****-****-****-[KART]` |
|
|
162
|
+
| E-posta | — | `ahmet@example.com` | `***@[EMAIL]` |
|
|
163
|
+
| Telefon (TR) | — | `0532 123 45 67` | `+90-***-***-**[TELEFON]` |
|
|
164
|
+
| JWT | — | `eyJhbGc...` | `[JWT-TOKEN]` |
|
|
165
|
+
| API Key | — | `sk_abc123...` | `[API-KEY]` |
|
|
166
|
+
|
|
167
|
+
Checksum'ı tutmayan sayılar (sipariş no, takip kodu) olduğu gibi bırakılır.
|
|
91
168
|
|
|
92
169
|
---
|
|
93
170
|
|
package/dist/audit/logger.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AuditConfig, AuditEvent } from "../types.js";
|
|
2
2
|
export declare class AuditLogger {
|
|
3
3
|
private config;
|
|
4
4
|
private stream;
|
|
5
|
-
|
|
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":"
|
|
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"}
|
package/dist/audit/logger.js
CHANGED
|
@@ -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
|
|
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
|
package/dist/audit/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/audit/logger.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/audit/logger.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
CHANGED
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
|
|
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
|
|
7
|
-
|
|
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":"
|
|
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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AuditConfig, McpServerConfig, ProxyConfig } from "../types.js";
|
|
2
|
+
import { type Label } from "./labels.js";
|
|
3
|
+
import type { PolicyAction, PolicyRule } from "./policy.js";
|
|
4
|
+
import type { TaintMode } from "./taint.js";
|
|
5
|
+
export interface GatewayConfig {
|
|
6
|
+
upstreams: Record<string, McpServerConfig>;
|
|
7
|
+
/** Prefix tool and prompt names with `<upstream>__`. Off only for the legacy single-server config. */
|
|
8
|
+
namespaced: boolean;
|
|
9
|
+
/** Label overrides by the tool name the agent sees; replaces the heuristic labels entirely. */
|
|
10
|
+
labels: Record<string, Label[]>;
|
|
11
|
+
rules: PolicyRule[];
|
|
12
|
+
taint: {
|
|
13
|
+
mode: TaintMode;
|
|
14
|
+
};
|
|
15
|
+
/** How long an `approve` decision waits for the person before it becomes a deny. */
|
|
16
|
+
approval: {
|
|
17
|
+
timeoutSeconds: number;
|
|
18
|
+
};
|
|
19
|
+
defaults: {
|
|
20
|
+
action: PolicyAction;
|
|
21
|
+
};
|
|
22
|
+
audit: AuditConfig;
|
|
23
|
+
interceptors: NonNullable<ProxyConfig["interceptors"]>;
|
|
24
|
+
}
|
|
25
|
+
export declare function parseGatewayYaml(source: string, fileLabel?: string): GatewayConfig;
|
|
26
|
+
/**
|
|
27
|
+
* The 0.x single-server config keeps its behavior: tool names are not prefixed,
|
|
28
|
+
* payloads are logged, and a toxic flow only warns.
|
|
29
|
+
*/
|
|
30
|
+
export declare function fromLegacyConfig(config: ProxyConfig): GatewayConfig;
|
|
31
|
+
/**
|
|
32
|
+
* 1. `--config <file.yaml>` 2. GUARDBEE_PROXY_CONFIG pointing at a .yaml/.yml
|
|
33
|
+
* 3. ./guardbee-proxy.yaml (or .yml) 4. the legacy JSON / `-- <command>` / PROXY_* setup.
|
|
34
|
+
*/
|
|
35
|
+
export declare function loadGatewayConfig(argv?: string[], cwd?: string): GatewayConfig;
|
|
36
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/gateway/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAU,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC3C,sGAAsG;IACtG,UAAU,EAAE,OAAO,CAAC;IACpB,+FAA+F;IAC/F,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAChC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,KAAK,EAAE;QAAE,IAAI,EAAE,SAAS,CAAA;KAAE,CAAC;IAC3B,oFAAoF;IACpF,QAAQ,EAAE;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,QAAQ,EAAE;QAAE,MAAM,EAAE,YAAY,CAAA;KAAE,CAAC;IACnC,KAAK,EAAE,WAAW,CAAC;IACnB,YAAY,EAAE,WAAW,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC;CACxD;AAyHD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,SAAwB,GAAG,aAAa,CAuBjG;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,aAAa,CAYnE;AAgBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,MAAM,EAAiB,EAAE,GAAG,GAAE,MAAsB,GAAG,aAAa,CAmB3G"}
|