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