@guardbee/mcp-security-proxy 0.3.1 → 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 +4 -2
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Gateway = void 0;
|
|
4
|
+
const guard_core_1 = require("@guardbee/guard-core");
|
|
5
|
+
const mcp_telemetry_1 = require("@guardbee/mcp-telemetry");
|
|
6
|
+
const definition_drift_js_1 = require("../interceptors/definition-drift.js");
|
|
7
|
+
const fields_js_1 = require("./fields.js");
|
|
8
|
+
const labels_js_1 = require("./labels.js");
|
|
9
|
+
const policy_js_1 = require("./policy.js");
|
|
10
|
+
const taint_js_1 = require("./taint.js");
|
|
11
|
+
const tokens_js_1 = require("./tokens.js");
|
|
12
|
+
const SEPARATOR = "__";
|
|
13
|
+
const PREFIX = "[GuardBee Security Proxy]";
|
|
14
|
+
function errorResult(text) {
|
|
15
|
+
return { content: [{ type: "text", text: `${PREFIX} ${text}` }], isError: true };
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The policy pipeline between the agent and its MCP servers. Transport-free:
|
|
19
|
+
* proxy.ts wires it to stdio, tests drive it directly.
|
|
20
|
+
*
|
|
21
|
+
* A tool call runs: input injection scan → definition drift → policy rules
|
|
22
|
+
* (incl. approval) → toxic-flow check → detokenize → upstream → result
|
|
23
|
+
* injection scan → PII masking / tokenizing → field masking → taint update.
|
|
24
|
+
*/
|
|
25
|
+
class Gateway {
|
|
26
|
+
constructor(upstreams, config, audit, approver) {
|
|
27
|
+
this.upstreams = upstreams;
|
|
28
|
+
this.config = config;
|
|
29
|
+
this.audit = audit;
|
|
30
|
+
this.approver = approver;
|
|
31
|
+
this.taint = new taint_js_1.TaintTracker();
|
|
32
|
+
this.vault = new tokens_js_1.PiiVault();
|
|
33
|
+
this.pins = new Map();
|
|
34
|
+
this.tools = new Map();
|
|
35
|
+
this.resources = new Map();
|
|
36
|
+
this.prompts = new Map();
|
|
37
|
+
/** Upstreams that announced tools/list_changed since the agent last listed tools. */
|
|
38
|
+
this.stale = new Set();
|
|
39
|
+
this.toolListeners = [];
|
|
40
|
+
for (const upstream of upstreams) {
|
|
41
|
+
upstream.onToolsChanged?.(() => {
|
|
42
|
+
this.stale.add(upstream.name);
|
|
43
|
+
for (const listener of this.toolListeners)
|
|
44
|
+
listener();
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Called when any upstream's tool list changed; proxy.ts relays it to the agent. */
|
|
49
|
+
onToolsChanged(listener) {
|
|
50
|
+
this.toolListeners.push(listener);
|
|
51
|
+
}
|
|
52
|
+
get interceptors() {
|
|
53
|
+
return this.config.interceptors;
|
|
54
|
+
}
|
|
55
|
+
exposed(upstream, name) {
|
|
56
|
+
return this.config.namespaced ? `${upstream.name}${SEPARATOR}${name}` : name;
|
|
57
|
+
}
|
|
58
|
+
pinFor(upstream) {
|
|
59
|
+
let pin = this.pins.get(upstream.name);
|
|
60
|
+
if (!pin)
|
|
61
|
+
this.pins.set(upstream.name, (pin = new definition_drift_js_1.ToolDefinitionPin()));
|
|
62
|
+
return pin;
|
|
63
|
+
}
|
|
64
|
+
log(event) {
|
|
65
|
+
this.audit.log({ ts: new Date().toISOString(), ...event, taint: this.taint.snapshot() });
|
|
66
|
+
}
|
|
67
|
+
get driftEnabled() {
|
|
68
|
+
return this.interceptors.definitionDrift?.enabled !== false;
|
|
69
|
+
}
|
|
70
|
+
get driftAction() {
|
|
71
|
+
return this.interceptors.definitionDrift?.action ?? this.interceptors.promptInjection?.action ?? "block";
|
|
72
|
+
}
|
|
73
|
+
get resultAction() {
|
|
74
|
+
return this.interceptors.toolResultInjection?.action ?? this.interceptors.promptInjection?.action ?? "block";
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Find PII in a value (it taints the session either way) and hide it unless
|
|
78
|
+
* masking is off. `force` comes from a `mask` rule and overrides "off".
|
|
79
|
+
*/
|
|
80
|
+
protect(value, force = false) {
|
|
81
|
+
const redacted = (0, guard_core_1.maskPiiInValue)(value);
|
|
82
|
+
const piiFound = (0, definition_drift_js_1.stable)(redacted) !== (0, definition_drift_js_1.stable)(value);
|
|
83
|
+
const enabled = this.interceptors.piiMasking?.enabled !== false || force;
|
|
84
|
+
if (!piiFound || !enabled)
|
|
85
|
+
return { value, piiFound };
|
|
86
|
+
const tokenize = this.interceptors.piiMasking?.mode === "tokenize";
|
|
87
|
+
return { value: tokenize ? this.vault.tokenize(value) : redacted, piiFound };
|
|
88
|
+
}
|
|
89
|
+
async listTools() {
|
|
90
|
+
const listed = [];
|
|
91
|
+
for (const upstream of this.upstreams) {
|
|
92
|
+
let tools;
|
|
93
|
+
try {
|
|
94
|
+
tools = await upstream.listTools();
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
// One broken server must not take the others' tools away.
|
|
98
|
+
this.log({ type: "warn", upstream: upstream.name, reason: `tools/list failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
this.stale.delete(upstream.name);
|
|
102
|
+
if (this.driftEnabled) {
|
|
103
|
+
const observed = this.pinFor(upstream).observe(tools, this.driftAction);
|
|
104
|
+
for (const finding of observed.findings) {
|
|
105
|
+
this.log({
|
|
106
|
+
type: this.driftAction === "block" ? "blocked" : "warn",
|
|
107
|
+
tool: this.exposed(upstream, finding.toolName),
|
|
108
|
+
server: upstream.name,
|
|
109
|
+
upstream: upstream.name,
|
|
110
|
+
reason: finding.reason,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
tools = observed.tools;
|
|
114
|
+
}
|
|
115
|
+
for (const tool of tools) {
|
|
116
|
+
const name = this.exposed(upstream, tool.name);
|
|
117
|
+
const labels = this.config.labels[name] ?? (0, labels_js_1.labelTool)(tool);
|
|
118
|
+
this.tools.set(name, { upstream, name: tool.name, labels });
|
|
119
|
+
listed.push({ ...tool, name });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return listed;
|
|
123
|
+
}
|
|
124
|
+
async callTool(name, args) {
|
|
125
|
+
const started = Date.now();
|
|
126
|
+
let route = this.tools.get(name);
|
|
127
|
+
if (!route) {
|
|
128
|
+
await this.listTools();
|
|
129
|
+
route = this.tools.get(name);
|
|
130
|
+
}
|
|
131
|
+
if (!route)
|
|
132
|
+
return errorResult(`Unknown tool: ${name}`);
|
|
133
|
+
const { upstream, labels } = route;
|
|
134
|
+
const base = { tool: name, server: upstream.name, upstream: upstream.name, labels };
|
|
135
|
+
const block = (reason, extra = {}) => {
|
|
136
|
+
this.log({ ...base, type: "blocked", input: args, reason, ...extra });
|
|
137
|
+
void (0, mcp_telemetry_1.recordEvent)({
|
|
138
|
+
server: "security-proxy",
|
|
139
|
+
tool: name,
|
|
140
|
+
params: args,
|
|
141
|
+
success: false,
|
|
142
|
+
durationMs: Date.now() - started,
|
|
143
|
+
error: `blocked: ${reason}`,
|
|
144
|
+
});
|
|
145
|
+
return errorResult(`Tool call blocked: ${reason}`);
|
|
146
|
+
};
|
|
147
|
+
/** null = approved, go on; otherwise the result to return. */
|
|
148
|
+
const askApproval = async (reason, extra = {}) => {
|
|
149
|
+
const outcome = this.approver
|
|
150
|
+
? await this.approver({ tool: name, upstream: upstream.name, args, reason })
|
|
151
|
+
: "unavailable";
|
|
152
|
+
this.log({ ...base, type: "approval", input: args, reason: `${outcome}: ${reason}`, ...extra });
|
|
153
|
+
if (outcome === "approved")
|
|
154
|
+
return null;
|
|
155
|
+
if (outcome === "unavailable") {
|
|
156
|
+
return errorResult(`Tool call blocked: it needs a person's approval, but this client cannot show an approval prompt (MCP elicitation). ${reason}`);
|
|
157
|
+
}
|
|
158
|
+
return errorResult(`Tool call not approved (${outcome}): ${reason}`);
|
|
159
|
+
};
|
|
160
|
+
// 1. Prompt injection in the arguments
|
|
161
|
+
if (this.interceptors.promptInjection?.enabled !== false) {
|
|
162
|
+
const scan = (0, guard_core_1.scanForPromptInjection)(args, this.interceptors.promptInjection?.action ?? "block");
|
|
163
|
+
if (scan.action === "block")
|
|
164
|
+
return block(scan.reason);
|
|
165
|
+
if (scan.action === "warn")
|
|
166
|
+
this.log({ ...base, type: "warn", input: args, reason: scan.reason });
|
|
167
|
+
}
|
|
168
|
+
// 2. The live definition must still match the session pin
|
|
169
|
+
const recheck = this.interceptors.definitionDrift?.recheck ?? "every-call";
|
|
170
|
+
if (this.driftEnabled && (recheck === "every-call" || this.stale.has(upstream.name))) {
|
|
171
|
+
try {
|
|
172
|
+
const live = (await upstream.listTools());
|
|
173
|
+
const pin = this.pinFor(upstream);
|
|
174
|
+
pin.ensurePinned(live);
|
|
175
|
+
const drift = pin.drifted(route.name, live);
|
|
176
|
+
if (drift && this.driftAction === "block")
|
|
177
|
+
return block(drift.reason);
|
|
178
|
+
if (drift)
|
|
179
|
+
this.log({ ...base, type: "warn", input: args, reason: drift.reason });
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
this.log({ ...base, type: "warn", reason: `definition re-check failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// 3. Policy rules
|
|
186
|
+
const decision = (0, policy_js_1.evaluatePolicy)(this.config.rules, this.config.defaults.action, {
|
|
187
|
+
tool: name,
|
|
188
|
+
upstream: upstream.name,
|
|
189
|
+
labels,
|
|
190
|
+
tainted: this.taint.tainted,
|
|
191
|
+
args,
|
|
192
|
+
});
|
|
193
|
+
const rule = decision.ruleId ?? "defaults.action";
|
|
194
|
+
if (decision.action === "deny")
|
|
195
|
+
return block(`denied by policy rule ${rule}`, { ruleId: decision.ruleId });
|
|
196
|
+
if (decision.action === "warn") {
|
|
197
|
+
this.log({ ...base, type: "warn", ruleId: decision.ruleId, input: args, reason: `policy rule ${rule}` });
|
|
198
|
+
}
|
|
199
|
+
if (decision.action === "approve") {
|
|
200
|
+
const refused = await askApproval(`policy rule ${rule} requires approval`, { ruleId: decision.ruleId });
|
|
201
|
+
if (refused)
|
|
202
|
+
return refused;
|
|
203
|
+
}
|
|
204
|
+
// 4. Toxic flow: untrusted + sensitive already in context, and this call can send data out
|
|
205
|
+
const taintMode = this.config.taint.mode;
|
|
206
|
+
if (taintMode !== "off" && this.taint.completesTrifecta(labels)) {
|
|
207
|
+
const reason = this.taint.describe(name);
|
|
208
|
+
if (taintMode === "strict") {
|
|
209
|
+
this.log({ ...base, type: "toxic_flow", input: args, reason: `${reason} — blocked (taint.mode=strict)` });
|
|
210
|
+
return errorResult(`Tool call blocked: ${reason}. Start a new session to use this tool, or relabel it in the proxy config.`);
|
|
211
|
+
}
|
|
212
|
+
if (taintMode === "approve") {
|
|
213
|
+
this.log({ ...base, type: "toxic_flow", input: args, reason: `${reason} — asking for approval (taint.mode=approve)` });
|
|
214
|
+
const refused = await askApproval(reason);
|
|
215
|
+
if (refused)
|
|
216
|
+
return refused;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
this.log({ ...base, type: "toxic_flow", input: args, reason: `${reason} — allowed (taint.mode=warn)` });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// 5. PII tokens go back to real values — but not into a tool that can send them out
|
|
223
|
+
let forwardArgs = args;
|
|
224
|
+
if (this.vault.holdsTokens(args)) {
|
|
225
|
+
if (labels.includes("egress") && !this.interceptors.piiMasking?.detokenizeForEgress) {
|
|
226
|
+
this.log({ ...base, type: "warn", reason: "PII tokens passed on as-is: the tool can send data out (piiMasking.detokenizeForEgress is off)" });
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
forwardArgs = this.vault.detokenize(args);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// 6. Forward
|
|
233
|
+
this.log({ ...base, type: "tool_call", ruleId: decision.ruleId, input: args });
|
|
234
|
+
const result = await upstream.callTool(route.name, forwardArgs);
|
|
235
|
+
// 7. Indirect injection in the result (text and structured)
|
|
236
|
+
let content = result.content;
|
|
237
|
+
if (this.interceptors.toolResultInjection?.enabled !== false) {
|
|
238
|
+
const scan = (0, guard_core_1.scanToolResult)([content, result.structuredContent ?? null], this.resultAction);
|
|
239
|
+
if (scan.action === "block") {
|
|
240
|
+
this.log({ ...base, type: "blocked", output: content, reason: scan.reason });
|
|
241
|
+
void (0, mcp_telemetry_1.recordEvent)({
|
|
242
|
+
server: "security-proxy",
|
|
243
|
+
tool: name,
|
|
244
|
+
params: args,
|
|
245
|
+
success: false,
|
|
246
|
+
durationMs: Date.now() - started,
|
|
247
|
+
error: `blocked: ${scan.reason}`,
|
|
248
|
+
});
|
|
249
|
+
return errorResult(`Tool result blocked: ${scan.reason}`);
|
|
250
|
+
}
|
|
251
|
+
if (scan.action === "warn") {
|
|
252
|
+
this.log({ ...base, type: "warn", output: content, reason: scan.reason });
|
|
253
|
+
content = [{ type: "text", text: `${PREFIX} Warning: ${scan.reason}` }, ...content];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// 8. PII: always detected (it taints the session), masked unless turned off — a `mask` rule forces it
|
|
257
|
+
const force = decision.action === "mask";
|
|
258
|
+
const text = this.protect(content, force);
|
|
259
|
+
let out = { ...result, content: text.value };
|
|
260
|
+
let piiFound = text.piiFound;
|
|
261
|
+
if (result.structuredContent) {
|
|
262
|
+
const structured = this.protect(result.structuredContent, force);
|
|
263
|
+
out.structuredContent = structured.value;
|
|
264
|
+
piiFound || (piiFound = structured.piiFound);
|
|
265
|
+
}
|
|
266
|
+
if (decision.maskFields)
|
|
267
|
+
out = (0, fields_js_1.maskFields)(out, decision.maskFields);
|
|
268
|
+
this.taint.observe(name, labels, piiFound);
|
|
269
|
+
this.log({ ...base, type: "tool_response", output: out.content });
|
|
270
|
+
void (0, mcp_telemetry_1.recordEvent)({
|
|
271
|
+
server: "security-proxy",
|
|
272
|
+
tool: name,
|
|
273
|
+
params: args,
|
|
274
|
+
success: !result.isError,
|
|
275
|
+
durationMs: Date.now() - started,
|
|
276
|
+
});
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
279
|
+
async listResources() {
|
|
280
|
+
const listed = [];
|
|
281
|
+
for (const upstream of this.upstreams) {
|
|
282
|
+
try {
|
|
283
|
+
for (const resource of await upstream.listResources()) {
|
|
284
|
+
if (!this.resources.has(resource.uri))
|
|
285
|
+
this.resources.set(resource.uri, upstream);
|
|
286
|
+
listed.push(resource);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
this.log({ type: "warn", upstream: upstream.name, reason: `resources/list failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return listed;
|
|
294
|
+
}
|
|
295
|
+
/** Resource contents come from outside the agent's control: they taint the session like an untrusted tool. */
|
|
296
|
+
async readResource(uri) {
|
|
297
|
+
let upstream = this.resources.get(uri);
|
|
298
|
+
if (!upstream) {
|
|
299
|
+
await this.listResources();
|
|
300
|
+
upstream = this.resources.get(uri) ?? (this.upstreams.length === 1 ? this.upstreams[0] : undefined);
|
|
301
|
+
}
|
|
302
|
+
if (!upstream)
|
|
303
|
+
throw new Error(`${PREFIX} Unknown resource: ${uri}`);
|
|
304
|
+
const result = await upstream.readResource(uri);
|
|
305
|
+
const texts = result.contents.flatMap((item) => ("text" in item && typeof item.text === "string" ? [item.text] : []));
|
|
306
|
+
const source = `resource:${uri}`;
|
|
307
|
+
if (this.interceptors.toolResultInjection?.enabled !== false) {
|
|
308
|
+
const scan = (0, guard_core_1.scanToolResult)(texts, this.resultAction);
|
|
309
|
+
if (scan.action === "block") {
|
|
310
|
+
this.log({ type: "blocked", tool: source, upstream: upstream.name, reason: scan.reason });
|
|
311
|
+
throw new Error(`${PREFIX} Resource blocked: ${scan.reason}`);
|
|
312
|
+
}
|
|
313
|
+
if (scan.action === "warn")
|
|
314
|
+
this.log({ type: "warn", tool: source, upstream: upstream.name, reason: scan.reason });
|
|
315
|
+
}
|
|
316
|
+
let piiFound = false;
|
|
317
|
+
const contents = result.contents.map((item) => {
|
|
318
|
+
if (!("text" in item) || typeof item.text !== "string")
|
|
319
|
+
return item;
|
|
320
|
+
const text = this.protect(item.text);
|
|
321
|
+
piiFound || (piiFound = text.piiFound);
|
|
322
|
+
return { ...item, text: text.value };
|
|
323
|
+
});
|
|
324
|
+
this.taint.observe(source, ["untrusted"], piiFound);
|
|
325
|
+
this.log({ type: "resource_read", tool: source, upstream: upstream.name });
|
|
326
|
+
return { ...result, contents };
|
|
327
|
+
}
|
|
328
|
+
async listPrompts() {
|
|
329
|
+
const listed = [];
|
|
330
|
+
for (const upstream of this.upstreams) {
|
|
331
|
+
try {
|
|
332
|
+
for (const prompt of await upstream.listPrompts()) {
|
|
333
|
+
const name = this.exposed(upstream, prompt.name);
|
|
334
|
+
this.prompts.set(name, { upstream, name: prompt.name });
|
|
335
|
+
listed.push({ ...prompt, name });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
this.log({ type: "warn", upstream: upstream.name, reason: `prompts/list failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return listed;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* A prompt template is text a server puts straight into the conversation, so
|
|
346
|
+
* it gets the same injection scan and PII masking as a tool result. It does
|
|
347
|
+
* not taint the session: the person picked it.
|
|
348
|
+
*/
|
|
349
|
+
async getPrompt(name, args) {
|
|
350
|
+
let route = this.prompts.get(name);
|
|
351
|
+
if (!route) {
|
|
352
|
+
await this.listPrompts();
|
|
353
|
+
route = this.prompts.get(name);
|
|
354
|
+
}
|
|
355
|
+
if (!route)
|
|
356
|
+
throw new Error(`${PREFIX} Unknown prompt: ${name}`);
|
|
357
|
+
const result = await route.upstream.getPrompt(route.name, args);
|
|
358
|
+
const source = `prompt:${name}`;
|
|
359
|
+
if (this.interceptors.toolResultInjection?.enabled !== false) {
|
|
360
|
+
const scan = (0, guard_core_1.scanToolResult)(result.messages, this.resultAction);
|
|
361
|
+
if (scan.action === "block") {
|
|
362
|
+
this.log({ type: "blocked", tool: source, upstream: route.upstream.name, reason: scan.reason });
|
|
363
|
+
throw new Error(`${PREFIX} Prompt blocked: ${scan.reason}`);
|
|
364
|
+
}
|
|
365
|
+
if (scan.action === "warn")
|
|
366
|
+
this.log({ type: "warn", tool: source, upstream: route.upstream.name, reason: scan.reason });
|
|
367
|
+
}
|
|
368
|
+
return { ...result, messages: this.protect(result.messages).value };
|
|
369
|
+
}
|
|
370
|
+
async close() {
|
|
371
|
+
this.audit.close();
|
|
372
|
+
await Promise.allSettled(this.upstreams.map((upstream) => upstream.close()));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
exports.Gateway = Gateway;
|
|
376
|
+
//# sourceMappingURL=gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../src/gateway/gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** The three legs of the lethal trifecta, plus destructive tools. */
|
|
2
|
+
export type Label = "untrusted" | "sensitive" | "egress" | "destructive";
|
|
3
|
+
export declare const LABELS: readonly ["untrusted", "sensitive", "egress", "destructive"];
|
|
4
|
+
/** Heuristic labels from the tool's name, description and schema (same rules as toxic-flow-auditor). */
|
|
5
|
+
export declare function labelTool(tool: {
|
|
6
|
+
name: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
inputSchema?: unknown;
|
|
9
|
+
}): Label[];
|
|
10
|
+
//# sourceMappingURL=labels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/gateway/labels.ts"],"names":[],"mappings":"AAEA,qEAAqE;AACrE,MAAM,MAAM,KAAK,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,aAAa,CAAC;AAEzE,eAAO,MAAM,MAAM,8DAA+D,CAAC;AASnF,wGAAwG;AACxG,wBAAgB,SAAS,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,EAAE,CAEtG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LABELS = void 0;
|
|
4
|
+
exports.labelTool = labelTool;
|
|
5
|
+
const guard_core_1 = require("@guardbee/guard-core");
|
|
6
|
+
exports.LABELS = ["untrusted", "sensitive", "egress", "destructive"];
|
|
7
|
+
const FROM_CAPABILITY = {
|
|
8
|
+
"untrusted-content": "untrusted",
|
|
9
|
+
"sensitive-data": "sensitive",
|
|
10
|
+
exfiltration: "egress",
|
|
11
|
+
destructive: "destructive",
|
|
12
|
+
};
|
|
13
|
+
/** Heuristic labels from the tool's name, description and schema (same rules as toxic-flow-auditor). */
|
|
14
|
+
function labelTool(tool) {
|
|
15
|
+
return (0, guard_core_1.classifyTool)(tool).map((capability) => FROM_CAPABILITY[capability]);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=labels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../src/gateway/labels.ts"],"names":[],"mappings":";;;AAeA,8BAEC;AAjBD,qDAAqE;AAKxD,QAAA,MAAM,GAAG,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,CAAU,CAAC;AAEnF,MAAM,eAAe,GAA8B;IACjD,mBAAmB,EAAE,WAAW;IAChC,gBAAgB,EAAE,WAAW;IAC7B,YAAY,EAAE,QAAQ;IACtB,WAAW,EAAE,aAAa;CAC3B,CAAC;AAEF,wGAAwG;AACxG,SAAgB,SAAS,CAAC,IAAmE;IAC3F,OAAO,IAAA,yBAAY,EAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Label } from "./labels.js";
|
|
2
|
+
export type PolicyAction = "allow" | "deny" | "mask" | "warn" | "approve";
|
|
3
|
+
export type ArgMatcher = string | number | boolean;
|
|
4
|
+
export interface PolicyRule {
|
|
5
|
+
id?: string;
|
|
6
|
+
match: {
|
|
7
|
+
/** Glob on the tool name the agent sees (`*` any run, `?` one character). */
|
|
8
|
+
tool?: string;
|
|
9
|
+
upstream?: string;
|
|
10
|
+
label?: Label;
|
|
11
|
+
session?: "clean" | "tainted";
|
|
12
|
+
/** Dotted path into the call arguments → expected value; strings are globs. */
|
|
13
|
+
args?: Record<string, ArgMatcher>;
|
|
14
|
+
};
|
|
15
|
+
action: PolicyAction;
|
|
16
|
+
/** For `mask`: also blank these JSON keys in the result (case-insensitive). */
|
|
17
|
+
mask?: {
|
|
18
|
+
fields: string[];
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface PolicyContext {
|
|
22
|
+
tool: string;
|
|
23
|
+
upstream: string;
|
|
24
|
+
labels: readonly Label[];
|
|
25
|
+
tainted: boolean;
|
|
26
|
+
args?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
export interface PolicyDecision {
|
|
29
|
+
action: PolicyAction;
|
|
30
|
+
/** The rule's `id`, or `rules[<index>]`; absent when the default applied. */
|
|
31
|
+
ruleId?: string;
|
|
32
|
+
maskFields?: string[];
|
|
33
|
+
}
|
|
34
|
+
export declare function globToRegExp(glob: string): RegExp;
|
|
35
|
+
/** Rules are checked top to bottom; the first match wins. No match → `defaultAction`. */
|
|
36
|
+
export declare function evaluatePolicy(rules: readonly PolicyRule[], defaultAction: PolicyAction, ctx: PolicyContext): PolicyDecision;
|
|
37
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/gateway/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1E,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEnD,MAAM,WAAW,UAAU;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE;QACL,6EAA6E;QAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,KAAK,CAAC;QACd,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC9B,+EAA+E;QAC/E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;KACnC,CAAC;IACF,MAAM,EAAE,YAAY,CAAC;IACrB,+EAA+E;IAC/E,IAAI,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMjD;AAgCD,yFAAyF;AACzF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,aAAa,EAAE,YAAY,EAC3B,GAAG,EAAE,aAAa,GACjB,cAAc,CAShB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.globToRegExp = globToRegExp;
|
|
4
|
+
exports.evaluatePolicy = evaluatePolicy;
|
|
5
|
+
function globToRegExp(glob) {
|
|
6
|
+
const source = glob
|
|
7
|
+
.split("")
|
|
8
|
+
.map((ch) => (ch === "*" ? ".*" : ch === "?" ? "." : ch.replace(/[.+^${}()|[\]\\]/g, "\\$&")))
|
|
9
|
+
.join("");
|
|
10
|
+
return new RegExp(`^${source}$`);
|
|
11
|
+
}
|
|
12
|
+
function valueAt(root, dotted) {
|
|
13
|
+
let current = root;
|
|
14
|
+
for (const key of dotted.split(".")) {
|
|
15
|
+
if (current === null || typeof current !== "object")
|
|
16
|
+
return undefined;
|
|
17
|
+
current = current[key];
|
|
18
|
+
}
|
|
19
|
+
return current;
|
|
20
|
+
}
|
|
21
|
+
function argMatches(actual, expected) {
|
|
22
|
+
if (typeof expected === "string") {
|
|
23
|
+
return (typeof actual === "string" || typeof actual === "number") && globToRegExp(expected).test(String(actual));
|
|
24
|
+
}
|
|
25
|
+
return actual === expected;
|
|
26
|
+
}
|
|
27
|
+
function matches(rule, ctx) {
|
|
28
|
+
const { tool, upstream, label, session, args } = rule.match;
|
|
29
|
+
if (tool !== undefined && !globToRegExp(tool).test(ctx.tool))
|
|
30
|
+
return false;
|
|
31
|
+
if (upstream !== undefined && upstream !== ctx.upstream)
|
|
32
|
+
return false;
|
|
33
|
+
if (label !== undefined && !ctx.labels.includes(label))
|
|
34
|
+
return false;
|
|
35
|
+
if (session !== undefined && (session === "tainted") !== ctx.tainted)
|
|
36
|
+
return false;
|
|
37
|
+
if (args !== undefined) {
|
|
38
|
+
for (const [dotted, expected] of Object.entries(args)) {
|
|
39
|
+
if (!argMatches(valueAt(ctx.args, dotted), expected))
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
/** Rules are checked top to bottom; the first match wins. No match → `defaultAction`. */
|
|
46
|
+
function evaluatePolicy(rules, defaultAction, ctx) {
|
|
47
|
+
for (const [index, rule] of rules.entries()) {
|
|
48
|
+
if (matches(rule, ctx)) {
|
|
49
|
+
const decision = { action: rule.action, ruleId: rule.id ?? `rules[${index}]` };
|
|
50
|
+
if (rule.mask)
|
|
51
|
+
decision.maskFields = rule.mask.fields;
|
|
52
|
+
return decision;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { action: defaultAction };
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/gateway/policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Label } from "./labels.js";
|
|
2
|
+
export type TaintMode = "strict" | "approve" | "warn" | "off";
|
|
3
|
+
export interface TaintSnapshot {
|
|
4
|
+
sawUntrusted: boolean;
|
|
5
|
+
sawSensitive: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Tracks which legs of the lethal trifecta a session has already touched.
|
|
9
|
+
* Flags only ever turn on: once the model has read untrusted content, every
|
|
10
|
+
* later decision it makes may be steered by that content.
|
|
11
|
+
*/
|
|
12
|
+
export declare class TaintTracker {
|
|
13
|
+
private untrustedFrom;
|
|
14
|
+
private sensitiveFrom;
|
|
15
|
+
get sawUntrusted(): boolean;
|
|
16
|
+
get sawSensitive(): boolean;
|
|
17
|
+
/** Both data legs are open: an egress call now would complete the trifecta. */
|
|
18
|
+
get tainted(): boolean;
|
|
19
|
+
/** Record what a tool result (or resource read) brought into the model's context. */
|
|
20
|
+
observe(source: string, labels: readonly Label[], piiFound: boolean): void;
|
|
21
|
+
completesTrifecta(labels: readonly Label[]): boolean;
|
|
22
|
+
describe(egressTool: string): string;
|
|
23
|
+
snapshot(): TaintSnapshot;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=taint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taint.d.ts","sourceRoot":"","sources":["../../src/gateway/taint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC;AAE9D,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,aAAa,CAAuB;IAE5C,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,+EAA+E;IAC/E,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,qFAAqF;IACrF,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI;IAK1E,iBAAiB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,OAAO;IAIpD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAOpC,QAAQ,IAAI,aAAa;CAG1B"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TaintTracker = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Tracks which legs of the lethal trifecta a session has already touched.
|
|
6
|
+
* Flags only ever turn on: once the model has read untrusted content, every
|
|
7
|
+
* later decision it makes may be steered by that content.
|
|
8
|
+
*/
|
|
9
|
+
class TaintTracker {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.untrustedFrom = null;
|
|
12
|
+
this.sensitiveFrom = null;
|
|
13
|
+
}
|
|
14
|
+
get sawUntrusted() {
|
|
15
|
+
return this.untrustedFrom !== null;
|
|
16
|
+
}
|
|
17
|
+
get sawSensitive() {
|
|
18
|
+
return this.sensitiveFrom !== null;
|
|
19
|
+
}
|
|
20
|
+
/** Both data legs are open: an egress call now would complete the trifecta. */
|
|
21
|
+
get tainted() {
|
|
22
|
+
return this.sawUntrusted && this.sawSensitive;
|
|
23
|
+
}
|
|
24
|
+
/** Record what a tool result (or resource read) brought into the model's context. */
|
|
25
|
+
observe(source, labels, piiFound) {
|
|
26
|
+
if (labels.includes("untrusted") && !this.untrustedFrom)
|
|
27
|
+
this.untrustedFrom = source;
|
|
28
|
+
if ((labels.includes("sensitive") || piiFound) && !this.sensitiveFrom)
|
|
29
|
+
this.sensitiveFrom = source;
|
|
30
|
+
}
|
|
31
|
+
completesTrifecta(labels) {
|
|
32
|
+
return labels.includes("egress") && this.tainted;
|
|
33
|
+
}
|
|
34
|
+
describe(egressTool) {
|
|
35
|
+
return (`Toxic flow (lethal trifecta): untrusted content from "${this.untrustedFrom}" and ` +
|
|
36
|
+
`sensitive data from "${this.sensitiveFrom}" are in this session, and "${egressTool}" can send data out`);
|
|
37
|
+
}
|
|
38
|
+
snapshot() {
|
|
39
|
+
return { sawUntrusted: this.sawUntrusted, sawSensitive: this.sawSensitive };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.TaintTracker = TaintTracker;
|
|
43
|
+
//# sourceMappingURL=taint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taint.js","sourceRoot":"","sources":["../../src/gateway/taint.ts"],"names":[],"mappings":";;;AASA;;;;GAIG;AACH,MAAa,YAAY;IAAzB;QACU,kBAAa,GAAkB,IAAI,CAAC;QACpC,kBAAa,GAAkB,IAAI,CAAC;IAmC9C,CAAC;IAjCC,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC;IACrC,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC;IACrC,CAAC;IAED,+EAA+E;IAC/E,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,CAAC;IAChD,CAAC;IAED,qFAAqF;IACrF,OAAO,CAAC,MAAc,EAAE,MAAwB,EAAE,QAAiB;QACjE,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa;YAAE,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC;QACrF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa;YAAE,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC;IACrG,CAAC;IAED,iBAAiB,CAAC,MAAwB;QACxC,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC;IACnD,CAAC;IAED,QAAQ,CAAC,UAAkB;QACzB,OAAO,CACL,yDAAyD,IAAI,CAAC,aAAa,QAAQ;YACnF,wBAAwB,IAAI,CAAC,aAAa,+BAA+B,UAAU,qBAAqB,CACzG,CAAC;IACJ,CAAC;IAED,QAAQ;QACN,OAAO,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;IAC9E,CAAC;CACF;AArCD,oCAqCC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-scoped reversible PII tokens. The model sees `<pii:tc_kimlik:7f3a9b21>`
|
|
3
|
+
* instead of the value, and can still pass that token to another tool; the
|
|
4
|
+
* gateway puts the real value back on the way to the upstream. The table lives
|
|
5
|
+
* only in memory and dies with the session.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PiiVault {
|
|
8
|
+
private readonly byToken;
|
|
9
|
+
private readonly byValue;
|
|
10
|
+
get size(): number;
|
|
11
|
+
/** Replace validated PII in any JSON-like value with tokens; the same value always gets the same token. */
|
|
12
|
+
tokenize<T>(value: T): T;
|
|
13
|
+
/** Put real values back for tokens this session issued; unknown tokens stay as they are. */
|
|
14
|
+
detokenize<T>(value: T): T;
|
|
15
|
+
/** True if the value carries a token this session issued. */
|
|
16
|
+
holdsTokens(value: unknown): boolean;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=tokens.d.ts.map
|