@kya-os/agentshield-nextjs 0.1.40 → 0.1.42

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 (79) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/api-client.d.mts +145 -0
  3. package/dist/api-client.d.ts +145 -0
  4. package/dist/api-client.js +130 -0
  5. package/dist/api-client.js.map +1 -0
  6. package/dist/api-client.mjs +126 -0
  7. package/dist/api-client.mjs.map +1 -0
  8. package/dist/api-middleware.d.mts +118 -0
  9. package/dist/api-middleware.d.ts +118 -0
  10. package/dist/api-middleware.js +295 -0
  11. package/dist/api-middleware.js.map +1 -0
  12. package/dist/api-middleware.mjs +292 -0
  13. package/dist/api-middleware.mjs.map +1 -0
  14. package/dist/create-middleware.d.mts +2 -1
  15. package/dist/create-middleware.d.ts +2 -1
  16. package/dist/create-middleware.js +474 -200
  17. package/dist/create-middleware.js.map +1 -1
  18. package/dist/create-middleware.mjs +454 -200
  19. package/dist/create-middleware.mjs.map +1 -1
  20. package/dist/edge/index.d.mts +110 -0
  21. package/dist/edge/index.d.ts +110 -0
  22. package/dist/edge/index.js +253 -0
  23. package/dist/edge/index.js.map +1 -0
  24. package/dist/edge/index.mjs +251 -0
  25. package/dist/edge/index.mjs.map +1 -0
  26. package/dist/edge-detector-wrapper.d.mts +6 -15
  27. package/dist/edge-detector-wrapper.d.ts +6 -15
  28. package/dist/edge-detector-wrapper.js +314 -95
  29. package/dist/edge-detector-wrapper.js.map +1 -1
  30. package/dist/edge-detector-wrapper.mjs +294 -95
  31. package/dist/edge-detector-wrapper.mjs.map +1 -1
  32. package/dist/edge-runtime-loader.d.mts +1 -1
  33. package/dist/edge-runtime-loader.d.ts +1 -1
  34. package/dist/edge-runtime-loader.js +10 -25
  35. package/dist/edge-runtime-loader.js.map +1 -1
  36. package/dist/edge-runtime-loader.mjs +11 -23
  37. package/dist/edge-runtime-loader.mjs.map +1 -1
  38. package/dist/edge-wasm-middleware.js +2 -1
  39. package/dist/edge-wasm-middleware.js.map +1 -1
  40. package/dist/edge-wasm-middleware.mjs +2 -1
  41. package/dist/edge-wasm-middleware.mjs.map +1 -1
  42. package/dist/enhanced-middleware.d.mts +153 -0
  43. package/dist/enhanced-middleware.d.ts +153 -0
  44. package/dist/enhanced-middleware.js +1074 -0
  45. package/dist/enhanced-middleware.js.map +1 -0
  46. package/dist/enhanced-middleware.mjs +1072 -0
  47. package/dist/enhanced-middleware.mjs.map +1 -0
  48. package/dist/index.d.mts +8 -153
  49. package/dist/index.d.ts +8 -153
  50. package/dist/index.js +1390 -680
  51. package/dist/index.js.map +1 -1
  52. package/dist/index.mjs +1370 -685
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/middleware.d.mts +2 -1
  55. package/dist/middleware.d.ts +2 -1
  56. package/dist/middleware.js +474 -200
  57. package/dist/middleware.js.map +1 -1
  58. package/dist/middleware.mjs +454 -200
  59. package/dist/middleware.mjs.map +1 -1
  60. package/dist/session-tracker.d.mts +1 -1
  61. package/dist/session-tracker.d.ts +1 -1
  62. package/dist/session-tracker.js.map +1 -1
  63. package/dist/session-tracker.mjs.map +1 -1
  64. package/dist/signature-verifier.d.mts +1 -0
  65. package/dist/signature-verifier.d.ts +1 -0
  66. package/dist/signature-verifier.js +204 -44
  67. package/dist/signature-verifier.js.map +1 -1
  68. package/dist/signature-verifier.mjs +184 -44
  69. package/dist/signature-verifier.mjs.map +1 -1
  70. package/dist/{types-BJTEUa4T.d.mts → types-DVmy9NE3.d.mts} +19 -2
  71. package/dist/{types-BJTEUa4T.d.ts → types-DVmy9NE3.d.ts} +19 -2
  72. package/dist/wasm-middleware.js +15 -6
  73. package/dist/wasm-middleware.js.map +1 -1
  74. package/dist/wasm-middleware.mjs +15 -6
  75. package/dist/wasm-middleware.mjs.map +1 -1
  76. package/package.json +43 -21
  77. package/wasm/agentshield_wasm.js +209 -152
  78. package/wasm/agentshield_wasm_bg.wasm +0 -0
  79. package/wasm/package.json +30 -0
package/dist/index.js CHANGED
@@ -1,381 +1,41 @@
1
1
  'use strict';
2
2
 
3
+ var agentshieldShared = require('@kya-os/agentshield-shared');
3
4
  var server = require('next/server');
5
+ var ed25519 = require('@noble/ed25519');
6
+ var sha2 = require('@noble/hashes/sha2');
4
7
 
5
- // src/create-middleware.ts
6
-
7
- // src/signature-verifier.ts
8
- var KNOWN_KEYS = {
9
- chatgpt: [
10
- {
11
- kid: "otMqcjr17mGyruktGvJU8oojQTSMHlVm7uO-lrcqbdg",
12
- // ChatGPT's current Ed25519 public key (base64)
13
- publicKey: "7F_3jDlxaquwh291MiACkcS3Opq88NksyHiakzS-Y1g",
14
- validFrom: (/* @__PURE__ */ new Date("2025-01-01")).getTime() / 1e3,
15
- validUntil: (/* @__PURE__ */ new Date("2025-04-11")).getTime() / 1e3
16
- }
17
- ]
18
- };
19
- function parseSignatureInput(signatureInput) {
20
- try {
21
- const match = signatureInput.match(/sig1=\((.*?)\);(.+)/);
22
- if (!match) return null;
23
- const [, headersList, params] = match;
24
- const signedHeaders = headersList ? headersList.split(" ").map((h) => h.replace(/"/g, "").trim()).filter((h) => h.length > 0) : [];
25
- const keyidMatch = params ? params.match(/keyid="([^"]+)"/) : null;
26
- const createdMatch = params ? params.match(/created=(\d+)/) : null;
27
- const expiresMatch = params ? params.match(/expires=(\d+)/) : null;
28
- if (!keyidMatch || !keyidMatch[1]) return null;
29
- return {
30
- keyid: keyidMatch[1],
31
- created: createdMatch && createdMatch[1] ? parseInt(createdMatch[1]) : void 0,
32
- expires: expiresMatch && expiresMatch[1] ? parseInt(expiresMatch[1]) : void 0,
33
- signedHeaders
34
- };
35
- } catch (error) {
36
- console.error("[Signature] Failed to parse Signature-Input:", error);
37
- return null;
38
- }
39
- }
40
- function buildSignatureBase(method, path, headers, signedHeaders) {
41
- const components = [];
42
- for (const headerName of signedHeaders) {
43
- let value;
44
- switch (headerName) {
45
- case "@method":
46
- value = method.toUpperCase();
47
- break;
48
- case "@path":
49
- value = path;
50
- break;
51
- case "@authority":
52
- value = headers["host"] || headers["Host"] || "";
53
- break;
54
- default:
55
- const key = Object.keys(headers).find(
56
- (k) => k.toLowerCase() === headerName.toLowerCase()
57
- );
58
- value = key ? headers[key] || "" : "";
59
- break;
60
- }
61
- components.push(`"${headerName}": ${value}`);
62
- }
63
- return components.join("\n");
64
- }
65
- async function verifyEd25519Signature(publicKeyBase64, signatureBase64, message) {
66
- try {
67
- const publicKeyBytes = Uint8Array.from(atob(publicKeyBase64), (c) => c.charCodeAt(0));
68
- const signatureBytes = Uint8Array.from(atob(signatureBase64), (c) => c.charCodeAt(0));
69
- const messageBytes = new TextEncoder().encode(message);
70
- if (publicKeyBytes.length !== 32) {
71
- console.error("[Signature] Invalid public key length:", publicKeyBytes.length);
72
- return false;
73
- }
74
- if (signatureBytes.length !== 64) {
75
- console.error("[Signature] Invalid signature length:", signatureBytes.length);
76
- return false;
77
- }
78
- const publicKey = await crypto.subtle.importKey(
79
- "raw",
80
- publicKeyBytes,
81
- {
82
- name: "Ed25519",
83
- namedCurve: "Ed25519"
84
- },
85
- false,
86
- ["verify"]
87
- );
88
- const isValid = await crypto.subtle.verify(
89
- "Ed25519",
90
- publicKey,
91
- signatureBytes,
92
- messageBytes
93
- );
94
- return isValid;
95
- } catch (error) {
96
- console.error("[Signature] Ed25519 verification failed:", error);
97
- if (typeof window === "undefined") {
98
- try {
99
- console.warn("[Signature] Ed25519 not supported in this environment");
100
- return false;
101
- } catch {
102
- return false;
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
+ });
103
19
  }
104
- }
105
- return false;
106
- }
107
- }
108
- async function verifyAgentSignature(method, path, headers) {
109
- const signature = headers["signature"] || headers["Signature"];
110
- const signatureInput = headers["signature-input"] || headers["Signature-Input"];
111
- const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
112
- if (!signature || !signatureInput) {
113
- return {
114
- isValid: false,
115
- confidence: 0,
116
- reason: "No signature headers present",
117
- verificationMethod: "none"
118
- };
119
- }
120
- const parsed = parseSignatureInput(signatureInput);
121
- if (!parsed) {
122
- return {
123
- isValid: false,
124
- confidence: 0,
125
- reason: "Invalid Signature-Input header",
126
- verificationMethod: "none"
127
- };
128
- }
129
- if (parsed.created) {
130
- const now2 = Math.floor(Date.now() / 1e3);
131
- const age = now2 - parsed.created;
132
- if (age > 300) {
133
- return {
134
- isValid: false,
135
- confidence: 0,
136
- reason: "Signature expired (older than 5 minutes)",
137
- verificationMethod: "none"
138
- };
139
- }
140
- if (age < -30) {
141
- return {
142
- isValid: false,
143
- confidence: 0,
144
- reason: "Signature timestamp is in the future",
145
- verificationMethod: "none"
146
- };
147
- }
148
- }
149
- let agent;
150
- let knownKeys;
151
- if (signatureAgent === '"https://chatgpt.com"' || signatureAgent?.includes("chatgpt.com")) {
152
- agent = "ChatGPT";
153
- knownKeys = KNOWN_KEYS.chatgpt;
154
- }
155
- if (!agent || !knownKeys) {
156
- return {
157
- isValid: false,
158
- confidence: 0,
159
- reason: "Unknown signature agent",
160
- verificationMethod: "none"
161
- };
162
- }
163
- const key = knownKeys.find((k) => k.kid === parsed.keyid);
164
- if (!key) {
165
- return {
166
- isValid: false,
167
- confidence: 0,
168
- reason: `Unknown key ID: ${parsed.keyid}`,
169
- verificationMethod: "none"
170
- };
171
- }
172
- const now = Math.floor(Date.now() / 1e3);
173
- if (now < key.validFrom || now > key.validUntil) {
174
- return {
175
- isValid: false,
176
- confidence: 0,
177
- reason: "Key is not valid at current time",
178
- verificationMethod: "none"
179
- };
180
- }
181
- const signatureBase = buildSignatureBase(method, path, headers, parsed.signedHeaders);
182
- let signatureValue = signature;
183
- if (signatureValue.startsWith("sig1=:")) {
184
- signatureValue = signatureValue.substring(6);
185
- }
186
- if (signatureValue.endsWith(":")) {
187
- signatureValue = signatureValue.slice(0, -1);
188
- }
189
- const isValid = await verifyEd25519Signature(
190
- key.publicKey,
191
- signatureValue,
192
- signatureBase
193
- );
194
- if (isValid) {
195
- return {
196
- isValid: true,
197
- agent,
198
- keyid: parsed.keyid,
199
- confidence: 1,
200
- // 100% confidence for valid signature
201
- verificationMethod: "signature"
202
- };
203
- } else {
204
- return {
205
- isValid: false,
206
- confidence: 0,
207
- reason: "Signature verification failed",
208
- verificationMethod: "none"
209
- };
20
+ });
210
21
  }
