@kya-os/agentshield-nextjs 0.1.40 → 0.1.41
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/dist/index.js +654 -532
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +654 -532
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -16
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,468 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __esm = (fn, res) => function __init() {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
};
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/wasm-loader.ts
|
|
14
|
+
function setWasmBaseUrl(url) {
|
|
15
|
+
baseUrl = url;
|
|
16
|
+
}
|
|
17
|
+
function getWasmUrl() {
|
|
18
|
+
if (baseUrl) {
|
|
19
|
+
try {
|
|
20
|
+
const url = new URL(baseUrl);
|
|
21
|
+
return `${url.origin}${WASM_PATH}`;
|
|
22
|
+
} catch {
|
|
23
|
+
return WASM_PATH;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return WASM_PATH;
|
|
27
|
+
}
|
|
28
|
+
async function initWasm() {
|
|
29
|
+
if (wasmExports) return true;
|
|
30
|
+
if (initPromise) {
|
|
31
|
+
await initPromise;
|
|
32
|
+
return !!wasmExports;
|
|
33
|
+
}
|
|
34
|
+
initPromise = (async () => {
|
|
35
|
+
try {
|
|
36
|
+
const controller = new AbortController();
|
|
37
|
+
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
38
|
+
try {
|
|
39
|
+
const wasmUrl = getWasmUrl();
|
|
40
|
+
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
41
|
+
try {
|
|
42
|
+
const response2 = await fetch(wasmUrl, { signal: controller.signal });
|
|
43
|
+
clearTimeout(timeout);
|
|
44
|
+
if (!response2.ok) {
|
|
45
|
+
throw new Error(`Failed to fetch WASM: ${response2.status}`);
|
|
46
|
+
}
|
|
47
|
+
const streamResponse = response2.clone();
|
|
48
|
+
const { instance } = await WebAssembly.instantiateStreaming(
|
|
49
|
+
streamResponse,
|
|
50
|
+
{
|
|
51
|
+
wbg: {
|
|
52
|
+
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
53
|
+
console.log("WASM:", ptr, len);
|
|
54
|
+
},
|
|
55
|
+
__wbindgen_throw: (ptr, len) => {
|
|
56
|
+
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
wasmInstance = instance;
|
|
62
|
+
wasmExports = instance.exports;
|
|
63
|
+
console.log("[AgentShield] \u2705 WASM module initialized with streaming");
|
|
64
|
+
return;
|
|
65
|
+
} catch (streamError) {
|
|
66
|
+
if (!controller.signal.aborted) {
|
|
67
|
+
console.log("[AgentShield] Streaming compilation failed, falling back to standard compilation");
|
|
68
|
+
} else {
|
|
69
|
+
throw streamError;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const response = await fetch(wasmUrl, { signal: controller.signal });
|
|
74
|
+
clearTimeout(timeout);
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new Error(`Failed to fetch WASM: ${response.status}`);
|
|
77
|
+
}
|
|
78
|
+
const wasmArrayBuffer = await response.arrayBuffer();
|
|
79
|
+
const compiledModule = await WebAssembly.compile(wasmArrayBuffer);
|
|
80
|
+
const imports = {
|
|
81
|
+
wbg: {
|
|
82
|
+
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
83
|
+
console.log("WASM:", ptr, len);
|
|
84
|
+
},
|
|
85
|
+
__wbindgen_throw: (ptr, len) => {
|
|
86
|
+
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
wasmInstance = await WebAssembly.instantiate(compiledModule, imports);
|
|
91
|
+
wasmExports = wasmInstance.exports;
|
|
92
|
+
console.log("[AgentShield] \u2705 WASM module initialized via fallback");
|
|
93
|
+
} catch (fetchError) {
|
|
94
|
+
if (fetchError.name === "AbortError") {
|
|
95
|
+
console.warn("[AgentShield] WASM fetch timed out after 3 seconds - using pattern detection");
|
|
96
|
+
} else {
|
|
97
|
+
console.warn("[AgentShield] Failed to fetch WASM file:", fetchError.message);
|
|
98
|
+
}
|
|
99
|
+
wasmExports = null;
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
103
|
+
wasmExports = null;
|
|
104
|
+
}
|
|
105
|
+
})();
|
|
106
|
+
await initPromise;
|
|
107
|
+
return !!wasmExports;
|
|
108
|
+
}
|
|
109
|
+
async function detectAgentWithWasm(userAgent, headers, ipAddress) {
|
|
110
|
+
const initialized = await initWasm();
|
|
111
|
+
if (!initialized || !wasmExports) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const headersJson = JSON.stringify(headers);
|
|
116
|
+
if (typeof wasmExports.detect_agent === "function") {
|
|
117
|
+
const result = wasmExports.detect_agent(
|
|
118
|
+
userAgent || "",
|
|
119
|
+
headersJson,
|
|
120
|
+
ipAddress || "",
|
|
121
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
122
|
+
);
|
|
123
|
+
return {
|
|
124
|
+
isAgent: result.is_agent || false,
|
|
125
|
+
confidence: result.confidence || 0,
|
|
126
|
+
agent: result.agent,
|
|
127
|
+
verificationMethod: result.verification_method || "wasm",
|
|
128
|
+
riskLevel: result.risk_level || "low"
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
console.warn("[AgentShield] WASM exports do not include detect_agent function");
|
|
132
|
+
return null;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error("[AgentShield] WASM detection failed:", error);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function getWasmVersion() {
|
|
139
|
+
const initialized = await initWasm();
|
|
140
|
+
if (!initialized || !wasmExports) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
if (typeof wasmExports.version === "function") {
|
|
144
|
+
return wasmExports.version();
|
|
145
|
+
}
|
|
146
|
+
return "unknown";
|
|
147
|
+
}
|
|
148
|
+
async function isWasmAvailable() {
|
|
149
|
+
try {
|
|
150
|
+
const initialized = await initWasm();
|
|
151
|
+
if (!initialized) return false;
|
|
152
|
+
const version = await getWasmVersion();
|
|
153
|
+
return version !== null;
|
|
154
|
+
} catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
var wasmInstance, wasmExports, initPromise, WASM_PATH, baseUrl;
|
|
159
|
+
var init_wasm_loader = __esm({
|
|
160
|
+
"src/wasm-loader.ts"() {
|
|
161
|
+
wasmInstance = null;
|
|
162
|
+
wasmExports = null;
|
|
163
|
+
initPromise = null;
|
|
164
|
+
WASM_PATH = "/wasm/agentshield_wasm_bg.wasm";
|
|
165
|
+
baseUrl = null;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// src/edge-detector-with-wasm.ts
|
|
170
|
+
var edge_detector_with_wasm_exports = {};
|
|
171
|
+
__export(edge_detector_with_wasm_exports, {
|
|
172
|
+
EdgeAgentDetectorWithWasm: () => EdgeAgentDetectorWithWasm,
|
|
173
|
+
EdgeAgentDetectorWrapperWithWasm: () => EdgeAgentDetectorWrapperWithWasm
|
|
174
|
+
});
|
|
175
|
+
var AI_AGENT_PATTERNS2, CLOUD_PROVIDERS2, EdgeAgentDetectorWithWasm, EdgeAgentDetectorWrapperWithWasm;
|
|
176
|
+
var init_edge_detector_with_wasm = __esm({
|
|
177
|
+
"src/edge-detector-with-wasm.ts"() {
|
|
178
|
+
init_wasm_loader();
|
|
179
|
+
AI_AGENT_PATTERNS2 = [
|
|
180
|
+
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
181
|
+
{ pattern: /claude-web/i, type: "claude", name: "Claude" },
|
|
182
|
+
{ pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
|
|
183
|
+
{ pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
|
|
184
|
+
{ pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
|
|
185
|
+
{ pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
|
|
186
|
+
{ pattern: /bingbot/i, type: "bing", name: "Bing AI" },
|
|
187
|
+
{ pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
|
|
188
|
+
];
|
|
189
|
+
CLOUD_PROVIDERS2 = {
|
|
190
|
+
aws: ["54.", "52.", "35.", "18.", "3."],
|
|
191
|
+
gcp: ["35.", "34.", "104.", "107.", "108."],
|
|
192
|
+
azure: ["13.", "20.", "40.", "52.", "104."]
|
|
193
|
+
};
|
|
194
|
+
EdgeAgentDetectorWithWasm = class {
|
|
195
|
+
constructor(enableWasm = true) {
|
|
196
|
+
this.enableWasm = enableWasm;
|
|
197
|
+
}
|
|
198
|
+
wasmEnabled = false;
|
|
199
|
+
initPromise = null;
|
|
200
|
+
baseUrl = null;
|
|
201
|
+
/**
|
|
202
|
+
* Set the base URL for WASM loading in Edge Runtime
|
|
203
|
+
*/
|
|
204
|
+
setBaseUrl(url) {
|
|
205
|
+
this.baseUrl = url;
|
|
206
|
+
setWasmBaseUrl(url);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Initialize the detector (including WASM if enabled)
|
|
210
|
+
*/
|
|
211
|
+
async init() {
|
|
212
|
+
if (!this.enableWasm) {
|
|
213
|
+
this.wasmEnabled = false;
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (this.initPromise) {
|
|
217
|
+
await this.initPromise;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
this.initPromise = (async () => {
|
|
221
|
+
try {
|
|
222
|
+
const wasmAvailable = await isWasmAvailable();
|
|
223
|
+
this.wasmEnabled = wasmAvailable;
|
|
224
|
+
if (this.wasmEnabled) {
|
|
225
|
+
console.log("[AgentShield] WASM detection enabled");
|
|
226
|
+
} else {
|
|
227
|
+
console.log("[AgentShield] WASM not available, using pattern detection");
|
|
228
|
+
}
|
|
229
|
+
} catch (error) {
|
|
230
|
+
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
231
|
+
this.wasmEnabled = false;
|
|
232
|
+
}
|
|
233
|
+
})();
|
|
234
|
+
await this.initPromise;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Pattern-based detection (fallback)
|
|
238
|
+
*/
|
|
239
|
+
async patternDetection(input) {
|
|
240
|
+
const reasons = [];
|
|
241
|
+
let detectedAgent;
|
|
242
|
+
let verificationMethod;
|
|
243
|
+
let confidence = 0;
|
|
244
|
+
const headers = input.headers || {};
|
|
245
|
+
const normalizedHeaders = {};
|
|
246
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
247
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
248
|
+
}
|
|
249
|
+
const signaturePresent = !!(normalizedHeaders["signature"] || normalizedHeaders["signature-input"]);
|
|
250
|
+
const signatureAgent = normalizedHeaders["signature-agent"];
|
|
251
|
+
if (signatureAgent?.includes("chatgpt.com")) {
|
|
252
|
+
confidence = 0.85;
|
|
253
|
+
reasons.push("signature_agent:chatgpt");
|
|
254
|
+
detectedAgent = { type: "chatgpt", name: "ChatGPT" };
|
|
255
|
+
verificationMethod = "signature";
|
|
256
|
+
} else if (signaturePresent) {
|
|
257
|
+
confidence = Math.max(confidence, 0.4);
|
|
258
|
+
reasons.push("signature_present");
|
|
259
|
+
}
|
|
260
|
+
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
261
|
+
if (userAgent) {
|
|
262
|
+
for (const { pattern, type, name } of AI_AGENT_PATTERNS2) {
|
|
263
|
+
if (pattern.test(userAgent)) {
|
|
264
|
+
const highConfidenceAgents = [
|
|
265
|
+
"chatgpt",
|
|
266
|
+
"claude",
|
|
267
|
+
"perplexity",
|
|
268
|
+
"anthropic"
|
|
269
|
+
];
|
|
270
|
+
const patternConfidence = highConfidenceAgents.includes(type) ? 0.85 : 0.5;
|
|
271
|
+
confidence = Math.max(confidence, patternConfidence);
|
|
272
|
+
reasons.push(`known_pattern:${type}`);
|
|
273
|
+
if (!detectedAgent) {
|
|
274
|
+
detectedAgent = { type, name };
|
|
275
|
+
verificationMethod = "pattern";
|
|
276
|
+
}
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const aiHeaders = [
|
|
282
|
+
"openai-conversation-id",
|
|
283
|
+
"openai-ephemeral-user-id",
|
|
284
|
+
"anthropic-client-id",
|
|
285
|
+
"x-goog-api-client",
|
|
286
|
+
"x-ms-copilot-id"
|
|
287
|
+
];
|
|
288
|
+
const foundAiHeaders = aiHeaders.filter(
|
|
289
|
+
(header) => normalizedHeaders[header]
|
|
290
|
+
);
|
|
291
|
+
if (foundAiHeaders.length > 0) {
|
|
292
|
+
confidence = Math.max(confidence, 0.6);
|
|
293
|
+
reasons.push(`ai_headers:${foundAiHeaders.length}`);
|
|
294
|
+
}
|
|
295
|
+
const ip = input.ip || input.ipAddress;
|
|
296
|
+
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
297
|
+
for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS2)) {
|
|
298
|
+
if (prefixes.some((prefix) => ip.startsWith(prefix))) {
|
|
299
|
+
confidence = Math.max(confidence, 0.4);
|
|
300
|
+
reasons.push(`cloud_provider:${provider}`);
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (reasons.length > 2) {
|
|
306
|
+
confidence = Math.min(confidence * 1.2, 0.95);
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
isAgent: confidence > 0.3,
|
|
310
|
+
confidence,
|
|
311
|
+
...detectedAgent && { detectedAgent },
|
|
312
|
+
reasons,
|
|
313
|
+
...verificationMethod && { verificationMethod },
|
|
314
|
+
forgeabilityRisk: confidence > 0.8 ? "medium" : "high",
|
|
315
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Analyze request with WASM enhancement when available
|
|
320
|
+
*/
|
|
321
|
+
async analyze(input) {
|
|
322
|
+
await this.init();
|
|
323
|
+
if (this.wasmEnabled) {
|
|
324
|
+
try {
|
|
325
|
+
const wasmResult = await detectAgentWithWasm(
|
|
326
|
+
input.userAgent || input.headers?.["user-agent"],
|
|
327
|
+
input.headers || {},
|
|
328
|
+
input.ip || input.ipAddress
|
|
329
|
+
);
|
|
330
|
+
if (wasmResult) {
|
|
331
|
+
console.log("[AgentShield] Using WASM detection result");
|
|
332
|
+
const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
|
|
333
|
+
return {
|
|
334
|
+
isAgent: wasmResult.isAgent,
|
|
335
|
+
confidence: wasmResult.confidence,
|
|
336
|
+
...detectedAgent && { detectedAgent },
|
|
337
|
+
reasons: [`wasm:${wasmResult.verificationMethod}`],
|
|
338
|
+
verificationMethod: wasmResult.verificationMethod,
|
|
339
|
+
forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 0.9 ? "medium" : "high",
|
|
340
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.error("[AgentShield] WASM detection error:", error);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const patternResult = await this.patternDetection(input);
|
|
348
|
+
if (this.wasmEnabled && patternResult.confidence >= 0.85) {
|
|
349
|
+
patternResult.confidence = Math.min(0.95, patternResult.confidence + 0.1);
|
|
350
|
+
patternResult.reasons.push("wasm_enhanced");
|
|
351
|
+
if (!patternResult.verificationMethod) {
|
|
352
|
+
patternResult.verificationMethod = "wasm-enhanced";
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return patternResult;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Map agent names from WASM to consistent format
|
|
359
|
+
*/
|
|
360
|
+
mapAgentName(agent) {
|
|
361
|
+
const lowerAgent = agent.toLowerCase();
|
|
362
|
+
if (lowerAgent.includes("chatgpt")) {
|
|
363
|
+
return { type: "chatgpt", name: "ChatGPT" };
|
|
364
|
+
} else if (lowerAgent.includes("claude")) {
|
|
365
|
+
return { type: "claude", name: "Claude" };
|
|
366
|
+
} else if (lowerAgent.includes("perplexity")) {
|
|
367
|
+
return { type: "perplexity", name: "Perplexity" };
|
|
368
|
+
} else if (lowerAgent.includes("bing")) {
|
|
369
|
+
return { type: "bing", name: "Bing AI" };
|
|
370
|
+
} else if (lowerAgent.includes("anthropic")) {
|
|
371
|
+
return { type: "anthropic", name: "Anthropic" };
|
|
372
|
+
}
|
|
373
|
+
return { type: "unknown", name: agent };
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
EdgeAgentDetectorWrapperWithWasm = class {
|
|
377
|
+
detector;
|
|
378
|
+
events = /* @__PURE__ */ new Map();
|
|
379
|
+
constructor(config) {
|
|
380
|
+
this.detector = new EdgeAgentDetectorWithWasm(config?.enableWasm ?? true);
|
|
381
|
+
if (config?.baseUrl) {
|
|
382
|
+
this.detector.setBaseUrl(config.baseUrl);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
setBaseUrl(url) {
|
|
386
|
+
this.detector.setBaseUrl(url);
|
|
387
|
+
}
|
|
388
|
+
async analyze(input) {
|
|
389
|
+
const result = await this.detector.analyze(input);
|
|
390
|
+
if (result.isAgent && this.events.has("agent.detected")) {
|
|
391
|
+
const handlers = this.events.get("agent.detected") || [];
|
|
392
|
+
handlers.forEach((handler) => handler(result, input));
|
|
393
|
+
}
|
|
394
|
+
return result;
|
|
395
|
+
}
|
|
396
|
+
on(event, handler) {
|
|
397
|
+
if (!this.events.has(event)) {
|
|
398
|
+
this.events.set(event, []);
|
|
399
|
+
}
|
|
400
|
+
this.events.get(event).push(handler);
|
|
401
|
+
}
|
|
402
|
+
emit(event, ...args) {
|
|
403
|
+
const handlers = this.events.get(event) || [];
|
|
404
|
+
handlers.forEach((handler) => handler(...args));
|
|
405
|
+
}
|
|
406
|
+
async init() {
|
|
407
|
+
await this.detector.init();
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// src/wasm-confidence.ts
|
|
414
|
+
var wasm_confidence_exports = {};
|
|
415
|
+
__export(wasm_confidence_exports, {
|
|
416
|
+
checkWasmAvailability: () => checkWasmAvailability,
|
|
417
|
+
getVerificationMethod: () => getVerificationMethod,
|
|
418
|
+
getWasmConfidenceBoost: () => getWasmConfidenceBoost,
|
|
419
|
+
shouldIndicateWasmVerification: () => shouldIndicateWasmVerification
|
|
420
|
+
});
|
|
421
|
+
async function checkWasmAvailability() {
|
|
422
|
+
try {
|
|
423
|
+
if (typeof WebAssembly === "undefined") {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
if (!WebAssembly.instantiate || !WebAssembly.Module) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
return true;
|
|
430
|
+
} catch {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function shouldIndicateWasmVerification(confidence) {
|
|
435
|
+
return confidence >= 0.85 && confidence < 1;
|
|
436
|
+
}
|
|
437
|
+
function getWasmConfidenceBoost(baseConfidence, reasons = []) {
|
|
438
|
+
if (reasons.some(
|
|
439
|
+
(r) => r.includes("signature_agent") && !r.includes("signature_headers_present")
|
|
440
|
+
)) {
|
|
441
|
+
return 1;
|
|
442
|
+
}
|
|
443
|
+
if (baseConfidence >= 0.85) {
|
|
444
|
+
return 0.95;
|
|
445
|
+
}
|
|
446
|
+
if (baseConfidence >= 0.7) {
|
|
447
|
+
return Math.min(baseConfidence * 1.1, 0.9);
|
|
448
|
+
}
|
|
449
|
+
return baseConfidence;
|
|
450
|
+
}
|
|
451
|
+
function getVerificationMethod(confidence, reasons = []) {
|
|
452
|
+
if (reasons.some(
|
|
453
|
+
(r) => r.includes("signature_agent") && !r.includes("signature_headers_present")
|
|
454
|
+
)) {
|
|
455
|
+
return "signature";
|
|
456
|
+
}
|
|
457
|
+
if (shouldIndicateWasmVerification(confidence)) {
|
|
458
|
+
return "wasm-enhanced";
|
|
459
|
+
}
|
|
460
|
+
return "pattern";
|
|
461
|
+
}
|
|
462
|
+
var init_wasm_confidence = __esm({
|
|
463
|
+
"src/wasm-confidence.ts"() {
|
|
464
|
+
}
|
|
465
|
+
});
|
|
4
466
|
|
|
5
467
|
// src/signature-verifier.ts
|
|
6
468
|
var KNOWN_KEYS = {
|
|
@@ -174,413 +636,66 @@ async function verifyAgentSignature(method, path, headers) {
|
|
|
174
636
|
confidence: 0,
|
|
175
637
|
reason: "Key is not valid at current time",
|
|
176
638
|
verificationMethod: "none"
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
const signatureBase = buildSignatureBase(method, path, headers, parsed.signedHeaders);
|
|
180
|
-
let signatureValue = signature;
|
|
181
|
-
if (signatureValue.startsWith("sig1=:")) {
|
|
182
|
-
signatureValue = signatureValue.substring(6);
|
|
183
|
-
}
|
|
184
|
-
if (signatureValue.endsWith(":")) {
|
|
185
|
-
signatureValue = signatureValue.slice(0, -1);
|
|
186
|
-
}
|
|
187
|
-
const isValid = await verifyEd25519Signature(
|
|
188
|
-
key.publicKey,
|
|
189
|
-
signatureValue,
|
|
190
|
-
signatureBase
|
|
191
|
-
);
|
|
192
|
-
if (isValid) {
|
|
193
|
-
return {
|
|
194
|
-
isValid: true,
|
|
195
|
-
agent,
|
|
196
|
-
keyid: parsed.keyid,
|
|
197
|
-
confidence: 1,
|
|
198
|
-
// 100% confidence for valid signature
|
|
199
|
-
verificationMethod: "signature"
|
|
200
|
-
};
|
|
201
|
-
} else {
|
|
202
|
-
return {
|
|
203
|
-
isValid: false,
|
|
204
|
-
confidence: 0,
|
|
205
|
-
reason: "Signature verification failed",
|
|
206
|
-
verificationMethod: "none"
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
function hasSignatureHeaders(headers) {
|
|
211
|
-
return !!((headers["signature"] || headers["Signature"]) && (headers["signature-input"] || headers["Signature-Input"]));
|
|
212
|
-
}
|
|
213
|
-
function isChatGPTSignature(headers) {
|
|
214
|
-
const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
|
|
215
|
-
return signatureAgent === '"https://chatgpt.com"' || (signatureAgent?.includes("chatgpt.com") || false);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// src/edge-detector-wrapper.ts
|
|
219
|
-
var AI_AGENT_PATTERNS = [
|
|
220
|
-
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
221
|
-
{ pattern: /claude-web/i, type: "claude", name: "Claude" },
|
|
222
|
-
{ pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
|
|
223
|
-
{ pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
|
|
224
|
-
{ pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
|
|
225
|
-
{ pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
|
|
226
|
-
// Fallback
|
|
227
|
-
{ pattern: /bingbot/i, type: "bing", name: "Bing AI" },
|
|
228
|
-
{ pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
|
|
229
|
-
];
|
|
230
|
-
var CLOUD_PROVIDERS = {
|
|
231
|
-
aws: ["54.", "52.", "35.", "18.", "3."],
|
|
232
|
-
gcp: ["35.", "34.", "104.", "107.", "108."],
|
|
233
|
-
azure: ["13.", "20.", "40.", "52.", "104."]
|
|
234
|
-
};
|
|
235
|
-
var EdgeAgentDetector = class {
|
|
236
|
-
async analyze(input) {
|
|
237
|
-
const reasons = [];
|
|
238
|
-
let detectedAgent;
|
|
239
|
-
let verificationMethod;
|
|
240
|
-
let confidence = 0;
|
|
241
|
-
const headers = input.headers || {};
|
|
242
|
-
const normalizedHeaders = {};
|
|
243
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
244
|
-
normalizedHeaders[key.toLowerCase()] = value;
|
|
245
|
-
}
|
|
246
|
-
if (hasSignatureHeaders(headers)) {
|
|
247
|
-
try {
|
|
248
|
-
const signatureResult = await verifyAgentSignature(
|
|
249
|
-
input.method || "GET",
|
|
250
|
-
input.url || "/",
|
|
251
|
-
headers
|
|
252
|
-
);
|
|
253
|
-
if (signatureResult.isValid) {
|
|
254
|
-
confidence = signatureResult.confidence;
|
|
255
|
-
reasons.push(`verified_signature:${signatureResult.agent?.toLowerCase() || "unknown"}`);
|
|
256
|
-
if (signatureResult.agent) {
|
|
257
|
-
detectedAgent = {
|
|
258
|
-
type: signatureResult.agent.toLowerCase(),
|
|
259
|
-
name: signatureResult.agent
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
verificationMethod = signatureResult.verificationMethod;
|
|
263
|
-
if (signatureResult.keyid) {
|
|
264
|
-
reasons.push(`keyid:${signatureResult.keyid}`);
|
|
265
|
-
}
|
|
266
|
-
} else {
|
|
267
|
-
confidence = Math.max(confidence, 0.3);
|
|
268
|
-
reasons.push("invalid_signature");
|
|
269
|
-
if (signatureResult.reason) {
|
|
270
|
-
reasons.push(`signature_error:${signatureResult.reason}`);
|
|
271
|
-
}
|
|
272
|
-
if (isChatGPTSignature(headers)) {
|
|
273
|
-
reasons.push("claims_chatgpt");
|
|
274
|
-
detectedAgent = { type: "chatgpt", name: "ChatGPT (unverified)" };
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
} catch (error) {
|
|
278
|
-
console.error("[EdgeAgentDetector] Signature verification error:", error);
|
|
279
|
-
confidence = Math.max(confidence, 0.2);
|
|
280
|
-
reasons.push("signature_verification_error");
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
284
|
-
if (userAgent) {
|
|
285
|
-
for (const { pattern, type, name } of AI_AGENT_PATTERNS) {
|
|
286
|
-
if (pattern.test(userAgent)) {
|
|
287
|
-
const highConfidenceAgents = [
|
|
288
|
-
"chatgpt",
|
|
289
|
-
"claude",
|
|
290
|
-
"perplexity",
|
|
291
|
-
"anthropic"
|
|
292
|
-
];
|
|
293
|
-
const patternConfidence = highConfidenceAgents.includes(type) ? 0.85 : 0.5;
|
|
294
|
-
confidence = Math.max(confidence, patternConfidence);
|
|
295
|
-
reasons.push(`known_pattern:${type}`);
|
|
296
|
-
if (!detectedAgent) {
|
|
297
|
-
detectedAgent = { type, name };
|
|
298
|
-
verificationMethod = "pattern";
|
|
299
|
-
}
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
const aiHeaders = [
|
|
305
|
-
"openai-conversation-id",
|
|
306
|
-
"openai-ephemeral-user-id",
|
|
307
|
-
"anthropic-client-id",
|
|
308
|
-
"x-goog-api-client",
|
|
309
|
-
"x-ms-copilot-id"
|
|
310
|
-
];
|
|
311
|
-
const foundAiHeaders = aiHeaders.filter(
|
|
312
|
-
(header) => normalizedHeaders[header]
|
|
313
|
-
);
|
|
314
|
-
if (foundAiHeaders.length > 0) {
|
|
315
|
-
confidence = Math.max(confidence, 0.6);
|
|
316
|
-
reasons.push(`ai_headers:${foundAiHeaders.length}`);
|
|
317
|
-
}
|
|
318
|
-
const ip = input.ip || input.ipAddress;
|
|
319
|
-
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
320
|
-
for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS)) {
|
|
321
|
-
if (prefixes.some((prefix) => ip.startsWith(prefix))) {
|
|
322
|
-
confidence = Math.max(confidence, 0.4);
|
|
323
|
-
reasons.push(`cloud_provider:${provider}`);
|
|
324
|
-
break;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
if (reasons.length > 2 && confidence < 1) {
|
|
329
|
-
confidence = Math.min(confidence * 1.2, 0.95);
|
|
330
|
-
}
|
|
331
|
-
return {
|
|
332
|
-
isAgent: confidence > 0.3,
|
|
333
|
-
confidence,
|
|
334
|
-
...detectedAgent && { detectedAgent },
|
|
335
|
-
reasons,
|
|
336
|
-
...verificationMethod && { verificationMethod },
|
|
337
|
-
forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 0.8 ? "medium" : "high",
|
|
338
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
var EdgeAgentDetectorWrapper = class {
|
|
343
|
-
detector;
|
|
344
|
-
events = /* @__PURE__ */ new Map();
|
|
345
|
-
constructor(_config) {
|
|
346
|
-
this.detector = new EdgeAgentDetector();
|
|
347
|
-
}
|
|
348
|
-
async analyze(input) {
|
|
349
|
-
const result = await this.detector.analyze(input);
|
|
350
|
-
if (result.isAgent && this.events.has("agent.detected")) {
|
|
351
|
-
const handlers = this.events.get("agent.detected") || [];
|
|
352
|
-
handlers.forEach((handler) => handler(result, input));
|
|
353
|
-
}
|
|
354
|
-
return result;
|
|
355
|
-
}
|
|
356
|
-
on(event, handler) {
|
|
357
|
-
if (!this.events.has(event)) {
|
|
358
|
-
this.events.set(event, []);
|
|
359
|
-
}
|
|
360
|
-
this.events.get(event).push(handler);
|
|
361
|
-
}
|
|
362
|
-
emit(event, ...args) {
|
|
363
|
-
const handlers = this.events.get(event) || [];
|
|
364
|
-
handlers.forEach((handler) => handler(...args));
|
|
365
|
-
}
|
|
366
|
-
async init() {
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
// src/wasm-loader.ts
|
|
372
|
-
var wasmInstance = null;
|
|
373
|
-
var wasmExports = null;
|
|
374
|
-
var initPromise = null;
|
|
375
|
-
var WASM_PATH = "/wasm/agentshield_wasm_bg.wasm";
|
|
376
|
-
var baseUrl = null;
|
|
377
|
-
function setWasmBaseUrl(url) {
|
|
378
|
-
baseUrl = url;
|
|
379
|
-
}
|
|
380
|
-
function getWasmUrl() {
|
|
381
|
-
if (baseUrl) {
|
|
382
|
-
try {
|
|
383
|
-
const url = new URL(baseUrl);
|
|
384
|
-
return `${url.origin}${WASM_PATH}`;
|
|
385
|
-
} catch {
|
|
386
|
-
return WASM_PATH;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
return WASM_PATH;
|
|
390
|
-
}
|
|
391
|
-
async function initWasm() {
|
|
392
|
-
if (wasmExports) return true;
|
|
393
|
-
if (initPromise) {
|
|
394
|
-
await initPromise;
|
|
395
|
-
return !!wasmExports;
|
|
396
|
-
}
|
|
397
|
-
initPromise = (async () => {
|
|
398
|
-
try {
|
|
399
|
-
const controller = new AbortController();
|
|
400
|
-
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
401
|
-
try {
|
|
402
|
-
const wasmUrl = getWasmUrl();
|
|
403
|
-
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
404
|
-
try {
|
|
405
|
-
const response2 = await fetch(wasmUrl, { signal: controller.signal });
|
|
406
|
-
clearTimeout(timeout);
|
|
407
|
-
if (!response2.ok) {
|
|
408
|
-
throw new Error(`Failed to fetch WASM: ${response2.status}`);
|
|
409
|
-
}
|
|
410
|
-
const streamResponse = response2.clone();
|
|
411
|
-
const { instance } = await WebAssembly.instantiateStreaming(
|
|
412
|
-
streamResponse,
|
|
413
|
-
{
|
|
414
|
-
wbg: {
|
|
415
|
-
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
416
|
-
console.log("WASM:", ptr, len);
|
|
417
|
-
},
|
|
418
|
-
__wbindgen_throw: (ptr, len) => {
|
|
419
|
-
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
);
|
|
424
|
-
wasmInstance = instance;
|
|
425
|
-
wasmExports = instance.exports;
|
|
426
|
-
console.log("[AgentShield] \u2705 WASM module initialized with streaming");
|
|
427
|
-
return;
|
|
428
|
-
} catch (streamError) {
|
|
429
|
-
if (!controller.signal.aborted) {
|
|
430
|
-
console.log("[AgentShield] Streaming compilation failed, falling back to standard compilation");
|
|
431
|
-
} else {
|
|
432
|
-
throw streamError;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
const response = await fetch(wasmUrl, { signal: controller.signal });
|
|
437
|
-
clearTimeout(timeout);
|
|
438
|
-
if (!response.ok) {
|
|
439
|
-
throw new Error(`Failed to fetch WASM: ${response.status}`);
|
|
440
|
-
}
|
|
441
|
-
const wasmArrayBuffer = await response.arrayBuffer();
|
|
442
|
-
const compiledModule = await WebAssembly.compile(wasmArrayBuffer);
|
|
443
|
-
const imports = {
|
|
444
|
-
wbg: {
|
|
445
|
-
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
446
|
-
console.log("WASM:", ptr, len);
|
|
447
|
-
},
|
|
448
|
-
__wbindgen_throw: (ptr, len) => {
|
|
449
|
-
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
wasmInstance = await WebAssembly.instantiate(compiledModule, imports);
|
|
454
|
-
wasmExports = wasmInstance.exports;
|
|
455
|
-
console.log("[AgentShield] \u2705 WASM module initialized via fallback");
|
|
456
|
-
} catch (fetchError) {
|
|
457
|
-
if (fetchError.name === "AbortError") {
|
|
458
|
-
console.warn("[AgentShield] WASM fetch timed out after 3 seconds - using pattern detection");
|
|
459
|
-
} else {
|
|
460
|
-
console.warn("[AgentShield] Failed to fetch WASM file:", fetchError.message);
|
|
461
|
-
}
|
|
462
|
-
wasmExports = null;
|
|
463
|
-
}
|
|
464
|
-
} catch (error) {
|
|
465
|
-
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
466
|
-
wasmExports = null;
|
|
467
|
-
}
|
|
468
|
-
})();
|
|
469
|
-
await initPromise;
|
|
470
|
-
return !!wasmExports;
|
|
471
|
-
}
|
|
472
|
-
async function detectAgentWithWasm(userAgent, headers, ipAddress) {
|
|
473
|
-
const initialized = await initWasm();
|
|
474
|
-
if (!initialized || !wasmExports) {
|
|
475
|
-
return null;
|
|
639
|
+
};
|
|
476
640
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
userAgent || "",
|
|
482
|
-
headersJson,
|
|
483
|
-
ipAddress || "",
|
|
484
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
485
|
-
);
|
|
486
|
-
return {
|
|
487
|
-
isAgent: result.is_agent || false,
|
|
488
|
-
confidence: result.confidence || 0,
|
|
489
|
-
agent: result.agent,
|
|
490
|
-
verificationMethod: result.verification_method || "wasm",
|
|
491
|
-
riskLevel: result.risk_level || "low"
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
console.warn("[AgentShield] WASM exports do not include detect_agent function");
|
|
495
|
-
return null;
|
|
496
|
-
} catch (error) {
|
|
497
|
-
console.error("[AgentShield] WASM detection failed:", error);
|
|
498
|
-
return null;
|
|
641
|
+
const signatureBase = buildSignatureBase(method, path, headers, parsed.signedHeaders);
|
|
642
|
+
let signatureValue = signature;
|
|
643
|
+
if (signatureValue.startsWith("sig1=:")) {
|
|
644
|
+
signatureValue = signatureValue.substring(6);
|
|
499
645
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const initialized = await initWasm();
|
|
503
|
-
if (!initialized || !wasmExports) {
|
|
504
|
-
return null;
|
|
646
|
+
if (signatureValue.endsWith(":")) {
|
|
647
|
+
signatureValue = signatureValue.slice(0, -1);
|
|
505
648
|
}
|
|
506
|
-
|
|
507
|
-
|
|
649
|
+
const isValid = await verifyEd25519Signature(
|
|
650
|
+
key.publicKey,
|
|
651
|
+
signatureValue,
|
|
652
|
+
signatureBase
|
|
653
|
+
);
|
|
654
|
+
if (isValid) {
|
|
655
|
+
return {
|
|
656
|
+
isValid: true,
|
|
657
|
+
agent,
|
|
658
|
+
keyid: parsed.keyid,
|
|
659
|
+
confidence: 1,
|
|
660
|
+
// 100% confidence for valid signature
|
|
661
|
+
verificationMethod: "signature"
|
|
662
|
+
};
|
|
663
|
+
} else {
|
|
664
|
+
return {
|
|
665
|
+
isValid: false,
|
|
666
|
+
confidence: 0,
|
|
667
|
+
reason: "Signature verification failed",
|
|
668
|
+
verificationMethod: "none"
|
|
669
|
+
};
|
|
508
670
|
}
|
|
509
|
-
return "unknown";
|
|
510
671
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
} catch {
|
|
518
|
-
return false;
|
|
519
|
-
}
|
|
672
|
+
function hasSignatureHeaders(headers) {
|
|
673
|
+
return !!((headers["signature"] || headers["Signature"]) && (headers["signature-input"] || headers["Signature-Input"]));
|
|
674
|
+
}
|
|
675
|
+
function isChatGPTSignature(headers) {
|
|
676
|
+
const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
|
|
677
|
+
return signatureAgent === '"https://chatgpt.com"' || (signatureAgent?.includes("chatgpt.com") || false);
|
|
520
678
|
}
|
|
521
679
|
|
|
522
|
-
// src/edge-detector-
|
|
523
|
-
var
|
|
680
|
+
// src/edge-detector-wrapper.ts
|
|
681
|
+
var AI_AGENT_PATTERNS = [
|
|
524
682
|
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
525
683
|
{ pattern: /claude-web/i, type: "claude", name: "Claude" },
|
|
526
684
|
{ pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
|
|
527
685
|
{ pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
|
|
528
686
|
{ pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
|
|
529
687
|
{ pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
|
|
688
|
+
// Fallback
|
|
530
689
|
{ pattern: /bingbot/i, type: "bing", name: "Bing AI" },
|
|
531
690
|
{ pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
|
|
532
691
|
];
|
|
533
|
-
var
|
|
692
|
+
var CLOUD_PROVIDERS = {
|
|
534
693
|
aws: ["54.", "52.", "35.", "18.", "3."],
|
|
535
694
|
gcp: ["35.", "34.", "104.", "107.", "108."],
|
|
536
695
|
azure: ["13.", "20.", "40.", "52.", "104."]
|
|
537
696
|
};
|
|
538
|
-
var
|
|
539
|
-
|
|
540
|
-
this.enableWasm = enableWasm;
|
|
541
|
-
}
|
|
542
|
-
wasmEnabled = false;
|
|
543
|
-
initPromise = null;
|
|
544
|
-
baseUrl = null;
|
|
545
|
-
/**
|
|
546
|
-
* Set the base URL for WASM loading in Edge Runtime
|
|
547
|
-
*/
|
|
548
|
-
setBaseUrl(url) {
|
|
549
|
-
this.baseUrl = url;
|
|
550
|
-
setWasmBaseUrl(url);
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* Initialize the detector (including WASM if enabled)
|
|
554
|
-
*/
|
|
555
|
-
async init() {
|
|
556
|
-
if (!this.enableWasm) {
|
|
557
|
-
this.wasmEnabled = false;
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
560
|
-
if (this.initPromise) {
|
|
561
|
-
await this.initPromise;
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
this.initPromise = (async () => {
|
|
565
|
-
try {
|
|
566
|
-
const wasmAvailable = await isWasmAvailable();
|
|
567
|
-
this.wasmEnabled = wasmAvailable;
|
|
568
|
-
if (this.wasmEnabled) {
|
|
569
|
-
console.log("[AgentShield] WASM detection enabled");
|
|
570
|
-
} else {
|
|
571
|
-
console.log("[AgentShield] WASM not available, using pattern detection");
|
|
572
|
-
}
|
|
573
|
-
} catch (error) {
|
|
574
|
-
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
575
|
-
this.wasmEnabled = false;
|
|
576
|
-
}
|
|
577
|
-
})();
|
|
578
|
-
await this.initPromise;
|
|
579
|
-
}
|
|
580
|
-
/**
|
|
581
|
-
* Pattern-based detection (fallback)
|
|
582
|
-
*/
|
|
583
|
-
async patternDetection(input) {
|
|
697
|
+
var EdgeAgentDetector = class {
|
|
698
|
+
async analyze(input) {
|
|
584
699
|
const reasons = [];
|
|
585
700
|
let detectedAgent;
|
|
586
701
|
let verificationMethod;
|
|
@@ -590,20 +705,46 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
590
705
|
for (const [key, value] of Object.entries(headers)) {
|
|
591
706
|
normalizedHeaders[key.toLowerCase()] = value;
|
|
592
707
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
708
|
+
if (hasSignatureHeaders(headers)) {
|
|
709
|
+
try {
|
|
710
|
+
const signatureResult = await verifyAgentSignature(
|
|
711
|
+
input.method || "GET",
|
|
712
|
+
input.url || "/",
|
|
713
|
+
headers
|
|
714
|
+
);
|
|
715
|
+
if (signatureResult.isValid) {
|
|
716
|
+
confidence = signatureResult.confidence;
|
|
717
|
+
reasons.push(`verified_signature:${signatureResult.agent?.toLowerCase() || "unknown"}`);
|
|
718
|
+
if (signatureResult.agent) {
|
|
719
|
+
detectedAgent = {
|
|
720
|
+
type: signatureResult.agent.toLowerCase(),
|
|
721
|
+
name: signatureResult.agent
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
verificationMethod = signatureResult.verificationMethod;
|
|
725
|
+
if (signatureResult.keyid) {
|
|
726
|
+
reasons.push(`keyid:${signatureResult.keyid}`);
|
|
727
|
+
}
|
|
728
|
+
} else {
|
|
729
|
+
confidence = Math.max(confidence, 0.3);
|
|
730
|
+
reasons.push("invalid_signature");
|
|
731
|
+
if (signatureResult.reason) {
|
|
732
|
+
reasons.push(`signature_error:${signatureResult.reason}`);
|
|
733
|
+
}
|
|
734
|
+
if (isChatGPTSignature(headers)) {
|
|
735
|
+
reasons.push("claims_chatgpt");
|
|
736
|
+
detectedAgent = { type: "chatgpt", name: "ChatGPT (unverified)" };
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
} catch (error) {
|
|
740
|
+
console.error("[EdgeAgentDetector] Signature verification error:", error);
|
|
741
|
+
confidence = Math.max(confidence, 0.2);
|
|
742
|
+
reasons.push("signature_verification_error");
|
|
743
|
+
}
|
|
603
744
|
}
|
|
604
745
|
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
605
746
|
if (userAgent) {
|
|
606
|
-
for (const { pattern, type, name } of
|
|
747
|
+
for (const { pattern, type, name } of AI_AGENT_PATTERNS) {
|
|
607
748
|
if (pattern.test(userAgent)) {
|
|
608
749
|
const highConfidenceAgents = [
|
|
609
750
|
"chatgpt",
|
|
@@ -638,7 +779,7 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
638
779
|
}
|
|
639
780
|
const ip = input.ip || input.ipAddress;
|
|
640
781
|
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
641
|
-
for (const [provider, prefixes] of Object.entries(
|
|
782
|
+
for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS)) {
|
|
642
783
|
if (prefixes.some((prefix) => ip.startsWith(prefix))) {
|
|
643
784
|
confidence = Math.max(confidence, 0.4);
|
|
644
785
|
reasons.push(`cloud_provider:${provider}`);
|
|
@@ -646,7 +787,7 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
646
787
|
}
|
|
647
788
|
}
|
|
648
789
|
}
|
|
649
|
-
if (reasons.length > 2) {
|
|
790
|
+
if (reasons.length > 2 && confidence < 1) {
|
|
650
791
|
confidence = Math.min(confidence * 1.2, 0.95);
|
|
651
792
|
}
|
|
652
793
|
return {
|
|
@@ -655,79 +796,16 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
655
796
|
...detectedAgent && { detectedAgent },
|
|
656
797
|
reasons,
|
|
657
798
|
...verificationMethod && { verificationMethod },
|
|
658
|
-
forgeabilityRisk: confidence > 0.8 ? "medium" : "high",
|
|
799
|
+
forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 0.8 ? "medium" : "high",
|
|
659
800
|
timestamp: /* @__PURE__ */ new Date()
|
|
660
801
|
};
|
|
661
802
|
}
|
|
662
|
-
/**
|
|
663
|
-
* Analyze request with WASM enhancement when available
|
|
664
|
-
*/
|
|
665
|
-
async analyze(input) {
|
|
666
|
-
await this.init();
|
|
667
|
-
if (this.wasmEnabled) {
|
|
668
|
-
try {
|
|
669
|
-
const wasmResult = await detectAgentWithWasm(
|
|
670
|
-
input.userAgent || input.headers?.["user-agent"],
|
|
671
|
-
input.headers || {},
|
|
672
|
-
input.ip || input.ipAddress
|
|
673
|
-
);
|
|
674
|
-
if (wasmResult) {
|
|
675
|
-
console.log("[AgentShield] Using WASM detection result");
|
|
676
|
-
const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
|
|
677
|
-
return {
|
|
678
|
-
isAgent: wasmResult.isAgent,
|
|
679
|
-
confidence: wasmResult.confidence,
|
|
680
|
-
...detectedAgent && { detectedAgent },
|
|
681
|
-
reasons: [`wasm:${wasmResult.verificationMethod}`],
|
|
682
|
-
verificationMethod: wasmResult.verificationMethod,
|
|
683
|
-
forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 0.9 ? "medium" : "high",
|
|
684
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
} catch (error) {
|
|
688
|
-
console.error("[AgentShield] WASM detection error:", error);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
const patternResult = await this.patternDetection(input);
|
|
692
|
-
if (this.wasmEnabled && patternResult.confidence >= 0.85) {
|
|
693
|
-
patternResult.confidence = Math.min(0.95, patternResult.confidence + 0.1);
|
|
694
|
-
patternResult.reasons.push("wasm_enhanced");
|
|
695
|
-
if (!patternResult.verificationMethod) {
|
|
696
|
-
patternResult.verificationMethod = "wasm-enhanced";
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
return patternResult;
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Map agent names from WASM to consistent format
|
|
703
|
-
*/
|
|
704
|
-
mapAgentName(agent) {
|
|
705
|
-
const lowerAgent = agent.toLowerCase();
|
|
706
|
-
if (lowerAgent.includes("chatgpt")) {
|
|
707
|
-
return { type: "chatgpt", name: "ChatGPT" };
|
|
708
|
-
} else if (lowerAgent.includes("claude")) {
|
|
709
|
-
return { type: "claude", name: "Claude" };
|
|
710
|
-
} else if (lowerAgent.includes("perplexity")) {
|
|
711
|
-
return { type: "perplexity", name: "Perplexity" };
|
|
712
|
-
} else if (lowerAgent.includes("bing")) {
|
|
713
|
-
return { type: "bing", name: "Bing AI" };
|
|
714
|
-
} else if (lowerAgent.includes("anthropic")) {
|
|
715
|
-
return { type: "anthropic", name: "Anthropic" };
|
|
716
|
-
}
|
|
717
|
-
return { type: "unknown", name: agent };
|
|
718
|
-
}
|
|
719
803
|
};
|
|
720
|
-
var
|
|
804
|
+
var EdgeAgentDetectorWrapper = class {
|
|
721
805
|
detector;
|
|
722
806
|
events = /* @__PURE__ */ new Map();
|
|
723
|
-
constructor(
|
|
724
|
-
this.detector = new
|
|
725
|
-
if (config?.baseUrl) {
|
|
726
|
-
this.detector.setBaseUrl(config.baseUrl);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
setBaseUrl(url) {
|
|
730
|
-
this.detector.setBaseUrl(url);
|
|
807
|
+
constructor(_config) {
|
|
808
|
+
this.detector = new EdgeAgentDetector();
|
|
731
809
|
}
|
|
732
810
|
async analyze(input) {
|
|
733
811
|
const result = await this.detector.analyze(input);
|
|
@@ -748,10 +826,13 @@ var EdgeAgentDetectorWrapperWithWasm = class {
|
|
|
748
826
|
handlers.forEach((handler) => handler(...args));
|
|
749
827
|
}
|
|
750
828
|
async init() {
|
|
751
|
-
|
|
829
|
+
return;
|
|
752
830
|
}
|
|
753
831
|
};
|
|
754
832
|
|
|
833
|
+
// src/middleware.ts
|
|
834
|
+
init_edge_detector_with_wasm();
|
|
835
|
+
|
|
755
836
|
// src/session-tracker.ts
|
|
756
837
|
var EdgeSessionTracker = class {
|
|
757
838
|
config;
|
|
@@ -1102,48 +1183,80 @@ function createAgentShieldMiddleware2(config) {
|
|
|
1102
1183
|
};
|
|
1103
1184
|
}
|
|
1104
1185
|
|
|
1105
|
-
// src/
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1186
|
+
// src/edge-safe-detector.ts
|
|
1187
|
+
var AI_AGENT_PATTERNS3 = [
|
|
1188
|
+
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
1189
|
+
{ pattern: /claude-web/i, type: "claude", name: "Claude" },
|
|
1190
|
+
{ pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
|
|
1191
|
+
{ pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
|
|
1192
|
+
{ pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
|
|
1193
|
+
{ pattern: /bingbot/i, type: "bing", name: "Bing AI" },
|
|
1194
|
+
{ pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
|
|
1195
|
+
];
|
|
1196
|
+
var EdgeSafeDetector = class {
|
|
1197
|
+
async analyze(input) {
|
|
1198
|
+
const reasons = [];
|
|
1199
|
+
let detectedAgent;
|
|
1200
|
+
let confidence = 0;
|
|
1201
|
+
const headers = input.headers || {};
|
|
1202
|
+
const normalizedHeaders = {};
|
|
1203
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1204
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
1110
1205
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1206
|
+
const userAgent = input.userAgent || normalizedHeaders["user-agent"] || "";
|
|
1207
|
+
if (userAgent) {
|
|
1208
|
+
for (const { pattern, type, name } of AI_AGENT_PATTERNS3) {
|
|
1209
|
+
if (pattern.test(userAgent)) {
|
|
1210
|
+
confidence = 0.85;
|
|
1211
|
+
reasons.push(`known_pattern:${type}`);
|
|
1212
|
+
detectedAgent = { type, name };
|
|
1213
|
+
break;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1113
1216
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1217
|
+
const hasChrome = userAgent.toLowerCase().includes("chrome");
|
|
1218
|
+
const hasFirefox = userAgent.toLowerCase().includes("firefox");
|
|
1219
|
+
const hasSafari = userAgent.toLowerCase().includes("safari");
|
|
1220
|
+
const hasBrowserUA = hasChrome || hasFirefox || hasSafari;
|
|
1221
|
+
if (hasBrowserUA) {
|
|
1222
|
+
const hasSecChUa = !!normalizedHeaders["sec-ch-ua"];
|
|
1223
|
+
const hasSecFetch = !!normalizedHeaders["sec-fetch-site"];
|
|
1224
|
+
const hasAcceptLanguage = !!normalizedHeaders["accept-language"];
|
|
1225
|
+
const hasCookies = !!normalizedHeaders["cookie"];
|
|
1226
|
+
const missingHeaders = [];
|
|
1227
|
+
if (!hasSecChUa && hasChrome) missingHeaders.push("sec-ch-ua");
|
|
1228
|
+
if (!hasSecFetch) missingHeaders.push("sec-fetch");
|
|
1229
|
+
if (!hasAcceptLanguage) missingHeaders.push("accept-language");
|
|
1230
|
+
if (!hasCookies) missingHeaders.push("cookies");
|
|
1231
|
+
if (missingHeaders.length >= 2) {
|
|
1232
|
+
confidence = Math.max(confidence, 0.85);
|
|
1233
|
+
reasons.push("browser_ua_missing_headers");
|
|
1234
|
+
if (!detectedAgent && hasChrome && !hasSecChUa) {
|
|
1235
|
+
detectedAgent = { type: "perplexity", name: "Perplexity" };
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
const aiHeaders = [
|
|
1240
|
+
"openai-conversation-id",
|
|
1241
|
+
"anthropic-client-id",
|
|
1242
|
+
"x-goog-api-client"
|
|
1243
|
+
];
|
|
1244
|
+
const foundAiHeaders = aiHeaders.filter((h) => normalizedHeaders[h]);
|
|
1245
|
+
if (foundAiHeaders.length > 0) {
|
|
1246
|
+
confidence = Math.max(confidence, 0.6);
|
|
1247
|
+
reasons.push(`ai_headers:${foundAiHeaders.length}`);
|
|
1248
|
+
}
|
|
1249
|
+
return {
|
|
1250
|
+
isAgent: confidence > 0.3,
|
|
1251
|
+
confidence,
|
|
1252
|
+
detectedAgent,
|
|
1253
|
+
reasons,
|
|
1254
|
+
verificationMethod: "pattern",
|
|
1255
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1256
|
+
confidenceLevel: confidence >= 0.8 ? "high" : confidence >= 0.5 ? "medium" : "low"
|
|
1257
|
+
};
|
|
1144
1258
|
}
|
|
1145
|
-
|
|
1146
|
-
}
|
|
1259
|
+
};
|
|
1147
1260
|
|
|
1148
1261
|
// src/storage/memory-adapter.ts
|
|
1149
1262
|
var MemoryStorageAdapter = class {
|
|
@@ -1418,39 +1531,48 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
|
|
|
1418
1531
|
};
|
|
1419
1532
|
let detector = null;
|
|
1420
1533
|
let detectorInitPromise = null;
|
|
1534
|
+
let wasmConfidenceUtils = null;
|
|
1421
1535
|
const getDetector = async (requestUrl) => {
|
|
1422
1536
|
if (detector) {
|
|
1423
|
-
if (requestUrl && "setBaseUrl" in detector) {
|
|
1424
|
-
detector.setBaseUrl(requestUrl);
|
|
1425
|
-
}
|
|
1426
1537
|
return detector;
|
|
1427
1538
|
}
|
|
1428
1539
|
if (detectorInitPromise) {
|
|
1429
1540
|
await detectorInitPromise;
|
|
1430
|
-
if (requestUrl && detector && "setBaseUrl" in detector) {
|
|
1431
|
-
detector.setBaseUrl(requestUrl);
|
|
1432
|
-
}
|
|
1433
1541
|
return detector;
|
|
1434
1542
|
}
|
|
1435
1543
|
detectorInitPromise = (async () => {
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1544
|
+
const isEdgeRuntime = typeof globalThis.EdgeRuntime !== "undefined" || process.env.NEXT_RUNTIME === "edge";
|
|
1545
|
+
if (isEdgeRuntime) {
|
|
1546
|
+
console.log("[AgentShield] Edge Runtime detected - using pattern detection");
|
|
1547
|
+
detector = new EdgeSafeDetector();
|
|
1548
|
+
} else {
|
|
1549
|
+
try {
|
|
1550
|
+
try {
|
|
1551
|
+
const wasmUtils = await Promise.resolve().then(() => (init_wasm_confidence(), wasm_confidence_exports));
|
|
1552
|
+
wasmConfidenceUtils = wasmUtils;
|
|
1553
|
+
const wasmAvailable = await wasmUtils.checkWasmAvailability();
|
|
1554
|
+
if (wasmAvailable) {
|
|
1555
|
+
const { EdgeAgentDetectorWrapperWithWasm: EdgeAgentDetectorWrapperWithWasm2 } = await Promise.resolve().then(() => (init_edge_detector_with_wasm(), edge_detector_with_wasm_exports));
|
|
1556
|
+
console.log("[AgentShield] \u2705 WASM support detected - enhanced detection enabled");
|
|
1557
|
+
detector = new EdgeAgentDetectorWrapperWithWasm2({ enableWasm: true });
|
|
1558
|
+
if (requestUrl && "setBaseUrl" in detector) {
|
|
1559
|
+
detector.setBaseUrl(requestUrl);
|
|
1560
|
+
}
|
|
1561
|
+
} else {
|
|
1562
|
+
console.log("[AgentShield] \u2139\uFE0F Using pattern-based detection (WASM not available)");
|
|
1563
|
+
detector = new EdgeSafeDetector();
|
|
1564
|
+
}
|
|
1565
|
+
} catch (wasmError) {
|
|
1566
|
+
console.log("[AgentShield] WASM utilities not available, using pattern detection");
|
|
1567
|
+
detector = new EdgeSafeDetector();
|
|
1568
|
+
}
|
|
1569
|
+
} catch (error) {
|
|
1570
|
+
console.warn("[AgentShield] Failed to initialize enhanced detector, using fallback");
|
|
1571
|
+
detector = new EdgeSafeDetector();
|
|
1444
1572
|
}
|
|
1445
|
-
} catch (error) {
|
|
1446
|
-
console.warn("[AgentShield] Failed to initialize WASM, using fallback:", error);
|
|
1447
|
-
detector = new EdgeAgentDetectorWrapper({});
|
|
1448
1573
|
}
|
|
1449
1574
|
})();
|
|
1450
1575
|
await detectorInitPromise;
|
|
1451
|
-
if (requestUrl && detector && "setBaseUrl" in detector) {
|
|
1452
|
-
detector.setBaseUrl(requestUrl);
|
|
1453
|
-
}
|
|
1454
1576
|
return detector;
|
|
1455
1577
|
};
|
|
1456
1578
|
const sessionManager = new SessionManager();
|
|
@@ -1476,11 +1598,11 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
|
|
|
1476
1598
|
const result = await activeDetector.analyze(context);
|
|
1477
1599
|
let finalConfidence = result.confidence;
|
|
1478
1600
|
let verificationMethod = result.verificationMethod || "pattern";
|
|
1479
|
-
if (result.isAgent) {
|
|
1601
|
+
if (result.isAgent && wasmConfidenceUtils) {
|
|
1480
1602
|
const reasons = result.reasons || [];
|
|
1481
|
-
if (shouldIndicateWasmVerification(result.confidence)) {
|
|
1482
|
-
finalConfidence = getWasmConfidenceBoost(result.confidence, reasons);
|
|
1483
|
-
verificationMethod = getVerificationMethod(finalConfidence, reasons);
|
|
1603
|
+
if (wasmConfidenceUtils.shouldIndicateWasmVerification(result.confidence)) {
|
|
1604
|
+
finalConfidence = wasmConfidenceUtils.getWasmConfidenceBoost(result.confidence, reasons);
|
|
1605
|
+
verificationMethod = wasmConfidenceUtils.getVerificationMethod(finalConfidence, reasons);
|
|
1484
1606
|
}
|
|
1485
1607
|
}
|
|
1486
1608
|
if (result.isAgent && finalConfidence >= (config.confidenceThreshold ?? 0.7)) {
|