@kya-os/agentshield-nextjs 0.1.38 → 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/create-middleware.js +59 -43
- package/dist/create-middleware.js.map +1 -1
- package/dist/create-middleware.mjs +59 -43
- package/dist/create-middleware.mjs.map +1 -1
- package/dist/index.js +655 -517
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +655 -517
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.js +59 -43
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +59 -43
- package/dist/middleware.mjs.map +1 -1
- package/package.json +1 -1
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 = {
|
|
@@ -173,398 +635,67 @@ async function verifyAgentSignature(method, path, headers) {
|
|
|
173
635
|
isValid: false,
|
|
174
636
|
confidence: 0,
|
|
175
637
|
reason: "Key is not valid at current time",
|
|
176
|
-
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
|
-
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
400
|
-
try {
|
|
401
|
-
const wasmUrl2 = getWasmUrl();
|
|
402
|
-
const response2 = await fetch(wasmUrl2);
|
|
403
|
-
if (!response2.ok) {
|
|
404
|
-
throw new Error(`Failed to fetch WASM: ${response2.status}`);
|
|
405
|
-
}
|
|
406
|
-
const streamResponse = response2.clone();
|
|
407
|
-
const { instance } = await WebAssembly.instantiateStreaming(
|
|
408
|
-
streamResponse,
|
|
409
|
-
{
|
|
410
|
-
wbg: {
|
|
411
|
-
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
412
|
-
console.log("WASM:", ptr, len);
|
|
413
|
-
},
|
|
414
|
-
__wbindgen_throw: (ptr, len) => {
|
|
415
|
-
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
);
|
|
420
|
-
wasmInstance = instance;
|
|
421
|
-
wasmExports = instance.exports;
|
|
422
|
-
console.log("[AgentShield] \u2705 WASM module initialized with streaming");
|
|
423
|
-
return;
|
|
424
|
-
} catch (streamError) {
|
|
425
|
-
console.log("[AgentShield] Streaming compilation failed, falling back to standard compilation");
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
const wasmUrl = getWasmUrl();
|
|
429
|
-
const response = await fetch(wasmUrl);
|
|
430
|
-
if (!response.ok) {
|
|
431
|
-
throw new Error(`Failed to fetch WASM: ${response.status}`);
|
|
432
|
-
}
|
|
433
|
-
const wasmArrayBuffer = await response.arrayBuffer();
|
|
434
|
-
const compiledModule = await WebAssembly.compile(wasmArrayBuffer);
|
|
435
|
-
const imports = {
|
|
436
|
-
wbg: {
|
|
437
|
-
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
438
|
-
console.log("WASM:", ptr, len);
|
|
439
|
-
},
|
|
440
|
-
__wbindgen_throw: (ptr, len) => {
|
|
441
|
-
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
};
|
|
445
|
-
wasmInstance = await WebAssembly.instantiate(compiledModule, imports);
|
|
446
|
-
wasmExports = wasmInstance.exports;
|
|
447
|
-
console.log("[AgentShield] \u2705 WASM module initialized via fallback");
|
|
448
|
-
} catch (error) {
|
|
449
|
-
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
450
|
-
wasmExports = null;
|
|
451
|
-
}
|
|
452
|
-
})();
|
|
453
|
-
await initPromise;
|
|
454
|
-
return !!wasmExports;
|
|
455
|
-
}
|
|
456
|
-
async function detectAgentWithWasm(userAgent, headers, ipAddress) {
|
|
457
|
-
const initialized = await initWasm();
|
|
458
|
-
if (!initialized || !wasmExports) {
|
|
459
|
-
return null;
|
|
638
|
+
verificationMethod: "none"
|
|
639
|
+
};
|
|
460
640
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
userAgent || "",
|
|
466
|
-
headersJson,
|
|
467
|
-
ipAddress || "",
|
|
468
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
469
|
-
);
|
|
470
|
-
return {
|
|
471
|
-
isAgent: result.is_agent || false,
|
|
472
|
-
confidence: result.confidence || 0,
|
|
473
|
-
agent: result.agent,
|
|
474
|
-
verificationMethod: result.verification_method || "wasm",
|
|
475
|
-
riskLevel: result.risk_level || "low"
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
console.warn("[AgentShield] WASM exports do not include detect_agent function");
|
|
479
|
-
return null;
|
|
480
|
-
} catch (error) {
|
|
481
|
-
console.error("[AgentShield] WASM detection failed:", error);
|
|
482
|
-
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);
|
|
483
645
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
const initialized = await initWasm();
|
|
487
|
-
if (!initialized || !wasmExports) {
|
|
488
|
-
return null;
|
|
646
|
+
if (signatureValue.endsWith(":")) {
|
|
647
|
+
signatureValue = signatureValue.slice(0, -1);
|
|
489
648
|
}
|
|
490
|
-
|
|
491
|
-
|
|
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
|
+
};
|
|
492
670
|
}
|
|
493
|
-
return "unknown";
|
|
494
671
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
} catch {
|
|
502
|
-
return false;
|
|
503
|
-
}
|
|
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);
|
|
504
678
|
}
|
|
505
679
|
|
|
506
|
-
// src/edge-detector-
|
|
507
|
-
var
|
|
680
|
+
// src/edge-detector-wrapper.ts
|
|
681
|
+
var AI_AGENT_PATTERNS = [
|
|
508
682
|
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
509
683
|
{ pattern: /claude-web/i, type: "claude", name: "Claude" },
|
|
510
684
|
{ pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
|
|
511
685
|
{ pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
|
|
512
686
|
{ pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
|
|
513
687
|
{ pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
|
|
688
|
+
// Fallback
|
|
514
689
|
{ pattern: /bingbot/i, type: "bing", name: "Bing AI" },
|
|
515
690
|
{ pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
|
|
516
691
|
];
|
|
517
|
-
var
|
|
692
|
+
var CLOUD_PROVIDERS = {
|
|
518
693
|
aws: ["54.", "52.", "35.", "18.", "3."],
|
|
519
694
|
gcp: ["35.", "34.", "104.", "107.", "108."],
|
|
520
695
|
azure: ["13.", "20.", "40.", "52.", "104."]
|
|
521
696
|
};
|
|
522
|
-
var
|
|
523
|
-
|
|
524
|
-
this.enableWasm = enableWasm;
|
|
525
|
-
}
|
|
526
|
-
wasmEnabled = false;
|
|
527
|
-
initPromise = null;
|
|
528
|
-
baseUrl = null;
|
|
529
|
-
/**
|
|
530
|
-
* Set the base URL for WASM loading in Edge Runtime
|
|
531
|
-
*/
|
|
532
|
-
setBaseUrl(url) {
|
|
533
|
-
this.baseUrl = url;
|
|
534
|
-
setWasmBaseUrl(url);
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Initialize the detector (including WASM if enabled)
|
|
538
|
-
*/
|
|
539
|
-
async init() {
|
|
540
|
-
if (!this.enableWasm) {
|
|
541
|
-
this.wasmEnabled = false;
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
if (this.initPromise) {
|
|
545
|
-
await this.initPromise;
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
this.initPromise = (async () => {
|
|
549
|
-
try {
|
|
550
|
-
const wasmAvailable = await isWasmAvailable();
|
|
551
|
-
this.wasmEnabled = wasmAvailable;
|
|
552
|
-
if (this.wasmEnabled) {
|
|
553
|
-
console.log("[AgentShield] WASM detection enabled");
|
|
554
|
-
} else {
|
|
555
|
-
console.log("[AgentShield] WASM not available, using pattern detection");
|
|
556
|
-
}
|
|
557
|
-
} catch (error) {
|
|
558
|
-
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
559
|
-
this.wasmEnabled = false;
|
|
560
|
-
}
|
|
561
|
-
})();
|
|
562
|
-
await this.initPromise;
|
|
563
|
-
}
|
|
564
|
-
/**
|
|
565
|
-
* Pattern-based detection (fallback)
|
|
566
|
-
*/
|
|
567
|
-
async patternDetection(input) {
|
|
697
|
+
var EdgeAgentDetector = class {
|
|
698
|
+
async analyze(input) {
|
|
568
699
|
const reasons = [];
|
|
569
700
|
let detectedAgent;
|
|
570
701
|
let verificationMethod;
|
|
@@ -574,20 +705,46 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
574
705
|
for (const [key, value] of Object.entries(headers)) {
|
|
575
706
|
normalizedHeaders[key.toLowerCase()] = value;
|
|
576
707
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
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
|
+
}
|
|
587
744
|
}
|
|
588
745
|
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
589
746
|
if (userAgent) {
|
|
590
|
-
for (const { pattern, type, name } of
|
|
747
|
+
for (const { pattern, type, name } of AI_AGENT_PATTERNS) {
|
|
591
748
|
if (pattern.test(userAgent)) {
|
|
592
749
|
const highConfidenceAgents = [
|
|
593
750
|
"chatgpt",
|
|
@@ -622,7 +779,7 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
622
779
|
}
|
|
623
780
|
const ip = input.ip || input.ipAddress;
|
|
624
781
|
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
625
|
-
for (const [provider, prefixes] of Object.entries(
|
|
782
|
+
for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS)) {
|
|
626
783
|
if (prefixes.some((prefix) => ip.startsWith(prefix))) {
|
|
627
784
|
confidence = Math.max(confidence, 0.4);
|
|
628
785
|
reasons.push(`cloud_provider:${provider}`);
|
|
@@ -630,7 +787,7 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
630
787
|
}
|
|
631
788
|
}
|
|
632
789
|
}
|
|
633
|
-
if (reasons.length > 2) {
|
|
790
|
+
if (reasons.length > 2 && confidence < 1) {
|
|
634
791
|
confidence = Math.min(confidence * 1.2, 0.95);
|
|
635
792
|
}
|
|
636
793
|
return {
|
|
@@ -639,79 +796,16 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
639
796
|
...detectedAgent && { detectedAgent },
|
|
640
797
|
reasons,
|
|
641
798
|
...verificationMethod && { verificationMethod },
|
|
642
|
-
forgeabilityRisk: confidence > 0.8 ? "medium" : "high",
|
|
799
|
+
forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 0.8 ? "medium" : "high",
|
|
643
800
|
timestamp: /* @__PURE__ */ new Date()
|
|
644
801
|
};
|
|
645
802
|
}
|
|
646
|
-
/**
|
|
647
|
-
* Analyze request with WASM enhancement when available
|
|
648
|
-
*/
|
|
649
|
-
async analyze(input) {
|
|
650
|
-
await this.init();
|
|
651
|
-
if (this.wasmEnabled) {
|
|
652
|
-
try {
|
|
653
|
-
const wasmResult = await detectAgentWithWasm(
|
|
654
|
-
input.userAgent || input.headers?.["user-agent"],
|
|
655
|
-
input.headers || {},
|
|
656
|
-
input.ip || input.ipAddress
|
|
657
|
-
);
|
|
658
|
-
if (wasmResult) {
|
|
659
|
-
console.log("[AgentShield] Using WASM detection result");
|
|
660
|
-
const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
|
|
661
|
-
return {
|
|
662
|
-
isAgent: wasmResult.isAgent,
|
|
663
|
-
confidence: wasmResult.confidence,
|
|
664
|
-
...detectedAgent && { detectedAgent },
|
|
665
|
-
reasons: [`wasm:${wasmResult.verificationMethod}`],
|
|
666
|
-
verificationMethod: wasmResult.verificationMethod,
|
|
667
|
-
forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 0.9 ? "medium" : "high",
|
|
668
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
669
|
-
};
|
|
670
|
-
}
|
|
671
|
-
} catch (error) {
|
|
672
|
-
console.error("[AgentShield] WASM detection error:", error);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
const patternResult = await this.patternDetection(input);
|
|
676
|
-
if (this.wasmEnabled && patternResult.confidence >= 0.85) {
|
|
677
|
-
patternResult.confidence = Math.min(0.95, patternResult.confidence + 0.1);
|
|
678
|
-
patternResult.reasons.push("wasm_enhanced");
|
|
679
|
-
if (!patternResult.verificationMethod) {
|
|
680
|
-
patternResult.verificationMethod = "wasm-enhanced";
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
return patternResult;
|
|
684
|
-
}
|
|
685
|
-
/**
|
|
686
|
-
* Map agent names from WASM to consistent format
|
|
687
|
-
*/
|
|
688
|
-
mapAgentName(agent) {
|
|
689
|
-
const lowerAgent = agent.toLowerCase();
|
|
690
|
-
if (lowerAgent.includes("chatgpt")) {
|
|
691
|
-
return { type: "chatgpt", name: "ChatGPT" };
|
|
692
|
-
} else if (lowerAgent.includes("claude")) {
|
|
693
|
-
return { type: "claude", name: "Claude" };
|
|
694
|
-
} else if (lowerAgent.includes("perplexity")) {
|
|
695
|
-
return { type: "perplexity", name: "Perplexity" };
|
|
696
|
-
} else if (lowerAgent.includes("bing")) {
|
|
697
|
-
return { type: "bing", name: "Bing AI" };
|
|
698
|
-
} else if (lowerAgent.includes("anthropic")) {
|
|
699
|
-
return { type: "anthropic", name: "Anthropic" };
|
|
700
|
-
}
|
|
701
|
-
return { type: "unknown", name: agent };
|
|
702
|
-
}
|
|
703
803
|
};
|
|
704
|
-
var
|
|
804
|
+
var EdgeAgentDetectorWrapper = class {
|
|
705
805
|
detector;
|
|
706
806
|
events = /* @__PURE__ */ new Map();
|
|
707
|
-
constructor(
|
|
708
|
-
this.detector = new
|
|
709
|
-
if (config?.baseUrl) {
|
|
710
|
-
this.detector.setBaseUrl(config.baseUrl);
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
setBaseUrl(url) {
|
|
714
|
-
this.detector.setBaseUrl(url);
|
|
807
|
+
constructor(_config) {
|
|
808
|
+
this.detector = new EdgeAgentDetector();
|
|
715
809
|
}
|
|
716
810
|
async analyze(input) {
|
|
717
811
|
const result = await this.detector.analyze(input);
|
|
@@ -732,10 +826,13 @@ var EdgeAgentDetectorWrapperWithWasm = class {
|
|
|
732
826
|
handlers.forEach((handler) => handler(...args));
|
|
733
827
|
}
|
|
734
828
|
async init() {
|
|
735
|
-
|
|
829
|
+
return;
|
|
736
830
|
}
|
|
737
831
|
};
|
|
738
832
|
|
|
833
|
+
// src/middleware.ts
|
|
834
|
+
init_edge_detector_with_wasm();
|
|
835
|
+
|
|
739
836
|
// src/session-tracker.ts
|
|
740
837
|
var EdgeSessionTracker = class {
|
|
741
838
|
config;
|
|
@@ -1086,48 +1183,80 @@ function createAgentShieldMiddleware2(config) {
|
|
|
1086
1183
|
};
|
|
1087
1184
|
}
|
|
1088
1185
|
|
|
1089
|
-
// src/
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
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;
|
|
1094
1205
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
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
|
+
}
|
|
1097
1216
|
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
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
|
+
};
|
|
1128
1258
|
}
|
|
1129
|
-
|
|
1130
|
-
}
|
|
1259
|
+
};
|
|
1131
1260
|
|
|
1132
1261
|
// src/storage/memory-adapter.ts
|
|
1133
1262
|
var MemoryStorageAdapter = class {
|
|
@@ -1402,39 +1531,48 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
|
|
|
1402
1531
|
};
|
|
1403
1532
|
let detector = null;
|
|
1404
1533
|
let detectorInitPromise = null;
|
|
1534
|
+
let wasmConfidenceUtils = null;
|
|
1405
1535
|
const getDetector = async (requestUrl) => {
|
|
1406
1536
|
if (detector) {
|
|
1407
|
-
if (requestUrl && "setBaseUrl" in detector) {
|
|
1408
|
-
detector.setBaseUrl(requestUrl);
|
|
1409
|
-
}
|
|
1410
1537
|
return detector;
|
|
1411
1538
|
}
|
|
1412
1539
|
if (detectorInitPromise) {
|
|
1413
1540
|
await detectorInitPromise;
|
|
1414
|
-
if (requestUrl && detector && "setBaseUrl" in detector) {
|
|
1415
|
-
detector.setBaseUrl(requestUrl);
|
|
1416
|
-
}
|
|
1417
1541
|
return detector;
|
|
1418
1542
|
}
|
|
1419
1543
|
detectorInitPromise = (async () => {
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
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();
|
|
1428
1572
|
}
|
|
1429
|
-
} catch (error) {
|
|
1430
|
-
console.warn("[AgentShield] Failed to initialize WASM, using fallback:", error);
|
|
1431
|
-
detector = new EdgeAgentDetectorWrapper({});
|
|
1432
1573
|
}
|
|
1433
1574
|
})();
|
|
1434
1575
|
await detectorInitPromise;
|
|
1435
|
-
if (requestUrl && detector && "setBaseUrl" in detector) {
|
|
1436
|
-
detector.setBaseUrl(requestUrl);
|
|
1437
|
-
}
|
|
1438
1576
|
return detector;
|
|
1439
1577
|
};
|
|
1440
1578
|
const sessionManager = new SessionManager();
|
|
@@ -1460,11 +1598,11 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
|
|
|
1460
1598
|
const result = await activeDetector.analyze(context);
|
|
1461
1599
|
let finalConfidence = result.confidence;
|
|
1462
1600
|
let verificationMethod = result.verificationMethod || "pattern";
|
|
1463
|
-
if (result.isAgent) {
|
|
1601
|
+
if (result.isAgent && wasmConfidenceUtils) {
|
|
1464
1602
|
const reasons = result.reasons || [];
|
|
1465
|
-
if (shouldIndicateWasmVerification(result.confidence)) {
|
|
1466
|
-
finalConfidence = getWasmConfidenceBoost(result.confidence, reasons);
|
|
1467
|
-
verificationMethod = getVerificationMethod(finalConfidence, reasons);
|
|
1603
|
+
if (wasmConfidenceUtils.shouldIndicateWasmVerification(result.confidence)) {
|
|
1604
|
+
finalConfidence = wasmConfidenceUtils.getWasmConfidenceBoost(result.confidence, reasons);
|
|
1605
|
+
verificationMethod = wasmConfidenceUtils.getVerificationMethod(finalConfidence, reasons);
|
|
1468
1606
|
}
|
|
1469
1607
|
}
|
|
1470
1608
|
if (result.isAgent && finalConfidence >= (config.confidenceThreshold ?? 0.7)) {
|