211
- }
212
- function hasSignatureHeaders(headers) {
213
- return !!((headers["signature"] || headers["Signature"]) && (headers["signature-input"] || headers["Signature-Input"]));
214
- }
215
- function isChatGPTSignature(headers) {
216
- const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
217
- return signatureAgent === '"https://chatgpt.com"' || (signatureAgent?.includes("chatgpt.com") || false);
22
+ n.default = e;
23
+ return Object.freeze(n);
218
24
  }
219
25
 
220
- // src/edge-detector-wrapper.ts
221
- var AI_AGENT_PATTERNS = [
222
- { pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
223
- { pattern: /claude-web/i, type: "claude", name: "Claude" },
224
- { pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
225
- { pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
226
- { pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
227
- { pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
228
- // Fallback
229
- { pattern: /bingbot/i, type: "bing", name: "Bing AI" },
230
- { pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
231
- ];
232
- var CLOUD_PROVIDERS = {
233
- aws: ["54.", "52.", "35.", "18.", "3."],
234
- gcp: ["35.", "34.", "104.", "107.", "108."],
235
- azure: ["13.", "20.", "40.", "52.", "104."]
236
- };
237
- var EdgeAgentDetector = class {
238
- async analyze(input) {
239
- const reasons = [];
240
- let detectedAgent;
241
- let verificationMethod;
242
- let confidence = 0;
243
- const headers = input.headers || {};
244
- const normalizedHeaders = {};
245
- for (const [key, value] of Object.entries(headers)) {
246
- normalizedHeaders[key.toLowerCase()] = value;
247
- }
248
- if (hasSignatureHeaders(headers)) {
249
- try {
250
- const signatureResult = await verifyAgentSignature(
251
- input.method || "GET",
252
- input.url || "/",
253
- headers
254
- );
255
- if (signatureResult.isValid) {
256
- confidence = signatureResult.confidence;
257
- reasons.push(`verified_signature:${signatureResult.agent?.toLowerCase() || "unknown"}`);
258
- if (signatureResult.agent) {
259
- detectedAgent = {
260
- type: signatureResult.agent.toLowerCase(),
261
- name: signatureResult.agent
262
- };
263
- }
264
- verificationMethod = signatureResult.verificationMethod;
265
- if (signatureResult.keyid) {
266
- reasons.push(`keyid:${signatureResult.keyid}`);
267
- }
268
- } else {
269
- confidence = Math.max(confidence, 0.3);
270
- reasons.push("invalid_signature");
271
- if (signatureResult.reason) {
272
- reasons.push(`signature_error:${signatureResult.reason}`);
273
- }
274
- if (isChatGPTSignature(headers)) {
275
- reasons.push("claims_chatgpt");
276
- detectedAgent = { type: "chatgpt", name: "ChatGPT (unverified)" };
277
- }
278
- }
279
- } catch (error) {
280
- console.error("[EdgeAgentDetector] Signature verification error:", error);
281
- confidence = Math.max(confidence, 0.2);
282
- reasons.push("signature_verification_error");
283
- }
284
- }
285
- const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
286
- if (userAgent) {
287
- for (const { pattern, type, name } of AI_AGENT_PATTERNS) {
288
- if (pattern.test(userAgent)) {
289
- const highConfidenceAgents = [
290
- "chatgpt",
291
- "claude",
292
- "perplexity",
293
- "anthropic"
294
- ];
295
- const patternConfidence = highConfidenceAgents.includes(type) ? 0.85 : 0.5;
296
- confidence = Math.max(confidence, patternConfidence);
297
- reasons.push(`known_pattern:${type}`);
298
- if (!detectedAgent) {
299
- detectedAgent = { type, name };
300
- verificationMethod = "pattern";
301
- }
302
- break;
303
- }
304
- }
305
- }
306
- const aiHeaders = [
307
- "openai-conversation-id",
308
- "openai-ephemeral-user-id",
309
- "anthropic-client-id",
310
- "x-goog-api-client",
311
- "x-ms-copilot-id"
312
- ];
313
- const foundAiHeaders = aiHeaders.filter(
314
- (header) => normalizedHeaders[header]
315
- );
316
- if (foundAiHeaders.length > 0) {
317
- confidence = Math.max(confidence, 0.6);
318
- reasons.push(`ai_headers:${foundAiHeaders.length}`);
319
- }
320
- const ip = input.ip || input.ipAddress;
321
- if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
322
- for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS)) {
323
- if (prefixes.some((prefix) => ip.startsWith(prefix))) {
324
- confidence = Math.max(confidence, 0.4);
325
- reasons.push(`cloud_provider:${provider}`);
326
- break;
327
- }
328
- }
329
- }
330
- if (reasons.length > 2 && confidence < 1) {
331
- confidence = Math.min(confidence * 1.2, 0.95);
332
- }
333
- return {
334
- isAgent: confidence > 0.3,
335
- confidence,
336
- ...detectedAgent && { detectedAgent },
337
- reasons,
338
- ...verificationMethod && { verificationMethod },
339
- forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 0.8 ? "medium" : "high",
340
- timestamp: /* @__PURE__ */ new Date()
341
- };
342
- }
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;
343
32
  };
344
- var EdgeAgentDetectorWrapper = class {
345
- detector;
346
- events = /* @__PURE__ */ new Map();
347
- constructor(_config) {
348
- this.detector = new EdgeAgentDetector();
349
- }
350
- async analyze(input) {
351
- const result = await this.detector.analyze(input);
352
- if (result.isAgent && this.events.has("agent.detected")) {
353
- const handlers = this.events.get("agent.detected") || [];
354
- handlers.forEach((handler) => handler(result, input));
355
- }
356
- return result;
357
- }
358
- on(event, handler) {
359
- if (!this.events.has(event)) {
360
- this.events.set(event, []);
361
- }
362
- this.events.get(event).push(handler);
363
- }
364
- emit(event, ...args) {
365
- const handlers = this.events.get(event) || [];
366
- handlers.forEach((handler) => handler(...args));
367
- }
368
- async init() {
369
- return;
370
- }
33
+ var __export = (target, all) => {
34
+ for (var name in all)
35
+ __defProp(target, name, { get: all[name], enumerable: true });
371
36
  };
372
37
 
373
38
  // src/wasm-loader.ts
374
- var wasmInstance = null;
375
- var wasmExports = null;
376
- var initPromise = null;
377
- var WASM_PATH = "/wasm/agentshield_wasm_bg.wasm";
378
- var baseUrl = null;
379
39
  function setWasmBaseUrl(url) {
380
40
  baseUrl = url;
381
41
  }
@@ -410,26 +70,31 @@ async function initWasm() {
410
70
  throw new Error(`Failed to fetch WASM: ${response2.status}`);
411
71
  }
412
72
  const streamResponse = response2.clone();
413
- const { instance } = await WebAssembly.instantiateStreaming(
414
- streamResponse,
415
- {
416
- wbg: {
417
- __wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
418
- console.log("WASM:", ptr, len);
419
- },
420
- __wbindgen_throw: (ptr, len) => {
421
- throw new Error(`WASM Error at ${ptr}, length ${len}`);
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);
422
78
  }
79
+ },
80
+ __wbindgen_throw: (ptr, len) => {
81
+ throw new Error(`WASM Error at ${ptr}, length ${len}`);
423
82
  }
424
83
  }
425
- );
84
+ });
426
85
  wasmInstance = instance;
427
86
  wasmExports = instance.exports;
428
- console.log("[AgentShield] \u2705 WASM module initialized with streaming");
87
+ if (process.env.NODE_ENV !== "production") {
88
+ console.debug("[AgentShield] \u2705 WASM module initialized with streaming");
89
+ }
429
90
  return;
