@kya-os/agentshield-nextjs 0.2.13 → 0.3.1
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/api-client.d.mts +6 -1
- package/dist/api-client.d.ts +6 -1
- package/dist/api-client.js.map +1 -1
- package/dist/api-client.mjs.map +1 -1
- package/dist/api-middleware.d.mts +13 -0
- package/dist/api-middleware.d.ts +13 -0
- package/dist/api-middleware.js +149 -19
- package/dist/api-middleware.js.map +1 -1
- package/dist/api-middleware.mjs +149 -19
- package/dist/api-middleware.mjs.map +1 -1
- package/dist/create-middleware.js +565 -487
- package/dist/create-middleware.js.map +1 -1
- package/dist/create-middleware.mjs +565 -487
- package/dist/create-middleware.mjs.map +1 -1
- package/dist/edge/index.js +69 -46
- package/dist/edge/index.js.map +1 -1
- package/dist/edge/index.mjs +69 -46
- package/dist/edge/index.mjs.map +1 -1
- package/dist/edge-detector-wrapper.js +9 -1
- package/dist/edge-detector-wrapper.js.map +1 -1
- package/dist/edge-detector-wrapper.mjs +9 -1
- package/dist/edge-detector-wrapper.mjs.map +1 -1
- package/dist/edge-runtime-loader.d.mts +1 -0
- package/dist/edge-runtime-loader.d.ts +1 -0
- package/dist/edge-runtime-loader.js +19 -3
- package/dist/edge-runtime-loader.js.map +1 -1
- package/dist/edge-runtime-loader.mjs +19 -3
- package/dist/edge-runtime-loader.mjs.map +1 -1
- package/dist/edge-wasm-middleware.d.mts +1 -0
- package/dist/edge-wasm-middleware.d.ts +1 -0
- package/dist/edge-wasm-middleware.js +10 -2
- package/dist/edge-wasm-middleware.js.map +1 -1
- package/dist/edge-wasm-middleware.mjs +11 -3
- package/dist/edge-wasm-middleware.mjs.map +1 -1
- package/dist/enhanced-middleware.js +48 -20
- package/dist/enhanced-middleware.js.map +1 -1
- package/dist/enhanced-middleware.mjs +49 -21
- package/dist/enhanced-middleware.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +263 -102
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +264 -103
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.js +565 -487
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +565 -487
- package/dist/middleware.mjs.map +1 -1
- package/dist/policy.d.mts +16 -4
- package/dist/policy.d.ts +16 -4
- package/dist/policy.js +14 -10
- package/dist/policy.js.map +1 -1
- package/dist/policy.mjs +14 -10
- package/dist/policy.mjs.map +1 -1
- package/dist/session-tracker.js +13 -19
- package/dist/session-tracker.js.map +1 -1
- package/dist/session-tracker.mjs +13 -19
- package/dist/session-tracker.mjs.map +1 -1
- package/dist/signature-verifier.js +9 -1
- package/dist/signature-verifier.js.map +1 -1
- package/dist/signature-verifier.mjs +9 -1
- package/dist/signature-verifier.mjs.map +1 -1
- package/dist/wasm-middleware.d.mts +1 -0
- package/dist/wasm-middleware.d.ts +1 -0
- package/dist/wasm-middleware.js +15 -15
- package/dist/wasm-middleware.js.map +1 -1
- package/dist/wasm-middleware.mjs +15 -15
- package/dist/wasm-middleware.mjs.map +1 -1
- package/package.json +3 -3
- package/wasm/agentshield_wasm.d.ts +379 -21
- package/wasm/agentshield_wasm.js +1409 -506
- package/wasm/agentshield_wasm_bg.wasm +0 -0
- package/dist/.tsbuildinfo +0 -1
package/dist/middleware.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var agentshieldShared = require('@kya-os/agentshield-shared');
|
|
3
4
|
var server = require('next/server');
|
|
4
5
|
var ed25519 = require('@noble/ed25519');
|
|
5
6
|
var sha2_js = require('@noble/hashes/sha2.js');
|
|
6
|
-
var agentshieldShared = require('@kya-os/agentshield-shared');
|
|
7
7
|
|
|
8
8
|
function _interopNamespace(e) {
|
|
9
9
|
if (e && e.__esModule) return e;
|
|
@@ -25,7 +25,455 @@ function _interopNamespace(e) {
|
|
|
25
25
|
|
|
26
26
|
var ed25519__namespace = /*#__PURE__*/_interopNamespace(ed25519);
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
var __defProp = Object.defineProperty;
|
|
29
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
30
|
+
var __esm = (fn, res) => function __init() {
|
|
31
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
32
|
+
};
|
|
33
|
+
var __export = (target, all) => {
|
|
34
|
+
for (var name in all)
|
|
35
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/wasm-loader.ts
|
|
39
|
+
function setWasmBaseUrl(url) {
|
|
40
|
+
baseUrl = url;
|
|
41
|
+
}
|
|
42
|
+
function getWasmUrl() {
|
|
43
|
+
if (baseUrl) {
|
|
44
|
+
try {
|
|
45
|
+
const url = new URL(baseUrl);
|
|
46
|
+
return `${url.origin}${WASM_PATH}`;
|
|
47
|
+
} catch {
|
|
48
|
+
return WASM_PATH;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return WASM_PATH;
|
|
52
|
+
}
|
|
53
|
+
async function initWasm() {
|
|
54
|
+
if (wasmExports) return true;
|
|
55
|
+
if (initPromise) {
|
|
56
|
+
await initPromise;
|
|
57
|
+
return !!wasmExports;
|
|
58
|
+
}
|
|
59
|
+
initPromise = (async () => {
|
|
60
|
+
try {
|
|
61
|
+
const controller = new AbortController();
|
|
62
|
+
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
63
|
+
try {
|
|
64
|
+
const wasmUrl = getWasmUrl();
|
|
65
|
+
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
66
|
+
try {
|
|
67
|
+
const response2 = await fetch(wasmUrl, { signal: controller.signal });
|
|
68
|
+
clearTimeout(timeout);
|
|
69
|
+
if (!response2.ok) {
|
|
70
|
+
throw new Error(`Failed to fetch WASM: ${response2.status}`);
|
|
71
|
+
}
|
|
72
|
+
const streamResponse = response2.clone();
|
|
73
|
+
const { instance } = await WebAssembly.instantiateStreaming(streamResponse, {
|
|
74
|
+
wbg: {
|
|
75
|
+
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
76
|
+
if (process.env.NODE_ENV !== "production") {
|
|
77
|
+
console.debug("WASM:", ptr, len);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
__wbindgen_throw: (ptr, len) => {
|
|
81
|
+
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
wasmInstance = instance;
|
|
86
|
+
wasmExports = instance.exports;
|
|
87
|
+
if (process.env.NODE_ENV !== "production") {
|
|
88
|
+
console.debug("[AgentShield] \u2705 WASM module initialized with streaming");
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
} catch (streamError) {
|
|
92
|
+
if (!controller.signal.aborted) {
|
|
93
|
+
if (process.env.NODE_ENV !== "production") {
|
|
94
|
+
console.debug(
|
|
95
|
+
"[AgentShield] Streaming compilation failed, falling back to standard compilation"
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
throw streamError;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const response = await fetch(wasmUrl, { signal: controller.signal });
|
|
104
|
+
clearTimeout(timeout);
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
throw new Error(`Failed to fetch WASM: ${response.status}`);
|
|
107
|
+
}
|
|
108
|
+
const wasmArrayBuffer = await response.arrayBuffer();
|
|
109
|
+
const compiledModule = await WebAssembly.compile(wasmArrayBuffer);
|
|
110
|
+
const imports = {
|
|
111
|
+
wbg: {
|
|
112
|
+
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
113
|
+
if (process.env.NODE_ENV !== "production") {
|
|
114
|
+
console.debug("WASM:", ptr, len);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
__wbindgen_throw: (ptr, len) => {
|
|
118
|
+
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
wasmInstance = await WebAssembly.instantiate(compiledModule, imports);
|
|
123
|
+
wasmExports = wasmInstance.exports;
|
|
124
|
+
if (process.env.NODE_ENV !== "production") {
|
|
125
|
+
console.debug("[AgentShield] \u2705 WASM module initialized via fallback");
|
|
126
|
+
}
|
|
127
|
+
} catch (fetchError) {
|
|
128
|
+
const error = fetchError;
|
|
129
|
+
if (error.name === "AbortError") {
|
|
130
|
+
console.warn(
|
|
131
|
+
"[AgentShield] WASM fetch timed out after 3 seconds - using pattern detection"
|
|
132
|
+
);
|
|
133
|
+
} else {
|
|
134
|
+
console.warn(
|
|
135
|
+
"[AgentShield] Failed to fetch WASM file:",
|
|
136
|
+
error.message || "Unknown error"
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
wasmExports = null;
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
143
|
+
wasmExports = null;
|
|
144
|
+
}
|
|
145
|
+
})();
|
|
146
|
+
await initPromise;
|
|
147
|
+
return !!wasmExports;
|
|
148
|
+
}
|
|
149
|
+
async function detectAgentWithWasm(_userAgent, _headers, _ipAddress) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
async function getWasmVersion() {
|
|
153
|
+
const initialized = await initWasm();
|
|
154
|
+
if (!initialized || !wasmExports) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
if (typeof wasmExports.version === "function") {
|
|
158
|
+
return wasmExports.version();
|
|
159
|
+
}
|
|
160
|
+
return "unknown";
|
|
161
|
+
}
|
|
162
|
+
async function isWasmAvailable() {
|
|
163
|
+
try {
|
|
164
|
+
const initialized = await initWasm();
|
|
165
|
+
if (!initialized) return false;
|
|
166
|
+
const version = await getWasmVersion();
|
|
167
|
+
return version !== null;
|
|
168
|
+
} catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
var wasmInstance, wasmExports, initPromise, WASM_PATH, baseUrl;
|
|
173
|
+
var init_wasm_loader = __esm({
|
|
174
|
+
"src/wasm-loader.ts"() {
|
|
175
|
+
wasmInstance = null;
|
|
176
|
+
wasmExports = null;
|
|
177
|
+
initPromise = null;
|
|
178
|
+
WASM_PATH = "/wasm/agentshield_wasm_bg.wasm";
|
|
179
|
+
baseUrl = null;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// src/edge-detector-with-wasm.ts
|
|
184
|
+
var edge_detector_with_wasm_exports = {};
|
|
185
|
+
__export(edge_detector_with_wasm_exports, {
|
|
186
|
+
EdgeAgentDetectorWithWasm: () => EdgeAgentDetectorWithWasm,
|
|
187
|
+
EdgeAgentDetectorWrapperWithWasm: () => EdgeAgentDetectorWrapperWithWasm
|
|
188
|
+
});
|
|
189
|
+
var rules2, EdgeAgentDetectorWithWasm, EdgeAgentDetectorWrapperWithWasm;
|
|
190
|
+
var init_edge_detector_with_wasm = __esm({
|
|
191
|
+
"src/edge-detector-with-wasm.ts"() {
|
|
192
|
+
init_wasm_loader();
|
|
193
|
+
rules2 = agentshieldShared.loadRulesSync();
|
|
194
|
+
EdgeAgentDetectorWithWasm = class {
|
|
195
|
+
constructor(enableWasm = true) {
|
|
196
|
+
this.enableWasm = enableWasm;
|
|
197
|
+
this.rules = rules2;
|
|
198
|
+
}
|
|
199
|
+
wasmEnabled = false;
|
|
200
|
+
initPromise = null;
|
|
201
|
+
baseUrl = null;
|
|
202
|
+
rules;
|
|
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
|
+
if (wasmAvailable) {
|
|
226
|
+
if (this.baseUrl) {
|
|
227
|
+
setWasmBaseUrl(this.baseUrl);
|
|
228
|
+
}
|
|
229
|
+
await initWasm();
|
|
230
|
+
this.wasmEnabled = true;
|
|
231
|
+
} else {
|
|
232
|
+
this.wasmEnabled = false;
|
|
233
|
+
}
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
236
|
+
this.wasmEnabled = false;
|
|
237
|
+
}
|
|
238
|
+
})();
|
|
239
|
+
await this.initPromise;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Pattern-based detection (fallback)
|
|
243
|
+
*/
|
|
244
|
+
async patternDetection(input) {
|
|
245
|
+
const reasons = [];
|
|
246
|
+
let detectedAgent;
|
|
247
|
+
let verificationMethod;
|
|
248
|
+
let confidence = 0;
|
|
249
|
+
const headers = input.headers || {};
|
|
250
|
+
const normalizedHeaders = {};
|
|
251
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
252
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
253
|
+
}
|
|
254
|
+
const signaturePresent = !!(normalizedHeaders["signature"] || normalizedHeaders["signature-input"]);
|
|
255
|
+
const signatureAgent = normalizedHeaders["signature-agent"];
|
|
256
|
+
const isChatGPT = (() => {
|
|
257
|
+
try {
|
|
258
|
+
const url = new URL(signatureAgent?.replace(/^"|"$/g, "") || "");
|
|
259
|
+
return url.hostname === "chatgpt.com" || url.hostname.endsWith(".chatgpt.com");
|
|
260
|
+
} catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
})();
|
|
264
|
+
if (isChatGPT) {
|
|
265
|
+
confidence = 85;
|
|
266
|
+
reasons.push("signature_agent:chatgpt");
|
|
267
|
+
detectedAgent = { type: "chatgpt", name: "ChatGPT" };
|
|
268
|
+
verificationMethod = "signature";
|
|
269
|
+
} else if (signaturePresent) {
|
|
270
|
+
confidence = Math.max(confidence, 40);
|
|
271
|
+
reasons.push("signature_present");
|
|
272
|
+
}
|
|
273
|
+
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
274
|
+
if (userAgent) {
|
|
275
|
+
for (const [agentKey, agentRule] of Object.entries(this.rules.rules.userAgents)) {
|
|
276
|
+
const matched = agentRule.patterns.some((pattern) => {
|
|
277
|
+
const regex = new RegExp(pattern, "i");
|
|
278
|
+
return regex.test(userAgent);
|
|
279
|
+
});
|
|
280
|
+
if (matched) {
|
|
281
|
+
const agentType = this.getAgentType(agentKey);
|
|
282
|
+
const agentName = this.getAgentName(agentKey);
|
|
283
|
+
confidence = Math.max(confidence, Math.round(agentRule.confidence * 0.85 * 100));
|
|
284
|
+
reasons.push(`known_pattern:${agentType}`);
|
|
285
|
+
if (!detectedAgent) {
|
|
286
|
+
detectedAgent = { type: agentType, name: agentName };
|
|
287
|
+
verificationMethod = "pattern";
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const suspiciousHeaders = this.rules.rules.headers.suspicious;
|
|
294
|
+
const foundAiHeaders = suspiciousHeaders.filter(
|
|
295
|
+
(headerRule) => normalizedHeaders[headerRule.name.toLowerCase()]
|
|
296
|
+
);
|
|
297
|
+
if (foundAiHeaders.length > 0) {
|
|
298
|
+
const maxConfidence = Math.max(...foundAiHeaders.map((h) => h.confidence));
|
|
299
|
+
confidence = Math.max(confidence, maxConfidence);
|
|
300
|
+
reasons.push(`ai_headers:${foundAiHeaders.length}`);
|
|
301
|
+
}
|
|
302
|
+
const ip = input.ip || input.ipAddress;
|
|
303
|
+
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
304
|
+
const ipRanges = "providers" in this.rules.rules.ipRanges ? this.rules.rules.ipRanges.providers : this.rules.rules.ipRanges;
|
|
305
|
+
for (const [provider, ipRule] of Object.entries(ipRanges)) {
|
|
306
|
+
if (!ipRule || typeof ipRule !== "object" || !("ranges" in ipRule) || !Array.isArray(ipRule.ranges))
|
|
307
|
+
continue;
|
|
308
|
+
const matched = ipRule.ranges.some((range) => {
|
|
309
|
+
const prefix = range.split("/")[0];
|
|
310
|
+
const prefixParts = prefix.split(".");
|
|
311
|
+
const ipParts = ip.split(".");
|
|
312
|
+
for (let i = 0; i < Math.min(prefixParts.length - 1, 2); i++) {
|
|
313
|
+
if (prefixParts[i] !== ipParts[i] && prefixParts[i] !== "0") {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return true;
|
|
318
|
+
});
|
|
319
|
+
if (matched) {
|
|
320
|
+
confidence = Math.max(confidence, Math.round(ipRule.confidence * 0.4 * 100));
|
|
321
|
+
reasons.push(`cloud_provider:${provider}`);
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (reasons.length > 2) {
|
|
327
|
+
confidence = Math.min(Math.round(confidence * 1.2), 95);
|
|
328
|
+
}
|
|
329
|
+
confidence = Math.min(Math.max(confidence, 0), 100);
|
|
330
|
+
return {
|
|
331
|
+
isAgent: confidence > 30,
|
|
332
|
+
// 30% threshold
|
|
333
|
+
confidence,
|
|
334
|
+
detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
|
|
335
|
+
signals: [],
|
|
336
|
+
// Will be populated by enhanced detection engine in future tasks
|
|
337
|
+
...detectedAgent && { detectedAgent },
|
|
338
|
+
reasons,
|
|
339
|
+
...verificationMethod && {
|
|
340
|
+
verificationMethod: agentshieldShared.mapVerificationMethod(verificationMethod)
|
|
341
|
+
},
|
|
342
|
+
forgeabilityRisk: confidence > 80 ? "medium" : "high",
|
|
343
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Analyze request with WASM enhancement when available
|
|
348
|
+
*/
|
|
349
|
+
async analyze(input) {
|
|
350
|
+
await this.init();
|
|
351
|
+
if (this.wasmEnabled) {
|
|
352
|
+
try {
|
|
353
|
+
const wasmResult = await detectAgentWithWasm(
|
|
354
|
+
input.userAgent || input.headers?.["user-agent"],
|
|
355
|
+
input.headers || {},
|
|
356
|
+
input.ip || input.ipAddress
|
|
357
|
+
);
|
|
358
|
+
if (wasmResult) {
|
|
359
|
+
const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
|
|
360
|
+
return {
|
|
361
|
+
isAgent: wasmResult.isAgent,
|
|
362
|
+
confidence: wasmResult.confidence,
|
|
363
|
+
detectionClass: wasmResult.isAgent && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : wasmResult.isAgent ? { type: "Unknown" } : { type: "Human" },
|
|
364
|
+
signals: [],
|
|
365
|
+
// Will be populated by enhanced detection engine in future tasks
|
|
366
|
+
...detectedAgent && { detectedAgent },
|
|
367
|
+
reasons: [`wasm:${wasmResult.verificationMethod}`],
|
|
368
|
+
verificationMethod: agentshieldShared.mapVerificationMethod(wasmResult.verificationMethod),
|
|
369
|
+
forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 90 ? "medium" : "high",
|
|
370
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.error("[AgentShield] WASM detection error:", error);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const patternResult = await this.patternDetection(input);
|
|
378
|
+
if (this.wasmEnabled && patternResult.confidence >= 85) {
|
|
379
|
+
patternResult.confidence = Math.min(95, patternResult.confidence + 10);
|
|
380
|
+
patternResult.reasons.push("wasm_enhanced");
|
|
381
|
+
}
|
|
382
|
+
return patternResult;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Get agent type from rule key
|
|
386
|
+
*/
|
|
387
|
+
getAgentType(agentKey) {
|
|
388
|
+
const typeMap = {
|
|
389
|
+
openai_gptbot: "openai",
|
|
390
|
+
anthropic_claude: "anthropic",
|
|
391
|
+
perplexity_bot: "perplexity",
|
|
392
|
+
google_ai: "google",
|
|
393
|
+
microsoft_ai: "microsoft",
|
|
394
|
+
meta_ai: "meta",
|
|
395
|
+
cohere_bot: "cohere",
|
|
396
|
+
huggingface_bot: "huggingface",
|
|
397
|
+
generic_bot: "generic",
|
|
398
|
+
dev_tools: "dev",
|
|
399
|
+
automation_tools: "automation"
|
|
400
|
+
};
|
|
401
|
+
return typeMap[agentKey] || agentKey;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Get agent name from rule key
|
|
405
|
+
*/
|
|
406
|
+
getAgentName(agentKey) {
|
|
407
|
+
const nameMap = {
|
|
408
|
+
openai_gptbot: "ChatGPT/GPTBot",
|
|
409
|
+
anthropic_claude: "Claude",
|
|
410
|
+
perplexity_bot: "Perplexity",
|
|
411
|
+
google_ai: "Google AI",
|
|
412
|
+
microsoft_ai: "Microsoft Copilot",
|
|
413
|
+
meta_ai: "Meta AI",
|
|
414
|
+
cohere_bot: "Cohere",
|
|
415
|
+
huggingface_bot: "HuggingFace",
|
|
416
|
+
generic_bot: "Generic Bot",
|
|
417
|
+
dev_tools: "Development Tool",
|
|
418
|
+
automation_tools: "Automation Tool"
|
|
419
|
+
};
|
|
420
|
+
return nameMap[agentKey] || agentKey;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Map agent names from WASM to consistent format
|
|
424
|
+
*/
|
|
425
|
+
mapAgentName(agent) {
|
|
426
|
+
const lowerAgent = agent.toLowerCase();
|
|
427
|
+
if (lowerAgent.includes("chatgpt")) {
|
|
428
|
+
return { type: "chatgpt", name: "ChatGPT" };
|
|
429
|
+
} else if (lowerAgent.includes("claude")) {
|
|
430
|
+
return { type: "claude", name: "Claude" };
|
|
431
|
+
} else if (lowerAgent.includes("perplexity")) {
|
|
432
|
+
return { type: "perplexity", name: "Perplexity" };
|
|
433
|
+
} else if (lowerAgent.includes("bing")) {
|
|
434
|
+
return { type: "bing", name: "Bing AI" };
|
|
435
|
+
} else if (lowerAgent.includes("anthropic")) {
|
|
436
|
+
return { type: "anthropic", name: "Anthropic" };
|
|
437
|
+
}
|
|
438
|
+
return { type: "unknown", name: agent };
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
EdgeAgentDetectorWrapperWithWasm = class {
|
|
442
|
+
detector;
|
|
443
|
+
events = /* @__PURE__ */ new Map();
|
|
444
|
+
constructor(config) {
|
|
445
|
+
this.detector = new EdgeAgentDetectorWithWasm(config?.enableWasm ?? true);
|
|
446
|
+
if (config?.baseUrl) {
|
|
447
|
+
this.detector.setBaseUrl(config.baseUrl);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
setBaseUrl(url) {
|
|
451
|
+
this.detector.setBaseUrl(url);
|
|
452
|
+
}
|
|
453
|
+
async analyze(input) {
|
|
454
|
+
const result = await this.detector.analyze(input);
|
|
455
|
+
if (result.isAgent && this.events.has("agent.detected")) {
|
|
456
|
+
const handlers = this.events.get("agent.detected") || [];
|
|
457
|
+
handlers.forEach((handler) => handler(result, input));
|
|
458
|
+
}
|
|
459
|
+
return result;
|
|
460
|
+
}
|
|
461
|
+
on(event, handler) {
|
|
462
|
+
if (!this.events.has(event)) {
|
|
463
|
+
this.events.set(event, []);
|
|
464
|
+
}
|
|
465
|
+
this.events.get(event).push(handler);
|
|
466
|
+
}
|
|
467
|
+
emit(event, ...args) {
|
|
468
|
+
const handlers = this.events.get(event) || [];
|
|
469
|
+
handlers.forEach((handler) => handler(...args));
|
|
470
|
+
}
|
|
471
|
+
async init() {
|
|
472
|
+
await this.detector.init();
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
});
|
|
29
477
|
ed25519__namespace.etc.sha512Sync = (...m) => sha2_js.sha512(ed25519__namespace.etc.concatBytes(...m));
|
|
30
478
|
var KNOWN_KEYS = {
|
|
31
479
|
chatgpt: [
|
|
@@ -286,7 +734,15 @@ async function verifyAgentSignature(method, path, headers) {
|
|
|
286
734
|
}
|
|
287
735
|
let agent;
|
|
288
736
|
let agentKey;
|
|
289
|
-
|
|
737
|
+
const isChatGPT = signatureAgent === '"https://chatgpt.com"' || (() => {
|
|
738
|
+
try {
|
|
739
|
+
const url = new URL(signatureAgent?.replace(/^"|"$/g, "") || "");
|
|
740
|
+
return url.hostname === "chatgpt.com" || url.hostname.endsWith(".chatgpt.com");
|
|
741
|
+
} catch {
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
})();
|
|
745
|
+
if (isChatGPT) {
|
|
290
746
|
agent = "ChatGPT";
|
|
291
747
|
agentKey = "chatgpt";
|
|
292
748
|
}
|
|
@@ -583,462 +1039,46 @@ var EdgeAgentDetectorWrapper = class {
|
|
|
583
1039
|
return;
|
|
584
1040
|
}
|
|
585
1041
|
};
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
|
|
1042
|
+
var EdgeSessionTracker = class {
|
|
1043
|
+
config;
|
|
1044
|
+
constructor(config) {
|
|
1045
|
+
this.config = {
|
|
1046
|
+
enabled: config.enabled,
|
|
1047
|
+
cookieName: config.cookieName || "__agentshield_session",
|
|
1048
|
+
cookieMaxAge: config.cookieMaxAge || 3600,
|
|
1049
|
+
// 1 hour default
|
|
1050
|
+
encryptionKey: config.encryptionKey || process.env.AGENTSHIELD_SECRET || "agentshield-default-key"
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Track a new AI agent session
|
|
1055
|
+
*/
|
|
1056
|
+
async track(_request, response, result) {
|
|
598
1057
|
try {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if (!response2.ok) {
|
|
624
|
-
throw new Error(`Failed to fetch WASM: ${response2.status}`);
|
|
625
|
-
}
|
|
626
|
-
const streamResponse = response2.clone();
|
|
627
|
-
const { instance } = await WebAssembly.instantiateStreaming(streamResponse, {
|
|
628
|
-
wbg: {
|
|
629
|
-
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
630
|
-
if (process.env.NODE_ENV !== "production") {
|
|
631
|
-
console.debug("WASM:", ptr, len);
|
|
632
|
-
}
|
|
633
|
-
},
|
|
634
|
-
__wbindgen_throw: (ptr, len) => {
|
|
635
|
-
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
wasmInstance = instance;
|
|
640
|
-
wasmExports = instance.exports;
|
|
641
|
-
if (process.env.NODE_ENV !== "production") {
|
|
642
|
-
console.debug("[AgentShield] \u2705 WASM module initialized with streaming");
|
|
643
|
-
}
|
|
644
|
-
return;
|
|
645
|
-
} catch (streamError) {
|
|
646
|
-
if (!controller.signal.aborted) {
|
|
647
|
-
if (process.env.NODE_ENV !== "production") {
|
|
648
|
-
console.debug(
|
|
649
|
-
"[AgentShield] Streaming compilation failed, falling back to standard compilation"
|
|
650
|
-
);
|
|
651
|
-
}
|
|
652
|
-
} else {
|
|
653
|
-
throw streamError;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
const response = await fetch(wasmUrl, { signal: controller.signal });
|
|
658
|
-
clearTimeout(timeout);
|
|
659
|
-
if (!response.ok) {
|
|
660
|
-
throw new Error(`Failed to fetch WASM: ${response.status}`);
|
|
661
|
-
}
|
|
662
|
-
const wasmArrayBuffer = await response.arrayBuffer();
|
|
663
|
-
const compiledModule = await WebAssembly.compile(wasmArrayBuffer);
|
|
664
|
-
const imports = {
|
|
665
|
-
wbg: {
|
|
666
|
-
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
667
|
-
if (process.env.NODE_ENV !== "production") {
|
|
668
|
-
console.debug("WASM:", ptr, len);
|
|
669
|
-
}
|
|
670
|
-
},
|
|
671
|
-
__wbindgen_throw: (ptr, len) => {
|
|
672
|
-
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
};
|
|
676
|
-
wasmInstance = await WebAssembly.instantiate(compiledModule, imports);
|
|
677
|
-
wasmExports = wasmInstance.exports;
|
|
678
|
-
if (process.env.NODE_ENV !== "production") {
|
|
679
|
-
console.debug("[AgentShield] \u2705 WASM module initialized via fallback");
|
|
680
|
-
}
|
|
681
|
-
} catch (fetchError) {
|
|
682
|
-
const error = fetchError;
|
|
683
|
-
if (error.name === "AbortError") {
|
|
684
|
-
console.warn(
|
|
685
|
-
"[AgentShield] WASM fetch timed out after 3 seconds - using pattern detection"
|
|
686
|
-
);
|
|
687
|
-
} else {
|
|
688
|
-
console.warn(
|
|
689
|
-
"[AgentShield] Failed to fetch WASM file:",
|
|
690
|
-
error.message || "Unknown error"
|
|
691
|
-
);
|
|
692
|
-
}
|
|
693
|
-
wasmExports = null;
|
|
694
|
-
}
|
|
695
|
-
} catch (error) {
|
|
696
|
-
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
697
|
-
wasmExports = null;
|
|
698
|
-
}
|
|
699
|
-
})();
|
|
700
|
-
await initPromise;
|
|
701
|
-
return !!wasmExports;
|
|
702
|
-
}
|
|
703
|
-
async function detectAgentWithWasm(_userAgent, _headers, _ipAddress) {
|
|
704
|
-
return null;
|
|
705
|
-
}
|
|
706
|
-
async function getWasmVersion() {
|
|
707
|
-
const initialized = await initWasm();
|
|
708
|
-
if (!initialized || !wasmExports) {
|
|
709
|
-
return null;
|
|
710
|
-
}
|
|
711
|
-
if (typeof wasmExports.version === "function") {
|
|
712
|
-
return wasmExports.version();
|
|
713
|
-
}
|
|
714
|
-
return "unknown";
|
|
715
|
-
}
|
|
716
|
-
async function isWasmAvailable() {
|
|
717
|
-
try {
|
|
718
|
-
const initialized = await initWasm();
|
|
719
|
-
if (!initialized) return false;
|
|
720
|
-
const version = await getWasmVersion();
|
|
721
|
-
return version !== null;
|
|
722
|
-
} catch {
|
|
723
|
-
return false;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
var rules2 = agentshieldShared.loadRulesSync();
|
|
727
|
-
var EdgeAgentDetectorWithWasm = class {
|
|
728
|
-
constructor(enableWasm = true) {
|
|
729
|
-
this.enableWasm = enableWasm;
|
|
730
|
-
this.rules = rules2;
|
|
731
|
-
}
|
|
732
|
-
wasmEnabled = false;
|
|
733
|
-
initPromise = null;
|
|
734
|
-
baseUrl = null;
|
|
735
|
-
rules;
|
|
736
|
-
/**
|
|
737
|
-
* Set the base URL for WASM loading in Edge Runtime
|
|
738
|
-
*/
|
|
739
|
-
setBaseUrl(url) {
|
|
740
|
-
this.baseUrl = url;
|
|
741
|
-
setWasmBaseUrl(url);
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* Initialize the detector (including WASM if enabled)
|
|
745
|
-
*/
|
|
746
|
-
async init() {
|
|
747
|
-
if (!this.enableWasm) {
|
|
748
|
-
this.wasmEnabled = false;
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
if (this.initPromise) {
|
|
752
|
-
await this.initPromise;
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
this.initPromise = (async () => {
|
|
756
|
-
try {
|
|
757
|
-
const wasmAvailable = await isWasmAvailable();
|
|
758
|
-
if (wasmAvailable) {
|
|
759
|
-
if (this.baseUrl) {
|
|
760
|
-
setWasmBaseUrl(this.baseUrl);
|
|
761
|
-
}
|
|
762
|
-
await initWasm();
|
|
763
|
-
this.wasmEnabled = true;
|
|
764
|
-
} else {
|
|
765
|
-
this.wasmEnabled = false;
|
|
766
|
-
}
|
|
767
|
-
} catch (error) {
|
|
768
|
-
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
769
|
-
this.wasmEnabled = false;
|
|
770
|
-
}
|
|
771
|
-
})();
|
|
772
|
-
await this.initPromise;
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Pattern-based detection (fallback)
|
|
776
|
-
*/
|
|
777
|
-
async patternDetection(input) {
|
|
778
|
-
const reasons = [];
|
|
779
|
-
let detectedAgent;
|
|
780
|
-
let verificationMethod;
|
|
781
|
-
let confidence = 0;
|
|
782
|
-
const headers = input.headers || {};
|
|
783
|
-
const normalizedHeaders = {};
|
|
784
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
785
|
-
normalizedHeaders[key.toLowerCase()] = value;
|
|
786
|
-
}
|
|
787
|
-
const signaturePresent = !!(normalizedHeaders["signature"] || normalizedHeaders["signature-input"]);
|
|
788
|
-
const signatureAgent = normalizedHeaders["signature-agent"];
|
|
789
|
-
if (signatureAgent?.includes("chatgpt.com")) {
|
|
790
|
-
confidence = 85;
|
|
791
|
-
reasons.push("signature_agent:chatgpt");
|
|
792
|
-
detectedAgent = { type: "chatgpt", name: "ChatGPT" };
|
|
793
|
-
verificationMethod = "signature";
|
|
794
|
-
} else if (signaturePresent) {
|
|
795
|
-
confidence = Math.max(confidence, 40);
|
|
796
|
-
reasons.push("signature_present");
|
|
797
|
-
}
|
|
798
|
-
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
799
|
-
if (userAgent) {
|
|
800
|
-
for (const [agentKey, agentRule] of Object.entries(this.rules.rules.userAgents)) {
|
|
801
|
-
const matched = agentRule.patterns.some((pattern) => {
|
|
802
|
-
const regex = new RegExp(pattern, "i");
|
|
803
|
-
return regex.test(userAgent);
|
|
804
|
-
});
|
|
805
|
-
if (matched) {
|
|
806
|
-
const agentType = this.getAgentType(agentKey);
|
|
807
|
-
const agentName = this.getAgentName(agentKey);
|
|
808
|
-
confidence = Math.max(confidence, Math.round(agentRule.confidence * 0.85 * 100));
|
|
809
|
-
reasons.push(`known_pattern:${agentType}`);
|
|
810
|
-
if (!detectedAgent) {
|
|
811
|
-
detectedAgent = { type: agentType, name: agentName };
|
|
812
|
-
verificationMethod = "pattern";
|
|
813
|
-
}
|
|
814
|
-
break;
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
const suspiciousHeaders = this.rules.rules.headers.suspicious;
|
|
819
|
-
const foundAiHeaders = suspiciousHeaders.filter(
|
|
820
|
-
(headerRule) => normalizedHeaders[headerRule.name.toLowerCase()]
|
|
821
|
-
);
|
|
822
|
-
if (foundAiHeaders.length > 0) {
|
|
823
|
-
const maxConfidence = Math.max(...foundAiHeaders.map((h) => h.confidence));
|
|
824
|
-
confidence = Math.max(confidence, maxConfidence);
|
|
825
|
-
reasons.push(`ai_headers:${foundAiHeaders.length}`);
|
|
826
|
-
}
|
|
827
|
-
const ip = input.ip || input.ipAddress;
|
|
828
|
-
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
829
|
-
const ipRanges = "providers" in this.rules.rules.ipRanges ? this.rules.rules.ipRanges.providers : this.rules.rules.ipRanges;
|
|
830
|
-
for (const [provider, ipRule] of Object.entries(ipRanges)) {
|
|
831
|
-
if (!ipRule || typeof ipRule !== "object" || !("ranges" in ipRule) || !Array.isArray(ipRule.ranges))
|
|
832
|
-
continue;
|
|
833
|
-
const matched = ipRule.ranges.some((range) => {
|
|
834
|
-
const prefix = range.split("/")[0];
|
|
835
|
-
const prefixParts = prefix.split(".");
|
|
836
|
-
const ipParts = ip.split(".");
|
|
837
|
-
for (let i = 0; i < Math.min(prefixParts.length - 1, 2); i++) {
|
|
838
|
-
if (prefixParts[i] !== ipParts[i] && prefixParts[i] !== "0") {
|
|
839
|
-
return false;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
return true;
|
|
843
|
-
});
|
|
844
|
-
if (matched) {
|
|
845
|
-
confidence = Math.max(confidence, Math.round(ipRule.confidence * 0.4 * 100));
|
|
846
|
-
reasons.push(`cloud_provider:${provider}`);
|
|
847
|
-
break;
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
if (reasons.length > 2) {
|
|
852
|
-
confidence = Math.min(Math.round(confidence * 1.2), 95);
|
|
853
|
-
}
|
|
854
|
-
confidence = Math.min(Math.max(confidence, 0), 100);
|
|
855
|
-
return {
|
|
856
|
-
isAgent: confidence > 30,
|
|
857
|
-
// 30% threshold
|
|
858
|
-
confidence,
|
|
859
|
-
detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
|
|
860
|
-
signals: [],
|
|
861
|
-
// Will be populated by enhanced detection engine in future tasks
|
|
862
|
-
...detectedAgent && { detectedAgent },
|
|
863
|
-
reasons,
|
|
864
|
-
...verificationMethod && {
|
|
865
|
-
verificationMethod: agentshieldShared.mapVerificationMethod(verificationMethod)
|
|
866
|
-
},
|
|
867
|
-
forgeabilityRisk: confidence > 80 ? "medium" : "high",
|
|
868
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
869
|
-
};
|
|
870
|
-
}
|
|
871
|
-
/**
|
|
872
|
-
* Analyze request with WASM enhancement when available
|
|
873
|
-
*/
|
|
874
|
-
async analyze(input) {
|
|
875
|
-
await this.init();
|
|
876
|
-
if (this.wasmEnabled) {
|
|
877
|
-
try {
|
|
878
|
-
const wasmResult = await detectAgentWithWasm(
|
|
879
|
-
input.userAgent || input.headers?.["user-agent"],
|
|
880
|
-
input.headers || {},
|
|
881
|
-
input.ip || input.ipAddress
|
|
882
|
-
);
|
|
883
|
-
if (wasmResult) {
|
|
884
|
-
const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
|
|
885
|
-
return {
|
|
886
|
-
isAgent: wasmResult.isAgent,
|
|
887
|
-
confidence: wasmResult.confidence,
|
|
888
|
-
detectionClass: wasmResult.isAgent && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : wasmResult.isAgent ? { type: "Unknown" } : { type: "Human" },
|
|
889
|
-
signals: [],
|
|
890
|
-
// Will be populated by enhanced detection engine in future tasks
|
|
891
|
-
...detectedAgent && { detectedAgent },
|
|
892
|
-
reasons: [`wasm:${wasmResult.verificationMethod}`],
|
|
893
|
-
verificationMethod: agentshieldShared.mapVerificationMethod(wasmResult.verificationMethod),
|
|
894
|
-
forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 90 ? "medium" : "high",
|
|
895
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
896
|
-
};
|
|
897
|
-
}
|
|
898
|
-
} catch (error) {
|
|
899
|
-
console.error("[AgentShield] WASM detection error:", error);
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
const patternResult = await this.patternDetection(input);
|
|
903
|
-
if (this.wasmEnabled && patternResult.confidence >= 85) {
|
|
904
|
-
patternResult.confidence = Math.min(95, patternResult.confidence + 10);
|
|
905
|
-
patternResult.reasons.push("wasm_enhanced");
|
|
906
|
-
}
|
|
907
|
-
return patternResult;
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* Get agent type from rule key
|
|
911
|
-
*/
|
|
912
|
-
getAgentType(agentKey) {
|
|
913
|
-
const typeMap = {
|
|
914
|
-
openai_gptbot: "openai",
|
|
915
|
-
anthropic_claude: "anthropic",
|
|
916
|
-
perplexity_bot: "perplexity",
|
|
917
|
-
google_ai: "google",
|
|
918
|
-
microsoft_ai: "microsoft",
|
|
919
|
-
meta_ai: "meta",
|
|
920
|
-
cohere_bot: "cohere",
|
|
921
|
-
huggingface_bot: "huggingface",
|
|
922
|
-
generic_bot: "generic",
|
|
923
|
-
dev_tools: "dev",
|
|
924
|
-
automation_tools: "automation"
|
|
925
|
-
};
|
|
926
|
-
return typeMap[agentKey] || agentKey;
|
|
927
|
-
}
|
|
928
|
-
/**
|
|
929
|
-
* Get agent name from rule key
|
|
930
|
-
*/
|
|
931
|
-
getAgentName(agentKey) {
|
|
932
|
-
const nameMap = {
|
|
933
|
-
openai_gptbot: "ChatGPT/GPTBot",
|
|
934
|
-
anthropic_claude: "Claude",
|
|
935
|
-
perplexity_bot: "Perplexity",
|
|
936
|
-
google_ai: "Google AI",
|
|
937
|
-
microsoft_ai: "Microsoft Copilot",
|
|
938
|
-
meta_ai: "Meta AI",
|
|
939
|
-
cohere_bot: "Cohere",
|
|
940
|
-
huggingface_bot: "HuggingFace",
|
|
941
|
-
generic_bot: "Generic Bot",
|
|
942
|
-
dev_tools: "Development Tool",
|
|
943
|
-
automation_tools: "Automation Tool"
|
|
944
|
-
};
|
|
945
|
-
return nameMap[agentKey] || agentKey;
|
|
946
|
-
}
|
|
947
|
-
/**
|
|
948
|
-
* Map agent names from WASM to consistent format
|
|
949
|
-
*/
|
|
950
|
-
mapAgentName(agent) {
|
|
951
|
-
const lowerAgent = agent.toLowerCase();
|
|
952
|
-
if (lowerAgent.includes("chatgpt")) {
|
|
953
|
-
return { type: "chatgpt", name: "ChatGPT" };
|
|
954
|
-
} else if (lowerAgent.includes("claude")) {
|
|
955
|
-
return { type: "claude", name: "Claude" };
|
|
956
|
-
} else if (lowerAgent.includes("perplexity")) {
|
|
957
|
-
return { type: "perplexity", name: "Perplexity" };
|
|
958
|
-
} else if (lowerAgent.includes("bing")) {
|
|
959
|
-
return { type: "bing", name: "Bing AI" };
|
|
960
|
-
} else if (lowerAgent.includes("anthropic")) {
|
|
961
|
-
return { type: "anthropic", name: "Anthropic" };
|
|
962
|
-
}
|
|
963
|
-
return { type: "unknown", name: agent };
|
|
964
|
-
}
|
|
965
|
-
};
|
|
966
|
-
var EdgeAgentDetectorWrapperWithWasm = class {
|
|
967
|
-
detector;
|
|
968
|
-
events = /* @__PURE__ */ new Map();
|
|
969
|
-
constructor(config) {
|
|
970
|
-
this.detector = new EdgeAgentDetectorWithWasm(config?.enableWasm ?? true);
|
|
971
|
-
if (config?.baseUrl) {
|
|
972
|
-
this.detector.setBaseUrl(config.baseUrl);
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
setBaseUrl(url) {
|
|
976
|
-
this.detector.setBaseUrl(url);
|
|
977
|
-
}
|
|
978
|
-
async analyze(input) {
|
|
979
|
-
const result = await this.detector.analyze(input);
|
|
980
|
-
if (result.isAgent && this.events.has("agent.detected")) {
|
|
981
|
-
const handlers = this.events.get("agent.detected") || [];
|
|
982
|
-
handlers.forEach((handler) => handler(result, input));
|
|
983
|
-
}
|
|
984
|
-
return result;
|
|
985
|
-
}
|
|
986
|
-
on(event, handler) {
|
|
987
|
-
if (!this.events.has(event)) {
|
|
988
|
-
this.events.set(event, []);
|
|
989
|
-
}
|
|
990
|
-
this.events.get(event).push(handler);
|
|
991
|
-
}
|
|
992
|
-
emit(event, ...args) {
|
|
993
|
-
const handlers = this.events.get(event) || [];
|
|
994
|
-
handlers.forEach((handler) => handler(...args));
|
|
995
|
-
}
|
|
996
|
-
async init() {
|
|
997
|
-
await this.detector.init();
|
|
998
|
-
}
|
|
999
|
-
};
|
|
1000
|
-
|
|
1001
|
-
// src/session-tracker.ts
|
|
1002
|
-
var EdgeSessionTracker = class {
|
|
1003
|
-
config;
|
|
1004
|
-
constructor(config) {
|
|
1005
|
-
this.config = {
|
|
1006
|
-
enabled: config.enabled,
|
|
1007
|
-
cookieName: config.cookieName || "__agentshield_session",
|
|
1008
|
-
cookieMaxAge: config.cookieMaxAge || 3600,
|
|
1009
|
-
// 1 hour default
|
|
1010
|
-
encryptionKey: config.encryptionKey || process.env.AGENTSHIELD_SECRET || "agentshield-default-key"
|
|
1011
|
-
};
|
|
1012
|
-
}
|
|
1013
|
-
/**
|
|
1014
|
-
* Track a new AI agent session
|
|
1015
|
-
*/
|
|
1016
|
-
async track(_request, response, result) {
|
|
1017
|
-
try {
|
|
1018
|
-
if (!this.config.enabled || !result.isAgent) {
|
|
1019
|
-
return response;
|
|
1020
|
-
}
|
|
1021
|
-
const sessionData = {
|
|
1022
|
-
id: crypto.randomUUID(),
|
|
1023
|
-
agent: result.detectedAgent?.name || "unknown",
|
|
1024
|
-
confidence: result.confidence,
|
|
1025
|
-
detectedAt: Date.now(),
|
|
1026
|
-
expires: Date.now() + this.config.cookieMaxAge * 1e3
|
|
1027
|
-
};
|
|
1028
|
-
const encrypted = await this.encrypt(JSON.stringify(sessionData));
|
|
1029
|
-
response.cookies.set(this.config.cookieName, encrypted, {
|
|
1030
|
-
httpOnly: true,
|
|
1031
|
-
secure: process.env.NODE_ENV === "production",
|
|
1032
|
-
sameSite: "lax",
|
|
1033
|
-
maxAge: this.config.cookieMaxAge,
|
|
1034
|
-
path: "/"
|
|
1035
|
-
});
|
|
1036
|
-
return response;
|
|
1037
|
-
} catch (error) {
|
|
1038
|
-
if (process.env.DEBUG_AGENTSHIELD) {
|
|
1039
|
-
console.warn("AgentShield: Failed to track session:", error);
|
|
1040
|
-
}
|
|
1041
|
-
return response;
|
|
1058
|
+
if (!this.config.enabled || !agentshieldShared.shouldEnforce(result)) {
|
|
1059
|
+
return response;
|
|
1060
|
+
}
|
|
1061
|
+
const sessionData = {
|
|
1062
|
+
id: crypto.randomUUID(),
|
|
1063
|
+
agent: result.detectedAgent?.name || "unknown",
|
|
1064
|
+
confidence: result.confidence,
|
|
1065
|
+
detectedAt: Date.now(),
|
|
1066
|
+
expires: Date.now() + this.config.cookieMaxAge * 1e3
|
|
1067
|
+
};
|
|
1068
|
+
const encrypted = await this.encrypt(JSON.stringify(sessionData));
|
|
1069
|
+
response.cookies.set(this.config.cookieName, encrypted, {
|
|
1070
|
+
httpOnly: true,
|
|
1071
|
+
secure: process.env.NODE_ENV === "production",
|
|
1072
|
+
sameSite: "lax",
|
|
1073
|
+
maxAge: this.config.cookieMaxAge,
|
|
1074
|
+
path: "/"
|
|
1075
|
+
});
|
|
1076
|
+
return response;
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
if (process.env.DEBUG_AGENTSHIELD) {
|
|
1079
|
+
console.warn("AgentShield: Failed to track session:", error);
|
|
1080
|
+
}
|
|
1081
|
+
return response;
|
|
1042
1082
|
}
|
|
1043
1083
|
}
|
|
1044
1084
|
/**
|
|
@@ -1090,9 +1130,7 @@ var EdgeSessionTracker = class {
|
|
|
1090
1130
|
for (let i = 0; i < encoded.length; i++) {
|
|
1091
1131
|
obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);
|
|
1092
1132
|
}
|
|
1093
|
-
return btoa(
|
|
1094
|
-
Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join("")
|
|
1095
|
-
);
|
|
1133
|
+
return btoa(Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join(""));
|
|
1096
1134
|
} catch (error) {
|
|
1097
1135
|
return btoa(data);
|
|
1098
1136
|
}
|
|
@@ -1115,14 +1153,29 @@ var EdgeSessionTracker = class {
|
|
|
1115
1153
|
}
|
|
1116
1154
|
};
|
|
1117
1155
|
|
|
1118
|
-
// src/
|
|
1156
|
+
// src/utils.ts
|
|
1157
|
+
function getClientIp(request) {
|
|
1158
|
+
const forwardedFor = request.headers.get("x-forwarded-for");
|
|
1159
|
+
if (forwardedFor) {
|
|
1160
|
+
const ip = forwardedFor.split(",")[0]?.trim();
|
|
1161
|
+
if (ip) return ip;
|
|
1162
|
+
}
|
|
1163
|
+
const realIp = request.headers.get("x-real-ip");
|
|
1164
|
+
if (realIp) return realIp;
|
|
1165
|
+
const cfIp = request.headers.get("cf-connecting-ip");
|
|
1166
|
+
if (cfIp) return cfIp;
|
|
1167
|
+
const clientIp = request.headers.get("x-client-ip");
|
|
1168
|
+
if (clientIp) return clientIp;
|
|
1169
|
+
return void 0;
|
|
1170
|
+
}
|
|
1119
1171
|
function createAgentShieldMiddleware(config = {}) {
|
|
1120
|
-
|
|
1172
|
+
let detector = config.enableWasm ? null : new EdgeAgentDetectorWrapper(config);
|
|
1173
|
+
let detectorInitPromise = null;
|
|
1121
1174
|
const sessionTracker = config.sessionTracking?.enabled || config.enableWasm ? new EdgeSessionTracker({
|
|
1122
1175
|
enabled: true,
|
|
1123
1176
|
...config.sessionTracking
|
|
1124
1177
|
}) : null;
|
|
1125
|
-
if (config.events) {
|
|
1178
|
+
if (detector && config.events) {
|
|
1126
1179
|
Object.entries(config.events).forEach(([event, handler]) => {
|
|
1127
1180
|
if (handler) {
|
|
1128
1181
|
detector.on(event, handler);
|
|
@@ -1143,6 +1196,23 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
1143
1196
|
} = config;
|
|
1144
1197
|
return async (request) => {
|
|
1145
1198
|
try {
|
|
1199
|
+
if (!detector) {
|
|
1200
|
+
if (!detectorInitPromise) {
|
|
1201
|
+
detectorInitPromise = (async () => {
|
|
1202
|
+
const { EdgeAgentDetectorWrapperWithWasm: EdgeAgentDetectorWrapperWithWasm2 } = await Promise.resolve().then(() => (init_edge_detector_with_wasm(), edge_detector_with_wasm_exports));
|
|
1203
|
+
detector = new EdgeAgentDetectorWrapperWithWasm2({ enableWasm: true });
|
|
1204
|
+
if (config.events) {
|
|
1205
|
+
Object.entries(config.events).forEach(([event, handler]) => {
|
|
1206
|
+
if (handler) {
|
|
1207
|
+
detector.on(event, handler);
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
})();
|
|
1212
|
+
}
|
|
1213
|
+
await detectorInitPromise;
|
|
1214
|
+
}
|
|
1215
|
+
const activeDetector = detector;
|
|
1146
1216
|
const shouldSkip = skipPaths.some((pattern) => {
|
|
1147
1217
|
if (typeof pattern === "string") {
|
|
1148
1218
|
return request.nextUrl.pathname.startsWith(pattern);
|
|
@@ -1156,11 +1226,11 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
1156
1226
|
const existingSession = sessionTracker ? await sessionTracker.check(request) : null;
|
|
1157
1227
|
if (existingSession) {
|
|
1158
1228
|
const response2 = server.NextResponse.next();
|
|
1159
|
-
response2.headers.set("
|
|
1160
|
-
response2.headers.set("
|
|
1161
|
-
response2.headers.set("
|
|
1162
|
-
response2.headers.set("
|
|
1163
|
-
response2.headers.set("
|
|
1229
|
+
response2.headers.set("kya-detected", "true");
|
|
1230
|
+
response2.headers.set("kya-agent", existingSession.agent);
|
|
1231
|
+
response2.headers.set("kya-confidence", existingSession.confidence.toString());
|
|
1232
|
+
response2.headers.set("kya-session", "continued");
|
|
1233
|
+
response2.headers.set("kya-session-id", existingSession.id);
|
|
1164
1234
|
request.agentShield = {
|
|
1165
1235
|
result: {
|
|
1166
1236
|
isAgent: true,
|
|
@@ -1180,17 +1250,17 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
1180
1250
|
};
|
|
1181
1251
|
const context2 = {
|
|
1182
1252
|
userAgent: request.headers.get("user-agent") || "",
|
|
1183
|
-
ipAddress: (request
|
|
1253
|
+
ipAddress: getClientIp(request) || "",
|
|
1184
1254
|
headers: Object.fromEntries(request.headers.entries()),
|
|
1185
1255
|
url: request.url,
|
|
1186
1256
|
method: request.method,
|
|
1187
1257
|
timestamp: /* @__PURE__ */ new Date()
|
|
1188
1258
|
};
|
|
1189
|
-
|
|
1259
|
+
activeDetector.emit("agent.session.continued", existingSession, context2);
|
|
1190
1260
|
return response2;
|
|
1191
1261
|
}
|
|
1192
1262
|
const userAgent = request.headers.get("user-agent");
|
|
1193
|
-
const ipAddress = request
|
|
1263
|
+
const ipAddress = getClientIp(request);
|
|
1194
1264
|
const url = new URL(request.url);
|
|
1195
1265
|
const pathWithQuery = url.pathname + url.search;
|
|
1196
1266
|
const context = {
|
|
@@ -1202,15 +1272,19 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
1202
1272
|
method: request.method,
|
|
1203
1273
|
timestamp: /* @__PURE__ */ new Date()
|
|
1204
1274
|
};
|
|
1205
|
-
const result = await
|
|
1206
|
-
|
|
1275
|
+
const result = await activeDetector.analyze(context);
|
|
1276
|
+
const decision = agentshieldShared.evaluateEnforcement(result, {
|
|
1277
|
+
confidenceThreshold: config.confidenceThreshold,
|
|
1278
|
+
defaultAction: onAgentDetected
|
|
1279
|
+
});
|
|
1280
|
+
if (decision.shouldNotify) {
|
|
1207
1281
|
if (onDetection) {
|
|
1208
1282
|
const customResponse = await onDetection(request, result);
|
|
1209
1283
|
if (customResponse) {
|
|
1210
1284
|
return customResponse;
|
|
1211
1285
|
}
|
|
1212
1286
|
}
|
|
1213
|
-
switch (
|
|
1287
|
+
switch (decision.action) {
|
|
1214
1288
|
case "block": {
|
|
1215
1289
|
const response2 = server.NextResponse.json(
|
|
1216
1290
|
{
|
|
@@ -1226,11 +1300,17 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
1226
1300
|
response2.headers.set(key, value);
|
|
1227
1301
|
});
|
|
1228
1302
|
}
|
|
1229
|
-
|
|
1303
|
+
activeDetector.emit("agent.blocked", result, context);
|
|
1230
1304
|
return response2;
|
|
1231
1305
|
}
|
|
1232
|
-
case "redirect":
|
|
1233
|
-
|
|
1306
|
+
case "redirect": {
|
|
1307
|
+
const redirectTarget = new URL(redirectUrl, request.url);
|
|
1308
|
+
const agentName = result.detectedAgent?.name;
|
|
1309
|
+
if (agentName && !redirectTarget.searchParams.has("agent")) {
|
|
1310
|
+
redirectTarget.searchParams.set("agent", agentName.toLowerCase());
|
|
1311
|
+
}
|
|
1312
|
+
return server.NextResponse.redirect(redirectTarget);
|
|
1313
|
+
}
|
|
1234
1314
|
case "rewrite":
|
|
1235
1315
|
return server.NextResponse.rewrite(new URL(rewriteUrl, request.url));
|
|
1236
1316
|
case "log":
|
|
@@ -1246,9 +1326,7 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
1246
1326
|
break;
|
|
1247
1327
|
case "allow":
|
|
1248
1328
|
default:
|
|
1249
|
-
|
|
1250
|
-
detector.emit("agent.allowed", result, context);
|
|
1251
|
-
}
|
|
1329
|
+
activeDetector.emit("agent.allowed", result, context);
|
|
1252
1330
|
break;
|
|
1253
1331
|
}
|
|
1254
1332
|
}
|
|
@@ -1257,15 +1335,15 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
1257
1335
|
skipped: false
|
|
1258
1336
|
};
|
|
1259
1337
|
let response = server.NextResponse.next();
|
|
1260
|
-
response.headers.set("
|
|
1261
|
-
response.headers.set("
|
|
1338
|
+
response.headers.set("kya-detected", result.isAgent.toString());
|
|
1339
|
+
response.headers.set("kya-confidence", result.confidence.toString());
|
|
1262
1340
|
if (result.detectedAgent?.name) {
|
|
1263
|
-
response.headers.set("
|
|
1341
|
+
response.headers.set("kya-agent", result.detectedAgent.name);
|
|
1264
1342
|
}
|
|
1265
|
-
if (sessionTracker &&
|
|
1343
|
+
if (sessionTracker && decision.shouldNotify) {
|
|
1266
1344
|
response = await sessionTracker.track(request, response, result);
|
|
1267
|
-
response.headers.set("
|
|
1268
|
-
|
|
1345
|
+
response.headers.set("kya-session", "new");
|
|
1346
|
+
activeDetector.emit("agent.session.started", result, context);
|
|
1269
1347
|
}
|
|
1270
1348
|
return response;
|
|
1271
1349
|
} catch (error) {
|