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