430
91
  } catch (streamError) {
431
92
  if (!controller.signal.aborted) {
432
- console.log("[AgentShield] Streaming compilation failed, falling back to standard compilation");
93
+ if (process.env.NODE_ENV !== "production") {
94
+ console.debug(
95
+ "[AgentShield] Streaming compilation failed, falling back to standard compilation"
96
+ );
97
+ }
433
98
  } else {
434
99
  throw streamError;
435
100
  }
@@ -445,7 +110,9 @@ async function initWasm() {
445
110
  const imports = {
446
111
  wbg: {
447
112
  __wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
448
- console.log("WASM:", ptr, len);
113
+ if (process.env.NODE_ENV !== "production") {
114
+ console.debug("WASM:", ptr, len);
115
+ }
449
116
  },
450
117
  __wbindgen_throw: (ptr, len) => {
451
118
  throw new Error(`WASM Error at ${ptr}, length ${len}`);
@@ -454,12 +121,20 @@ async function initWasm() {
454
121
  };
455
122
  wasmInstance = await WebAssembly.instantiate(compiledModule, imports);
456
123
  wasmExports = wasmInstance.exports;
457
- console.log("[AgentShield] \u2705 WASM module initialized via fallback");
124
+ if (process.env.NODE_ENV !== "production") {
125
+ console.debug("[AgentShield] \u2705 WASM module initialized via fallback");
126
+ }
458
127
  } catch (fetchError) {
459
- if (fetchError.name === "AbortError") {
460
- console.warn("[AgentShield] WASM fetch timed out after 3 seconds - using pattern detection");
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
+ );
461
133
  } else {
462
- console.warn("[AgentShield] Failed to fetch WASM file:", fetchError.message);
134
+ console.warn(
135
+ "[AgentShield] Failed to fetch WASM file:",
136
+ error.message || "Unknown error"
137
+ );
463
138
  }
464
139
  wasmExports = null;
465
140
  }
@@ -467,122 +142,765 @@ async function initWasm() {
467
142
  console.error("[AgentShield] Failed to initialize WASM:", error);
468
143
  wasmExports = null;
469
144
  }
470
- })();
471
- await initPromise;
472
- return !!wasmExports;
145
+ })();
146
+ await initPromise;
147
+ return !!wasmExports;
148
+ }
149
+ async function detectAgentWithWasm(userAgent, headers, ipAddress) {
150
+ const initialized = await initWasm();
151
+ if (!initialized || !wasmExports) {
152
+ return null;
153
+ }
154
+ try {
155
+ const headersJson = JSON.stringify(headers);
156
+ if (typeof wasmExports.detect_agent === "function") {
157
+ const result = wasmExports.detect_agent(
158
+ userAgent || "",
159
+ headersJson,
160
+ ipAddress || "",
161
+ (/* @__PURE__ */ new Date()).toISOString()
162
+ );
163
+ return {
164
+ isAgent: result.is_agent || false,
165
+ confidence: result.confidence || 0,
166
+ agent: result.agent,
167
+ verificationMethod: result.verification_method || "wasm",
168
+ riskLevel: result.risk_level || "low"
169
+ };
170
+ }
171
+ console.warn("[AgentShield] WASM exports do not include detect_agent function");
172
+ return null;
173
+ } catch (error) {
174
+ console.error("[AgentShield] WASM detection failed:", error);
175
+ return null;
176
+ }
177
+ }
178
+ async function getWasmVersion() {
179
+ const initialized = await initWasm();
180
+ if (!initialized || !wasmExports) {
181
+ return null;
182
+ }
183
+ if (typeof wasmExports.version === "function") {
184
+ return wasmExports.version();
185
+ }
186
+ return "unknown";
187
+ }
188
+ async function isWasmAvailable() {
189
+ try {
190
+ const initialized = await initWasm();
191
+ if (!initialized) return false;
192
+ const version = await getWasmVersion();
193
+ return version !== null;
194
+ } catch {
195
+ return false;
196
+ }
197
+ }
198
+ var wasmInstance, wasmExports, initPromise, WASM_PATH, baseUrl;
199
+ var init_wasm_loader = __esm({
200
+ "src/wasm-loader.ts"() {
201
+ wasmInstance = null;
202
+ wasmExports = null;
203
+ initPromise = null;
204
+ WASM_PATH = "/wasm/agentshield_wasm_bg.wasm";
205
+ baseUrl = null;
206
+ }
207
+ });
208
+
209
+ // src/edge-detector-with-wasm.ts
210
+ var edge_detector_with_wasm_exports = {};
211
+ __export(edge_detector_with_wasm_exports, {
212
+ EdgeAgentDetectorWithWasm: () => EdgeAgentDetectorWithWasm,
213
+ EdgeAgentDetectorWrapperWithWasm: () => EdgeAgentDetectorWrapperWithWasm
214
+ });
215
+ var rules2, EdgeAgentDetectorWithWasm, EdgeAgentDetectorWrapperWithWasm;
216
+ var init_edge_detector_with_wasm = __esm({
217
+ "src/edge-detector-with-wasm.ts"() {
218
+ init_wasm_loader();
219
+ rules2 = agentshieldShared.loadRulesSync();
220
+ EdgeAgentDetectorWithWasm = class {
221
+ constructor(enableWasm = true) {
222
+ this.enableWasm = enableWasm;
223
+ this.rules = rules2;
224
+ }
225
+ wasmEnabled = false;
226
+ initPromise = null;
227
+ baseUrl = null;
228
+ rules;
229
+ /**
230
+ * Set the base URL for WASM loading in Edge Runtime
231
+ */
232
+ setBaseUrl(url) {
233
+ this.baseUrl = url;
234
+ setWasmBaseUrl(url);
235
+ }
236
+ /**
237
+ * Initialize the detector (including WASM if enabled)
238
+ */
239
+ async init() {
240
+ if (!this.enableWasm) {
241
+ this.wasmEnabled = false;
242
+ return;
243
+ }
244
+ if (this.initPromise) {
245
+ await this.initPromise;
246
+ return;
247
+ }
248
+ this.initPromise = (async () => {
249
+ try {
250
+ const wasmAvailable = await isWasmAvailable();
251
+ if (wasmAvailable) {
252
+ if (this.baseUrl) {
253
+ setWasmBaseUrl(this.baseUrl);
254
+ }
255
+ await initWasm();
256
+ this.wasmEnabled = true;
257
+ } else {
258
+ this.wasmEnabled = false;
259
+ }
260
+ } catch (error) {
261
+ console.error("[AgentShield] Failed to initialize WASM:", error);
262
+ this.wasmEnabled = false;
263
+ }
264
+ })();
265
+ await this.initPromise;
266
+ }
267
+ /**
268
+ * Pattern-based detection (fallback)
269
+ */
270
+ async patternDetection(input) {
271
+ const reasons = [];
272
+ let detectedAgent;
273
+ let verificationMethod;
274
+ let confidence = 0;
275
+ const headers = input.headers || {};
276
+ const normalizedHeaders = {};
277
+ for (const [key, value] of Object.entries(headers)) {
278
+ normalizedHeaders[key.toLowerCase()] = value;
279
+ }
280
+ const signaturePresent = !!(normalizedHeaders["signature"] || normalizedHeaders["signature-input"]);
281
+ const signatureAgent = normalizedHeaders["signature-agent"];
282
+ if (signatureAgent?.includes("chatgpt.com")) {
283
+ confidence = 85;
284
+ reasons.push("signature_agent:chatgpt");
285
+ detectedAgent = { type: "chatgpt", name: "ChatGPT" };
286
+ verificationMethod = "signature";
287
+ } else if (signaturePresent) {
288
+ confidence = Math.max(confidence, 40);
289
+ reasons.push("signature_present");
290
+ }
291
+ const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
292
+ if (userAgent) {
293
+ for (const [agentKey, agentRule] of Object.entries(this.rules.rules.userAgents)) {
294
+ const matched = agentRule.patterns.some((pattern) => {
295
+ const regex = new RegExp(pattern, "i");
296
+ return regex.test(userAgent);
297
+ });
298
+ if (matched) {
299
+ const agentType = this.getAgentType(agentKey);
300
+ const agentName = this.getAgentName(agentKey);
301
+ confidence = Math.max(confidence, Math.round(agentRule.confidence * 0.85 * 100));
302
+ reasons.push(`known_pattern:${agentType}`);
303
+ if (!detectedAgent) {
304
+ detectedAgent = { type: agentType, name: agentName };
305
+ verificationMethod = "pattern";
306
+ }
307
+ break;
308
+ }
309
+ }
310
+ }
311
+ const suspiciousHeaders = this.rules.rules.headers.suspicious;
312
+ const foundAiHeaders = suspiciousHeaders.filter(
313
+ (headerRule) => normalizedHeaders[headerRule.name.toLowerCase()]
314
+ );
315
+ if (foundAiHeaders.length > 0) {
316
+ const maxConfidence = Math.max(...foundAiHeaders.map((h) => h.confidence));
317
+ confidence = Math.max(confidence, maxConfidence);
318
+ reasons.push(`ai_headers:${foundAiHeaders.length}`);
319
+ }
320
+ const ip = input.ip || input.ipAddress;
321
+ if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
322
+ const ipRanges = "providers" in this.rules.rules.ipRanges ? this.rules.rules.ipRanges.providers : this.rules.rules.ipRanges;
323
+ for (const [provider, ipRule] of Object.entries(ipRanges)) {
324
+ if (!ipRule || typeof ipRule !== "object" || !("ranges" in ipRule) || !Array.isArray(ipRule.ranges))
325
+ continue;
326
+ const matched = ipRule.ranges.some((range) => {
327
+ const prefix = range.split("/")[0];
328
+ const prefixParts = prefix.split(".");
329
+ const ipParts = ip.split(".");
330
+ for (let i = 0; i < Math.min(prefixParts.length - 1, 2); i++) {
331
+ if (prefixParts[i] !== ipParts[i] && prefixParts[i] !== "0") {
332
+ return false;
333
+ }
334
+ }
335
+ return true;
336
+ });
337
+ if (matched) {
338
+ confidence = Math.max(confidence, Math.round(ipRule.confidence * 0.4 * 100));
339
+ reasons.push(`cloud_provider:${provider}`);
340
+ break;
341
+ }
342
+ }
343
+ }
344
+ if (reasons.length > 2) {
345
+ confidence = Math.min(Math.round(confidence * 1.2), 95);
346
+ }
347
+ confidence = Math.min(Math.max(confidence, 0), 100);
348
+ return {
349
+ isAgent: confidence > 30,
350
+ // 30% threshold
351
+ confidence,
352
+ detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
353
+ signals: [],
354
+ // Will be populated by enhanced detection engine in future tasks
355
+ ...detectedAgent && { detectedAgent },
356
+ reasons,
357
+ ...verificationMethod && {
358
+ verificationMethod: agentshieldShared.mapVerificationMethod(verificationMethod)
359
+ },
360
+ forgeabilityRisk: confidence > 80 ? "medium" : "high",
361
+ timestamp: /* @__PURE__ */ new Date()
362
+ };
363
+ }
364
+ /**
365
+ * Analyze request with WASM enhancement when available
366
+ */
367
+ async analyze(input) {
368
+ await this.init();
369
+ if (this.wasmEnabled) {
370
+ try {
371
+ const wasmResult = await detectAgentWithWasm(
372
+ input.userAgent || input.headers?.["user-agent"],
373
+ input.headers || {},
374
+ input.ip || input.ipAddress
375
+ );
376
+ if (wasmResult) {
377
+ const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
378
+ return {
379
+ isAgent: wasmResult.isAgent,
380
+ confidence: wasmResult.confidence,
381
+ detectionClass: wasmResult.isAgent && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : wasmResult.isAgent ? { type: "Unknown" } : { type: "Human" },
382
+ signals: [],
383
+ // Will be populated by enhanced detection engine in future tasks
384
+ ...detectedAgent && { detectedAgent },
385
+ reasons: [`wasm:${wasmResult.verificationMethod}`],
386
+ verificationMethod: agentshieldShared.mapVerificationMethod(wasmResult.verificationMethod),
387
+ forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 90 ? "medium" : "high",
388
+ timestamp: /* @__PURE__ */ new Date()
389
+ };
390
+ }
391
+ } catch (error) {
392
+ console.error("[AgentShield] WASM detection error:", error);
393
+ }
394
+ }
395
+ const patternResult = await this.patternDetection(input);
396
+ if (this.wasmEnabled && patternResult.confidence >= 85) {
397
+ patternResult.confidence = Math.min(95, patternResult.confidence + 10);
398
+ patternResult.reasons.push("wasm_enhanced");
399
+ }
400
+ return patternResult;
401
+ }
402
+ /**
403
+ * Get agent type from rule key
404
+ */
405
+ getAgentType(agentKey) {
406
+ const typeMap = {
407
+ openai_gptbot: "openai",
408
+ anthropic_claude: "anthropic",
409
+ perplexity_bot: "perplexity",
410
+ google_ai: "google",
411
+ microsoft_ai: "microsoft",
412
+ meta_ai: "meta",
413
+ cohere_bot: "cohere",
414
+ huggingface_bot: "huggingface",
415
+ generic_bot: "generic",
416
+ dev_tools: "dev",
417
+ automation_tools: "automation"
418
+ };
419
+ return typeMap[agentKey] || agentKey;
420
+ }
421
+ /**
422
+ * Get agent name from rule key
423
+ */
424
+ getAgentName(agentKey) {
425
+ const nameMap = {
426
+ openai_gptbot: "ChatGPT/GPTBot",
427
+ anthropic_claude: "Claude",
428
+ perplexity_bot: "Perplexity",
429
+ google_ai: "Google AI",
430
+ microsoft_ai: "Microsoft Copilot",
431
+ meta_ai: "Meta AI",
432
+ cohere_bot: "Cohere",
433
+ huggingface_bot: "HuggingFace",
434
+ generic_bot: "Generic Bot",
435
+ dev_tools: "Development Tool",
436
+ automation_tools: "Automation Tool"
437
+ };
438
+ return nameMap[agentKey] || agentKey;
439
+ }
440
+ /**
441
+ * Map agent names from WASM to consistent format
442
+ */
443
+ mapAgentName(agent) {
444
+ const lowerAgent = agent.toLowerCase();
445
+ if (lowerAgent.includes("chatgpt")) {
446
+ return { type: "chatgpt", name: "ChatGPT" };
447
+ } else if (lowerAgent.includes("claude")) {
448
+ return { type: "claude", name: "Claude" };
449
+ } else if (lowerAgent.includes("perplexity")) {
450
+ return { type: "perplexity", name: "Perplexity" };
451
+ } else if (lowerAgent.includes("bing")) {
452
+ return { type: "bing", name: "Bing AI" };
453
+ } else if (lowerAgent.includes("anthropic")) {
454
+ return { type: "anthropic", name: "Anthropic" };
455
+ }
456
+ return { type: "unknown", name: agent };
457
+ }
458
+ };
459
+ EdgeAgentDetectorWrapperWithWasm = class {
460
+ detector;
461
+ events = /* @__PURE__ */ new Map();
462
+ constructor(config) {
463
+ this.detector = new EdgeAgentDetectorWithWasm(config?.enableWasm ?? true);
464
+ if (config?.baseUrl) {
465
+ this.detector.setBaseUrl(config.baseUrl);
466
+ }
467
+ }
468
+ setBaseUrl(url) {
469
+ this.detector.setBaseUrl(url);
470
+ }
471
+ async analyze(input) {
472
+ const result = await this.detector.analyze(input);
473
+ if (result.isAgent && this.events.has("agent.detected")) {
474
+ const handlers = this.events.get("agent.detected") || [];
475
+ handlers.forEach((handler) => handler(result, input));
476
+ }
477
+ return result;
478
+ }
479
+ on(event, handler) {
480
+ if (!this.events.has(event)) {
481
+ this.events.set(event, []);
482
+ }
483
+ this.events.get(event).push(handler);
484
+ }
485
+ emit(event, ...args) {
486
+ const handlers = this.events.get(event) || [];
487
+ handlers.forEach((handler) => handler(...args));
488
+ }
489
+ async init() {
490
+ await this.detector.init();
491
+ }
492
+ };
493
+ }
494
+ });
495
+
496
+ // src/wasm-confidence.ts
497
+ var wasm_confidence_exports = {};
498
+ __export(wasm_confidence_exports, {
499
+ checkWasmAvailability: () => checkWasmAvailability,
500
+ getVerificationMethod: () => getVerificationMethod,
501
+ getWasmConfidenceBoost: () => getWasmConfidenceBoost,
502
+ shouldIndicateWasmVerification: () => shouldIndicateWasmVerification
503
+ });
504
+ async function checkWasmAvailability() {
505
+ try {
506
+ if (typeof WebAssembly === "undefined") {
507
+ return false;
508
+ }
509
+ if (!WebAssembly.instantiate || !WebAssembly.Module) {
510
+ return false;
511
+ }
512
+ return true;
513
+ } catch {
514
+ return false;
515
+ }
516
+ }
517
+ function shouldIndicateWasmVerification(confidence) {
518
+ return confidence >= 85 && confidence < 100;
519
+ }
520
+ function getWasmConfidenceBoost(baseConfidence, reasons = []) {
521
+ if (reasons.some(
522
+ (r) => r.includes("signature_agent") && !r.includes("signature_headers_present")
523
+ )) {
524
+ return 100;
525
+ }
526
+ if (baseConfidence >= 85) {
527
+ return 95;
528
+ }
529
+ if (baseConfidence >= 70) {
530
+ return Math.min(baseConfidence * 1.1, 90);
531
+ }
532
+ return baseConfidence;
533
+ }
534
+ function getVerificationMethod(confidence, reasons = []) {
535
+ if (reasons.some(
536
+ (r) => r.includes("signature_agent") && !r.includes("signature_headers_present")
537
+ )) {
538
+ return "signature";
539
+ }
540
+ if (shouldIndicateWasmVerification(confidence)) {
541
+ return "wasm-enhanced";
542
+ }
543
+ return "pattern";
544
+ }
545
+ var init_wasm_confidence = __esm({
546
+ "src/wasm-confidence.ts"() {
547
+ }
548
+ });
549
+ ed25519__namespace.etc.sha512Sync = (...m) => sha2.sha512(ed25519__namespace.etc.concatBytes(...m));
550
+ var KNOWN_KEYS = {
551
+ chatgpt: [
552
+ {
553
+ kid: "otMqcjr17mGyruktGvJU8oojQTSMHlVm7uO-lrcqbdg",
554
+ // ChatGPT's current Ed25519 public key (base64)
555
+ // Source: https://chatgpt.com/.well-known/http-message-signatures-directory
556
+ publicKey: "7F_3jDlxaquwh291MiACkcS3Opq88NksyHiakzS-Y1g",
557
+ validFrom: 1735689600,
558
+ // Jan 1, 2025 (from OpenAI's nbf)
559
+ // Extended expiration as fallback safety - API fetch should provide fresh keys
560
+ // Check OpenAI's well-known endpoint for actual expiration dates
561
+ validUntil: 1799625600
562
+ // Jan 1, 2027 (extended for fallback safety)
563
+ }
564
+ ]
565
+ };
566
+ var keyCache = /* @__PURE__ */ new Map();
567
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
568
+ var CACHE_MAX_SIZE = 100;
569
+ function getApiBaseUrl() {
570
+ if (typeof window !== "undefined") {
571
+ return "/api/internal";
572
+ }
573
+ 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);
574
+ if (baseUrl2) {
575
+ return baseUrl2.replace(/\/$/, "") + "/api/internal";
576
+ }
577
+ if (process.env.NODE_ENV !== "production") {
578
+ console.warn(
579
+ "[Signature] No base URL configured for server-side fetch. Using localhost fallback."
580
+ );
581
+ return "http://localhost:3000/api/internal";
582
+ }
583
+ console.error(
584
+ "[Signature] CRITICAL: No base URL configured for server-side fetch in production!"
585
+ );
586
+ return "/api/internal";
587
+ }
588
+ function cleanupExpiredCache() {
589
+ const now = Date.now();
590
+ const entriesToDelete = [];
591
+ for (const [agent, cached] of keyCache.entries()) {
592
+ if (now - cached.cachedAt > CACHE_TTL_MS) {
593
+ entriesToDelete.push(agent);
594
+ }
595
+ }
596
+ for (const agent of entriesToDelete) {
597
+ keyCache.delete(agent);
598
+ }
599
+ if (keyCache.size > CACHE_MAX_SIZE) {
600
+ const entries = Array.from(keyCache.entries()).map(([agent, cached]) => ({
601
+ agent,
602
+ cachedAt: cached.cachedAt
603
+ }));
604
+ entries.sort((a, b) => a.cachedAt - b.cachedAt);
605
+ const toRemove = entries.slice(0, keyCache.size - CACHE_MAX_SIZE);
606
+ for (const entry of toRemove) {
607
+ keyCache.delete(entry.agent);
608
+ }
609
+ }
473
610
  }
