@kya-os/agentshield-nextjs 0.3.2 → 0.3.5

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.
Files changed (110) hide show
  1. package/README.md +21 -369
  2. package/index.js +9 -0
  3. package/package.json +6 -141
  4. package/EDGE_RUNTIME_WASM_SETUP.md +0 -348
  5. package/bin/setup-edge-wasm.js +0 -525
  6. package/dist/.tsbuildinfo +0 -1
  7. package/dist/api-client.d.mts +0 -196
  8. package/dist/api-client.d.ts +0 -196
  9. package/dist/api-client.js +0 -200
  10. package/dist/api-client.js.map +0 -1
  11. package/dist/api-client.mjs +0 -196
  12. package/dist/api-client.mjs.map +0 -1
  13. package/dist/api-middleware.d.mts +0 -140
  14. package/dist/api-middleware.d.ts +0 -140
  15. package/dist/api-middleware.js +0 -511
  16. package/dist/api-middleware.js.map +0 -1
  17. package/dist/api-middleware.mjs +0 -508
  18. package/dist/api-middleware.mjs.map +0 -1
  19. package/dist/create-middleware.d.mts +0 -17
  20. package/dist/create-middleware.d.ts +0 -17
  21. package/dist/create-middleware.js +0 -1381
  22. package/dist/create-middleware.js.map +0 -1
  23. package/dist/create-middleware.mjs +0 -1358
  24. package/dist/create-middleware.mjs.map +0 -1
  25. package/dist/edge/index.d.mts +0 -110
  26. package/dist/edge/index.d.ts +0 -110
  27. package/dist/edge/index.js +0 -277
  28. package/dist/edge/index.js.map +0 -1
  29. package/dist/edge/index.mjs +0 -275
  30. package/dist/edge/index.mjs.map +0 -1
  31. package/dist/edge-detector-wrapper.d.mts +0 -34
  32. package/dist/edge-detector-wrapper.d.ts +0 -34
  33. package/dist/edge-detector-wrapper.js +0 -596
  34. package/dist/edge-detector-wrapper.js.map +0 -1
  35. package/dist/edge-detector-wrapper.mjs +0 -574
  36. package/dist/edge-detector-wrapper.mjs.map +0 -1
  37. package/dist/edge-runtime-loader.d.mts +0 -50
  38. package/dist/edge-runtime-loader.d.ts +0 -50
  39. package/dist/edge-runtime-loader.js +0 -204
  40. package/dist/edge-runtime-loader.js.map +0 -1
  41. package/dist/edge-runtime-loader.mjs +0 -201
  42. package/dist/edge-runtime-loader.mjs.map +0 -1
  43. package/dist/edge-wasm-middleware.d.mts +0 -68
  44. package/dist/edge-wasm-middleware.d.ts +0 -68
  45. package/dist/edge-wasm-middleware.js +0 -318
  46. package/dist/edge-wasm-middleware.js.map +0 -1
  47. package/dist/edge-wasm-middleware.mjs +0 -315
  48. package/dist/edge-wasm-middleware.mjs.map +0 -1
  49. package/dist/enhanced-middleware.d.mts +0 -153
  50. package/dist/enhanced-middleware.d.ts +0 -153
  51. package/dist/enhanced-middleware.js +0 -1082
  52. package/dist/enhanced-middleware.js.map +0 -1
  53. package/dist/enhanced-middleware.mjs +0 -1080
  54. package/dist/enhanced-middleware.mjs.map +0 -1
  55. package/dist/index.d.mts +0 -24
  56. package/dist/index.d.ts +0 -24
  57. package/dist/index.js +0 -2717
  58. package/dist/index.js.map +0 -1
  59. package/dist/index.mjs +0 -2662
  60. package/dist/index.mjs.map +0 -1
  61. package/dist/middleware.d.mts +0 -21
  62. package/dist/middleware.d.ts +0 -21
  63. package/dist/middleware.js +0 -1362
  64. package/dist/middleware.js.map +0 -1
  65. package/dist/middleware.mjs +0 -1339
  66. package/dist/middleware.mjs.map +0 -1
  67. package/dist/nodejs-wasm-loader.d.mts +0 -25
  68. package/dist/nodejs-wasm-loader.d.ts +0 -25
  69. package/dist/nodejs-wasm-loader.js +0 -78
  70. package/dist/nodejs-wasm-loader.js.map +0 -1
  71. package/dist/nodejs-wasm-loader.mjs +0 -68
  72. package/dist/nodejs-wasm-loader.mjs.map +0 -1
  73. package/dist/policy.d.mts +0 -162
  74. package/dist/policy.d.ts +0 -162
  75. package/dist/policy.js +0 -189
  76. package/dist/policy.js.map +0 -1
  77. package/dist/policy.mjs +0 -165
  78. package/dist/policy.mjs.map +0 -1
  79. package/dist/session-tracker.d.mts +0 -55
  80. package/dist/session-tracker.d.ts +0 -55
  81. package/dist/session-tracker.js +0 -170
  82. package/dist/session-tracker.js.map +0 -1
  83. package/dist/session-tracker.mjs +0 -167
  84. package/dist/session-tracker.mjs.map +0 -1
  85. package/dist/signature-verifier.d.mts +0 -33
  86. package/dist/signature-verifier.d.ts +0 -33
  87. package/dist/signature-verifier.js +0 -386
  88. package/dist/signature-verifier.js.map +0 -1
  89. package/dist/signature-verifier.mjs +0 -362
  90. package/dist/signature-verifier.mjs.map +0 -1
  91. package/dist/types-DVmy9NE3.d.mts +0 -105
  92. package/dist/types-DVmy9NE3.d.ts +0 -105
  93. package/dist/wasm-middleware.d.mts +0 -63
  94. package/dist/wasm-middleware.d.ts +0 -63
  95. package/dist/wasm-middleware.js +0 -98
  96. package/dist/wasm-middleware.js.map +0 -1
  97. package/dist/wasm-middleware.mjs +0 -95
  98. package/dist/wasm-middleware.mjs.map +0 -1
  99. package/dist/wasm-setup.d.mts +0 -46
  100. package/dist/wasm-setup.d.ts +0 -46
  101. package/dist/wasm-setup.js +0 -157
  102. package/dist/wasm-setup.js.map +0 -1
  103. package/dist/wasm-setup.mjs +0 -148
  104. package/dist/wasm-setup.mjs.map +0 -1
  105. package/templates/middleware-wasm-100.ts +0 -151
  106. package/wasm/agentshield_wasm.d.ts +0 -479
  107. package/wasm/agentshield_wasm.js +0 -1536
  108. package/wasm/agentshield_wasm_bg.wasm +0 -0
  109. package/wasm/package.json +0 -30
  110. package/wasm.d.ts +0 -21
