@kya-os/agentshield-nextjs 0.1.40 → 0.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +654 -532
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +654 -532
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -16
package/dist/index.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 = {
|
|
@@ -176,413 +638,66 @@ async function verifyAgentSignature(method, path, headers) {
|
|
|
176
638
|
confidence: 0,
|
|
177
639
|
reason: "Key is not valid at current time",
|
|
178
640
|
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
|
-
const controller = new AbortController();
|
|
402
|
-
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
403
|
-
try {
|
|
404
|
-
const wasmUrl = getWasmUrl();
|
|
405
|
-
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
406
|
-
try {
|
|
407
|
-
const response2 = await fetch(wasmUrl, { signal: controller.signal });
|
|
408
|
-
clearTimeout(timeout);
|
|
409
|
-
if (!response2.ok) {
|
|
410
|
-
throw new Error(`Failed to fetch WASM: ${response2.status}`);
|
|
411
|
-
}
|
|
412
|
-
const streamResponse = response2.clone();
|
|
413
|
-
const { instance } = await WebAssembly.instantiateStreaming(
|
|
414
|
-
streamResponse,
|
|
415
|
-
{
|
|
416
|
-
wbg: {
|
|
417
|
-
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
418
|
-
console.log("WASM:", ptr, len);
|
|
419
|
-
},
|
|
420
|
-
__wbindgen_throw: (ptr, len) => {
|
|
421
|
-
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
);
|
|
426
|
-
wasmInstance = instance;
|
|
427
|
-
wasmExports = instance.exports;
|
|
428
|
-
console.log("[AgentShield] \u2705 WASM module initialized with streaming");
|
|
429
|
-
return;
|
|
430
|
-
} catch (streamError) {
|
|
431
|
-
if (!controller.signal.aborted) {
|
|
432
|
-
console.log("[AgentShield] Streaming compilation failed, falling back to standard compilation");
|
|
433
|
-
} else {
|
|
434
|
-
throw streamError;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
const response = await fetch(wasmUrl, { signal: controller.signal });
|
|
439
|
-
clearTimeout(timeout);
|
|
440
|
-
if (!response.ok) {
|
|
441
|
-
throw new Error(`Failed to fetch WASM: ${response.status}`);
|
|
442
|
-
}
|
|
443
|
-
const wasmArrayBuffer = await response.arrayBuffer();
|
|
444
|
-
const compiledModule = await WebAssembly.compile(wasmArrayBuffer);
|
|
445
|
-
const imports = {
|
|
446
|
-
wbg: {
|
|
447
|
-
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
448
|
-
console.log("WASM:", ptr, len);
|
|
449
|
-
},
|
|
450
|
-
__wbindgen_throw: (ptr, len) => {
|
|
451
|
-
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
wasmInstance = await WebAssembly.instantiate(compiledModule, imports);
|
|
456
|
-
wasmExports = wasmInstance.exports;
|
|
457
|
-
console.log("[AgentShield] \u2705 WASM module initialized via fallback");
|
|
458
|
-
} catch (fetchError) {
|
|
459
|
-
if (fetchError.name === "AbortError") {
|
|
460
|
-
console.warn("[AgentShield] WASM fetch timed out after 3 seconds - using pattern detection");
|
|
461
|
-
} else {
|
|
462
|
-
console.warn("[AgentShield] Failed to fetch WASM file:", fetchError.message);
|
|
463
|
-
}
|
|
464
|
-
wasmExports = null;
|
|
465
|
-
}
|
|
466
|
-
} catch (error) {
|
|
467
|
-
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
468
|
-
wasmExports = null;
|
|
469
|
-
}
|
|
470
|
-
})();
|
|
471
|
-
await initPromise;
|
|
472
|
-
return !!wasmExports;
|
|
473
|
-
}
|
|
474
|
-
async function detectAgentWithWasm(userAgent, headers, ipAddress) {
|
|
475
|
-
const initialized = await initWasm();
|
|
476
|
-
if (!initialized || !wasmExports) {
|
|
477
|
-
return null;
|
|
641
|
+
};
|
|
478
642
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
userAgent || "",
|
|
484
|
-
headersJson,
|
|
485
|
-
ipAddress || "",
|
|
486
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
487
|
-
);
|
|
488
|
-
return {
|
|
489
|
-
isAgent: result.is_agent || false,
|
|
490
|
-
confidence: result.confidence || 0,
|
|
491
|
-
agent: result.agent,
|
|
492
|
-
verificationMethod: result.verification_method || "wasm",
|
|
493
|
-
riskLevel: result.risk_level || "low"
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
console.warn("[AgentShield] WASM exports do not include detect_agent function");
|
|
497
|
-
return null;
|
|
498
|
-
} catch (error) {
|
|
499
|
-
console.error("[AgentShield] WASM detection failed:", error);
|
|
500
|
-
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);
|
|
501
647
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const initialized = await initWasm();
|
|
505
|
-
if (!initialized || !wasmExports) {
|
|
506
|
-
return null;
|
|
648
|
+
if (signatureValue.endsWith(":")) {
|
|
649
|
+
signatureValue = signatureValue.slice(0, -1);
|
|
507
650
|
}
|
|
508
|
-
|
|
509
|
-
|
|
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
|
+
};
|
|
510
672
|
}
|
|
511
|
-
return "unknown";
|
|
512
673
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
} catch {
|
|
520
|
-
return false;
|
|
521
|
-
}
|
|
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);
|
|
522
680
|
}
|
|
523
681
|
|
|
524
|
-
// src/edge-detector-
|
|
525
|
-
var
|
|
682
|
+
// src/edge-detector-wrapper.ts
|
|
683
|
+
var AI_AGENT_PATTERNS = [
|
|
526
684
|
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
527
685
|
{ pattern: /claude-web/i, type: "claude", name: "Claude" },
|
|
528
686
|
{ pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
|
|
529
687
|
{ pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
|
|
530
688
|
{ pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
|
|
531
689
|
{ pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
|
|
690
|
+
// Fallback
|
|
532
691
|
{ pattern: /bingbot/i, type: "bing", name: "Bing AI" },
|
|
533
692
|
{ pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
|
|
534
693
|
];
|
|
535
|
-
var
|
|
694
|
+
var CLOUD_PROVIDERS = {
|
|
536
695
|
aws: ["54.", "52.", "35.", "18.", "3."],
|
|
537
696
|
gcp: ["35.", "34.", "104.", "107.", "108."],
|
|
538
697
|
azure: ["13.", "20.", "40.", "52.", "104."]
|
|
539
698
|
};
|
|
540
|
-
var
|
|
541
|
-
|
|
542
|
-
this.enableWasm = enableWasm;
|
|
543
|
-
}
|
|
544
|
-
wasmEnabled = false;
|
|
545
|
-
initPromise = null;
|
|
546
|
-
baseUrl = null;
|
|
547
|
-
/**
|
|
548
|
-
* Set the base URL for WASM loading in Edge Runtime
|
|
549
|
-
*/
|
|
550
|
-
setBaseUrl(url) {
|
|
551
|
-
this.baseUrl = url;
|
|
552
|
-
setWasmBaseUrl(url);
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* Initialize the detector (including WASM if enabled)
|
|
556
|
-
*/
|
|
557
|
-
async init() {
|
|
558
|
-
if (!this.enableWasm) {
|
|
559
|
-
this.wasmEnabled = false;
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
if (this.initPromise) {
|
|
563
|
-
await this.initPromise;
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
|
-
this.initPromise = (async () => {
|
|
567
|
-
try {
|
|
568
|
-
const wasmAvailable = await isWasmAvailable();
|
|
569
|
-
this.wasmEnabled = wasmAvailable;
|
|
570
|
-
if (this.wasmEnabled) {
|
|
571
|
-
console.log("[AgentShield] WASM detection enabled");
|
|
572
|
-
} else {
|
|
573
|
-
console.log("[AgentShield] WASM not available, using pattern detection");
|
|
574
|
-
}
|
|
575
|
-
} catch (error) {
|
|
576
|
-
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
577
|
-
this.wasmEnabled = false;
|
|
578
|
-
}
|
|
579
|
-
})();
|
|
580
|
-
await this.initPromise;
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* Pattern-based detection (fallback)
|
|
584
|
-
*/
|
|
585
|
-
async patternDetection(input) {
|
|
699
|
+
var EdgeAgentDetector = class {
|
|
700
|
+
async analyze(input) {
|
|
586
701
|
const reasons = [];
|
|
587
702
|
let detectedAgent;
|
|
588
703
|
let verificationMethod;
|
|
@@ -592,20 +707,46 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
592
707
|
for (const [key, value] of Object.entries(headers)) {
|
|
593
708
|
normalizedHeaders[key.toLowerCase()] = value;
|
|
594
709
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
+
}
|
|
605
746
|
}
|
|
606
747
|
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
607
748
|
if (userAgent) {
|
|
608
|
-
for (const { pattern, type, name } of
|
|
749
|
+
for (const { pattern, type, name } of AI_AGENT_PATTERNS) {
|
|
609
750
|
if (pattern.test(userAgent)) {
|
|
610
751
|
const highConfidenceAgents = [
|
|
611
752
|
"chatgpt",
|
|
@@ -640,7 +781,7 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
640
781
|
}
|
|
641
782
|
const ip = input.ip || input.ipAddress;
|
|
642
783
|
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
643
|
-
for (const [provider, prefixes] of Object.entries(
|
|
784
|
+
for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS)) {
|
|
644
785
|
if (prefixes.some((prefix) => ip.startsWith(prefix))) {
|
|
645
786
|
confidence = Math.max(confidence, 0.4);
|
|
646
787
|
reasons.push(`cloud_provider:${provider}`);
|
|
@@ -648,7 +789,7 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
648
789
|
}
|
|
649
790
|
}
|
|
650
791
|
}
|
|
651
|
-
if (reasons.length > 2) {
|
|
792
|
+
if (reasons.length > 2 && confidence < 1) {
|
|
652
793
|
confidence = Math.min(confidence * 1.2, 0.95);
|
|
653
794
|
}
|
|
654
795
|
return {
|
|
@@ -657,79 +798,16 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
657
798
|
...detectedAgent && { detectedAgent },
|
|
658
799
|
reasons,
|
|
659
800
|
...verificationMethod && { verificationMethod },
|
|
660
|
-
forgeabilityRisk: confidence > 0.8 ? "medium" : "high",
|
|
801
|
+
forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 0.8 ? "medium" : "high",
|
|
661
802
|
timestamp: /* @__PURE__ */ new Date()
|
|
662
803
|
};
|
|
663
804
|
}
|
|
664
|
-
/**
|
|
665
|
-
* Analyze request with WASM enhancement when available
|
|
666
|
-
*/
|
|
667
|
-
async analyze(input) {
|
|
668
|
-
await this.init();
|
|
669
|
-
if (this.wasmEnabled) {
|
|
670
|
-
try {
|
|
671
|
-
const wasmResult = await detectAgentWithWasm(
|
|
672
|
-
input.userAgent || input.headers?.["user-agent"],
|
|
673
|
-
input.headers || {},
|
|
674
|
-
input.ip || input.ipAddress
|
|
675
|
-
);
|
|
676
|
-
if (wasmResult) {
|
|
677
|
-
console.log("[AgentShield] Using WASM detection result");
|
|
678
|
-
const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
|
|
679
|
-
return {
|
|
680
|
-
isAgent: wasmResult.isAgent,
|
|
681
|
-
confidence: wasmResult.confidence,
|
|
682
|
-
...detectedAgent && { detectedAgent },
|
|
683
|
-
reasons: [`wasm:${wasmResult.verificationMethod}`],
|
|
684
|
-
verificationMethod: wasmResult.verificationMethod,
|
|
685
|
-
forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 0.9 ? "medium" : "high",
|
|
686
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
} catch (error) {
|
|
690
|
-
console.error("[AgentShield] WASM detection error:", error);
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
const patternResult = await this.patternDetection(input);
|
|
694
|
-
if (this.wasmEnabled && patternResult.confidence >= 0.85) {
|
|
695
|
-
patternResult.confidence = Math.min(0.95, patternResult.confidence + 0.1);
|
|
696
|
-
patternResult.reasons.push("wasm_enhanced");
|
|
697
|
-
if (!patternResult.verificationMethod) {
|
|
698
|
-
patternResult.verificationMethod = "wasm-enhanced";
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
return patternResult;
|
|
702
|
-
}
|
|
703
|
-
/**
|
|
704
|
-
* Map agent names from WASM to consistent format
|
|
705
|
-
*/
|
|
706
|
-
mapAgentName(agent) {
|
|
707
|
-
const lowerAgent = agent.toLowerCase();
|
|
708
|
-
if (lowerAgent.includes("chatgpt")) {
|
|
709
|
-
return { type: "chatgpt", name: "ChatGPT" };
|
|
710
|
-
} else if (lowerAgent.includes("claude")) {
|
|
711
|
-
return { type: "claude", name: "Claude" };
|
|
712
|
-
} else if (lowerAgent.includes("perplexity")) {
|
|
713
|
-
return { type: "perplexity", name: "Perplexity" };
|
|
714
|
-
} else if (lowerAgent.includes("bing")) {
|
|
715
|
-
return { type: "bing", name: "Bing AI" };
|
|
716
|
-
} else if (lowerAgent.includes("anthropic")) {
|
|
717
|
-
return { type: "anthropic", name: "Anthropic" };
|
|
718
|
-
}
|
|
719
|
-
return { type: "unknown", name: agent };
|
|
720
|
-
}
|
|
721
805
|
};
|
|
722
|
-
var
|
|
806
|
+
var EdgeAgentDetectorWrapper = class {
|
|
723
807
|
detector;
|
|
724
808
|
events = /* @__PURE__ */ new Map();
|
|
725
|
-
constructor(
|
|
726
|
-
this.detector = new
|
|
727
|
-
if (config?.baseUrl) {
|
|
728
|
-
this.detector.setBaseUrl(config.baseUrl);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
setBaseUrl(url) {
|
|
732
|
-
this.detector.setBaseUrl(url);
|
|
809
|
+
constructor(_config) {
|
|
810
|
+
this.detector = new EdgeAgentDetector();
|
|
733
811
|
}
|
|
734
812
|
async analyze(input) {
|
|
735
813
|
const result = await this.detector.analyze(input);
|
|
@@ -750,10 +828,13 @@ var EdgeAgentDetectorWrapperWithWasm = class {
|
|
|
750
828
|
handlers.forEach((handler) => handler(...args));
|
|
751
829
|
}
|
|
752
830
|
async init() {
|
|
753
|
-
|
|
831
|
+
return;
|
|
754
832
|
}
|
|
755
833
|
};
|
|
756
834
|
|
|
835
|
+
// src/middleware.ts
|
|
836
|
+
init_edge_detector_with_wasm();
|
|
837
|
+
|
|
757
838
|
// src/session-tracker.ts
|
|
758
839
|
var EdgeSessionTracker = class {
|
|
759
840
|
config;
|
|
@@ -1104,48 +1185,80 @@ function createAgentShieldMiddleware2(config) {
|
|
|
1104
1185
|
};
|
|
1105
1186
|
}
|
|
1106
1187
|
|
|
1107
|
-
// src/
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
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;
|
|
1112
1207
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
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
|
+
}
|
|
1115
1218
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
+
};
|
|
1146
1260
|
}
|
|
1147
|
-
|
|
1148
|
-
}
|
|
1261
|
+
};
|
|
1149
1262
|
|
|
1150
1263
|
// src/storage/memory-adapter.ts
|
|
1151
1264
|
var MemoryStorageAdapter = class {
|
|
@@ -1420,39 +1533,48 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
|
|
|
1420
1533
|
};
|
|
1421
1534
|
let detector = null;
|
|
1422
1535
|
let detectorInitPromise = null;
|
|
1536
|
+
let wasmConfidenceUtils = null;
|
|
1423
1537
|
const getDetector = async (requestUrl) => {
|
|
1424
1538
|
if (detector) {
|
|
1425
|
-
if (requestUrl && "setBaseUrl" in detector) {
|
|
1426
|
-
detector.setBaseUrl(requestUrl);
|
|
1427
|
-
}
|
|
1428
1539
|
return detector;
|
|
1429
1540
|
}
|
|
1430
1541
|
if (detectorInitPromise) {
|
|
1431
1542
|
await detectorInitPromise;
|
|
1432
|
-
if (requestUrl && detector && "setBaseUrl" in detector) {
|
|
1433
|
-
detector.setBaseUrl(requestUrl);
|
|
1434
|
-
}
|
|
1435
1543
|
return detector;
|
|
1436
1544
|
}
|
|
1437
1545
|
detectorInitPromise = (async () => {
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
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();
|
|
1446
1574
|
}
|
|
1447
|
-
} catch (error) {
|
|
1448
|
-
console.warn("[AgentShield] Failed to initialize WASM, using fallback:", error);
|
|
1449
|
-
detector = new EdgeAgentDetectorWrapper({});
|
|
1450
1575
|
}
|
|
1451
1576
|
})();
|
|
1452
1577
|
await detectorInitPromise;
|
|
1453
|
-
if (requestUrl && detector && "setBaseUrl" in detector) {
|
|
1454
|
-
detector.setBaseUrl(requestUrl);
|
|
1455
|
-
}
|
|
1456
1578
|
return detector;
|
|
1457
1579
|
};
|
|
1458
1580
|
const sessionManager = new SessionManager();
|
|
@@ -1478,11 +1600,11 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
|
|
|
1478
1600
|
const result = await activeDetector.analyze(context);
|
|
1479
1601
|
let finalConfidence = result.confidence;
|
|
1480
1602
|
let verificationMethod = result.verificationMethod || "pattern";
|
|
1481
|
-
if (result.isAgent) {
|
|
1603
|
+
if (result.isAgent && wasmConfidenceUtils) {
|
|
1482
1604
|
const reasons = result.reasons || [];
|
|
1483
|
-
if (shouldIndicateWasmVerification(result.confidence)) {
|
|
1484
|
-
finalConfidence = getWasmConfidenceBoost(result.confidence, reasons);
|
|
1485
|
-
verificationMethod = getVerificationMethod(finalConfidence, reasons);
|
|
1605
|
+
if (wasmConfidenceUtils.shouldIndicateWasmVerification(result.confidence)) {
|
|
1606
|
+
finalConfidence = wasmConfidenceUtils.getWasmConfidenceBoost(result.confidence, reasons);
|
|
1607
|
+
verificationMethod = wasmConfidenceUtils.getVerificationMethod(finalConfidence, reasons);
|
|
1486
1608
|
}
|
|
1487
1609
|
}
|
|
1488
1610
|
if (result.isAgent && finalConfidence >= (config.confidenceThreshold ?? 0.7)) {
|