474
- async function detectAgentWithWasm(userAgent, headers, ipAddress) {
475
- const initialized = await initWasm();
476
- if (!initialized || !wasmExports) {
611
+ async function fetchKeysFromApi(agent) {
612
+ if (keyCache.size > CACHE_MAX_SIZE) {
613
+ cleanupExpiredCache();
614
+ }
615
+ const cached = keyCache.get(agent);
616
+ if (cached && Date.now() - cached.cachedAt < CACHE_TTL_MS) {
617
+ return cached.keys;
618
+ }
619
+ if (typeof fetch === "undefined") {
620
+ console.warn("[Signature] fetch() not available in this environment");
477
621
  return null;
478
622
  }
479
623
  try {
480
- const headersJson = JSON.stringify(headers);
481
- if (typeof wasmExports.detect_agent === "function") {
482
- const result = wasmExports.detect_agent(
483
- userAgent || "",
484
- headersJson,
485
- ipAddress || "",
486
- (/* @__PURE__ */ new Date()).toISOString()
487
- );
488
- return {
489
- isAgent: result.is_agent || false,
490
- confidence: result.confidence || 0,
491
- agent: result.agent,
492
- verificationMethod: result.verification_method || "wasm",
493
- riskLevel: result.risk_level || "low"
494
- };
624
+ const apiBaseUrl = getApiBaseUrl();
625
+ const url = `${apiBaseUrl}/signature-keys?agent=${encodeURIComponent(agent)}`;
626
+ const response = await fetch(url, {
627
+ method: "GET",
628
+ headers: {
629
+ "Content-Type": "application/json"
630
+ },
631
+ // 5 second timeout
632
+ signal: AbortSignal.timeout(5e3)
633
+ });
634
+ if (!response.ok) {
635
+ console.warn(`[Signature] Failed to fetch keys from API: ${response.status}`);
636
+ return null;
495
637
  }
496
- console.warn("[AgentShield] WASM exports do not include detect_agent function");
497
- return null;
638
+ const data = await response.json();
639
+ if (!data.keys || !Array.isArray(data.keys) || data.keys.length === 0) {
640
+ console.warn(`[Signature] No keys returned from API for agent: ${agent}`);
641
+ return null;
642
+ }
643
+ keyCache.set(agent, {
644
+ keys: data.keys,
645
+ cachedAt: Date.now()
646
+ });
647
+ return data.keys;
498
648
  } catch (error) {
499
- console.error("[AgentShield] WASM detection failed:", error);
649
+ console.warn("[Signature] Error fetching keys from API, using fallback", {
650
+ error: error instanceof Error ? error.message : "Unknown error",
651
+ agent
652
+ });
500
653
  return null;
501
654
  }
502
655
  }
503
- async function getWasmVersion() {
504
- const initialized = await initWasm();
505
- if (!initialized || !wasmExports) {
656
+ function isValidAgent(agent) {
657
+ return agent in KNOWN_KEYS;
658
+ }
659
+ async function getKeysForAgent(agent) {
660
+ const apiKeys = await fetchKeysFromApi(agent);
661
+ if (apiKeys && apiKeys.length > 0) {
662
+ return apiKeys;
663
+ }
664
+ if (isValidAgent(agent)) {
665
+ return KNOWN_KEYS[agent];
666
+ }
667
+ return [];
668
+ }
669
+ function parseSignatureInput(signatureInput) {
670
+ try {
671
+ const match = signatureInput.match(/sig1=\((.*?)\);(.+)/);
672
+ if (!match) return null;
673
+ const [, headersList, params] = match;
674
+ const signedHeaders = headersList ? headersList.split(" ").map((h) => h.replace(/"/g, "").trim()).filter((h) => h.length > 0) : [];
675
+ const keyidMatch = params ? params.match(/keyid="([^"]+)"/) : null;
676
+ const createdMatch = params ? params.match(/created=(\d+)/) : null;
677
+ const expiresMatch = params ? params.match(/expires=(\d+)/) : null;
678
+ if (!keyidMatch || !keyidMatch[1]) return null;
679
+ return {
680
+ keyid: keyidMatch[1],
681
+ created: createdMatch && createdMatch[1] ? parseInt(createdMatch[1]) : void 0,
682
+ expires: expiresMatch && expiresMatch[1] ? parseInt(expiresMatch[1]) : void 0,
683
+ signedHeaders
684
+ };
685
+ } catch (error) {
686
+ console.error("[Signature] Failed to parse Signature-Input:", error);
506
687
  return null;
507
688
  }
508
- if (typeof wasmExports.version === "function") {
509
- return wasmExports.version();
689
+ }
690
+ function buildSignatureBase(method, path, headers, signedHeaders) {
691
+ const components = [];
692
+ for (const headerName of signedHeaders) {
693
+ let value;
694
+ switch (headerName) {
695
+ case "@method":
696
+ value = method.toUpperCase();
697
+ break;
698
+ case "@path":
699
+ value = path;
700
+ break;
701
+ case "@authority":
702
+ value = headers["host"] || headers["Host"] || "";
703
+ break;
704
+ default: {
705
+ const key = Object.keys(headers).find((k) => k.toLowerCase() === headerName.toLowerCase());
706
+ value = key ? headers[key] || "" : "";
707
+ break;
708
+ }
709
+ }
710
+ components.push(`"${headerName}": ${value}`);
510
711
  }
511
- return "unknown";
712
+ return components.join("\n");
512
713
  }
513
- async function isWasmAvailable() {
714
+ function base64ToBytes(base64) {
715
+ let standardBase64 = base64.replace(/-/g, "+").replace(/_/g, "/");
716
+ const padding = standardBase64.length % 4;
717
+ if (padding) {
718
+ standardBase64 += "=".repeat(4 - padding);
719
+ }
720
+ const binaryString = atob(standardBase64);
721
+ return Uint8Array.from(binaryString, (c) => c.charCodeAt(0));
722
+ }
723
+ async function verifyEd25519Signature(publicKeyBase64, signatureBase64, message) {
514
724
  try {
515
- const initialized = await initWasm();
516
- if (!initialized) return false;
517
- const version = await getWasmVersion();
518
- return version !== null;
519
- } catch {
520
- return false;
725
+ const publicKeyBytes = base64ToBytes(publicKeyBase64);
726
+ const signatureBytes = base64ToBytes(signatureBase64);
727
+ const messageBytes = new TextEncoder().encode(message);
728
+ if (publicKeyBytes.length !== 32) {
729
+ console.error("[Signature] Invalid public key length:", publicKeyBytes.length);
730
+ return false;
731
+ }
732
+ if (signatureBytes.length !== 64) {
733
+ console.error("[Signature] Invalid signature length:", signatureBytes.length);
734
+ return false;
735
+ }
736
+ return ed25519__namespace.verify(signatureBytes, messageBytes, publicKeyBytes);
737
+ } catch (nobleError) {
738
+ console.warn("[Signature] @noble/ed25519 failed, trying Web Crypto fallback:", nobleError);
739
+ try {
740
+ const publicKeyBytes = base64ToBytes(publicKeyBase64);
741
+ const signatureBytes = base64ToBytes(signatureBase64);
742
+ const messageBytes = new TextEncoder().encode(message);
743
+ const publicKey = await crypto.subtle.importKey(
744
+ "raw",
745
+ publicKeyBytes.buffer,
746
+ {
747
+ name: "Ed25519",
748
+ namedCurve: "Ed25519"
749
+ },
750
+ false,
751
+ ["verify"]
752
+ );
753
+ return await crypto.subtle.verify(
754
+ "Ed25519",
755
+ publicKey,
756
+ signatureBytes.buffer,
757
+ messageBytes
758
+ );
759
+ } catch (cryptoError) {
760
+ console.error("[Signature] Both @noble/ed25519 and Web Crypto failed:", {
761
+ nobleError: nobleError instanceof Error ? nobleError.message : "Unknown",
762
+ cryptoError: cryptoError instanceof Error ? cryptoError.message : "Unknown"
763
+ });
764
+ return false;
765
+ }
521
766
  }
522
767
  }
523
-
524
- // src/edge-detector-with-wasm.ts
525
- var AI_AGENT_PATTERNS2 = [
526
- { pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
527
- { pattern: /claude-web/i, type: "claude", name: "Claude" },
528
- { pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
529
- { pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
530
- { pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
531
- { pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
532
- { pattern: /bingbot/i, type: "bing", name: "Bing AI" },
533
- { pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
534
- ];
535
- var CLOUD_PROVIDERS2 = {
536
- aws: ["54.", "52.", "35.", "18.", "3."],
537
- gcp: ["35.", "34.", "104.", "107.", "108."],
538
- azure: ["13.", "20.", "40.", "52.", "104."]
539
- };
540
- var EdgeAgentDetectorWithWasm = class {
541
- constructor(enableWasm = true) {
542
- this.enableWasm = enableWasm;
768
+ async function verifyAgentSignature(method, path, headers) {
769
+ const signature = headers["signature"] || headers["Signature"];
770
+ const signatureInput = headers["signature-input"] || headers["Signature-Input"];
771
+ const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
772
+ if (!signature || !signatureInput) {
773
+ return {
774
+ isValid: false,
775
+ confidence: 0,
776
+ reason: "No signature headers present",
777
+ verificationMethod: "none"
778
+ };
543
779
  }
544
- wasmEnabled = false;
545
- initPromise = null;
546
- baseUrl = null;
547
- /**
548
- * Set the base URL for WASM loading in Edge Runtime
549
- */
550
- setBaseUrl(url) {
551
- this.baseUrl = url;
552
- setWasmBaseUrl(url);
780
+ const parsed = parseSignatureInput(signatureInput);
781
+ if (!parsed) {
782
+ return {
783
+ isValid: false,
784
+ confidence: 0,
785
+ reason: "Invalid Signature-Input header",
786
+ verificationMethod: "none"
787
+ };
553
788
  }
554
- /**
555
- * Initialize the detector (including WASM if enabled)
556
- */
557
- async init() {
558
- if (!this.enableWasm) {
559
- this.wasmEnabled = false;
560
- return;
789
+ if (parsed.created) {
790
+ const now2 = Math.floor(Date.now() / 1e3);
791
+ const age = now2 - parsed.created;
792
+ if (age > 300) {
793
+ return {
794
+ isValid: false,
795
+ confidence: 0,
796
+ reason: "Signature expired (older than 5 minutes)",
797
+ verificationMethod: "none"
798
+ };
561
799
  }
562
- if (this.initPromise) {
563
- await this.initPromise;
564
- return;
800
+ if (age < -30) {
801
+ return {
802
+ isValid: false,
803
+ confidence: 0,
804
+ reason: "Signature timestamp is in the future",
805
+ verificationMethod: "none"
806
+ };
565
807
  }
566
- this.initPromise = (async () => {
567
- try {
568
- const wasmAvailable = await isWasmAvailable();
569
- this.wasmEnabled = wasmAvailable;
570
- if (this.wasmEnabled) {
571
- console.log("[AgentShield] WASM detection enabled");
572
- } else {
573
- console.log("[AgentShield] WASM not available, using pattern detection");
574
- }
575
- } catch (error) {
576
- console.error("[AgentShield] Failed to initialize WASM:", error);
577
- this.wasmEnabled = false;
578
- }
579
- })();
580
- await this.initPromise;
581
808
  }
582
- /**
583
- * Pattern-based detection (fallback)
584
- */
585
- async patternDetection(input) {
809
+ let agent;
810
+ let agentKey;
811
+ if (signatureAgent === '"https://chatgpt.com"' || signatureAgent?.includes("chatgpt.com")) {
812
+ agent = "ChatGPT";
813
+ agentKey = "chatgpt";
814
+ }
815
+ if (!agent || !agentKey) {
816
+ return {
817
+ isValid: false,
818
+ confidence: 0,
819
+ reason: "Unknown signature agent",
820
+ verificationMethod: "none"
821
+ };
822
+ }
823
+ const knownKeys = await getKeysForAgent(agentKey);
824
+ if (knownKeys.length === 0) {
825
+ return {
826
+ isValid: false,
827
+ confidence: 0,
828
+ reason: "No keys available for agent",
829
+ verificationMethod: "none"
830
+ };
831
+ }
832
+ const key = knownKeys.find((k) => k.kid === parsed.keyid);
833
+ if (!key) {
834
+ return {
835
+ isValid: false,
836
+ confidence: 0,
837
+ reason: `Unknown key ID: ${parsed.keyid}`,
838
+ verificationMethod: "none"
839
+ };
840
+ }
841
+ const now = Math.floor(Date.now() / 1e3);
842
+ if (now < key.validFrom || now > key.validUntil) {
843
+ return {
844
+ isValid: false,
845
+ confidence: 0,
846
+ reason: "Key is not valid at current time",
847
+ verificationMethod: "none"
848
+ };
849
+ }
850
+ const signatureBase = buildSignatureBase(method, path, headers, parsed.signedHeaders);
851
+ let signatureValue = signature;
852
+ if (signatureValue.startsWith("sig1=:")) {
853
+ signatureValue = signatureValue.substring(6);
854
+ }
855
+ if (signatureValue.endsWith(":")) {
856
+ signatureValue = signatureValue.slice(0, -1);
857
+ }
858
+ const isValid = await verifyEd25519Signature(key.publicKey, signatureValue, signatureBase);
859
+ if (isValid) {
860
+ return {
861
+ isValid: true,
862
+ agent,
863
+ keyid: parsed.keyid,
864
+ confidence: 1,
865
+ // 100% confidence for valid signature
866
+ verificationMethod: "signature"
867
+ };
868
+ } else {
869
+ return {
870
+ isValid: false,
871
+ confidence: 0,
872
+ reason: "Signature verification failed",
873
+ verificationMethod: "none"
874
+ };
875
+ }
876
+ }
877
+ function hasSignatureHeaders(headers) {
878
+ return !!((headers["signature"] || headers["Signature"]) && (headers["signature-input"] || headers["Signature-Input"]));
879
+ }
880
+ function isChatGPTSignature(headers) {
881
+ const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
882
+ if (!signatureAgent) {
883
+ return false;
884
+ }
885
+ const agentUrlStr = signatureAgent.replace(/^"+|"+$/g, "");
886
+ if (agentUrlStr === "https://chatgpt.com") {
887
+ return true;
888
+ }
889
+ try {
890
+ const agentUrl = new URL(agentUrlStr);
891
+ const allowedHosts = ["chatgpt.com", "www.chatgpt.com"];
892
+ return allowedHosts.includes(agentUrl.host);
893
+ } catch {
894
+ return false;
895
+ }
896
+ }
897
+ var rules = agentshieldShared.loadRulesSync();
898
+ var EdgeAgentDetector = class {
899
+ rules;
900
+ constructor() {
901
+ this.rules = rules;
902
+ }
903
+ async analyze(input) {
586
904
  const reasons = [];
587
905
  let detectedAgent;
588
906
  let verificationMethod;
@@ -592,144 +910,178 @@ var EdgeAgentDetectorWithWasm = class {
592
910
  for (const [key, value] of Object.entries(headers)) {
593
911
  normalizedHeaders[key.toLowerCase()] = value;
594
912
  }
595
- const signaturePresent = !!(normalizedHeaders["signature"] || normalizedHeaders["signature-input"]);
596
- const signatureAgent = normalizedHeaders["signature-agent"];
597
- if (signatureAgent?.includes("chatgpt.com")) {
598
- confidence = 0.85;
599
- reasons.push("signature_agent:chatgpt");
600
- detectedAgent = { type: "chatgpt", name: "ChatGPT" };
601
- verificationMethod = "signature";
602
- } else if (signaturePresent) {
603
- confidence = Math.max(confidence, 0.4);
604
- reasons.push("signature_present");
913
+ if (hasSignatureHeaders(headers)) {
914
+ try {
915
+ const signatureResult = await verifyAgentSignature(
916
+ input.method || "GET",
917
+ input.url || "/",
918
+ headers
919
+ );
920
+ if (signatureResult.isValid) {
921
+ confidence = signatureResult.confidence * 100;
922
+ reasons.push(`verified_signature:${signatureResult.agent?.toLowerCase() || "unknown"}`);
923
+ if (signatureResult.agent) {
924
+ detectedAgent = {
925
+ type: signatureResult.agent.toLowerCase(),
926
+ name: signatureResult.agent
927
+ };
928
+ }
929
+ verificationMethod = signatureResult.verificationMethod;
930
+ if (signatureResult.keyid) {
931
+ reasons.push(`keyid:${signatureResult.keyid}`);
932
+ }
933
+ } else {
934
+ console.warn("[EdgeAgentDetector] Signature verification failed:", {
935
+ reason: signatureResult.reason,
936
+ agent: signatureResult.agent,
937
+ hasSignatureAgent: !!headers["signature-agent"] || !!headers["Signature-Agent"],
938
+ signatureAgentValue: headers["signature-agent"] || headers["Signature-Agent"]
939
+ });
940
+ confidence = Math.max(confidence, 30);
941
+ reasons.push("invalid_signature");
942
+ if (signatureResult.reason) {
943
+ reasons.push(`signature_error:${signatureResult.reason}`);
944
+ }
945
+ if (isChatGPTSignature(headers)) {
946
+ reasons.push("claims_chatgpt");
947
+ detectedAgent = { type: "chatgpt", name: "ChatGPT (unverified)" };
948
+ }
949
+ }
950
+ } catch (error) {
951
+ console.error("[EdgeAgentDetector] Signature verification error:", error);
952
+ confidence = Math.max(confidence, 20);
953
+ reasons.push("signature_verification_error");
954
+ }
605
955
  }
606
956
  const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
607
957
  if (userAgent) {
608
- for (const { pattern, type, name } of AI_AGENT_PATTERNS2) {
609
- if (pattern.test(userAgent)) {
610
- const highConfidenceAgents = [
611
- "chatgpt",
612
- "claude",
613
- "perplexity",
614
- "anthropic"
615
- ];
616
- const patternConfidence = highConfidenceAgents.includes(type) ? 0.85 : 0.5;
617
- confidence = Math.max(confidence, patternConfidence);
618
- reasons.push(`known_pattern:${type}`);
958
+ const userAgentEntries = Object.entries(this.rules.rules.userAgents);
959
+ const genericKeys = ["generic_bot", "dev_tools", "automation_tools"];
960
+ const sortedEntries = userAgentEntries.sort((a, b) => {
961
+ const aIsGeneric = genericKeys.includes(a[0]);
962
+ const bIsGeneric = genericKeys.includes(b[0]);
963
+ if (aIsGeneric && !bIsGeneric) return 1;
964
+ if (!aIsGeneric && bIsGeneric) return -1;
965
+ return 0;
966
+ });
967
+ for (const [agentKey, agentRule] of sortedEntries) {
968
+ const rule = agentRule;
969
+ const matched = rule.patterns.some((pattern) => {
970
+ const regex = new RegExp(pattern, "i");
971
+ return regex.test(userAgent);
972
+ });
973
+ if (matched) {
974
+ const agentType = this.getAgentType(agentKey);
975
+ const agentName = this.getAgentName(agentKey);
976
+ confidence = Math.max(confidence, rule.confidence * 100);
977
+ reasons.push(`known_pattern:${agentType}`);
619
978
  if (!detectedAgent) {
620
- detectedAgent = { type, name };
979
+ detectedAgent = { type: agentType, name: agentName };
621
980
  verificationMethod = "pattern";
622
981
  }
623
982
  break;
624
983
  }
625
984
  }
626
985
  }
627
- const aiHeaders = [
628
- "openai-conversation-id",
629
- "openai-ephemeral-user-id",
630
- "anthropic-client-id",
631
- "x-goog-api-client",
632
- "x-ms-copilot-id"
633
- ];
634
- const foundAiHeaders = aiHeaders.filter(
635
- (header) => normalizedHeaders[header]
986
+ const suspiciousHeaders = this.rules.rules.headers.suspicious;
987
+ const foundAiHeaders = suspiciousHeaders.filter(
988
+ (headerRule) => normalizedHeaders[headerRule.name.toLowerCase()]
636
989
  );
637
990
  if (foundAiHeaders.length > 0) {
638
- confidence = Math.max(confidence, 0.6);
991
+ const maxConfidence = Math.max(...foundAiHeaders.map((h) => h.confidence * 100));
992
+ confidence = Math.max(confidence, maxConfidence);
639
993
  reasons.push(`ai_headers:${foundAiHeaders.length}`);
640
994
  }
641
995
  const ip = input.ip || input.ipAddress;
642
996
  if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
643
- for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS2)) {
644
- if (prefixes.some((prefix) => ip.startsWith(prefix))) {
645
- confidence = Math.max(confidence, 0.4);
997
+ const ipRanges = "providers" in this.rules.rules.ipRanges ? this.rules.rules.ipRanges.providers : this.rules.rules.ipRanges;
998
+ for (const [provider, ipRule] of Object.entries(ipRanges)) {
999
+ if (!ipRule || typeof ipRule !== "object" || !("ranges" in ipRule) || !Array.isArray(ipRule.ranges))
1000
+ continue;
1001
+ const matched = ipRule.ranges.some((range) => {
1002
+ const prefix = range.split("/")[0];
1003
+ const prefixParts = prefix.split(".");
1004
+ const ipParts = ip.split(".");
1005
+ for (let i = 0; i < Math.min(prefixParts.length - 1, 2); i++) {
1006
+ if (prefixParts[i] !== ipParts[i] && prefixParts[i] !== "0") {
1007
+ return false;
1008
+ }
1009
+ }
1010
+ return true;
1011
+ });
1012
+ if (matched) {
1013
+ const rule = ipRule;
1014
+ confidence = Math.max(confidence, rule.confidence * 40);
646
1015
  reasons.push(`cloud_provider:${provider}`);
647
1016
  break;
648
1017
  }
649
1018
  }
650
1019
  }
651
- if (reasons.length > 2) {
652
- confidence = Math.min(confidence * 1.2, 0.95);
1020
+ if (reasons.length > 2 && confidence < 100) {
1021
+ confidence = Math.min(confidence * 1.2, 95);
653
1022
  }
1023
+ confidence = Math.min(Math.max(confidence, 0), 100);
654
1024
  return {
655
- isAgent: confidence > 0.3,
1025
+ isAgent: confidence > 30,
1026
+ // Updated to 0-100 scale (was 0.3)
656
1027
  confidence,
1028
+ detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
1029
+ signals: [],
1030
+ // Will be populated by enhanced detection engine in future tasks
657
1031
  ...detectedAgent && { detectedAgent },
658
1032
  reasons,
659
- ...verificationMethod && { verificationMethod },
660
- forgeabilityRisk: confidence > 0.8 ? "medium" : "high",
1033
+ ...verificationMethod && {
1034
+ verificationMethod
1035
+ },
1036
+ forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 80 ? "medium" : "high",
1037
+ // Updated to 0-100 scale
661
1038
  timestamp: /* @__PURE__ */ new Date()
662
1039
  };
663
1040
  }
664
1041
  /**
665
- * Analyze request with WASM enhancement when available
1042
+ * Get agent type from rule key
666
1043
  */
667
- async analyze(input) {
668
- await this.init();
669
- if (this.wasmEnabled) {
670
- try {
671
- const wasmResult = await detectAgentWithWasm(
672
- input.userAgent || input.headers?.["user-agent"],
673
- input.headers || {},
674
- input.ip || input.ipAddress
675
- );
676
- if (wasmResult) {
677
- console.log("[AgentShield] Using WASM detection result");
678
- const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
679
- return {
680
- isAgent: wasmResult.isAgent,
681
- confidence: wasmResult.confidence,
682
- ...detectedAgent && { detectedAgent },
683
- reasons: [`wasm:${wasmResult.verificationMethod}`],
684
- verificationMethod: wasmResult.verificationMethod,
685
- forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 0.9 ? "medium" : "high",
686
- timestamp: /* @__PURE__ */ new Date()
687
- };
688
- }
689
- } catch (error) {
690
- console.error("[AgentShield] WASM detection error:", error);
691
- }
692
- }
693
- const patternResult = await this.patternDetection(input);
694
- if (this.wasmEnabled && patternResult.confidence >= 0.85) {
695
- patternResult.confidence = Math.min(0.95, patternResult.confidence + 0.1);
696
- patternResult.reasons.push("wasm_enhanced");
697
- if (!patternResult.verificationMethod) {
698
- patternResult.verificationMethod = "wasm-enhanced";
699
- }
700
- }
701
- return patternResult;
1044
+ getAgentType(agentKey) {
1045
+ const typeMap = {
1046
+ openai_gptbot: "openai",
1047
+ anthropic_claude: "anthropic",
1048
+ perplexity_bot: "perplexity",
1049
+ google_ai: "google",
1050
+ microsoft_ai: "microsoft",
1051
+ meta_ai: "meta",
1052
+ cohere_bot: "cohere",
1053
+ huggingface_bot: "huggingface",
1054
+ generic_bot: "generic",
1055
+ dev_tools: "dev",
1056
+ automation_tools: "automation"
1057
+ };
1058
+ return typeMap[agentKey] || agentKey;
702
1059
  }
703
1060
  /**
704
- * Map agent names from WASM to consistent format
1061
+ * Get agent name from rule key
705
1062
  */
706
- mapAgentName(agent) {
707
- const lowerAgent = agent.toLowerCase();
708
- if (lowerAgent.includes("chatgpt")) {
709
- return { type: "chatgpt", name: "ChatGPT" };
710
- } else if (lowerAgent.includes("claude")) {
711
- return { type: "claude", name: "Claude" };
712
- } else if (lowerAgent.includes("perplexity")) {
713
- return { type: "perplexity", name: "Perplexity" };
714
- } else if (lowerAgent.includes("bing")) {
715
- return { type: "bing", name: "Bing AI" };
716
- } else if (lowerAgent.includes("anthropic")) {
717
- return { type: "anthropic", name: "Anthropic" };
718
- }
719
- return { type: "unknown", name: agent };
1063
+ getAgentName(agentKey) {
1064
+ const nameMap = {
1065
+ openai_gptbot: "ChatGPT/GPTBot",
1066
+ anthropic_claude: "Claude",
1067
+ perplexity_bot: "Perplexity",
1068
+ google_ai: "Google AI",
1069
+ microsoft_ai: "Microsoft Copilot",
1070
+ meta_ai: "Meta AI",
1071
+ cohere_bot: "Cohere",
1072
+ huggingface_bot: "HuggingFace",
1073
+ generic_bot: "Generic Bot",
1074
+ dev_tools: "Development Tool",
1075
+ automation_tools: "Automation Tool"
1076
+ };
1077
+ return nameMap[agentKey] || agentKey;
720
1078
  }
721
1079
  };
722
- var EdgeAgentDetectorWrapperWithWasm = class {
1080
+ var EdgeAgentDetectorWrapper = class {
723
1081
  detector;
724
1082
  events = /* @__PURE__ */ new Map();
725
- constructor(config) {
726
- this.detector = new EdgeAgentDetectorWithWasm(config?.enableWasm ?? true);
727
- if (config?.baseUrl) {
728
- this.detector.setBaseUrl(config.baseUrl);
729
- }
730
- }
731
- setBaseUrl(url) {
732
- this.detector.setBaseUrl(url);
1083
+ constructor(_config) {
1084
+ this.detector = new EdgeAgentDetector();
733
1085
  }
734
1086
  async analyze(input) {
735
1087
  const result = await this.detector.analyze(input);
@@ -750,10 +1102,13 @@ var EdgeAgentDetectorWrapperWithWasm = class {
750
1102
  handlers.forEach((handler) => handler(...args));
751
1103
  }
752
1104
  async init() {
753
- await this.detector.init();
1105
+ return;
754
1106
  }
755
1107
  };
756
1108
 
1109
+ // src/middleware.ts
1110
+ init_edge_detector_with_wasm();
1111
+
757
1112
  // src/session-tracker.ts
758
1113
  var EdgeSessionTracker = class {
759
1114
  config;
@@ -933,7 +1288,9 @@ function createAgentShieldMiddleware(config = {}) {
933
1288
  }) : null;
934
1289
  if (config.events) {
935
1290
  Object.entries(config.events).forEach(([event, handler]) => {
936
- detector.on(event, handler);
1291
+ if (handler) {
1292
+ detector.on(event, handler);
1293
+ }
937
1294
  });
938
1295
  }
939
1296
  const {
@@ -965,19 +1322,22 @@ function createAgentShieldMiddleware(config = {}) {
965
1322
  const response2 = server.NextResponse.next();
966
1323
  response2.headers.set("x-agentshield-detected", "true");
967
1324
  response2.headers.set("x-agentshield-agent", existingSession.agent);
968
- response2.headers.set(
969
- "x-agentshield-confidence",
970
- existingSession.confidence.toString()
971
- );
1325
+ response2.headers.set("x-agentshield-confidence", existingSession.confidence.toString());
972
1326
  response2.headers.set("x-agentshield-session", "continued");
973
1327
  response2.headers.set("x-agentshield-session-id", existingSession.id);
974
1328
  request.agentShield = {
975
1329
  result: {
976
1330
  isAgent: true,
977
1331
  confidence: existingSession.confidence,
978
- detectedAgent: { name: existingSession.agent },
1332
+ detectionClass: { type: "AiAgent" },
1333
+ detectedAgent: {
1334
+ type: "ai_agent",
1335
+ name: existingSession.agent
1336
+ },
979
1337
  timestamp: /* @__PURE__ */ new Date(),
980
- verificationMethod: "session"
1338
+ verificationMethod: "behavioral",
1339
+ reasons: ["Session continued"],
1340
+ signals: []
981
1341
  },
982
1342
  session: existingSession,
983
1343
  skipped: false
@@ -1007,7 +1367,7 @@ function createAgentShieldMiddleware(config = {}) {
1007
1367
  timestamp: /* @__PURE__ */ new Date()
1008
1368
  };
1009
1369
  const result = await detector.analyze(context);
1010
- if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 0.7)) {
1370
+ if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
1011
1371
  if (onDetection) {
1012
1372
  const customResponse = await onDetection(request, result);
1013
1373
  if (customResponse) {
@@ -1026,11 +1386,9 @@ function createAgentShieldMiddleware(config = {}) {
1026
1386
  { status: blockedResponse.status }
1027
1387
  );
1028
1388
  if (blockedResponse.headers) {
1029
- Object.entries(blockedResponse.headers).forEach(
1030
- ([key, value]) => {
1031
- response2.headers.set(key, value);
1032
- }
1033
- );
1389
+ Object.entries(blockedResponse.headers).forEach(([key, value]) => {
1390
+ response2.headers.set(key, value);
1391
+ });
1034
1392
  }
1035
1393
  detector.emit("agent.blocked", result, context);
1036
1394
  return response2;
@@ -1040,17 +1398,19 @@ function createAgentShieldMiddleware(config = {}) {
1040
1398
  case "rewrite":
1041
1399
  return server.NextResponse.rewrite(new URL(rewriteUrl, request.url));
1042
1400
  case "log":
1043
- console.warn("AgentShield: Agent detected", {
1044
- ipAddress: context.ipAddress,
1045
- userAgent: context.userAgent,
1046
- confidence: result.confidence,
1047
- reasons: result.reasons,
1048
- pathname: request.nextUrl.pathname
1049
- });
1401
+ if (process.env.NODE_ENV !== "production") {
1402
+ console.debug("AgentShield: Agent detected", {
1403
+ ipAddress: context.ipAddress,
1404
+ userAgent: context.userAgent,
1405
+ confidence: result.confidence,
1406
+ reasons: result.reasons,
1407
+ pathname: request.nextUrl.pathname
1408
+ });
1409
+ }
1050
1410
  break;
1051
1411
  case "allow":
1052
1412
  default:
1053
- if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 0.7)) {
1413
+ if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
1054
1414
  detector.emit("agent.allowed", result, context);
1055
1415
  }
1056
1416
  break;
@@ -1062,14 +1422,11 @@ function createAgentShieldMiddleware(config = {}) {
1062
1422
  };
1063
1423
  let response = server.NextResponse.next();
1064
1424
  response.headers.set("x-agentshield-detected", result.isAgent.toString());
1065
- response.headers.set(
1066
- "x-agentshield-confidence",
1067
- result.confidence.toString()
1068
- );
1425
+ response.headers.set("x-agentshield-confidence", result.confidence.toString());
1069
1426
  if (result.detectedAgent?.name) {
1070
1427
  response.headers.set("x-agentshield-agent", result.detectedAgent.name);
1071
1428
  }
1072
- if (sessionTracker && result.isAgent && result.confidence >= (config.confidenceThreshold ?? 0.7)) {
1429
+ if (sessionTracker && result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
1073
1430
  response = await sessionTracker.track(request, response, result);
1074
1431
  response.headers.set("x-agentshield-session", "new");
1075
1432
  detector.emit("agent.session.started", result, context);
@@ -1087,7 +1444,7 @@ var middlewareInstance = null;
1087
1444
  var isInitializing = false;
1088
1445
  var initPromise2 = null;
1089
1446
  function createAgentShieldMiddleware2(config) {
1090
- return async function agentShieldMiddleware(request) {
1447
+ return async function agentShieldMiddleware2(request) {
1091
1448
  if (!middlewareInstance) {
1092
1449
  if (!isInitializing) {
1093
1450
  isInitializing = true;
@@ -1104,48 +1461,81 @@ function createAgentShieldMiddleware2(config) {
1104
1461
  };
1105
1462
  }
1106
1463
 
1107
- // src/wasm-confidence.ts
1108
- async function checkWasmAvailability() {
1109
- try {
1110
- if (typeof WebAssembly === "undefined") {
1111
- return false;
1464
+ // src/edge-safe-detector.ts
1465
+ var AI_AGENT_PATTERNS = [
1466
+ { pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
1467
+ { pattern: /claude-web/i, type: "claude", name: "Claude" },
1468
+ { pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
1469
+ { pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
1470
+ { pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
1471
+ { pattern: /bingbot/i, type: "bing", name: "Bing AI" },
1472
+ { pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
1473
+ ];
1474
+ var EdgeSafeDetector = class {
1475
+ async analyze(input) {
1476
+ const reasons = [];
1477
+ let detectedAgent;
1478
+ let confidence = 0;
1479
+ const headers = input.headers || {};
1480
+ const normalizedHeaders = {};
1481
+ for (const [key, value] of Object.entries(headers)) {
1482
+ normalizedHeaders[key.toLowerCase()] = value;
1112
1483
  }
1113
- if (!WebAssembly.instantiate || !WebAssembly.Module) {
1114
- return false;
1484
+ const userAgent = input.userAgent || normalizedHeaders["user-agent"] || "";
1485
+ if (userAgent) {
1486
+ for (const { pattern, type, name } of AI_AGENT_PATTERNS) {
1487
+ if (pattern.test(userAgent)) {
1488
+ confidence = 85;
1489
+ reasons.push(`known_pattern:${type}`);
1490
+ detectedAgent = { type, name };
1491
+ break;
1492
+ }
1493
+ }
1115
1494
  }
1116
- return true;
1117
- } catch {
1118
- return false;
1119
- }
1120
- }
1121
- function shouldIndicateWasmVerification(confidence) {
1122
- return confidence >= 0.85 && confidence < 1;
1123
- }
1124
- function getWasmConfidenceBoost(baseConfidence, reasons = []) {
1125
- if (reasons.some(
1126
- (r) => r.includes("signature_agent") && !r.includes("signature_headers_present")
1127
- )) {
1128
- return 1;
1129
- }
1130
- if (baseConfidence >= 0.85) {
1131
- return 0.95;
1132
- }
1133
- if (baseConfidence >= 0.7) {
1134
- return Math.min(baseConfidence * 1.1, 0.9);
1135
- }
1136
- return baseConfidence;
1137
- }
1138
- function getVerificationMethod(confidence, reasons = []) {
1139
- if (reasons.some(
1140
- (r) => r.includes("signature_agent") && !r.includes("signature_headers_present")
1141
- )) {
1142
- return "signature";
1143
- }
1144
- if (shouldIndicateWasmVerification(confidence)) {
1145
- return "wasm-enhanced";
1495
+ const hasChrome = userAgent.toLowerCase().includes("chrome");
1496
+ const hasFirefox = userAgent.toLowerCase().includes("firefox");
1497
+ const hasSafari = userAgent.toLowerCase().includes("safari");
1498
+ const hasBrowserUA = hasChrome || hasFirefox || hasSafari;
1499
+ if (hasBrowserUA) {
1500
+ const hasSecChUa = !!normalizedHeaders["sec-ch-ua"];
1501
+ const hasSecFetch = !!normalizedHeaders["sec-fetch-site"];
1502
+ const hasAcceptLanguage = !!normalizedHeaders["accept-language"];
1503
+ const hasCookies = !!normalizedHeaders["cookie"];
1504
+ const missingHeaders = [];
1505
+ if (!hasSecChUa && hasChrome) missingHeaders.push("sec-ch-ua");
1506
+ if (!hasSecFetch) missingHeaders.push("sec-fetch");
1507
+ if (!hasAcceptLanguage) missingHeaders.push("accept-language");
1508
+ if (!hasCookies) missingHeaders.push("cookies");
1509
+ if (missingHeaders.length >= 2) {
1510
+ confidence = Math.max(confidence, 85);
1511
+ reasons.push("browser_ua_missing_headers");
1512
+ if (!detectedAgent && hasChrome && !hasSecChUa) {
1513
+ detectedAgent = { type: "perplexity", name: "Perplexity" };
1514
+ }
1515
+ }
1516
+ }
1517
+ const aiHeaders = ["openai-conversation-id", "anthropic-client-id", "x-goog-api-client"];
1518
+ const foundAiHeaders = aiHeaders.filter((h) => normalizedHeaders[h]);
1519
+ if (foundAiHeaders.length > 0) {
1520
+ confidence = Math.max(confidence, 60);
1521
+ reasons.push(`ai_headers:${foundAiHeaders.length}`);
1522
+ }
1523
+ return {
1524
+ isAgent: confidence > 30,
1525
+ // Updated to 0-100 scale (was 0.3)
1526
+ confidence,
1527
+ detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
1528
+ signals: [],
1529
+ // Will be populated by enhanced detection engine in future tasks
1530
+ detectedAgent,
1531
+ reasons,
1532
+ verificationMethod: "pattern",
1533
+ timestamp: /* @__PURE__ */ new Date(),
1534
+ confidenceLevel: confidence >= 80 ? "high" : confidence >= 50 ? "medium" : "low"
1535
+ // Updated to 0-100 scale
1536
+ };
1146
1537
  }
1147
- return "pattern";
1148
- }
1538
+ };
1149
1539
 
1150
1540
  // src/storage/memory-adapter.ts
1151
1541
  var MemoryStorageAdapter = class {
@@ -1345,11 +1735,7 @@ var RedisStorageAdapter = class {
1345
1735
  }
1346
1736
  async cleanup(olderThan) {
1347
1737
  const cutoff = olderThan.getTime();
1348
- await this.redis.zremrangebyrank(
1349
- this.timelineKey(),
1350
- 0,
1351
- Math.floor(cutoff / 1e3)
1352
- );
1738
+ await this.redis.zremrangebyrank(this.timelineKey(), 0, Math.floor(cutoff / 1e3));
1353
1739
  }
1354
1740
  };
1355
1741
 
@@ -1370,7 +1756,10 @@ async function createStorageAdapter(config) {
1370
1756
  });
1371
1757
  return new RedisStorageAdapter(redis, config.ttl);
1372
1758
  } catch (error) {
1373
- console.warn("[AgentShield] Failed to initialize Redis storage, falling back to memory:", error);
1759
+ console.warn(
1760
+ "[AgentShield] Failed to initialize Redis storage, falling back to memory:",
1761
+ error
1762
+ );
1374
1763
  return new MemoryStorageAdapter();
1375
1764
  }
1376
1765
  }
@@ -1420,39 +1809,60 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1420
1809
  };
1421
1810
  let detector = null;
1422
1811
  let detectorInitPromise = null;
1812
+ let wasmConfidenceUtils = null;
1423
1813
  const getDetector = async (requestUrl) => {
1424
1814
  if (detector) {
1425
- if (requestUrl && "setBaseUrl" in detector) {
1426
- detector.setBaseUrl(requestUrl);
1427
- }
1428
1815
  return detector;
1429
1816
  }
1430
1817
  if (detectorInitPromise) {
1431
1818
  await detectorInitPromise;
1432
- if (requestUrl && detector && "setBaseUrl" in detector) {
1433
- detector.setBaseUrl(requestUrl);
1434
- }
1435
1819
  return detector;
1436
1820
  }
1437
1821
  detectorInitPromise = (async () => {
1438
- try {
1439
- const wasmAvailable = await checkWasmAvailability();
1440
- if (wasmAvailable) {
1441
- console.log("[AgentShield] \u2705 WASM support detected - enhanced detection enabled");
1442
- detector = new EdgeAgentDetectorWrapperWithWasm({ enableWasm: true });
1443
- } else {
1444
- console.log("[AgentShield] \u2139\uFE0F Using pattern-based detection (WASM not available)");
1445
- detector = new EdgeAgentDetectorWrapper({});
1822
+ const isEdgeRuntime = typeof globalThis.EdgeRuntime !== "undefined" || process.env.NEXT_RUNTIME === "edge";
1823
+ if (isEdgeRuntime) {
1824
+ if (process.env.NODE_ENV !== "production") {
1825
+ console.debug("[AgentShield] Edge Runtime detected - using pattern detection");
1826
+ }
1827
+ detector = new EdgeSafeDetector();
1828
+ } else {
1829
+ try {
1830
+ try {
1831
+ const wasmUtils = await Promise.resolve().then(() => (init_wasm_confidence(), wasm_confidence_exports));
1832
+ wasmConfidenceUtils = wasmUtils;
1833
+ const wasmAvailable = await wasmUtils.checkWasmAvailability();
1834
+ if (wasmAvailable) {
1835
+ const { EdgeAgentDetectorWrapperWithWasm: EdgeAgentDetectorWrapperWithWasm2 } = await Promise.resolve().then(() => (init_edge_detector_with_wasm(), edge_detector_with_wasm_exports));
1836
+ if (process.env.NODE_ENV !== "production") {
1837
+ console.debug(
1838
+ "[AgentShield] \u2705 WASM support detected - enhanced detection enabled"
1839
+ );
1840
+ }
1841
+ detector = new EdgeAgentDetectorWrapperWithWasm2({ enableWasm: true });
1842
+ if (requestUrl && "setBaseUrl" in detector) {
1843
+ detector.setBaseUrl(requestUrl);
1844
+ }
1845
+ } else {
1846
+ if (process.env.NODE_ENV !== "production") {
1847
+ console.debug(
1848
+ "[AgentShield] \u2139\uFE0F Using pattern-based detection (WASM not available)"
1849
+ );
1850
+ }
1851
+ detector = new EdgeSafeDetector();
1852
+ }
1853
+ } catch (wasmError) {
1854
+ if (process.env.NODE_ENV !== "production") {
1855
+ console.debug("[AgentShield] WASM utilities not available, using pattern detection");
1856
+ }
1857
+ detector = new EdgeSafeDetector();
1858
+ }
1859
+ } catch (error) {
1860
+ console.warn("[AgentShield] Failed to initialize enhanced detector, using fallback");
1861
+ detector = new EdgeSafeDetector();
1446
1862
  }
1447
- } catch (error) {
1448
- console.warn("[AgentShield] Failed to initialize WASM, using fallback:", error);
1449
- detector = new EdgeAgentDetectorWrapper({});
1450
1863
  }
1451
1864
  })();
1452
1865
  await detectorInitPromise;
1453
- if (requestUrl && detector && "setBaseUrl" in detector) {
1454
- detector.setBaseUrl(requestUrl);
1455
- }
1456
1866
  return detector;
1457
1867
  };
1458
1868
  const sessionManager = new SessionManager();
@@ -1478,17 +1888,20 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1478
1888
  const result = await activeDetector.analyze(context);
1479
1889
  let finalConfidence = result.confidence;
1480
1890
  let verificationMethod = result.verificationMethod || "pattern";
1481
- if (result.isAgent) {
1891
+ if (result.isAgent && wasmConfidenceUtils) {
1482
1892
  const reasons = result.reasons || [];
1483
- if (shouldIndicateWasmVerification(result.confidence)) {
1484
- finalConfidence = getWasmConfidenceBoost(result.confidence, reasons);
1485
- verificationMethod = getVerificationMethod(finalConfidence, reasons);
1893
+ if (wasmConfidenceUtils.shouldIndicateWasmVerification(result.confidence)) {
1894
+ finalConfidence = wasmConfidenceUtils.getWasmConfidenceBoost(result.confidence, reasons);
1895
+ verificationMethod = wasmConfidenceUtils.getVerificationMethod(finalConfidence, reasons);
1486
1896
  }
1487
1897
  }
1488
1898
  if (result.isAgent && finalConfidence >= (config.confidenceThreshold ?? 0.7)) {
1489
1899
  if (sessionTrackingEnabled) {
1490
1900
  const storage = await getStorage();
1491
- const sessionId = sessionManager.generateSessionId(ipAddress || void 0, userAgent || void 0);
1901
+ const sessionId = sessionManager.generateSessionId(
1902
+ ipAddress || void 0,
1903
+ userAgent || void 0
1904
+ );
1492
1905
  const event = {
1493
1906
  eventId: `agent_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
1494
1907
  sessionId,
@@ -1554,15 +1967,18 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1554
1967
  { status }
1555
1968
  );
1556
1969
  response2.headers.set("x-agentshield-detected", "true");
1557
- response2.headers.set("x-agentshield-confidence", String(Math.round(finalConfidence * 100)));
1970
+ response2.headers.set(
1971
+ "x-agentshield-confidence",
1972
+ String(Math.round(finalConfidence * 100))
1973
+ );
1558
1974
  response2.headers.set("x-agentshield-agent", result.detectedAgent?.name || "Unknown");
1559
1975
  response2.headers.set("x-agentshield-verification", verificationMethod);
1560
1976
  return response2;
1561
1977
  }
1562
- case "log":
1978
+ case "log": {
1563
1979
  const isInteresting = finalConfidence >= 0.9 || result.detectedAgent?.name?.toLowerCase().includes("chatgpt") || result.detectedAgent?.name?.toLowerCase().includes("perplexity") || verificationMethod === "signature";
1564
- if (isInteresting) {
1565
- console.log(`[AgentShield] \u{1F916} AI Agent detected (${verificationMethod}):`, {
1980
+ if (isInteresting && process.env.NODE_ENV !== "production") {
1981
+ console.debug(`[AgentShield] \u{1F916} AI Agent detected (${verificationMethod}):`, {
1566
1982
  agent: result.detectedAgent?.name,
1567
1983
  confidence: `${(finalConfidence * 100).toFixed(0)}%`,
1568
1984
  path: pathWithQuery,
@@ -1570,6 +1986,7 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1570
1986
  });
1571
1987
  }
1572
1988
  break;
1989
+ }
1573
1990
  }
1574
1991
  }
1575
1992
  const response = server.NextResponse.next();
@@ -1588,6 +2005,294 @@ function createEnhancedAgentShieldMiddleware(config = {}) {
1588
2005
  };
1589
2006
  }
1590
2007
 
2008
+ // src/api-client.ts
2009
+ var DEFAULT_BASE_URL = "https://api.agentshield.ai";
2010
+ var DEFAULT_TIMEOUT = 5e3;
2011
+ var AgentShieldClient = class {
2012
+ apiKey;
2013
+ baseUrl;
2014
+ timeout;
2015
+ debug;
2016
+ constructor(config) {
2017
+ if (!config.apiKey) {
2018
+ throw new Error("AgentShield API key is required");
2019
+ }
2020
+ this.apiKey = config.apiKey;
2021
+ this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
2022
+ this.timeout = config.timeout || DEFAULT_TIMEOUT;
2023
+ this.debug = config.debug || false;
2024
+ }
2025
+ /**
2026
+ * Call the enforce API to check if a request should be allowed
2027
+ */
2028
+ async enforce(input) {
2029
+ const startTime = Date.now();
2030
+ try {
2031
+ const controller = new AbortController();
2032
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2033
+ try {
2034
+ const response = await fetch(`${this.baseUrl}/api/v1/enforce`, {
2035
+ method: "POST",
2036
+ headers: {
2037
+ "Content-Type": "application/json",
2038
+ Authorization: `Bearer ${this.apiKey}`,
2039
+ "X-Request-ID": input.requestId || crypto.randomUUID()
2040
+ },
2041
+ body: JSON.stringify(input),
2042
+ signal: controller.signal
2043
+ });
2044
+ clearTimeout(timeoutId);
2045
+ const data = await response.json();
2046
+ if (this.debug) {
2047
+ console.log("[AgentShield] Enforce response:", {
2048
+ status: response.status,
2049
+ action: data.data?.decision.action,
2050
+ processingTimeMs: Date.now() - startTime
2051
+ });
2052
+ }
2053
+ if (!response.ok) {
2054
+ return {
2055
+ success: false,
2056
+ error: {
2057
+ code: `HTTP_${response.status}`,
2058
+ message: data.error?.message || `HTTP error: ${response.status}`
2059
+ }
2060
+ };
2061
+ }
2062
+ return data;
2063
+ } catch (error) {
2064
+ clearTimeout(timeoutId);
2065
+ throw error;
2066
+ }
2067
+ } catch (error) {
2068
+ if (error instanceof Error && error.name === "AbortError") {
2069
+ if (this.debug) {
2070
+ console.warn("[AgentShield] Request timed out");
2071
+ }
2072
+ return {
2073
+ success: false,
2074
+ error: {
2075
+ code: "TIMEOUT",
2076
+ message: `Request timed out after ${this.timeout}ms`
2077
+ }
2078
+ };
2079
+ }
2080
+ if (this.debug) {
2081
+ console.error("[AgentShield] Request failed:", error);
2082
+ }
2083
+ return {
2084
+ success: false,
2085
+ error: {
2086
+ code: "NETWORK_ERROR",
2087
+ message: error instanceof Error ? error.message : "Network request failed"
2088
+ }
2089
+ };
2090
+ }
2091
+ }
2092
+ /**
2093
+ * Quick check - returns just the action without full response parsing
2094
+ * Useful for very fast middleware that just needs allow/block
2095
+ */
2096
+ async quickCheck(input) {
2097
+ const result = await this.enforce(input);
2098
+ if (!result.success || !result.data) {
2099
+ return {
2100
+ action: "allow",
2101
+ error: result.error?.message
2102
+ };
2103
+ }
2104
+ return {
2105
+ action: result.data.decision.action
2106
+ };
2107
+ }
2108
+ };
2109
+ var clientInstance = null;
2110
+ function getAgentShieldClient(config) {
2111
+ if (!clientInstance) {
2112
+ const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;
2113
+ if (!apiKey) {
2114
+ throw new Error(
2115
+ "AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config."
2116
+ );
2117
+ }
2118
+ clientInstance = new AgentShieldClient({
2119
+ apiKey,
2120
+ baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,
2121
+ timeout: config?.timeout,
2122
+ debug: config?.debug || process.env.AGENTSHIELD_DEBUG === "true"
2123
+ });
2124
+ }
2125
+ return clientInstance;
2126
+ }
2127
+ function resetAgentShieldClient() {
2128
+ clientInstance = null;
2129
+ }
2130
+
2131
+ // src/api-middleware.ts
2132
+ function matchPath(path, pattern) {
2133
+ if (pattern === path) return true;
2134
+ if (pattern.includes("*")) {
2135
+ const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
2136
+ return new RegExp(`^${regexPattern}$`).test(path);
2137
+ }
2138
+ if (pattern.endsWith("/")) {
2139
+ return path.startsWith(pattern) || path === pattern.slice(0, -1);
2140
+ }
2141
+ return path.startsWith(pattern);
2142
+ }
2143
+ function shouldSkipPath(path, skipPaths) {
2144
+ return skipPaths.some((pattern) => matchPath(path, pattern));
2145
+ }
2146
+ function shouldIncludePath(path, includePaths) {
2147
+ if (!includePaths || includePaths.length === 0) return true;
2148
+ return includePaths.some((pattern) => matchPath(path, pattern));
2149
+ }
2150
+ function buildBlockedResponse(decision, config) {
2151
+ const status = config.blockedResponse?.status ?? 403;
2152
+ const message = config.blockedResponse?.message ?? decision.message ?? "Access denied";
2153
+ const response = server.NextResponse.json(
2154
+ {
2155
+ error: message,
2156
+ code: "AGENT_BLOCKED",
2157
+ reason: decision.reason,
2158
+ agentType: decision.agentType
2159
+ },
2160
+ { status }
2161
+ );
2162
+ if (config.blockedResponse?.headers) {
2163
+ for (const [key, value] of Object.entries(config.blockedResponse.headers)) {
2164
+ response.headers.set(key, value);
2165
+ }
2166
+ }
2167
+ response.headers.set("X-AgentShield-Action", decision.action);
2168
+ response.headers.set("X-AgentShield-Reason", decision.reason);
2169
+ return response;
2170
+ }
2171
+ function buildRedirectResponse(request, decision, config) {
2172
+ const redirectUrl = config.redirectUrl || decision.redirectUrl || "/blocked";
2173
+ const url = new URL(redirectUrl, request.url);
2174
+ url.searchParams.set("reason", decision.reason);
2175
+ if (decision.agentType) {
2176
+ url.searchParams.set("agent", decision.agentType);
2177
+ }
2178
+ return server.NextResponse.redirect(url);
2179
+ }
2180
+ function withAgentShield(config = {}) {
2181
+ let client = null;
2182
+ const getClient = () => {
2183
+ if (!client) {
2184
+ client = getAgentShieldClient({
2185
+ apiKey: config.apiKey,
2186
+ baseUrl: config.apiUrl,
2187
+ timeout: config.timeout,
2188
+ debug: config.debug
2189
+ });
2190
+ }
2191
+ return client;
2192
+ };
2193
+ const defaultSkipPaths = [
2194
+ "/_next/static/*",
2195
+ "/_next/image/*",
2196
+ "/favicon.ico",
2197
+ "/robots.txt",
2198
+ "/sitemap.xml"
2199
+ ];
2200
+ const skipPaths = [...defaultSkipPaths, ...config.skipPaths || []];
2201
+ const failOpen = config.failOpen ?? true;
2202
+ return async function middleware(request) {
2203
+ const path = request.nextUrl.pathname;
2204
+ const startTime = Date.now();
2205
+ if (shouldSkipPath(path, skipPaths)) {
2206
+ return server.NextResponse.next();
2207
+ }
2208
+ if (!shouldIncludePath(path, config.includePaths)) {
2209
+ return server.NextResponse.next();
2210
+ }
2211
+ try {
2212
+ const result = await getClient().enforce({
2213
+ headers: Object.fromEntries(request.headers.entries()),
2214
+ userAgent: request.headers.get("user-agent") || void 0,
2215
+ ipAddress: request.ip || request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip") || void 0,
2216
+ path,
2217
+ url: request.url,
2218
+ method: request.method,
2219
+ requestId: request.headers.get("x-request-id") || void 0,
2220
+ options: {
2221
+ includeDetectionResult: config.debug
2222
+ }
2223
+ });
2224
+ if (!result.success || !result.data) {
2225
+ if (config.debug) {
2226
+ console.warn("[AgentShield] API error:", result.error);
2227
+ }
2228
+ if (failOpen) {
2229
+ return server.NextResponse.next();
2230
+ }
2231
+ return server.NextResponse.json(
2232
+ { error: "Security check failed", code: "API_ERROR" },
2233
+ { status: 503 }
2234
+ );
2235
+ }
2236
+ const decision = result.data.decision;
2237
+ if (config.debug) {
2238
+ console.log("[AgentShield] Decision:", {
2239
+ path,
2240
+ action: decision.action,
2241
+ isAgent: decision.isAgent,
2242
+ confidence: decision.confidence,
2243
+ agentName: decision.agentName,
2244
+ processingTimeMs: Date.now() - startTime
2245
+ });
2246
+ }
2247
+ if (decision.isAgent && config.onAgentDetected) {
2248
+ await config.onAgentDetected(request, decision);
2249
+ }
2250
+ switch (decision.action) {
2251
+ case "block": {
2252
+ if (config.customBlockedResponse) {
2253
+ return config.customBlockedResponse(request, decision);
2254
+ }
2255
+ if (config.onBlock === "redirect") {
2256
+ return buildRedirectResponse(request, decision, config);
2257
+ }
2258
+ return buildBlockedResponse(decision, config);
2259
+ }
2260
+ case "redirect": {
2261
+ return buildRedirectResponse(request, decision, config);
2262
+ }
2263
+ case "challenge": {
2264
+ return buildRedirectResponse(request, decision, config);
2265
+ }
2266
+ case "log":
2267
+ case "allow":
2268
+ default: {
2269
+ const response = server.NextResponse.next();
2270
+ if (decision.isAgent) {
2271
+ response.headers.set("X-AgentShield-Detected", "true");
2272
+ response.headers.set("X-AgentShield-Confidence", decision.confidence.toString());
2273
+ if (decision.agentName) {
2274
+ response.headers.set("X-AgentShield-Agent", decision.agentName);
2275
+ }
2276
+ }
2277
+ return response;
2278
+ }
2279
+ }
2280
+ } catch (error) {
2281
+ if (config.debug) {
2282
+ console.error("[AgentShield] Middleware error:", error);
2283
+ }
2284
+ if (failOpen) {
2285
+ return server.NextResponse.next();
2286
+ }
2287
+ return server.NextResponse.json(
2288
+ { error: "Security check failed", code: "MIDDLEWARE_ERROR" },
2289
+ { status: 503 }
2290
+ );
2291
+ }
2292
+ };
2293
+ }
2294
+ var agentShieldMiddleware = withAgentShield();
2295
+
1591
2296
  // src/index.ts
1592
2297
  var VERSION = "0.1.0";
1593
2298
  /**
@@ -1596,12 +2301,17 @@ var VERSION = "0.1.0";
1596
2301
  * @license MIT OR Apache-2.0
1597
2302
  */
1598
2303
 
2304
+ exports.AgentShieldClient = AgentShieldClient;
1599
2305
  exports.EdgeSessionTracker = EdgeSessionTracker;
1600
2306
  exports.StatelessSessionChecker = StatelessSessionChecker;
1601
2307
  exports.VERSION = VERSION;
2308
+ exports.agentShieldMiddleware = agentShieldMiddleware;
1602
2309
  exports.createAgentShieldMiddleware = createAgentShieldMiddleware2;
1603
2310
  exports.createAgentShieldMiddlewareBase = createAgentShieldMiddleware;
1604
2311
  exports.createEnhancedAgentShieldMiddleware = createEnhancedAgentShieldMiddleware;
1605
2312
  exports.createMiddleware = createAgentShieldMiddleware2;
2313
+ exports.getAgentShieldClient = getAgentShieldClient;
2314
+ exports.resetAgentShieldClient = resetAgentShieldClient;
2315
+ exports.withAgentShield = withAgentShield;
1606
2316
  //# sourceMappingURL=index.js.map
1607
2317
  //# sourceMappingURL=index.js.map