@@ -1,1362 +0,0 @@
1
- 'use strict';
2
-
3
- var agentshieldShared = require('@kya-os/agentshield-shared');
4
- var server = require('next/server');
5
- var ed25519 = require('@noble/ed25519');
6
- var sha2_js = require('@noble/hashes/sha2.js');
7
-
8
- function _interopNamespace(e) {
9
- if (e && e.__esModule) return e;
10
- var n = Object.create(null);
11
- if (e) {
12
- Object.keys(e).forEach(function (k) {
13
- if (k !== 'default') {
14
- var d = Object.getOwnPropertyDescriptor(e, k);
15
- Object.defineProperty(n, k, d.get ? d : {
16
- enumerable: true,
17
- get: function () { return e[k]; }
18
- });
19
- }
20
- });
21
- }
22
- n.default = e;
23
- return Object.freeze(n);
24
- }
25
-
26
- var ed25519__namespace = /*#__PURE__*/_interopNamespace(ed25519);
27
-
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
- });
477
- ed25519__namespace.etc.sha512Sync = (...m) => sha2_js.sha512(ed25519__namespace.etc.concatBytes(...m));
478
- var KNOWN_KEYS = {
479
- chatgpt: [
480
- {
481
- kid: "otMqcjr17mGyruktGvJU8oojQTSMHlVm7uO-lrcqbdg",
482
- // ChatGPT's current Ed25519 public key (base64)
483
- // Source: https://chatgpt.com/.well-known/http-message-signatures-directory
484
- publicKey: "7F_3jDlxaquwh291MiACkcS3Opq88NksyHiakzS-Y1g",
485
- validFrom: 1735689600,
486
- // Jan 1, 2025 (nbf from OpenAI)
487
- validUntil: 1769029093
488
- // Jan 21, 2026 (exp from OpenAI)
489
- }
490
- ]
491
- };
492
- var keyCache = /* @__PURE__ */ new Map();
493
- var CACHE_TTL_MS = 5 * 60 * 1e3;
494
- var CACHE_MAX_SIZE = 100;
495
- function getApiBaseUrl() {
496
- if (typeof window !== "undefined") {
497
- return "/api/internal";
498
- }
499
- const baseUrl2 = process.env.NEXT_PUBLIC_APP_URL || process.env.NEXT_PUBLIC_API_URL || process.env.API_URL || (process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : null);
500
- if (baseUrl2) {
501
- return baseUrl2.replace(/\/$/, "") + "/api/internal";
502
- }
503
- if (process.env.NODE_ENV !== "production") {
504
- console.warn(
505
- "[Signature] No base URL configured for server-side fetch. Using localhost fallback."
506
- );
507
- return "http://localhost:3000/api/internal";
508
- }
509
- console.error(
510
- "[Signature] CRITICAL: No base URL configured for server-side fetch in production!"
511
- );
512
- return "/api/internal";
513
- }
514
- function cleanupExpiredCache() {
515
- const now = Date.now();
516
- const entriesToDelete = [];
517
- for (const [agent, cached] of keyCache.entries()) {
518
- if (now - cached.cachedAt > CACHE_TTL_MS) {
519
- entriesToDelete.push(agent);
520
- }
521
- }
522
- for (const agent of entriesToDelete) {
523
- keyCache.delete(agent);
524
- }
525
- if (keyCache.size > CACHE_MAX_SIZE) {
526
- const entries = Array.from(keyCache.entries()).map(([agent, cached]) => ({
527
- agent,
528
- cachedAt: cached.cachedAt
529
- }));
530
- entries.sort((a, b) => a.cachedAt - b.cachedAt);
531
- const toRemove = entries.slice(0, keyCache.size - CACHE_MAX_SIZE);
532
- for (const entry of toRemove) {
533
- keyCache.delete(entry.agent);
534
- }
535
- }
536
- }
537
- async function fetchKeysFromApi(agent) {
538
- if (keyCache.size > CACHE_MAX_SIZE) {
539
- cleanupExpiredCache();
540
- }
541
- const cached = keyCache.get(agent);
542
- if (cached && Date.now() - cached.cachedAt < CACHE_TTL_MS) {
543
- return cached.keys;
544
- }
545
- if (typeof fetch === "undefined") {
546
- console.warn("[Signature] fetch() not available in this environment");
547
- return null;
548
- }
549
- try {
550
- const apiBaseUrl = getApiBaseUrl();
551
- const url = `${apiBaseUrl}/signature-keys?agent=${encodeURIComponent(agent)}`;
552
- const response = await fetch(url, {
553
- method: "GET",
554
- headers: {
555
- "Content-Type": "application/json"
556
- },
557
- // 5 second timeout
558
- signal: AbortSignal.timeout(5e3)
559
- });
560
- if (!response.ok) {
561
- console.warn(`[Signature] Failed to fetch keys from API: ${response.status}`);
562
- return null;
563
- }
564
- const data = await response.json();
565
- if (!data.keys || !Array.isArray(data.keys) || data.keys.length === 0) {
566
- console.warn(`[Signature] No keys returned from API for agent: ${agent}`);
567
- return null;
568
- }
569
- keyCache.set(agent, {
570
- keys: data.keys,
571
- cachedAt: Date.now()
572
- });
573
- return data.keys;
574
- } catch (error) {
575
- console.warn("[Signature] Error fetching keys from API, using fallback", {
576
- error: error instanceof Error ? error.message : "Unknown error",
577
- agent
578
- });
579
- return null;
580
- }
581
- }
582
- function isValidAgent(agent) {
583
- return agent in KNOWN_KEYS;
584
- }
585
- async function getKeysForAgent(agent) {
586
- const apiKeys = await fetchKeysFromApi(agent);
587
- if (apiKeys && apiKeys.length > 0) {
588
- return apiKeys;
589
- }
590
- if (isValidAgent(agent)) {
591
- return KNOWN_KEYS[agent];
592
- }
593
- return [];
594
- }
595
- function parseSignatureInput(signatureInput) {
596
- try {
597
- const match = signatureInput.match(/sig1=\((.*?)\);(.+)/);
598
- if (!match) return null;
599
- const [, headersList, params] = match;
600
- const signedHeaders = headersList ? headersList.split(" ").map((h) => h.replace(/"/g, "").trim()).filter((h) => h.length > 0) : [];
601
- const keyidMatch = params ? params.match(/keyid="([^"]+)"/) : null;
602
- const createdMatch = params ? params.match(/created=(\d+)/) : null;
603
- const expiresMatch = params ? params.match(/expires=(\d+)/) : null;
604
- if (!keyidMatch || !keyidMatch[1]) return null;
605
- return {
606
- keyid: keyidMatch[1],
607
- created: createdMatch && createdMatch[1] ? parseInt(createdMatch[1]) : void 0,
608
- expires: expiresMatch && expiresMatch[1] ? parseInt(expiresMatch[1]) : void 0,
609
- signedHeaders
610
- };
611
- } catch (error) {
612
- console.error("[Signature] Failed to parse Signature-Input:", error);
613
- return null;
614
- }
615
- }
616
- function buildSignatureBase(method, path, headers, signedHeaders) {
617
- const components = [];
618
- for (const headerName of signedHeaders) {
619
- let value;
620
- switch (headerName) {
621
- case "@method":
622
- value = method.toUpperCase();
623
- break;
624
- case "@path":
625
- value = path;
626
- break;
627
- case "@authority":
628
- value = headers["host"] || headers["Host"] || "";
629
- break;
630
- default: {
631
- const key = Object.keys(headers).find((k) => k.toLowerCase() === headerName.toLowerCase());
632
- value = key ? headers[key] || "" : "";
633
- break;
634
- }
635
- }
636
- components.push(`"${headerName}": ${value}`);
637
- }
638
- return components.join("\n");
639
- }
640
- function base64ToBytes(base64) {
641
- let standardBase64 = base64.replace(/-/g, "+").replace(/_/g, "/");
642
- const padding = standardBase64.length % 4;
643
- if (padding) {
644
- standardBase64 += "=".repeat(4 - padding);
645
- }
646
- const binaryString = atob(standardBase64);
647
- return Uint8Array.from(binaryString, (c) => c.charCodeAt(0));
648
- }
649
- async function verifyEd25519Signature(publicKeyBase64, signatureBase64, message) {
650
- try {
651
- const publicKeyBytes = base64ToBytes(publicKeyBase64);
652
- const signatureBytes = base64ToBytes(signatureBase64);
653
- const messageBytes = new TextEncoder().encode(message);
654
- if (publicKeyBytes.length !== 32) {
655
- console.error("[Signature] Invalid public key length:", publicKeyBytes.length);
656
- return false;
657
- }
658
- if (signatureBytes.length !== 64) {
659
- console.error("[Signature] Invalid signature length:", signatureBytes.length);
660
- return false;
661
- }
662
- return ed25519__namespace.verify(signatureBytes, messageBytes, publicKeyBytes);
663
- } catch (nobleError) {
664
- console.warn("[Signature] @noble/ed25519 failed, trying Web Crypto fallback:", nobleError);
665
- try {
666
- const publicKeyBytes = base64ToBytes(publicKeyBase64);
667
- const signatureBytes = base64ToBytes(signatureBase64);
668
- const messageBytes = new TextEncoder().encode(message);
669
- const publicKey = await crypto.subtle.importKey(
670
- "raw",
671
- publicKeyBytes.buffer,
672
- {
673
- name: "Ed25519",
674
- namedCurve: "Ed25519"
675
- },
676
- false,
677
- ["verify"]
678
- );
679
- return await crypto.subtle.verify(
680
- "Ed25519",
681
- publicKey,
682
- signatureBytes.buffer,
683
- messageBytes
684
- );
685
- } catch (cryptoError) {
686
- console.error("[Signature] Both @noble/ed25519 and Web Crypto failed:", {
687
- nobleError: nobleError instanceof Error ? nobleError.message : "Unknown",
688
- cryptoError: cryptoError instanceof Error ? cryptoError.message : "Unknown"
689
- });
690
- return false;
691
- }
692
- }
693
- }
694
- async function verifyAgentSignature(method, path, headers) {
695
- const signature = headers["signature"] || headers["Signature"];
696
- const signatureInput = headers["signature-input"] || headers["Signature-Input"];
697
- const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
698
- if (!signature || !signatureInput) {
699
- return {
700
- isValid: false,
701
- confidence: 0,
702
- reason: "No signature headers present",
703
- verificationMethod: "none"
704
- };
705
- }
706
- const parsed = parseSignatureInput(signatureInput);
707
- if (!parsed) {
708
- return {
709
- isValid: false,
710
- confidence: 0,
711
- reason: "Invalid Signature-Input header",
712
- verificationMethod: "none"
713
- };
714
- }
715
- if (parsed.created) {
716
- const now2 = Math.floor(Date.now() / 1e3);
717
- const age = now2 - parsed.created;
718
- if (age > 300) {
719
- return {
720
- isValid: false,
721
- confidence: 0,
722
- reason: "Signature expired (older than 5 minutes)",
723
- verificationMethod: "none"
724
- };
725
- }
726
- if (age < -30) {
727
- return {
728
- isValid: false,
729
- confidence: 0,
730
- reason: "Signature timestamp is in the future",
731
- verificationMethod: "none"
732
- };
733
- }
734
- }
735
- let agent;
736
- let agentKey;
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) {
746
- agent = "ChatGPT";
747
- agentKey = "chatgpt";
748
- }
749
- if (!agent || !agentKey) {
750
- return {
751
- isValid: false,
752
- confidence: 0,
753
- reason: "Unknown signature agent",
754
- verificationMethod: "none"
755
- };
756
- }
757
- const knownKeys = await getKeysForAgent(agentKey);
758
- if (knownKeys.length === 0) {
759
- return {
760
- isValid: false,
761
- confidence: 0,
762
- reason: "No keys available for agent",
763
- verificationMethod: "none"
764
- };
765
- }
766
- const key = knownKeys.find((k) => k.kid === parsed.keyid);
767
- if (!key) {
768
- return {
769
- isValid: false,
770
- confidence: 0,
771
- reason: `Unknown key ID: ${parsed.keyid}`,
772
- verificationMethod: "none"
773
- };
774
- }
775
- const now = Math.floor(Date.now() / 1e3);
776
- if (now < key.validFrom || now > key.validUntil) {
777
- return {
778
- isValid: false,
779
- confidence: 0,
780
- reason: "Key is not valid at current time",
781
- verificationMethod: "none"
782
- };
783
- }
784
- const signatureBase = buildSignatureBase(method, path, headers, parsed.signedHeaders);
785
- let signatureValue = signature;
786
- if (signatureValue.startsWith("sig1=:")) {
787
- signatureValue = signatureValue.substring(6);
788
- }
789
- if (signatureValue.endsWith(":")) {
790
- signatureValue = signatureValue.slice(0, -1);
791
- }
792
- const isValid = await verifyEd25519Signature(key.publicKey, signatureValue, signatureBase);
793
- if (isValid) {
794
- return {
795
- isValid: true,
796
- agent,
797
- keyid: parsed.keyid,
798
- confidence: 1,
799
- // 100% confidence for valid signature
800
- verificationMethod: "signature"
801
- };
802
- } else {
803
- return {
804
- isValid: false,
805
- confidence: 0,
806
- reason: "Signature verification failed",
807
- verificationMethod: "none"
808
- };
809
- }
810
- }
811
- function hasSignatureHeaders(headers) {
812
- return !!((headers["signature"] || headers["Signature"]) && (headers["signature-input"] || headers["Signature-Input"]));
813
- }
814
- function isChatGPTSignature(headers) {
815
- const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
816
- if (!signatureAgent) {
817
- return false;
818
- }
819
- const agentUrlStr = signatureAgent.replace(/^"+|"+$/g, "");
820
- if (agentUrlStr === "https://chatgpt.com") {
821
- return true;
822
- }
823
- try {
824
- const agentUrl = new URL(agentUrlStr);
825
- const allowedHosts = ["chatgpt.com", "www.chatgpt.com"];
826
- return allowedHosts.includes(agentUrl.host);
827
- } catch {
828
- return false;
829
- }
830
- }
831
- var rules = agentshieldShared.loadRulesSync();
832
- var EdgeAgentDetector = class {
833
- rules;
834
- constructor() {
835
- this.rules = rules;
836
- }
837
- async analyze(input) {
838
- const reasons = [];
839
- let detectedAgent;
840
- let verificationMethod;
841
- let confidence = 0;
842
- const headers = input.headers || {};
843
- const normalizedHeaders = {};
844
- for (const [key, value] of Object.entries(headers)) {
845
- normalizedHeaders[key.toLowerCase()] = value;
846
- }
847
- if (hasSignatureHeaders(headers)) {
848
- try {
849
- const signatureResult = await verifyAgentSignature(
850
- input.method || "GET",
851
- input.url || "/",
852
- headers
853
- );
854
- if (signatureResult.isValid) {
855
- confidence = signatureResult.confidence * 100;
856
- reasons.push(`verified_signature:${signatureResult.agent?.toLowerCase() || "unknown"}`);
857
- if (signatureResult.agent) {
858
- detectedAgent = {
859
- type: signatureResult.agent.toLowerCase(),
860
- name: signatureResult.agent
861
- };
862
- }
863
- verificationMethod = signatureResult.verificationMethod;
864
- if (signatureResult.keyid) {
865
- reasons.push(`keyid:${signatureResult.keyid}`);
866
- }
867
- } else {
868
- console.warn("[EdgeAgentDetector] Signature verification failed:", {
869
- reason: signatureResult.reason,
870
- agent: signatureResult.agent,
871
- hasSignatureAgent: !!headers["signature-agent"] || !!headers["Signature-Agent"],
872
- signatureAgentValue: headers["signature-agent"] || headers["Signature-Agent"]
873
- });
874
- confidence = Math.max(confidence, 30);
875
- reasons.push("invalid_signature");
876
- if (signatureResult.reason) {
877
- reasons.push(`signature_error:${signatureResult.reason}`);
878
- }
879
- if (isChatGPTSignature(headers)) {
880
- reasons.push("claims_chatgpt");
881
- detectedAgent = { type: "chatgpt", name: "ChatGPT (unverified)" };
882
- }
883
- }
884
- } catch (error) {
885
- console.error("[EdgeAgentDetector] Signature verification error:", error);
886
- confidence = Math.max(confidence, 20);
887
- reasons.push("signature_verification_error");
888
- }
889
- }
890
- const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
891
- if (userAgent) {
892
- const userAgentEntries = Object.entries(this.rules.rules.userAgents);
893
- const genericKeys = ["generic_bot", "dev_tools", "automation_tools"];
894
- const sortedEntries = userAgentEntries.sort((a, b) => {
895
- const aIsGeneric = genericKeys.includes(a[0]);
896
- const bIsGeneric = genericKeys.includes(b[0]);
897
- if (aIsGeneric && !bIsGeneric) return 1;
898
- if (!aIsGeneric && bIsGeneric) return -1;
899
- return 0;
900
- });
901
- for (const [agentKey, agentRule] of sortedEntries) {
902
- const rule = agentRule;
903
- const matched = rule.patterns.some((pattern) => {
904
- const regex = new RegExp(pattern, "i");
905
- return regex.test(userAgent);
906
- });
907
- if (matched) {
908
- const agentType = this.getAgentType(agentKey);
909
- const agentName = this.getAgentName(agentKey);
910
- confidence = Math.max(confidence, rule.confidence * 100);
911
- reasons.push(`known_pattern:${agentType}`);
912
- if (!detectedAgent) {
913
- detectedAgent = { type: agentType, name: agentName };
914
- verificationMethod = "pattern";
915
- }
916
- break;
917
- }
918
- }
919
- }
920
- const suspiciousHeaders = this.rules.rules.headers.suspicious;
921
- const foundAiHeaders = suspiciousHeaders.filter(
922
- (headerRule) => normalizedHeaders[headerRule.name.toLowerCase()]
923
- );
924
- if (foundAiHeaders.length > 0) {
925
- const maxConfidence = Math.max(...foundAiHeaders.map((h) => h.confidence * 100));
926
- confidence = Math.max(confidence, maxConfidence);
927
- reasons.push(`ai_headers:${foundAiHeaders.length}`);
928
- }
929
- const ip = input.ip || input.ipAddress;
930
- if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
931
- const ipRanges = "providers" in this.rules.rules.ipRanges ? this.rules.rules.ipRanges.providers : this.rules.rules.ipRanges;
932
- for (const [provider, ipRule] of Object.entries(ipRanges)) {
933
- if (!ipRule || typeof ipRule !== "object" || !("ranges" in ipRule) || !Array.isArray(ipRule.ranges))
934
- continue;
935
- const matched = ipRule.ranges.some((range) => {
936
- const prefix = range.split("/")[0];
937
- const prefixParts = prefix.split(".");
938
- const ipParts = ip.split(".");
939
- for (let i = 0; i < Math.min(prefixParts.length - 1, 2); i++) {
940
- if (prefixParts[i] !== ipParts[i] && prefixParts[i] !== "0") {
941
- return false;
942
- }
943
- }
944
- return true;
945
- });
946
- if (matched) {
947
- const rule = ipRule;
948
- confidence = Math.max(confidence, rule.confidence * 40);
949
- reasons.push(`cloud_provider:${provider}`);
950
- break;
951
- }
952
- }
953
- }
954
- if (reasons.length > 2 && confidence < 100) {
955
- confidence = Math.min(confidence * 1.2, 95);
956
- }
957
- confidence = Math.min(Math.max(confidence, 0), 100);
958
- return {
959
- isAgent: confidence > 30,
960
- // Updated to 0-100 scale (was 0.3)
961
- confidence,
962
- detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
963
- signals: [],
964
- // Will be populated by enhanced detection engine in future tasks
965
- ...detectedAgent && { detectedAgent },
966
- reasons,
967
- ...verificationMethod && {
968
- verificationMethod
969
- },
970
- forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 80 ? "medium" : "high",
971
- // Updated to 0-100 scale
972
- timestamp: /* @__PURE__ */ new Date()
973
- };
974
- }
975
- /**
976
- * Get agent type from rule key
977
- */
978
- getAgentType(agentKey) {
979
- const typeMap = {
980
- openai_gptbot: "openai",
981
- anthropic_claude: "anthropic",
982
- perplexity_bot: "perplexity",
983
- google_ai: "google",
984
- microsoft_ai: "microsoft",
985
- meta_ai: "meta",
986
- cohere_bot: "cohere",
987
- huggingface_bot: "huggingface",
988
- generic_bot: "generic",
989
- dev_tools: "dev",
990
- automation_tools: "automation"
991
- };
992
- return typeMap[agentKey] || agentKey;
993
- }
994
- /**
995
- * Get agent name from rule key
996
- */
997
- getAgentName(agentKey) {
998
- const nameMap = {
999
- openai_gptbot: "ChatGPT/GPTBot",
1000
- anthropic_claude: "Claude",
1001
- perplexity_bot: "Perplexity",
1002
- google_ai: "Google AI",
1003
- microsoft_ai: "Microsoft Copilot",
1004
- meta_ai: "Meta AI",
1005
- cohere_bot: "Cohere",
1006
- huggingface_bot: "HuggingFace",
1007
- generic_bot: "Generic Bot",
1008
- dev_tools: "Development Tool",
1009
- automation_tools: "Automation Tool"
1010
- };
1011
- return nameMap[agentKey] || agentKey;
1012
- }
1013
- };
1014
- var EdgeAgentDetectorWrapper = class {
1015
- detector;
1016
- events = /* @__PURE__ */ new Map();
1017
- constructor(_config) {
1018
- this.detector = new EdgeAgentDetector();
1019
- }
1020
- async analyze(input) {
1021
- const result = await this.detector.analyze(input);
1022
- if (result.isAgent && this.events.has("agent.detected")) {
1023
- const handlers = this.events.get("agent.detected") || [];
1024
- handlers.forEach((handler) => handler(result, input));
1025
- }
1026
- return result;
1027
- }
1028
- on(event, handler) {
1029
- if (!this.events.has(event)) {
1030
- this.events.set(event, []);
1031
- }
1032
- this.events.get(event).push(handler);
1033
- }
1034
- emit(event, ...args) {
1035
- const handlers = this.events.get(event) || [];
1036
- handlers.forEach((handler) => handler(...args));
1037
- }
1038
- async init() {
1039
- return;
1040
- }
1041
- };
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) {
1057
- try {
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;
1082
- }
1083
- }
1084
- /**
1085
- * Check for existing AI agent session
1086
- */
1087
- async check(request) {
1088
- try {
1089
- if (!this.config.enabled) {
1090
- return null;
1091
- }
1092
- const cookie = request.cookies.get(this.config.cookieName);
1093
- if (!cookie?.value) {
1094
- return null;
1095
- }
1096
- const decrypted = await this.decrypt(cookie.value);
1097
- const session = JSON.parse(decrypted);
1098
- if (session.expires < Date.now()) {
1099
- return null;
1100
- }
1101
- return session;
1102
- } catch (error) {
1103
- if (process.env.DEBUG_AGENTSHIELD) {
1104
- console.warn("AgentShield: Failed to check session:", error);
1105
- }
1106
- return null;
1107
- }
1108
- }
1109
- /**
1110
- * Clear an existing session
1111
- */
1112
- clear(response) {
1113
- try {
1114
- response.cookies.delete(this.config.cookieName);
1115
- } catch (error) {
1116
- if (process.env.DEBUG_AGENTSHIELD) {
1117
- console.warn("AgentShield: Failed to clear session:", error);
1118
- }
1119
- }
1120
- return response;
1121
- }
1122
- /**
1123
- * Simple encryption using Web Crypto API (Edge-compatible)
1124
- */
1125
- async encrypt(data) {
1126
- try {
1127
- const key = this.config.encryptionKey;
1128
- const encoded = new TextEncoder().encode(data);
1129
- const obfuscated = new Uint8Array(encoded.length);
1130
- for (let i = 0; i < encoded.length; i++) {
1131
- obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);
1132
- }
1133
- return btoa(Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join(""));
1134
- } catch (error) {
1135
- return btoa(data);
1136
- }
1137
- }
1138
- /**
1139
- * Simple decryption (Edge-compatible)
1140
- */
1141
- async decrypt(data) {
1142
- try {
1143
- const key = this.config.encryptionKey;
1144
- const decoded = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
1145
- const deobfuscated = new Uint8Array(decoded.length);
1146
- for (let i = 0; i < decoded.length; i++) {
1147
- deobfuscated[i] = (decoded[i] || 0) ^ key.charCodeAt(i % key.length);
1148
- }
1149
- return new TextDecoder().decode(deobfuscated);
1150
- } catch (error) {
1151
- return atob(data);
1152
- }
1153
- }
1154
- };
1155
-
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
- }
1171
- function createAgentShieldMiddleware(config = {}) {
1172
- let detector = config.enableWasm ? null : new EdgeAgentDetectorWrapper(config);
1173
- let detectorInitPromise = null;
1174
- const sessionTracker = config.sessionTracking?.enabled || config.enableWasm ? new EdgeSessionTracker({
1175
- enabled: true,
1176
- ...config.sessionTracking
1177
- }) : null;
1178
- if (detector && config.events) {
1179
- Object.entries(config.events).forEach(([event, handler]) => {
1180
- if (handler) {
1181
- detector.on(event, handler);
1182
- }
1183
- });
1184
- }
1185
- const {
1186
- onAgentDetected = "log",
1187
- onDetection,
1188
- skipPaths = [],
1189
- blockedResponse = {
1190
- status: 403,
1191
- message: "Access denied: Automated agent detected",
1192
- headers: { "Content-Type": "application/json" }
1193
- },
1194
- redirectUrl = "/blocked",
1195
- rewriteUrl = "/blocked"
1196
- } = config;
1197
- return async (request) => {
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;
1216
- const shouldSkip = skipPaths.some((pattern) => {
1217
- if (typeof pattern === "string") {
1218
- return request.nextUrl.pathname.startsWith(pattern);
1219
- }
1220
- return pattern.test(request.nextUrl.pathname);
1221
- });
1222
- if (shouldSkip) {
1223
- request.agentShield = { skipped: true };
1224
- return server.NextResponse.next();
1225
- }
1226
- const existingSession = sessionTracker ? await sessionTracker.check(request) : null;
1227
- if (existingSession) {
1228
- const response2 = server.NextResponse.next();
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);
1234
- request.agentShield = {
1235
- result: {
1236
- isAgent: true,
1237
- confidence: existingSession.confidence,
1238
- detectionClass: { type: "AiAgent" },
1239
- detectedAgent: {
1240
- type: "ai_agent",
1241
- name: existingSession.agent
1242
- },
1243
- timestamp: /* @__PURE__ */ new Date(),
1244
- verificationMethod: "behavioral",
1245
- reasons: ["Session continued"],
1246
- signals: []
1247
- },
1248
- session: existingSession,
1249
- skipped: false
1250
- };
1251
- const context2 = {
1252
- userAgent: request.headers.get("user-agent") || "",
1253
- ipAddress: getClientIp(request) || "",
1254
- headers: Object.fromEntries(request.headers.entries()),
1255
- url: request.url,
1256
- method: request.method,
1257
- timestamp: /* @__PURE__ */ new Date()
1258
- };
1259
- activeDetector.emit("agent.session.continued", existingSession, context2);
1260
- return response2;
1261
- }
1262
- const userAgent = request.headers.get("user-agent");
1263
- const ipAddress = getClientIp(request);
1264
- const url = new URL(request.url);
1265
- const pathWithQuery = url.pathname + url.search;
1266
- const context = {
1267
- ...userAgent && { userAgent },
1268
- ...ipAddress && { ipAddress },
1269
- headers: Object.fromEntries(request.headers.entries()),
1270
- url: pathWithQuery,
1271
- // Use path instead of full URL for signature verification
1272
- method: request.method,
1273
- timestamp: /* @__PURE__ */ new Date()
1274
- };
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) {
1281
- if (onDetection) {
1282
- const customResponse = await onDetection(request, result);
1283
- if (customResponse) {
1284
- return customResponse;
1285
- }
1286
- }
1287
- switch (decision.action) {
1288
- case "block": {
1289
- const response2 = server.NextResponse.json(
1290
- {
1291
- error: blockedResponse.message,
1292
- detected: true,
1293
- confidence: result.confidence,
1294
- timestamp: result.timestamp
1295
- },
1296
- { status: blockedResponse.status }
1297
- );
1298
- if (blockedResponse.headers) {
1299
- Object.entries(blockedResponse.headers).forEach(([key, value]) => {
1300
- response2.headers.set(key, value);
1301
- });
1302
- }
1303
- activeDetector.emit("agent.blocked", result, context);
1304
- return response2;
1305
- }
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
- }
1314
- case "rewrite":
1315
- return server.NextResponse.rewrite(new URL(rewriteUrl, request.url));
1316
- case "log":
1317
- if (process.env.NODE_ENV !== "production") {
1318
- console.debug("AgentShield: Agent detected", {
1319
- ipAddress: context.ipAddress,
1320
- userAgent: context.userAgent,
1321
- confidence: result.confidence,
1322
- reasons: result.reasons,
1323
- pathname: request.nextUrl.pathname
1324
- });
1325
- }
1326
- break;
1327
- case "allow":
1328
- default:
1329
- activeDetector.emit("agent.allowed", result, context);
1330
- break;
1331
- }
1332
- }
1333
- request.agentShield = {
1334
- result,
1335
- skipped: false
1336
- };
1337
- let response = server.NextResponse.next();
1338
- response.headers.set("kya-detected", result.isAgent.toString());
1339
- response.headers.set("kya-confidence", result.confidence.toString());
1340
- if (result.detectedAgent?.name) {
1341
- response.headers.set("kya-agent", result.detectedAgent.name);
1342
- }
1343
- if (sessionTracker && decision.shouldNotify) {
1344
- response = await sessionTracker.track(request, response, result);
1345
- response.headers.set("kya-session", "new");
1346
- activeDetector.emit("agent.session.started", result, context);
1347
- }
1348
- return response;
1349
- } catch (error) {
1350
- console.error("AgentShield middleware error:", error);
1351
- return server.NextResponse.next();
1352
- }
1353
- };
1354
- }
1355
- function agentShield(config = {}) {
1356
- return createAgentShieldMiddleware(config);
1357
- }
1358
-
1359
- exports.agentShield = agentShield;
1360
- exports.createAgentShieldMiddleware = createAgentShieldMiddleware;
1361
- //# sourceMappingURL=middleware.js.map
1362
- //# sourceMappingURL=middleware.js.map