@exfil/canary 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/LICENSE +21 -0
- package/README.md +387 -0
- package/SECURITY.md +50 -0
- package/dist/entities.d.ts +43 -0
- package/dist/entities.d.ts.map +1 -0
- package/dist/entities.js +218 -0
- package/dist/entities.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +183 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +29 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +50 -0
- package/dist/logger.js.map +1 -0
- package/dist/persistence.d.ts +48 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +296 -0
- package/dist/persistence.js.map +1 -0
- package/dist/proxy/DownstreamManager.d.ts +55 -0
- package/dist/proxy/DownstreamManager.d.ts.map +1 -0
- package/dist/proxy/DownstreamManager.js +110 -0
- package/dist/proxy/DownstreamManager.js.map +1 -0
- package/dist/proxy/ProxyServer.d.ts +60 -0
- package/dist/proxy/ProxyServer.d.ts.map +1 -0
- package/dist/proxy/ProxyServer.js +480 -0
- package/dist/proxy/ProxyServer.js.map +1 -0
- package/dist/proxy/auditor/DualAuditor.d.ts +27 -0
- package/dist/proxy/auditor/DualAuditor.d.ts.map +1 -0
- package/dist/proxy/auditor/DualAuditor.js +44 -0
- package/dist/proxy/auditor/DualAuditor.js.map +1 -0
- package/dist/proxy/auditor/LLMAuditor.d.ts +16 -0
- package/dist/proxy/auditor/LLMAuditor.d.ts.map +1 -0
- package/dist/proxy/auditor/LLMAuditor.js +221 -0
- package/dist/proxy/auditor/LLMAuditor.js.map +1 -0
- package/dist/proxy/auditor/types.d.ts +54 -0
- package/dist/proxy/auditor/types.d.ts.map +1 -0
- package/dist/proxy/auditor/types.js +11 -0
- package/dist/proxy/auditor/types.js.map +1 -0
- package/dist/proxy/types.d.ts +71 -0
- package/dist/proxy/types.d.ts.map +1 -0
- package/dist/proxy/types.js +8 -0
- package/dist/proxy/types.js.map +1 -0
- package/dist/scanner.d.ts +37 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +57 -0
- package/dist/scanner.js.map +1 -0
- package/dist/server.d.ts +59 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +711 -0
- package/dist/server.js.map +1 -0
- package/dist/simhash.d.ts +65 -0
- package/dist/simhash.d.ts.map +1 -0
- package/dist/simhash.js +151 -0
- package/dist/simhash.js.map +1 -0
- package/dist/state.d.ts +86 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +136 -0
- package/dist/state.js.map +1 -0
- package/dist/token.d.ts +70 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +146 -0
- package/dist/token.js.map +1 -0
- package/dist/types.d.ts +190 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
- package/proxy.example.json +53 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProxyServer — transparent canary-token proxy for MCP tool calls.
|
|
3
|
+
*
|
|
4
|
+
* The agent connects to ProxyServer as its only MCP server. ProxyServer:
|
|
5
|
+
*
|
|
6
|
+
* 1. Aggregates all tools from downstream servers (namespaced as `{id}__{tool}`).
|
|
7
|
+
* 2. On every tool call:
|
|
8
|
+
* a. Serialises the arguments and scans for active canary sequences.
|
|
9
|
+
* → Blocks (or logs/alerts) if a token is found in outbound args.
|
|
10
|
+
* b. Forwards the call to the downstream server.
|
|
11
|
+
* c. Walks the response, wrapping every text string ≥ MIN_WRAP_CHARS
|
|
12
|
+
* with a freshly generated canary token registered in session state.
|
|
13
|
+
* 3. Exposes a single built-in tool `canary__get_report` for operator use.
|
|
14
|
+
*
|
|
15
|
+
* The agent never needs to explicitly call wrap_content or scan_outbound —
|
|
16
|
+
* interception happens transparently on every I/O crossing.
|
|
17
|
+
*/
|
|
18
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
19
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
20
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
|
|
21
|
+
import { generateTokenSequence, generateTokenId, embedToken, } from '../token.js';
|
|
22
|
+
import { registerToken, getActiveTokens, recordLeakage, pruneExpiredTokens, } from '../state.js';
|
|
23
|
+
import { scanForAllTokens } from '../scanner.js';
|
|
24
|
+
import { extractEntities, scanForEntityValues } from '../entities.js';
|
|
25
|
+
import { computeSimHash, isSimilar } from '../simhash.js';
|
|
26
|
+
import { persistState } from '../persistence.js';
|
|
27
|
+
import { log } from '../logger.js';
|
|
28
|
+
import { DownstreamManager } from './DownstreamManager.js';
|
|
29
|
+
import { DualAuditor } from './auditor/DualAuditor.js';
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Constants
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
/** Minimum character length to bother wrapping a text response. */
|
|
34
|
+
const MIN_WRAP_CHARS = 50;
|
|
35
|
+
/** Management key env var (same convention as CanaryMcpServer). */
|
|
36
|
+
const MGMT_KEY_ENV = 'CANARY_MCP_MGMT_KEY';
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Tool allowlist helpers
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
/**
|
|
41
|
+
* Returns true if `toolName` is permitted by `allowlist`.
|
|
42
|
+
*
|
|
43
|
+
* Entries ending with `*` are prefix wildcards:
|
|
44
|
+
* "filesystem__*" matches any tool whose name starts with "filesystem__".
|
|
45
|
+
* "*" matches any tool name.
|
|
46
|
+
* All other entries are exact matches.
|
|
47
|
+
*/
|
|
48
|
+
function isToolAllowed(toolName, allowlist) {
|
|
49
|
+
for (const entry of allowlist) {
|
|
50
|
+
if (entry.endsWith('*')) {
|
|
51
|
+
const prefix = entry.slice(0, -1);
|
|
52
|
+
if (toolName.startsWith(prefix))
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
if (toolName === entry)
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Domain allowlist helpers
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
/** Extracts all unique hostnames from URLs found in serialised args. */
|
|
66
|
+
function extractHostnames(dataStr) {
|
|
67
|
+
const seen = new Set();
|
|
68
|
+
const re = /https?:\/\/([^/"'\s<>?#]+)/g;
|
|
69
|
+
let m;
|
|
70
|
+
while ((m = re.exec(dataStr)) !== null) {
|
|
71
|
+
const raw = m[1];
|
|
72
|
+
// Strip port if present.
|
|
73
|
+
const hostname = raw.split(':')[0].toLowerCase();
|
|
74
|
+
seen.add(hostname);
|
|
75
|
+
}
|
|
76
|
+
return [...seen];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Returns true if `hostname` is permitted by `allowlist`.
|
|
80
|
+
* Entries starting with `*.` match any direct subdomain.
|
|
81
|
+
* All matching is case-insensitive.
|
|
82
|
+
*/
|
|
83
|
+
function isDomainAllowed(hostname, allowlist) {
|
|
84
|
+
const h = hostname.toLowerCase();
|
|
85
|
+
for (const entry of allowlist) {
|
|
86
|
+
const e = entry.toLowerCase();
|
|
87
|
+
if (e.startsWith('*.')) {
|
|
88
|
+
// *.github.com matches api.github.com but not github.com itself.
|
|
89
|
+
const parent = e.slice(2);
|
|
90
|
+
if (h.endsWith('.' + parent))
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
if (h === e)
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// ProxyServer
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
export class ProxyServer {
|
|
104
|
+
upstream;
|
|
105
|
+
downstream;
|
|
106
|
+
config;
|
|
107
|
+
state;
|
|
108
|
+
/** Accumulated tool results for auditor context (ring-buffered to 50). */
|
|
109
|
+
storedResults = [];
|
|
110
|
+
resultCounter = 0;
|
|
111
|
+
/** Dual-auditor instance, or null if not configured. */
|
|
112
|
+
auditor;
|
|
113
|
+
/** Number of detections made by the LLM auditor layer (for report). */
|
|
114
|
+
auditorDetections = 0;
|
|
115
|
+
constructor(state, config, proxyConfig) {
|
|
116
|
+
this.state = state;
|
|
117
|
+
this.config = config;
|
|
118
|
+
this.downstream = new DownstreamManager();
|
|
119
|
+
this.upstream = new Server({ name: 'exfil/canary-proxy', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
120
|
+
// Initialise dual auditor if configured.
|
|
121
|
+
if (proxyConfig.auditors && proxyConfig.auditors.length === 2) {
|
|
122
|
+
this.auditor = new DualAuditor(proxyConfig.auditors);
|
|
123
|
+
log('info', 'ProxyServer: dual-auditor enabled.', {
|
|
124
|
+
providers: proxyConfig.auditors.map((a) => a.provider),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.auditor = null;
|
|
129
|
+
}
|
|
130
|
+
// Stash proxy config for later connect() call.
|
|
131
|
+
this._proxyConfig = proxyConfig;
|
|
132
|
+
this.registerHandlers();
|
|
133
|
+
}
|
|
134
|
+
_proxyConfig;
|
|
135
|
+
// -------------------------------------------------------------------------
|
|
136
|
+
// Lifecycle
|
|
137
|
+
// -------------------------------------------------------------------------
|
|
138
|
+
async start() {
|
|
139
|
+
// Connect to downstream servers first.
|
|
140
|
+
await this.downstream.connect(this._proxyConfig);
|
|
141
|
+
const transport = new StdioServerTransport();
|
|
142
|
+
await this.upstream.connect(transport);
|
|
143
|
+
log('info', 'exfil/canary-proxy started.', { session_id: this.state.session_id });
|
|
144
|
+
}
|
|
145
|
+
async shutdown() {
|
|
146
|
+
log('info', 'Shutting down exfil/canary-proxy.');
|
|
147
|
+
await persistState(this.state, this.config).catch(() => { });
|
|
148
|
+
await this.downstream.disconnect();
|
|
149
|
+
await this.upstream.close();
|
|
150
|
+
}
|
|
151
|
+
sweepExpiredTokens() {
|
|
152
|
+
const removed = pruneExpiredTokens(this.state);
|
|
153
|
+
if (removed > 0) {
|
|
154
|
+
log('debug', `Expiry sweep removed ${removed} token(s).`);
|
|
155
|
+
persistState(this.state, this.config).catch(() => { });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// -------------------------------------------------------------------------
|
|
159
|
+
// Handler registration
|
|
160
|
+
// -------------------------------------------------------------------------
|
|
161
|
+
registerHandlers() {
|
|
162
|
+
this.upstream.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
163
|
+
const downstreamTools = this.downstream.listAllTools().map((dt) => dt.tool);
|
|
164
|
+
return {
|
|
165
|
+
tools: [
|
|
166
|
+
...downstreamTools,
|
|
167
|
+
{
|
|
168
|
+
name: 'canary__get_report',
|
|
169
|
+
description: 'Returns the canary token session report: active tokens, detected leakage events, and action history.',
|
|
170
|
+
inputSchema: {
|
|
171
|
+
type: 'object',
|
|
172
|
+
properties: {
|
|
173
|
+
mgmt_key: {
|
|
174
|
+
type: 'string',
|
|
175
|
+
description: 'Management key (required if CANARY_MCP_MGMT_KEY is set).',
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
required: [],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
this.upstream.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
185
|
+
const { name, arguments: rawArgs = {} } = request.params;
|
|
186
|
+
const args = rawArgs;
|
|
187
|
+
if (name === 'canary__get_report') {
|
|
188
|
+
return this.handleGetReport(args);
|
|
189
|
+
}
|
|
190
|
+
// ── Tool allowlist ─────────────────────────────────────────────────────
|
|
191
|
+
const toolAllowlist = this._proxyConfig.allowed_tools;
|
|
192
|
+
if (toolAllowlist && toolAllowlist.length > 0 && !isToolAllowed(name, toolAllowlist)) {
|
|
193
|
+
log('warn', 'Proxy: tool call blocked by allowed_tools.', { name });
|
|
194
|
+
throw new McpError(ErrorCode.MethodNotFound, `Tool "${name}" is not in the allowed_tools list.`);
|
|
195
|
+
}
|
|
196
|
+
// Resolve which downstream server handles this tool.
|
|
197
|
+
const resolved = this.downstream.resolveTool(name);
|
|
198
|
+
if (!resolved) {
|
|
199
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: "${name}"`);
|
|
200
|
+
}
|
|
201
|
+
// ── Inbound scan: args → check for canary exfiltration attempt ─────────
|
|
202
|
+
await this.scanInboundArgs(args, resolved.server_id, resolved.original_name);
|
|
203
|
+
// ── Forward to downstream ──────────────────────────────────────────────
|
|
204
|
+
let response;
|
|
205
|
+
try {
|
|
206
|
+
response = await this.downstream.callTool(resolved.server_id, resolved.original_name, args);
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
// Propagate downstream errors as MCP errors.
|
|
210
|
+
throw new McpError(ErrorCode.InternalError, `Downstream error from "${name}": ${err.message}`);
|
|
211
|
+
}
|
|
212
|
+
// ── Wrap text responses with canary tokens ─────────────────────────────
|
|
213
|
+
const wrappedContent = await this.wrapResponseContent(response.content, resolved.server_id, resolved.original_name);
|
|
214
|
+
return { content: wrappedContent, isError: response.isError };
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
// -------------------------------------------------------------------------
|
|
218
|
+
// Inbound scan (outbound args from the agent → downstream)
|
|
219
|
+
// -------------------------------------------------------------------------
|
|
220
|
+
/**
|
|
221
|
+
* Scans serialised tool arguments for any active canary tokens.
|
|
222
|
+
* Fires resolveAction (which may throw McpError to halt) if a token is found.
|
|
223
|
+
*/
|
|
224
|
+
async scanInboundArgs(args, targetServer, targetTool) {
|
|
225
|
+
const dataStr = JSON.stringify(args);
|
|
226
|
+
// ── Domain allowlist (fail-closed) ───────────────────────────────────────
|
|
227
|
+
// Absent or empty = block all outbound URLs.
|
|
228
|
+
// Only URLs whose hostname matches an entry are allowed through.
|
|
229
|
+
const allowlist = this._proxyConfig.allowed_domains ?? [];
|
|
230
|
+
const hostnames = extractHostnames(dataStr);
|
|
231
|
+
for (const hostname of hostnames) {
|
|
232
|
+
if (!isDomainAllowed(hostname, allowlist)) {
|
|
233
|
+
log('warn', 'Proxy: outbound call to non-allowlisted domain blocked.', {
|
|
234
|
+
hostname,
|
|
235
|
+
target_server: targetServer,
|
|
236
|
+
target_tool: targetTool,
|
|
237
|
+
});
|
|
238
|
+
throw new McpError(ErrorCode.InternalError, `Outbound domain "${hostname}" is not in the allowed_domains list.`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const activeTokens = getActiveTokens(this.state).filter((t) => t.sequence !== '');
|
|
242
|
+
if (activeTokens.length === 0)
|
|
243
|
+
return;
|
|
244
|
+
const resolvedTokenIds = new Set();
|
|
245
|
+
// ── Unicode marker scan ──────────────────────────────────────────────────
|
|
246
|
+
const results = scanForAllTokens(dataStr, activeTokens);
|
|
247
|
+
for (const result of results) {
|
|
248
|
+
if (result.found) {
|
|
249
|
+
log('warn', 'Proxy inbound scan: canary token found in tool args.', {
|
|
250
|
+
token_id: result.token_id,
|
|
251
|
+
target_server: targetServer,
|
|
252
|
+
target_tool: targetTool,
|
|
253
|
+
});
|
|
254
|
+
await this.resolveAction(result.token_id, 'scan_outbound', targetServer, targetTool);
|
|
255
|
+
resolvedTokenIds.add(result.token_id);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// ── v1.1: Named entity scan ──────────────────────────────────────────────
|
|
259
|
+
for (const token of activeTokens) {
|
|
260
|
+
if (token.entity_canaries.length === 0)
|
|
261
|
+
continue;
|
|
262
|
+
const matched = scanForEntityValues(dataStr, token.entity_canaries);
|
|
263
|
+
if (matched.length > 0 && !resolvedTokenIds.has(token.token_id)) {
|
|
264
|
+
log('warn', 'Proxy inbound scan: entity canary found in tool args.', {
|
|
265
|
+
token_id: token.token_id,
|
|
266
|
+
entity_types: matched.map((e) => e.entity_type),
|
|
267
|
+
target_server: targetServer,
|
|
268
|
+
target_tool: targetTool,
|
|
269
|
+
});
|
|
270
|
+
await this.resolveAction(token.token_id, 'scan_outbound', targetServer, targetTool);
|
|
271
|
+
resolvedTokenIds.add(token.token_id);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// ── v1.6: SimHash semantic scan ──────────────────────────────────────────
|
|
275
|
+
const dataSimhash = computeSimHash(dataStr);
|
|
276
|
+
if (dataSimhash !== null) {
|
|
277
|
+
for (const token of activeTokens) {
|
|
278
|
+
if (token.simhash === null || resolvedTokenIds.has(token.token_id))
|
|
279
|
+
continue;
|
|
280
|
+
if (isSimilar(token.simhash, dataSimhash)) {
|
|
281
|
+
log('warn', 'Proxy inbound scan: semantic match in tool args.', {
|
|
282
|
+
token_id: token.token_id,
|
|
283
|
+
target_server: targetServer,
|
|
284
|
+
target_tool: targetTool,
|
|
285
|
+
});
|
|
286
|
+
await this.resolveAction(token.token_id, 'scan_outbound', targetServer, targetTool);
|
|
287
|
+
resolvedTokenIds.add(token.token_id);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// ── v2.0: Dual-LLM auditor ───────────────────────────────────────────────
|
|
292
|
+
if (this.auditor !== null && this.storedResults.length > 0) {
|
|
293
|
+
let outcome;
|
|
294
|
+
try {
|
|
295
|
+
outcome = await this.auditor.check(this.storedResults, dataStr);
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
// Should not happen — DualAuditor catches internally — but be safe.
|
|
299
|
+
log('warn', `Proxy: auditor threw unexpectedly: ${err.message} — applying timeout action.`);
|
|
300
|
+
const timeoutAction = this._proxyConfig.audit_timeout_action ?? 'block';
|
|
301
|
+
if (timeoutAction === 'block') {
|
|
302
|
+
outcome = { clean: false, results: [] };
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (!outcome.clean) {
|
|
309
|
+
this.auditorDetections++;
|
|
310
|
+
// Use a sentinel token_id since the auditor fires without a specific token.
|
|
311
|
+
const derivedCallId = outcome.results.find((r) => r.result.derived_call_id)?.result.derived_call_id;
|
|
312
|
+
log('warn', 'Proxy inbound scan: auditor DERIVED verdict.', {
|
|
313
|
+
derived_call_id: derivedCallId,
|
|
314
|
+
target_server: targetServer,
|
|
315
|
+
target_tool: targetTool,
|
|
316
|
+
});
|
|
317
|
+
await this.resolveAuditorAction(targetServer, targetTool, derivedCallId);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// -------------------------------------------------------------------------
|
|
322
|
+
// Response wrapping
|
|
323
|
+
// -------------------------------------------------------------------------
|
|
324
|
+
/**
|
|
325
|
+
* Walks the content array from a downstream response.
|
|
326
|
+
* For every text item with ≥ MIN_WRAP_CHARS characters, embeds a new
|
|
327
|
+
* canary token and registers it in session state.
|
|
328
|
+
*
|
|
329
|
+
* Non-text content (images, etc.) is passed through unchanged.
|
|
330
|
+
*/
|
|
331
|
+
async wrapResponseContent(content, sourceServer, sourceTool) {
|
|
332
|
+
const wrapped = [];
|
|
333
|
+
for (const item of content) {
|
|
334
|
+
if (item !== null &&
|
|
335
|
+
typeof item === 'object' &&
|
|
336
|
+
item['type'] === 'text' &&
|
|
337
|
+
typeof item['text'] === 'string') {
|
|
338
|
+
const text = item.text;
|
|
339
|
+
if (text.length >= MIN_WRAP_CHARS) {
|
|
340
|
+
const sequence = generateTokenSequence();
|
|
341
|
+
const token_id = generateTokenId();
|
|
342
|
+
// Store raw text for the LLM auditor before embedding the token.
|
|
343
|
+
const call_id = `r${++this.resultCounter}`;
|
|
344
|
+
this.storedResults.push({ call_id, source_server: sourceServer, source_tool: sourceTool, text });
|
|
345
|
+
// Ring-buffer: keep only the 50 most recent results.
|
|
346
|
+
if (this.storedResults.length > 50) {
|
|
347
|
+
this.storedResults.shift();
|
|
348
|
+
}
|
|
349
|
+
const entity_canaries = extractEntities(text);
|
|
350
|
+
const simhash = computeSimHash(text);
|
|
351
|
+
registerToken(this.state, {
|
|
352
|
+
token_id,
|
|
353
|
+
sequence,
|
|
354
|
+
source_type: 'tool_result',
|
|
355
|
+
source_server: sourceServer,
|
|
356
|
+
source_tool: sourceTool,
|
|
357
|
+
embed_position: 'suffix',
|
|
358
|
+
ttl_ms: this.config.token_ttl_seconds * 1000,
|
|
359
|
+
entity_canaries,
|
|
360
|
+
simhash,
|
|
361
|
+
});
|
|
362
|
+
const wrappedText = embedToken(text, sequence, 'suffix');
|
|
363
|
+
log('debug', 'Proxy: wrapped response text.', { token_id, source_server: sourceServer, source_tool: sourceTool });
|
|
364
|
+
wrapped.push({ type: 'text', text: wrappedText });
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
wrapped.push(item);
|
|
369
|
+
}
|
|
370
|
+
// Persist after potentially many token registrations.
|
|
371
|
+
await persistState(this.state, this.config).catch(() => { });
|
|
372
|
+
return wrapped;
|
|
373
|
+
}
|
|
374
|
+
// -------------------------------------------------------------------------
|
|
375
|
+
// Built-in tool: canary__get_report
|
|
376
|
+
// -------------------------------------------------------------------------
|
|
377
|
+
handleGetReport(args) {
|
|
378
|
+
const requiredKey = process.env[MGMT_KEY_ENV];
|
|
379
|
+
if (requiredKey) {
|
|
380
|
+
const provided = args['mgmt_key'];
|
|
381
|
+
if (typeof provided !== 'string' || provided !== requiredKey) {
|
|
382
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid or missing mgmt_key.');
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const now = Date.now();
|
|
386
|
+
const allTokens = [...this.state.tokens.values()];
|
|
387
|
+
const activeCount = allTokens.filter((t) => t.expires_at > now).length;
|
|
388
|
+
const expiredCount = allTokens.length - activeCount;
|
|
389
|
+
const leakedCount = allTokens.filter((t) => t.leaked).length;
|
|
390
|
+
const allEvents = [...this.state.leakage_events.values()];
|
|
391
|
+
const tokenSummaries = allTokens.map((t) => ({
|
|
392
|
+
token_id: t.token_id,
|
|
393
|
+
source_type: t.source_type,
|
|
394
|
+
source_server: t.source_server,
|
|
395
|
+
source_tool: t.source_tool,
|
|
396
|
+
embed_position: t.embed_position,
|
|
397
|
+
created_at: t.created_at,
|
|
398
|
+
expires_at: t.expires_at,
|
|
399
|
+
status: t.expires_at > now ? 'active' : 'expired',
|
|
400
|
+
leaked: t.leaked,
|
|
401
|
+
leakage_event_ids: t.leakage_event_ids,
|
|
402
|
+
// sequence deliberately omitted (RC-5).
|
|
403
|
+
}));
|
|
404
|
+
return {
|
|
405
|
+
content: [
|
|
406
|
+
{
|
|
407
|
+
type: 'text',
|
|
408
|
+
text: JSON.stringify({
|
|
409
|
+
session_id: this.state.session_id,
|
|
410
|
+
created_at: this.state.created_at,
|
|
411
|
+
token_counter: this.state.token_counter,
|
|
412
|
+
proxy_mode: true,
|
|
413
|
+
summary: {
|
|
414
|
+
total_tokens: allTokens.length,
|
|
415
|
+
active_tokens: activeCount,
|
|
416
|
+
expired_tokens: expiredCount,
|
|
417
|
+
leaked_tokens: leakedCount,
|
|
418
|
+
total_leakage_events: allEvents.length,
|
|
419
|
+
auditor_detections: this.auditorDetections,
|
|
420
|
+
auditor_enabled: this.auditor !== null,
|
|
421
|
+
domain_allowlist: this._proxyConfig.allowed_domains ?? [],
|
|
422
|
+
tool_allowlist: this._proxyConfig.allowed_tools ?? null,
|
|
423
|
+
},
|
|
424
|
+
tokens: tokenSummaries,
|
|
425
|
+
leakage_events: allEvents,
|
|
426
|
+
}),
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
// -------------------------------------------------------------------------
|
|
432
|
+
// Action dispatch helper
|
|
433
|
+
// -------------------------------------------------------------------------
|
|
434
|
+
/**
|
|
435
|
+
* Fires response_mode action for an auditor DERIVED verdict.
|
|
436
|
+
* Unlike resolveAction, there may be no specific token_id — the auditor
|
|
437
|
+
* detected derived content without matching a canary marker.
|
|
438
|
+
*/
|
|
439
|
+
async resolveAuditorAction(target_server, target_tool, derived_call_id) {
|
|
440
|
+
const mode = this.config.response_mode;
|
|
441
|
+
const action_taken = mode === 'halt' ? 'halted'
|
|
442
|
+
: mode === 'alert' ? 'alerted'
|
|
443
|
+
: 'logged';
|
|
444
|
+
// Record as a leakage event with a sentinel token_id.
|
|
445
|
+
recordLeakage(this.state, {
|
|
446
|
+
token_id: `auditor:${derived_call_id ?? 'unknown'}`,
|
|
447
|
+
detection_method: 'scan_outbound',
|
|
448
|
+
target_server,
|
|
449
|
+
target_tool,
|
|
450
|
+
action_taken,
|
|
451
|
+
webhook_attempted: false,
|
|
452
|
+
webhook_delivered: null,
|
|
453
|
+
});
|
|
454
|
+
await persistState(this.state, this.config).catch(() => { });
|
|
455
|
+
if (mode === 'halt') {
|
|
456
|
+
throw new McpError(ErrorCode.InternalError, 'Auditor detected derived content in outbound data. Operation halted.');
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
async resolveAction(token_id, detection_method, target_server, target_tool) {
|
|
460
|
+
const mode = this.config.response_mode;
|
|
461
|
+
const action_taken = mode === 'halt' ? 'halted'
|
|
462
|
+
: mode === 'alert' ? 'alerted'
|
|
463
|
+
: 'logged';
|
|
464
|
+
recordLeakage(this.state, {
|
|
465
|
+
token_id,
|
|
466
|
+
detection_method,
|
|
467
|
+
target_server,
|
|
468
|
+
target_tool,
|
|
469
|
+
action_taken,
|
|
470
|
+
webhook_attempted: false,
|
|
471
|
+
webhook_delivered: null,
|
|
472
|
+
});
|
|
473
|
+
await persistState(this.state, this.config).catch(() => { });
|
|
474
|
+
if (mode === 'halt') {
|
|
475
|
+
throw new McpError(ErrorCode.InternalError, 'Integrity marker detected in outbound data. Operation halted.');
|
|
476
|
+
}
|
|
477
|
+
return action_taken;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
//# sourceMappingURL=ProxyServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProxyServer.js","sourceRoot":"","sources":["../../src/proxy/ProxyServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,SAAS,EACT,QAAQ,GACT,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,eAAe,EACf,aAAa,EACb,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,mEAAmE;AACnE,MAAM,cAAc,GAAG,EAAE,CAAC;AAE1B,mEAAmE;AACnE,MAAM,YAAY,GAAG,qBAAqB,CAAC;AAE3C,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,QAAgB,EAAE,SAAmB;IAC1D,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAClC,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,IAAI,QAAQ,KAAK,KAAK;gBAAE,OAAO,IAAI,CAAC;QACtC,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E,wEAAwE;AACxE,SAAS,gBAAgB,CAAC,OAAe;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,EAAE,GAAG,6BAA6B,CAAC;IACzC,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QAClB,yBAAyB;QACzB,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC;QAClD,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,QAAgB,EAAE,SAAmB;IAC5D,MAAM,CAAC,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAC9B,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,iEAAiE;YACjE,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,GAAG,MAAM,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,MAAM,OAAO,WAAW;IACL,QAAQ,CAAS;IACjB,UAAU,CAAoB;IAC9B,MAAM,CAAe;IAC9B,KAAK,CAAe;IAC5B,0EAA0E;IAClE,aAAa,GAAuB,EAAE,CAAC;IACvC,aAAa,GAAG,CAAC,CAAC;IAC1B,wDAAwD;IACvC,OAAO,CAAqB;IAC7C,uEAAuE;IAC/D,iBAAiB,GAAG,CAAC,CAAC;IAE9B,YAAY,KAAmB,EAAE,MAAoB,EAAE,WAAwB;QAC7E,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,IAAI,iBAAiB,EAAE,CAAC;QAE1C,IAAI,CAAC,QAAQ,GAAG,IAAI,MAAM,CACxB,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,EAChD,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAC;QAEF,yCAAyC;QACzC,IAAI,WAAW,CAAC,QAAQ,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9D,IAAI,CAAC,OAAO,GAAG,IAAI,WAAW,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YACrD,GAAG,CAAC,MAAM,EAAE,oCAAoC,EAAE;gBAChD,SAAS,EAAE,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;aACvD,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;QAED,+CAA+C;QAC/C,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAEO,YAAY,CAAc;IAElC,4EAA4E;IAC5E,YAAY;IACZ,4EAA4E;IAE5E,KAAK,CAAC,KAAK;QACT,uCAAuC;QACvC,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAEjD,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC7C,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACvC,GAAG,CAAC,MAAM,EAAE,6BAA6B,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IACpF,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,GAAG,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;QACjD,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC5D,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,kBAAkB;QAChB,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,GAAG,CAAC,OAAO,EAAE,wBAAwB,OAAO,YAAY,CAAC,CAAC;YAC1D,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,uBAAuB;IACvB,4EAA4E;IAEpE,gBAAgB;QACtB,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;YACjE,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;YAE5E,OAAO;gBACL,KAAK,EAAE;oBACL,GAAG,eAAe;oBAClB;wBACE,IAAI,EAAE,oBAAoB;wBAC1B,WAAW,EACT,sGAAsG;wBACxG,WAAW,EAAE;4BACX,IAAI,EAAE,QAAQ;4BACd,UAAU,EAAE;gCACV,QAAQ,EAAE;oCACR,IAAI,EAAE,QAAQ;oCACd,WAAW,EAAE,0DAA0D;iCACxE;6BACF;4BACD,QAAQ,EAAE,EAAE;yBACb;qBACF;iBACF;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACvE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;YACzD,MAAM,IAAI,GAAG,OAAkC,CAAC;YAEhD,IAAI,IAAI,KAAK,oBAAoB,EAAE,CAAC;gBAClC,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YACpC,CAAC;YAED,0EAA0E;YAC1E,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC;YACtD,IAAI,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,CAAC;gBACrF,GAAG,CAAC,MAAM,EAAE,4CAA4C,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpE,MAAM,IAAI,QAAQ,CAChB,SAAS,CAAC,cAAc,EACxB,SAAS,IAAI,qCAAqC,CACnD,CAAC;YACJ,CAAC;YAED,qDAAqD;YACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACnD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,IAAI,GAAG,CAAC,CAAC;YAC1E,CAAC;YAED,0EAA0E;YAC1E,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;YAE7E,0EAA0E;YAC1E,IAAI,QAAmD,CAAC;YACxD,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CACvC,QAAQ,CAAC,SAAS,EAClB,QAAQ,CAAC,aAAa,EACtB,IAAI,CACL,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,6CAA6C;gBAC7C,MAAM,IAAI,QAAQ,CAChB,SAAS,CAAC,aAAa,EACvB,0BAA0B,IAAI,MAAO,GAAa,CAAC,OAAO,EAAE,CAC7D,CAAC;YACJ,CAAC;YAED,0EAA0E;YAC1E,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,mBAAmB,CACnD,QAAQ,CAAC,OAAO,EAChB,QAAQ,CAAC,SAAS,EAClB,QAAQ,CAAC,aAAa,CACvB,CAAC;YAEF,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,4EAA4E;IAC5E,2DAA2D;IAC3D,4EAA4E;IAE5E;;;OAGG;IACK,KAAK,CAAC,eAAe,CAC3B,IAA6B,EAC7B,YAAoB,EACpB,UAAkB;QAElB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAErC,4EAA4E;QAC5E,6CAA6C;QAC7C,iEAAiE;QACjE,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,IAAI,EAAE,CAAC;QAC1D,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAC5C,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC;gBAC1C,GAAG,CAAC,MAAM,EAAE,yDAAyD,EAAE;oBACrE,QAAQ;oBACR,aAAa,EAAE,YAAY;oBAC3B,WAAW,EAAE,UAAU;iBACxB,CAAC,CAAC;gBACH,MAAM,IAAI,QAAQ,CAChB,SAAS,CAAC,aAAa,EACvB,oBAAoB,QAAQ,uCAAuC,CACpE,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,YAAY,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC;QAClF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEtC,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;QAE3C,4EAA4E;QAC5E,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QACxD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,GAAG,CAAC,MAAM,EAAE,sDAAsD,EAAE;oBAClE,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,aAAa,EAAE,YAAY;oBAC3B,WAAW,EAAE,UAAU;iBACxB,CAAC,CAAC;gBACH,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;gBACrF,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAED,4EAA4E;QAC5E,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACjD,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;YACpE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChE,GAAG,CAAC,MAAM,EAAE,uDAAuD,EAAE;oBACnE,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC;oBAC/C,aAAa,EAAE,YAAY;oBAC3B,WAAW,EAAE,UAAU;iBACxB,CAAC,CAAC;gBACH,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;gBACpF,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QAED,4EAA4E;QAC5E,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,IAAI,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;oBAAE,SAAS;gBAC7E,IAAI,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC;oBAC1C,GAAG,CAAC,MAAM,EAAE,kDAAkD,EAAE;wBAC9D,QAAQ,EAAE,KAAK,CAAC,QAAQ;wBACxB,aAAa,EAAE,YAAY;wBAC3B,WAAW,EAAE,UAAU;qBACxB,CAAC,CAAC;oBACH,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;oBACpF,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACvC,CAAC;YACH,CAAC;QACH,CAAC;QAED,4EAA4E;QAC5E,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3D,IAAI,OAAkD,CAAC;YACvD,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YAClE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,oEAAoE;gBACpE,GAAG,CAAC,MAAM,EAAE,sCAAuC,GAAa,CAAC,OAAO,6BAA6B,CAAC,CAAC;gBACvG,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,oBAAoB,IAAI,OAAO,CAAC;gBACxE,IAAI,aAAa,KAAK,OAAO,EAAE,CAAC;oBAC9B,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;gBAC1C,CAAC;qBAAM,CAAC;oBACN,OAAO;gBACT,CAAC;YACH,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBACnB,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBACzB,4EAA4E;gBAC5E,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC;gBACpG,GAAG,CAAC,MAAM,EAAE,8CAA8C,EAAE;oBAC1D,eAAe,EAAE,aAAa;oBAC9B,aAAa,EAAE,YAAY;oBAC3B,WAAW,EAAE,UAAU;iBACxB,CAAC,CAAC;gBACH,MAAM,IAAI,CAAC,oBAAoB,CAAC,YAAY,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;YAC3E,CAAC;QACH,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,oBAAoB;IACpB,4EAA4E;IAE5E;;;;;;OAMG;IACK,KAAK,CAAC,mBAAmB,CAC/B,OAAkB,EAClB,YAAoB,EACpB,UAAkB;QAElB,MAAM,OAAO,GAAc,EAAE,CAAC;QAE9B,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,IACE,IAAI,KAAK,IAAI;gBACb,OAAO,IAAI,KAAK,QAAQ;gBACvB,IAAgC,CAAC,MAAM,CAAC,KAAK,MAAM;gBACpD,OAAQ,IAAgC,CAAC,MAAM,CAAC,KAAK,QAAQ,EAC7D,CAAC;gBACD,MAAM,IAAI,GAAI,IAAuC,CAAC,IAAI,CAAC;gBAE3D,IAAI,IAAI,CAAC,MAAM,IAAI,cAAc,EAAE,CAAC;oBAClC,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAC;oBACzC,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;oBAEnC,iEAAiE;oBACjE,MAAM,OAAO,GAAG,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC;oBAC3C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;oBACjG,qDAAqD;oBACrD,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;wBACnC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;oBAC7B,CAAC;oBAED,MAAM,eAAe,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;oBAC9C,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;oBAErC,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE;wBACxB,QAAQ;wBACR,QAAQ;wBACR,WAAW,EAAE,aAAa;wBAC1B,aAAa,EAAE,YAAY;wBAC3B,WAAW,EAAE,UAAU;wBACvB,cAAc,EAAE,QAAQ;wBACxB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB,GAAG,IAAI;wBAC5C,eAAe;wBACf,OAAO;qBACR,CAAC,CAAC;oBAEH,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;oBACzD,GAAG,CAAC,OAAO,EAAE,+BAA+B,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;oBAElH,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;oBAClD,SAAS;gBACX,CAAC;YACH,CAAC;YAED,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QAED,sDAAsD;QACtD,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAE5D,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,4EAA4E;IAC5E,oCAAoC;IACpC,4EAA4E;IAEpE,eAAe,CACrB,IAA6B;QAE7B,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC9C,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;YAClC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;gBAC7D,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,8BAA8B,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAClD,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC;QACvE,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,GAAG,WAAW,CAAC;QACpD,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;QAC7D,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC;QAE1D,MAAM,cAAc,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3C,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,aAAa,EAAE,CAAC,CAAC,aAAa;YAC9B,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,cAAc,EAAE,CAAC,CAAC,cAAc;YAChC,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,MAAM,EAAE,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;YACjD,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,iBAAiB,EAAE,CAAC,CAAC,iBAAiB;YACtC,wCAAwC;SACzC,CAAC,CAAC,CAAC;QAEJ,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;wBACnB,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU;wBACjC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU;wBACjC,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa;wBACvC,UAAU,EAAE,IAAI;wBAChB,OAAO,EAAE;4BACP,YAAY,EAAE,SAAS,CAAC,MAAM;4BAC9B,aAAa,EAAE,WAAW;4BAC1B,cAAc,EAAE,YAAY;4BAC5B,aAAa,EAAE,WAAW;4BAC1B,oBAAoB,EAAE,SAAS,CAAC,MAAM;4BACtC,kBAAkB,EAAE,IAAI,CAAC,iBAAiB;4BAC1C,eAAe,EAAE,IAAI,CAAC,OAAO,KAAK,IAAI;4BACtC,gBAAgB,EAAE,IAAI,CAAC,YAAY,CAAC,eAAe,IAAI,EAAE;4BACzD,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,aAAa,IAAI,IAAI;yBACxD;wBACD,MAAM,EAAE,cAAc;wBACtB,cAAc,EAAE,SAAS;qBAC1B,CAAC;iBACH;aACF;SACF,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,yBAAyB;IACzB,4EAA4E;IAE5E;;;;OAIG;IACK,KAAK,CAAC,oBAAoB,CAChC,aAAqB,EACrB,WAAmB,EACnB,eAAwB;QAExB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;QAEvC,MAAM,YAAY,GAChB,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ;YAC1B,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS;gBAC9B,CAAC,CAAC,QAAQ,CAAC;QAEb,sDAAsD;QACtD,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE;YACxB,QAAQ,EAAE,WAAW,eAAe,IAAI,SAAS,EAAE;YACnD,gBAAgB,EAAE,eAAe;YACjC,aAAa;YACb,WAAW;YACX,YAAY;YACZ,iBAAiB,EAAE,KAAK;YACxB,iBAAiB,EAAE,IAAI;SACxB,CAAC,CAAC;QAEH,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAE5D,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,MAAM,IAAI,QAAQ,CAChB,SAAS,CAAC,aAAa,EACvB,sEAAsE,CACvE,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,aAAa,CACzB,QAAgB,EAChB,gBAAmD,EACnD,aAAsB,EACtB,WAAoB;QAEpB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;QAEvC,MAAM,YAAY,GAChB,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ;YAC1B,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS;gBAC9B,CAAC,CAAC,QAAQ,CAAC;QAEb,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE;YACxB,QAAQ;YACR,gBAAgB;YAChB,aAAa;YACb,WAAW;YACX,YAAY;YACZ,iBAAiB,EAAE,KAAK;YACxB,iBAAiB,EAAE,IAAI;SACxB,CAAC,CAAC;QAEH,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAE5D,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,MAAM,IAAI,QAAQ,CAChB,SAAS,CAAC,aAAa,EACvB,+DAA+D,CAChE,CAAC;QACJ,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DualAuditor — runs two independent LLM auditors in parallel.
|
|
3
|
+
*
|
|
4
|
+
* Both auditors must return CLEAN for the overall verdict to be CLEAN.
|
|
5
|
+
* If either returns DERIVED (or times out / errors), the call is blocked.
|
|
6
|
+
*
|
|
7
|
+
* Using two different model providers makes it much harder for a
|
|
8
|
+
* prompt-injection payload to fool both simultaneously.
|
|
9
|
+
*/
|
|
10
|
+
import type { AuditorConfig, AuditResult } from './types.js';
|
|
11
|
+
import type { StoredToolResult } from './types.js';
|
|
12
|
+
export interface DualAuditOutcome {
|
|
13
|
+
/** True only if both auditors returned CLEAN. */
|
|
14
|
+
clean: boolean;
|
|
15
|
+
/** Individual results from each auditor. */
|
|
16
|
+
results: Array<{
|
|
17
|
+
provider: string;
|
|
18
|
+
result: AuditResult;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
export declare class DualAuditor {
|
|
22
|
+
private readonly auditors;
|
|
23
|
+
private readonly configs;
|
|
24
|
+
constructor(configs: AuditorConfig[]);
|
|
25
|
+
check(storedResults: StoredToolResult[], outboundArgs: string): Promise<DualAuditOutcome>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=DualAuditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DualAuditor.d.ts","sourceRoot":"","sources":["../../../src/proxy/auditor/DualAuditor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInD,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,KAAK,EAAE,OAAO,CAAC;IACf,4CAA4C;IAC5C,OAAO,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;CAC3D;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;gBAE9B,OAAO,EAAE,aAAa,EAAE;IAU9B,KAAK,CACT,aAAa,EAAE,gBAAgB,EAAE,EACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,gBAAgB,CAAC;CAwB7B"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DualAuditor — runs two independent LLM auditors in parallel.
|
|
3
|
+
*
|
|
4
|
+
* Both auditors must return CLEAN for the overall verdict to be CLEAN.
|
|
5
|
+
* If either returns DERIVED (or times out / errors), the call is blocked.
|
|
6
|
+
*
|
|
7
|
+
* Using two different model providers makes it much harder for a
|
|
8
|
+
* prompt-injection payload to fool both simultaneously.
|
|
9
|
+
*/
|
|
10
|
+
import { LLMAuditor } from './LLMAuditor.js';
|
|
11
|
+
import { log } from '../../logger.js';
|
|
12
|
+
export class DualAuditor {
|
|
13
|
+
auditors;
|
|
14
|
+
configs;
|
|
15
|
+
constructor(configs) {
|
|
16
|
+
if (configs.length < 2) {
|
|
17
|
+
throw new Error(`DualAuditor requires exactly 2 auditor configs, got ${configs.length}.`);
|
|
18
|
+
}
|
|
19
|
+
this.configs = configs;
|
|
20
|
+
this.auditors = configs.map((c) => new LLMAuditor(c));
|
|
21
|
+
}
|
|
22
|
+
async check(storedResults, outboundArgs) {
|
|
23
|
+
const [resultA, resultB] = await Promise.all([
|
|
24
|
+
this.auditors[0].check(storedResults, outboundArgs),
|
|
25
|
+
this.auditors[1].check(storedResults, outboundArgs),
|
|
26
|
+
]);
|
|
27
|
+
const results = [
|
|
28
|
+
{ provider: this.configs[0].provider, result: resultA },
|
|
29
|
+
{ provider: this.configs[1].provider, result: resultB },
|
|
30
|
+
];
|
|
31
|
+
const clean = resultA.verdict === 'CLEAN' && resultB.verdict === 'CLEAN';
|
|
32
|
+
if (!clean) {
|
|
33
|
+
log('warn', 'DualAuditor: DERIVED verdict — outbound call blocked.', {
|
|
34
|
+
auditor_a: { provider: this.configs[0].provider, verdict: resultA.verdict, fail_safe: resultA.fail_safe },
|
|
35
|
+
auditor_b: { provider: this.configs[1].provider, verdict: resultB.verdict, fail_safe: resultB.fail_safe },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
log('debug', 'DualAuditor: both auditors returned CLEAN.');
|
|
40
|
+
}
|
|
41
|
+
return { clean, results };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=DualAuditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DualAuditor.js","sourceRoot":"","sources":["../../../src/proxy/auditor/DualAuditor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAStC,MAAM,OAAO,WAAW;IACL,QAAQ,CAAe;IACvB,OAAO,CAAkB;IAE1C,YAAY,OAAwB;QAClC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,uDAAuD,OAAO,CAAC,MAAM,GAAG,CACzE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,KAAK,CACT,aAAiC,EACjC,YAAoB;QAEpB,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC3C,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC;YACpD,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC;SACrD,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG;YACd,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;YACxD,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;SACzD,CAAC;QAEF,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,KAAK,OAAO,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC;QAEzE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,GAAG,CAAC,MAAM,EAAE,uDAAuD,EAAE;gBACnE,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE;gBAC1G,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE;aAC3G,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,OAAO,EAAE,4CAA4C,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC5B,CAAC;CACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLMAuditor — sends one audit request to a single LLM provider.
|
|
3
|
+
*
|
|
4
|
+
* Supports Anthropic, OpenAI, and Google (Gemini) via their REST APIs.
|
|
5
|
+
* Uses Node 18+ built-in `fetch` — no extra runtime dependencies.
|
|
6
|
+
*
|
|
7
|
+
* Fail-safe: any unexpected response, timeout, or network error returns
|
|
8
|
+
* { verdict: 'DERIVED', fail_safe: true } so the proxy blocks by default.
|
|
9
|
+
*/
|
|
10
|
+
import type { AuditorConfig, AuditResult, StoredToolResult } from './types.js';
|
|
11
|
+
export declare class LLMAuditor {
|
|
12
|
+
private readonly config;
|
|
13
|
+
constructor(config: AuditorConfig);
|
|
14
|
+
check(storedResults: StoredToolResult[], outboundArgs: string): Promise<AuditResult>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=LLMAuditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LLMAuditor.d.ts","sourceRoot":"","sources":["../../../src/proxy/auditor/LLMAuditor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAgM/E,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAE3B,MAAM,EAAE,aAAa;IAI3B,KAAK,CACT,aAAa,EAAE,gBAAgB,EAAE,EACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,WAAW,CAAC;CA+DxB"}
|