@kya-os/agentshield-nextjs 0.2.13 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/api-client.d.mts +6 -1
  2. package/dist/api-client.d.ts +6 -1
  3. package/dist/api-client.js.map +1 -1
  4. package/dist/api-client.mjs.map +1 -1
  5. package/dist/api-middleware.d.mts +13 -0
  6. package/dist/api-middleware.d.ts +13 -0
  7. package/dist/api-middleware.js +149 -19
  8. package/dist/api-middleware.js.map +1 -1
  9. package/dist/api-middleware.mjs +149 -19
  10. package/dist/api-middleware.mjs.map +1 -1
  11. package/dist/create-middleware.js +565 -487
  12. package/dist/create-middleware.js.map +1 -1
  13. package/dist/create-middleware.mjs +565 -487
  14. package/dist/create-middleware.mjs.map +1 -1
  15. package/dist/edge/index.js +69 -46
  16. package/dist/edge/index.js.map +1 -1
  17. package/dist/edge/index.mjs +69 -46
  18. package/dist/edge/index.mjs.map +1 -1
  19. package/dist/edge-detector-wrapper.js +9 -1
  20. package/dist/edge-detector-wrapper.js.map +1 -1
  21. package/dist/edge-detector-wrapper.mjs +9 -1
  22. package/dist/edge-detector-wrapper.mjs.map +1 -1
  23. package/dist/edge-runtime-loader.d.mts +1 -0
  24. package/dist/edge-runtime-loader.d.ts +1 -0
  25. package/dist/edge-runtime-loader.js +19 -3
  26. package/dist/edge-runtime-loader.js.map +1 -1
  27. package/dist/edge-runtime-loader.mjs +19 -3
  28. package/dist/edge-runtime-loader.mjs.map +1 -1
  29. package/dist/edge-wasm-middleware.d.mts +1 -0
  30. package/dist/edge-wasm-middleware.d.ts +1 -0
  31. package/dist/edge-wasm-middleware.js +10 -2
  32. package/dist/edge-wasm-middleware.js.map +1 -1
  33. package/dist/edge-wasm-middleware.mjs +11 -3
  34. package/dist/edge-wasm-middleware.mjs.map +1 -1
  35. package/dist/enhanced-middleware.js +48 -20
  36. package/dist/enhanced-middleware.js.map +1 -1
  37. package/dist/enhanced-middleware.mjs +49 -21
  38. package/dist/enhanced-middleware.mjs.map +1 -1
  39. package/dist/index.d.mts +1 -1
  40. package/dist/index.d.ts +1 -1
  41. package/dist/index.js +263 -102
  42. package/dist/index.js.map +1 -1
  43. package/dist/index.mjs +264 -103
  44. package/dist/index.mjs.map +1 -1
  45. package/dist/middleware.js +565 -487
  46. package/dist/middleware.js.map +1 -1
  47. package/dist/middleware.mjs +565 -487
  48. package/dist/middleware.mjs.map +1 -1
  49. package/dist/policy.d.mts +16 -4
  50. package/dist/policy.d.ts +16 -4
  51. package/dist/policy.js +14 -10
  52. package/dist/policy.js.map +1 -1
  53. package/dist/policy.mjs +14 -10
  54. package/dist/policy.mjs.map +1 -1
  55. package/dist/session-tracker.js +13 -19
  56. package/dist/session-tracker.js.map +1 -1
  57. package/dist/session-tracker.mjs +13 -19
  58. package/dist/session-tracker.mjs.map +1 -1
  59. package/dist/signature-verifier.js +9 -1
  60. package/dist/signature-verifier.js.map +1 -1
  61. package/dist/signature-verifier.mjs +9 -1
  62. package/dist/signature-verifier.mjs.map +1 -1
  63. package/dist/wasm-middleware.d.mts +1 -0
  64. package/dist/wasm-middleware.d.ts +1 -0
  65. package/dist/wasm-middleware.js +15 -15
  66. package/dist/wasm-middleware.js.map +1 -1
  67. package/dist/wasm-middleware.mjs +15 -15
  68. package/dist/wasm-middleware.mjs.map +1 -1
  69. package/package.json +3 -3
  70. package/wasm/agentshield_wasm.d.ts +379 -21
  71. package/wasm/agentshield_wasm.js +1409 -506
  72. package/wasm/agentshield_wasm_bg.wasm +0 -0
  73. package/dist/.tsbuildinfo +0 -1
@@ -1,6 +1,25 @@
1
1
  'use strict';
2
2
 
3
3
  var server = require('next/server');
4
+ var agentshieldShared = require('@kya-os/agentshield-shared');
5
+
6
+ // src/edge/index.ts
7
+
8
+ // src/utils.ts
9
+ function getClientIp(request) {
10
+ const forwardedFor = request.headers.get("x-forwarded-for");
11
+ if (forwardedFor) {
12
+ const ip = forwardedFor.split(",")[0]?.trim();
13
+ if (ip) return ip;
14
+ }
15
+ const realIp = request.headers.get("x-real-ip");
16
+ if (realIp) return realIp;
17
+ const cfIp = request.headers.get("cf-connecting-ip");
18
+ if (cfIp) return cfIp;
19
+ const clientIp = request.headers.get("x-client-ip");
20
+ if (clientIp) return clientIp;
21
+ return void 0;
22
+ }
4
23
 
5
24
  // src/edge/index.ts
6
25
  async function patternDetect(input) {
@@ -82,6 +101,7 @@ async function createDetector(config) {
82
101
  const result = await wasmDetector.detect(input);
83
102
  return {
84
103
  isAgent: result.isAgent,
104
+ isAiCrawler: result.isAiCrawler,
85
105
  confidence: result.confidence,
86
106
  detectionClass: result.detectionClass,
87
107
  detectedAgent: result.detectedAgent,
@@ -149,11 +169,9 @@ function createEdgeMiddleware(config = {}) {
149
169
  return server.NextResponse.next();
150
170
  }
151
171
  const url = new URL(request.url);
152
- const xForwardedFor = request.headers.get("x-forwarded-for");
153
- const clientIp = xForwardedFor?.split(",")[0]?.trim();
154
172
  const input = {
155
173
  userAgent: request.headers.get("user-agent") || void 0,
156
- ipAddress: request.ip || clientIp || void 0,
174
+ ipAddress: getClientIp(request),
157
175
  headers: Object.fromEntries(request.headers.entries()),
158
176
  url: url.pathname + url.search,
159
177
  method: request.method
@@ -189,56 +207,61 @@ function createEdgeMiddleware(config = {}) {
189
207
  }
190
208
  );
191
209
  }
192
- if (result.shouldBlock !== false && result.isAgent && result.confidence >= confidenceThreshold) {
193
- if (onDetection) {
194
- const customResponse = await onDetection(request, result);
195
- if (customResponse) {
196
- return customResponse;
197
- }
198
- }
199
- switch (onAgentDetected) {
200
- case "block": {
201
- const response2 = server.NextResponse.json(
202
- {
203
- error: blockedResponse.message,
204
- detected: true,
205
- confidence: result.confidence,
206
- timestamp: result.timestamp
207
- },
208
- { status: blockedResponse.status }
209
- );
210
- if (blockedResponse.headers) {
211
- Object.entries(blockedResponse.headers).forEach(([key, value]) => {
212
- response2.headers.set(key, value);
213
- });
210
+ if (result.shouldBlock !== false) {
211
+ const decision = agentshieldShared.evaluateEnforcement(result, {
212
+ confidenceThreshold,
213
+ defaultAction: onAgentDetected
214
+ });
215
+ if (decision.shouldNotify) {
216
+ if (onDetection) {
217
+ const customResponse = await onDetection(request, result);
218
+ if (customResponse) {
219
+ return customResponse;
214
220
  }
215
- return response2;
216
221
  }
217
- case "redirect":
218
- return server.NextResponse.redirect(new URL(redirectUrl, request.url));
219
- case "rewrite":
220
- return server.NextResponse.rewrite(new URL(rewriteUrl, request.url));
221
- case "log":
222
- if (debug || process.env.NODE_ENV !== "production") {
223
- console.debug("AgentShield: Agent detected", {
224
- ipAddress: input.ipAddress,
225
- userAgent: input.userAgent,
226
- confidence: result.confidence,
227
- agent: result.detectedAgent?.name,
228
- pathname: request.nextUrl.pathname
229
- });
222
+ switch (decision.action) {
223
+ case "block": {
224
+ const response2 = server.NextResponse.json(
225
+ {
226
+ error: blockedResponse.message,
227
+ detected: true,
228
+ confidence: result.confidence,
229
+ timestamp: result.timestamp
230
+ },
231
+ { status: blockedResponse.status }
232
+ );
233
+ if (blockedResponse.headers) {
234
+ Object.entries(blockedResponse.headers).forEach(([key, value]) => {
235
+ response2.headers.set(key, value);
236
+ });
237
+ }
238
+ return response2;
230
239
  }
231
- break;
232
- case "allow":
233
- default:
234
- break;
240
+ case "redirect":
241
+ return server.NextResponse.redirect(new URL(redirectUrl, request.url));
242
+ case "rewrite":
243
+ return server.NextResponse.rewrite(new URL(rewriteUrl, request.url));
244
+ case "log":
245
+ if (debug || process.env.NODE_ENV !== "production") {
246
+ console.debug("AgentShield: Agent detected", {
247
+ ipAddress: input.ipAddress,
248
+ userAgent: input.userAgent,
249
+ confidence: result.confidence,
250
+ agent: result.detectedAgent?.name,
251
+ pathname: request.nextUrl.pathname
252
+ });
253
+ }
254
+ break;
255
+ default:
256
+ break;
257
+ }
235
258
  }
236
259
  }
237
260
  const response = server.NextResponse.next();
238
- response.headers.set("x-agentshield-detected", result.isAgent.toString());
239
- response.headers.set("x-agentshield-confidence", result.confidence.toString());
261
+ response.headers.set("kya-detected", result.isAgent.toString());
262
+ response.headers.set("kya-confidence", result.confidence.toString());
240
263
  if (result.detectedAgent?.name) {
241
- response.headers.set("x-agentshield-agent", result.detectedAgent.name);
264
+ response.headers.set("kya-agent", result.detectedAgent.name);
242
265
  }
243
266
  return response;
244
267
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/edge/index.ts"],"names":["NextResponse","response"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Edge Runtime Middleware with WASM Support\n *\n * This module provides Next.js middleware that uses the unified WASM runtime.\n * For Edge Runtime, consumers must provide a pre-compiled WASM module via static import:\n *\n * ```typescript\n * // In your middleware.ts\n * import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module';\n * import { createEdgeMiddleware } from '@kya-os/agentshield-nextjs/edge';\n *\n * export const middleware = createEdgeMiddleware({\n * wasmModule,\n * apiKey: process.env.AGENTSHIELD_API_KEY,\n * onAgentDetected: 'block',\n * });\n * ```\n *\n * When an API key is provided, the middleware will:\n * 1. Load customer policies (deny lists, allow lists, thresholds) from AgentShield API\n * 2. Automatically block agents that match deny list entries\n * 3. Cache policies for 5 minutes with background refresh\n */\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport type { DetectionResult } from '@kya-os/agentshield-shared';\n\n/**\n * Edge middleware configuration\n */\nexport interface EdgeMiddlewareConfig {\n /**\n * Pre-compiled WebAssembly.Module for Edge Runtime\n * Use static import: `import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module'`\n */\n wasmModule?: WebAssembly.Module;\n\n /**\n * API key for loading customer policies from AgentShield dashboard\n * When provided, enables policy enforcement (deny lists, allow lists, thresholds)\n */\n apiKey?: string;\n\n /**\n * Custom URL for the policy API\n * @default 'https://api.agentshield.io'\n */\n policyApiUrl?: string;\n\n /**\n * Action to take when an agent is detected\n * @default 'log'\n */\n onAgentDetected?: 'block' | 'redirect' | 'rewrite' | 'log' | 'allow';\n\n /**\n * Custom handler called when an agent is detected\n * Return a NextResponse to override default behavior\n */\n onDetection?: (\n request: NextRequest,\n result: DetectionResult\n ) => Promise<NextResponse | void> | NextResponse | void;\n\n /**\n * Paths to skip detection (can be string prefixes or RegExp)\n */\n skipPaths?: (string | RegExp)[];\n\n /**\n * Minimum confidence threshold (0-100 scale) for considering a request as agent traffic\n * @default 70\n */\n confidenceThreshold?: number;\n\n /**\n * Response to send when blocking agents\n */\n blockedResponse?: {\n status?: number;\n message?: string;\n headers?: Record<string, string>;\n };\n\n /**\n * URL to redirect to when redirecting agents\n */\n redirectUrl?: string;\n\n /**\n * URL to rewrite to when rewriting agent requests\n */\n rewriteUrl?: string;\n\n /**\n * Enable debug logging\n */\n debug?: boolean;\n}\n\n/**\n * Detection input extracted from request\n */\ninterface DetectionInput {\n userAgent?: string;\n ipAddress?: string;\n headers: Record<string, string>;\n url?: string;\n method?: string;\n}\n\n/**\n * Simple edge-safe detector that uses pattern matching\n * Falls back to this when WASM is not available\n */\nasync function patternDetect(input: DetectionInput): Promise<DetectionResult> {\n const userAgent = input.userAgent?.toLowerCase() || '';\n const reasons: string[] = [];\n let confidence = 0;\n let detectedAgent: { type: string; name: string } | undefined;\n\n // AI agent patterns with confidence scores (0-100 scale)\n const patterns = [\n { pattern: /chatgpt-user/i, name: 'ChatGPT', confidence: 95 },\n { pattern: /claude-web/i, name: 'Claude', confidence: 95 },\n { pattern: /claude-?bot/i, name: 'ClaudeBot', confidence: 95 },\n { pattern: /anthropic/i, name: 'Claude', confidence: 90 },\n { pattern: /gptbot/i, name: 'GPTBot', confidence: 90 },\n { pattern: /perplexity/i, name: 'Perplexity', confidence: 90 },\n { pattern: /openai/i, name: 'OpenAI', confidence: 85 },\n { pattern: /copilot/i, name: 'Copilot', confidence: 85 },\n { pattern: /gemini/i, name: 'Gemini', confidence: 85 },\n { pattern: /bard/i, name: 'Bard', confidence: 85 },\n // HTTP client libraries (often used by AI agents/scrapers)\n { pattern: /^axios\\//i, name: 'HTTP Client (axios)', confidence: 75 },\n { pattern: /^node-fetch\\//i, name: 'HTTP Client (node-fetch)', confidence: 75 },\n { pattern: /^got\\//i, name: 'HTTP Client (got)', confidence: 75 },\n { pattern: /^python-requests\\//i, name: 'HTTP Client (requests)', confidence: 75 },\n { pattern: /^python-urllib/i, name: 'HTTP Client (urllib)', confidence: 75 },\n { pattern: /^curl\\//i, name: 'HTTP Client (curl)', confidence: 60 },\n { pattern: /^wget\\//i, name: 'HTTP Client (wget)', confidence: 60 },\n { pattern: /^httpie\\//i, name: 'HTTP Client (httpie)', confidence: 70 },\n { pattern: /^undici\\//i, name: 'HTTP Client (undici)', confidence: 75 },\n ];\n\n for (const { pattern, name, confidence: patternConfidence } of patterns) {\n if (pattern.test(userAgent)) {\n confidence = patternConfidence;\n detectedAgent = { type: name.toLowerCase(), name };\n reasons.push(`known_pattern:${name.toLowerCase()}`);\n break;\n }\n }\n\n // Check for AI-specific headers\n const aiHeaders = ['x-openai-', 'x-anthropic-', 'x-ai-', 'signature-agent'];\n for (const [key] of Object.entries(input.headers)) {\n if (aiHeaders.some((prefix) => key.toLowerCase().startsWith(prefix))) {\n confidence = Math.max(confidence, 45);\n reasons.push(`ai_headers:${key}`);\n break;\n }\n }\n\n return {\n isAgent: confidence > 30,\n confidence,\n detectionClass:\n confidence > 30 && detectedAgent\n ? { type: 'AiAgent' as const, agentType: detectedAgent.name }\n : confidence > 30\n ? { type: 'Unknown' as const }\n : { type: 'Human' as const },\n signals: [],\n ...(detectedAgent && { detectedAgent }),\n reasons,\n verificationMethod: 'pattern' as const,\n forgeabilityRisk: confidence > 80 ? 'medium' : 'high',\n timestamp: new Date(),\n };\n}\n\n/**\n * Extended detection result with policy enforcement\n */\ninterface PolicyEnforcedResult extends DetectionResult {\n shouldBlock?: boolean;\n blockReason?: 'deny_list' | 'not_in_allow_list' | 'threshold';\n}\n\n/**\n * Create detector using WASM runtime or fallback\n * When apiKey is provided, policy enforcement is enabled\n */\nasync function createDetector(config: EdgeMiddlewareConfig) {\n let detector: {\n detect: (input: DetectionInput) => Promise<PolicyEnforcedResult>;\n isReady: () => boolean;\n };\n\n // Try to use wasm-runtime if WASM module is provided\n if (config.wasmModule) {\n try {\n const { StaticWasmLoader, WasmDetector, PolicyLoader } = await import(\n '@kya-os/agentshield-wasm-runtime/edge'\n );\n const loader = new StaticWasmLoader(config.wasmModule);\n\n // Create policy loader if API key is provided\n const policyLoader = config.apiKey\n ? new PolicyLoader({\n apiUrl: config.policyApiUrl,\n })\n : undefined;\n\n // Create detector with policy support\n const wasmDetector = new WasmDetector(loader, policyLoader, {\n apiKey: config.apiKey,\n debug: config.debug,\n });\n\n await wasmDetector.ensureReady();\n\n if (config.debug) {\n console.debug('[AgentShield] WASM detector initialized in Edge Runtime', {\n policyEnabled: !!config.apiKey,\n });\n }\n\n detector = {\n detect: async (input: DetectionInput): Promise<PolicyEnforcedResult> => {\n const result = await wasmDetector.detect(input);\n // Convert IDetectionResult to DetectionResult by adding required fields\n // Type assertion needed due to different DetectionClass definitions\n return {\n isAgent: result.isAgent,\n confidence: result.confidence,\n detectionClass: result.detectionClass as DetectionResult['detectionClass'],\n detectedAgent: result.detectedAgent,\n verificationMethod: result.verificationMethod,\n forgeabilityRisk: result.forgeabilityRisk,\n reasons: result.reasons,\n timestamp: result.timestamp,\n signals: [], // Required by DetectionResult, empty for WASM results\n shouldBlock: result.shouldBlock,\n blockReason: result.blockReason as PolicyEnforcedResult['blockReason'],\n };\n },\n isReady: () => wasmDetector.isReady(),\n };\n } catch (error) {\n if (config.debug) {\n console.warn('[AgentShield] WASM runtime not available, using pattern detection:', error);\n }\n detector = {\n detect: patternDetect,\n isReady: () => true,\n };\n }\n } else {\n // Use pattern-based detection\n if (config.debug) {\n console.debug('[AgentShield] No WASM module provided, using pattern detection');\n }\n detector = {\n detect: patternDetect,\n isReady: () => true,\n };\n }\n\n return detector;\n}\n\n/**\n * Create Edge middleware with WASM support\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module';\n * import { createEdgeMiddleware } from '@kya-os/agentshield-nextjs/edge';\n *\n * export const middleware = createEdgeMiddleware({\n * wasmModule,\n * onAgentDetected: 'block',\n * confidenceThreshold: 70,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\n * };\n * ```\n */\nexport function createEdgeMiddleware(config: EdgeMiddlewareConfig = {}) {\n let detectorPromise: Promise<Awaited<ReturnType<typeof createDetector>>> | null = null;\n\n const {\n onAgentDetected = 'log',\n onDetection,\n skipPaths = [],\n confidenceThreshold = 70,\n blockedResponse = {\n status: 403,\n message: 'Access denied: Automated agent detected',\n headers: { 'Content-Type': 'application/json' },\n },\n redirectUrl = '/blocked',\n rewriteUrl = '/blocked',\n debug = false,\n } = config;\n\n return async (request: NextRequest): Promise<NextResponse> => {\n try {\n // Initialize detector lazily\n if (!detectorPromise) {\n detectorPromise = createDetector({ ...config, debug });\n }\n const detector = await detectorPromise;\n\n // Check if path should be skipped\n const shouldSkip = skipPaths.some((pattern) => {\n if (typeof pattern === 'string') {\n return request.nextUrl.pathname.startsWith(pattern);\n }\n return pattern.test(request.nextUrl.pathname);\n });\n\n if (shouldSkip) {\n return NextResponse.next();\n }\n\n // Extract request context\n const url = new URL(request.url);\n // Parse X-Forwarded-For header to get client IP (first in comma-separated list)\n const xForwardedFor = request.headers.get('x-forwarded-for');\n const clientIp = xForwardedFor?.split(',')[0]?.trim();\n const input: DetectionInput = {\n userAgent: request.headers.get('user-agent') || undefined,\n ipAddress: request.ip || clientIp || undefined,\n headers: Object.fromEntries(request.headers.entries()),\n url: url.pathname + url.search,\n method: request.method,\n };\n\n // Run detection\n const result = await detector.detect(input);\n\n // POLICY ENFORCEMENT: Check shouldBlock from policy rules (deny list, allow list, threshold)\n // This takes precedence over the general threshold check below\n if (result.shouldBlock) {\n if (debug) {\n console.debug('[AgentShield] Blocked by policy', {\n reason: result.blockReason,\n agent: result.detectedAgent?.name,\n confidence: result.confidence,\n pathname: request.nextUrl.pathname,\n });\n }\n\n // Call custom detection handler if provided\n if (onDetection) {\n const customResponse = await onDetection(request, result);\n if (customResponse) {\n return customResponse;\n }\n }\n\n // Return 403 with policy block reason\n return NextResponse.json(\n {\n error: 'Access denied by policy',\n reason: result.blockReason,\n detected: true,\n confidence: result.confidence,\n agent: result.detectedAgent?.name,\n timestamp: result.timestamp,\n },\n {\n status: 403,\n headers: { 'Content-Type': 'application/json' },\n }\n );\n }\n\n // Check if agent detected above threshold (when policy doesn't block)\n // Skip if shouldBlock === false (agent explicitly allowed by policy)\n if (\n result.shouldBlock !== false &&\n result.isAgent &&\n result.confidence >= confidenceThreshold\n ) {\n // Call custom detection handler if provided\n if (onDetection) {\n const customResponse = await onDetection(request, result);\n if (customResponse) {\n return customResponse;\n }\n }\n\n // Handle based on configuration\n switch (onAgentDetected) {\n case 'block': {\n const response = NextResponse.json(\n {\n error: blockedResponse.message,\n detected: true,\n confidence: result.confidence,\n timestamp: result.timestamp,\n },\n { status: blockedResponse.status }\n );\n\n if (blockedResponse.headers) {\n Object.entries(blockedResponse.headers).forEach(([key, value]) => {\n response.headers.set(key, value);\n });\n }\n\n return response;\n }\n\n case 'redirect':\n return NextResponse.redirect(new URL(redirectUrl, request.url));\n\n case 'rewrite':\n return NextResponse.rewrite(new URL(rewriteUrl, request.url));\n\n case 'log':\n if (debug || process.env.NODE_ENV !== 'production') {\n console.debug('AgentShield: Agent detected', {\n ipAddress: input.ipAddress,\n userAgent: input.userAgent,\n confidence: result.confidence,\n agent: result.detectedAgent?.name,\n pathname: request.nextUrl.pathname,\n });\n }\n break;\n\n case 'allow':\n default:\n break;\n }\n }\n\n // Continue with request\n const response = NextResponse.next();\n\n // Add detection headers\n response.headers.set('x-agentshield-detected', result.isAgent.toString());\n response.headers.set('x-agentshield-confidence', result.confidence.toString());\n if (result.detectedAgent?.name) {\n response.headers.set('x-agentshield-agent', result.detectedAgent.name);\n }\n\n return response;\n } catch (error) {\n console.error('AgentShield middleware error:', error);\n return NextResponse.next();\n }\n };\n}\n\n// Re-export types for convenience\nexport type { DetectionResult };\n"]}
1
+ {"version":3,"sources":["../../src/utils.ts","../../src/edge/index.ts"],"names":["NextResponse","evaluateEnforcement","response"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Utility functions for agentshield-nextjs\n */\n\nimport type { NextRequest } from 'next/server';\n\n/**\n * Extract client IP address from a NextRequest.\n * In Next.js 15+, the `ip` property was removed from NextRequest.\n * This function uses headers to determine the client IP.\n *\n * @param request - The NextRequest object\n * @returns The client IP address or undefined if not available\n */\nexport function getClientIp(request: NextRequest): string | undefined {\n // Check x-forwarded-for header (standard for proxies/load balancers)\n const forwardedFor = request.headers.get('x-forwarded-for');\n if (forwardedFor) {\n // Take the first IP in the chain (original client)\n const ip = forwardedFor.split(',')[0]?.trim();\n if (ip) return ip;\n }\n\n // Check x-real-ip header (commonly used by nginx)\n const realIp = request.headers.get('x-real-ip');\n if (realIp) return realIp;\n\n // Check cf-connecting-ip header (Cloudflare)\n const cfIp = request.headers.get('cf-connecting-ip');\n if (cfIp) return cfIp;\n\n // Check x-client-ip header (some proxies use this)\n const clientIp = request.headers.get('x-client-ip');\n if (clientIp) return clientIp;\n\n return undefined;\n}\n\n/**\n * Safely extract the hostname from a URL string.\n * Returns a friendly fallback when parsing fails so UX copy doesn't leak\n * \"undefined\" or similar to end users.\n */\nexport function safeHostname(url: string): string {\n try {\n return new URL(url).hostname;\n } catch {\n return 'this site';\n }\n}\n","/**\n * Edge Runtime Middleware with WASM Support\n *\n * This module provides Next.js middleware that uses the unified WASM runtime.\n * For Edge Runtime, consumers must provide a pre-compiled WASM module via static import:\n *\n * ```typescript\n * // In your middleware.ts\n * import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module';\n * import { createEdgeMiddleware } from '@kya-os/agentshield-nextjs/edge';\n *\n * export const middleware = createEdgeMiddleware({\n * wasmModule,\n * apiKey: process.env.AGENTSHIELD_API_KEY,\n * onAgentDetected: 'block',\n * });\n * ```\n *\n * When an API key is provided, the middleware will:\n * 1. Load customer policies (deny lists, allow lists, thresholds) from AgentShield API\n * 2. Automatically block agents that match deny list entries\n * 3. Cache policies for 5 minutes with background refresh\n */\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport type { DetectionResult } from '@kya-os/agentshield-shared';\nimport { evaluateEnforcement } from '@kya-os/agentshield-shared';\nimport { getClientIp } from '../utils';\n\n/**\n * Edge middleware configuration\n */\nexport interface EdgeMiddlewareConfig {\n /**\n * Pre-compiled WebAssembly.Module for Edge Runtime\n * Use static import: `import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module'`\n */\n wasmModule?: WebAssembly.Module;\n\n /**\n * API key for loading customer policies from AgentShield dashboard\n * When provided, enables policy enforcement (deny lists, allow lists, thresholds)\n */\n apiKey?: string;\n\n /**\n * Custom URL for the policy API\n * @default 'https://api.agentshield.io'\n */\n policyApiUrl?: string;\n\n /**\n * Action to take when an agent is detected\n * @default 'log'\n */\n onAgentDetected?: 'block' | 'redirect' | 'rewrite' | 'log' | 'allow';\n\n /**\n * Custom handler called when an agent is detected\n * Return a NextResponse to override default behavior\n */\n onDetection?: (\n request: NextRequest,\n result: DetectionResult\n ) => Promise<NextResponse | void> | NextResponse | void;\n\n /**\n * Paths to skip detection (can be string prefixes or RegExp)\n */\n skipPaths?: (string | RegExp)[];\n\n /**\n * Minimum confidence threshold (0-100 scale) for considering a request as agent traffic\n * @default 70\n */\n confidenceThreshold?: number;\n\n /**\n * Response to send when blocking agents\n */\n blockedResponse?: {\n status?: number;\n message?: string;\n headers?: Record<string, string>;\n };\n\n /**\n * URL to redirect to when redirecting agents\n */\n redirectUrl?: string;\n\n /**\n * URL to rewrite to when rewriting agent requests\n */\n rewriteUrl?: string;\n\n /**\n * Enable debug logging\n */\n debug?: boolean;\n}\n\n/**\n * Detection input extracted from request\n */\ninterface DetectionInput {\n userAgent?: string;\n ipAddress?: string;\n headers: Record<string, string>;\n url?: string;\n method?: string;\n}\n\n/**\n * Simple edge-safe detector that uses pattern matching\n * Falls back to this when WASM is not available\n */\nasync function patternDetect(input: DetectionInput): Promise<DetectionResult> {\n const userAgent = input.userAgent?.toLowerCase() || '';\n const reasons: string[] = [];\n let confidence = 0;\n let detectedAgent: { type: string; name: string } | undefined;\n\n // AI agent patterns with confidence scores (0-100 scale)\n const patterns = [\n { pattern: /chatgpt-user/i, name: 'ChatGPT', confidence: 95 },\n { pattern: /claude-web/i, name: 'Claude', confidence: 95 },\n { pattern: /claude-?bot/i, name: 'ClaudeBot', confidence: 95 },\n { pattern: /anthropic/i, name: 'Claude', confidence: 90 },\n { pattern: /gptbot/i, name: 'GPTBot', confidence: 90 },\n { pattern: /perplexity/i, name: 'Perplexity', confidence: 90 },\n { pattern: /openai/i, name: 'OpenAI', confidence: 85 },\n { pattern: /copilot/i, name: 'Copilot', confidence: 85 },\n { pattern: /gemini/i, name: 'Gemini', confidence: 85 },\n { pattern: /bard/i, name: 'Bard', confidence: 85 },\n // HTTP client libraries (often used by AI agents/scrapers)\n { pattern: /^axios\\//i, name: 'HTTP Client (axios)', confidence: 75 },\n { pattern: /^node-fetch\\//i, name: 'HTTP Client (node-fetch)', confidence: 75 },\n { pattern: /^got\\//i, name: 'HTTP Client (got)', confidence: 75 },\n { pattern: /^python-requests\\//i, name: 'HTTP Client (requests)', confidence: 75 },\n { pattern: /^python-urllib/i, name: 'HTTP Client (urllib)', confidence: 75 },\n { pattern: /^curl\\//i, name: 'HTTP Client (curl)', confidence: 60 },\n { pattern: /^wget\\//i, name: 'HTTP Client (wget)', confidence: 60 },\n { pattern: /^httpie\\//i, name: 'HTTP Client (httpie)', confidence: 70 },\n { pattern: /^undici\\//i, name: 'HTTP Client (undici)', confidence: 75 },\n ];\n\n for (const { pattern, name, confidence: patternConfidence } of patterns) {\n if (pattern.test(userAgent)) {\n confidence = patternConfidence;\n detectedAgent = { type: name.toLowerCase(), name };\n reasons.push(`known_pattern:${name.toLowerCase()}`);\n break;\n }\n }\n\n // Check for AI-specific headers\n const aiHeaders = ['x-openai-', 'x-anthropic-', 'x-ai-', 'signature-agent'];\n for (const [key] of Object.entries(input.headers)) {\n if (aiHeaders.some((prefix) => key.toLowerCase().startsWith(prefix))) {\n confidence = Math.max(confidence, 45);\n reasons.push(`ai_headers:${key}`);\n break;\n }\n }\n\n return {\n isAgent: confidence > 30,\n confidence,\n detectionClass:\n confidence > 30 && detectedAgent\n ? { type: 'AiAgent' as const, agentType: detectedAgent.name }\n : confidence > 30\n ? { type: 'Unknown' as const }\n : { type: 'Human' as const },\n signals: [],\n ...(detectedAgent && { detectedAgent }),\n reasons,\n verificationMethod: 'pattern' as const,\n forgeabilityRisk: confidence > 80 ? 'medium' : 'high',\n timestamp: new Date(),\n };\n}\n\n/**\n * Extended detection result with policy enforcement\n */\ninterface PolicyEnforcedResult extends DetectionResult {\n shouldBlock?: boolean;\n blockReason?: 'deny_list' | 'not_in_allow_list' | 'threshold';\n}\n\n/**\n * Create detector using WASM runtime or fallback\n * When apiKey is provided, policy enforcement is enabled\n */\nasync function createDetector(config: EdgeMiddlewareConfig) {\n let detector: {\n detect: (input: DetectionInput) => Promise<PolicyEnforcedResult>;\n isReady: () => boolean;\n };\n\n // Try to use wasm-runtime if WASM module is provided\n if (config.wasmModule) {\n try {\n const { StaticWasmLoader, WasmDetector, PolicyLoader } = await import(\n '@kya-os/agentshield-wasm-runtime/edge'\n );\n const loader = new StaticWasmLoader(config.wasmModule);\n\n // Create policy loader if API key is provided\n const policyLoader = config.apiKey\n ? new PolicyLoader({\n apiUrl: config.policyApiUrl,\n })\n : undefined;\n\n // Create detector with policy support\n const wasmDetector = new WasmDetector(loader, policyLoader, {\n apiKey: config.apiKey,\n debug: config.debug,\n });\n\n await wasmDetector.ensureReady();\n\n if (config.debug) {\n console.debug('[AgentShield] WASM detector initialized in Edge Runtime', {\n policyEnabled: !!config.apiKey,\n });\n }\n\n detector = {\n detect: async (input: DetectionInput): Promise<PolicyEnforcedResult> => {\n const result = await wasmDetector.detect(input);\n // Convert IDetectionResult to DetectionResult by adding required fields\n // Type assertion needed due to different DetectionClass definitions\n return {\n isAgent: result.isAgent,\n isAiCrawler: (result as unknown as Record<string, unknown>).isAiCrawler as\n | boolean\n | undefined,\n confidence: result.confidence,\n detectionClass: result.detectionClass as DetectionResult['detectionClass'],\n detectedAgent: result.detectedAgent,\n verificationMethod: result.verificationMethod,\n forgeabilityRisk: result.forgeabilityRisk,\n reasons: result.reasons,\n timestamp: result.timestamp,\n signals: [], // Required by DetectionResult, empty for WASM results\n shouldBlock: result.shouldBlock,\n blockReason: result.blockReason as PolicyEnforcedResult['blockReason'],\n };\n },\n isReady: () => wasmDetector.isReady(),\n };\n } catch (error) {\n if (config.debug) {\n console.warn('[AgentShield] WASM runtime not available, using pattern detection:', error);\n }\n detector = {\n detect: patternDetect,\n isReady: () => true,\n };\n }\n } else {\n // Use pattern-based detection\n if (config.debug) {\n console.debug('[AgentShield] No WASM module provided, using pattern detection');\n }\n detector = {\n detect: patternDetect,\n isReady: () => true,\n };\n }\n\n return detector;\n}\n\n/**\n * Create Edge middleware with WASM support\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module';\n * import { createEdgeMiddleware } from '@kya-os/agentshield-nextjs/edge';\n *\n * export const middleware = createEdgeMiddleware({\n * wasmModule,\n * onAgentDetected: 'block',\n * confidenceThreshold: 70,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\n * };\n * ```\n */\nexport function createEdgeMiddleware(config: EdgeMiddlewareConfig = {}) {\n let detectorPromise: Promise<Awaited<ReturnType<typeof createDetector>>> | null = null;\n\n const {\n onAgentDetected = 'log',\n onDetection,\n skipPaths = [],\n confidenceThreshold = 70,\n blockedResponse = {\n status: 403,\n message: 'Access denied: Automated agent detected',\n headers: { 'Content-Type': 'application/json' },\n },\n redirectUrl = '/blocked',\n rewriteUrl = '/blocked',\n debug = false,\n } = config;\n\n return async (request: NextRequest): Promise<NextResponse> => {\n try {\n // Initialize detector lazily\n if (!detectorPromise) {\n detectorPromise = createDetector({ ...config, debug });\n }\n const detector = await detectorPromise;\n\n // Check if path should be skipped\n const shouldSkip = skipPaths.some((pattern) => {\n if (typeof pattern === 'string') {\n return request.nextUrl.pathname.startsWith(pattern);\n }\n return pattern.test(request.nextUrl.pathname);\n });\n\n if (shouldSkip) {\n return NextResponse.next();\n }\n\n // Extract request context\n const url = new URL(request.url);\n const input: DetectionInput = {\n userAgent: request.headers.get('user-agent') || undefined,\n ipAddress: getClientIp(request),\n headers: Object.fromEntries(request.headers.entries()),\n url: url.pathname + url.search,\n method: request.method,\n };\n\n // Run detection\n const result = await detector.detect(input);\n\n // POLICY ENFORCEMENT: Check shouldBlock from policy rules (deny list, allow list, threshold)\n // This takes precedence over the general threshold check below\n if (result.shouldBlock) {\n if (debug) {\n console.debug('[AgentShield] Blocked by policy', {\n reason: result.blockReason,\n agent: result.detectedAgent?.name,\n confidence: result.confidence,\n pathname: request.nextUrl.pathname,\n });\n }\n\n // Call custom detection handler if provided\n if (onDetection) {\n const customResponse = await onDetection(request, result);\n if (customResponse) {\n return customResponse;\n }\n }\n\n // Return 403 with policy block reason\n return NextResponse.json(\n {\n error: 'Access denied by policy',\n reason: result.blockReason,\n detected: true,\n confidence: result.confidence,\n agent: result.detectedAgent?.name,\n timestamp: result.timestamp,\n },\n {\n status: 403,\n headers: { 'Content-Type': 'application/json' },\n }\n );\n }\n\n // Check if agent detected above threshold (when policy doesn't block)\n // Skip if shouldBlock === false (agent explicitly allowed by policy)\n if (result.shouldBlock !== false) {\n const decision = evaluateEnforcement(result, {\n confidenceThreshold,\n defaultAction: onAgentDetected,\n });\n\n if (decision.shouldNotify) {\n // Call custom detection handler if provided\n if (onDetection) {\n const customResponse = await onDetection(request, result);\n if (customResponse) {\n return customResponse;\n }\n }\n\n // Handle based on decision action\n switch (decision.action) {\n case 'block': {\n const response = NextResponse.json(\n {\n error: blockedResponse.message,\n detected: true,\n confidence: result.confidence,\n timestamp: result.timestamp,\n },\n { status: blockedResponse.status }\n );\n\n if (blockedResponse.headers) {\n Object.entries(blockedResponse.headers).forEach(([key, value]) => {\n response.headers.set(key, value);\n });\n }\n\n return response;\n }\n\n case 'redirect':\n return NextResponse.redirect(new URL(redirectUrl, request.url));\n\n case 'rewrite':\n return NextResponse.rewrite(new URL(rewriteUrl, request.url));\n\n case 'log':\n if (debug || process.env.NODE_ENV !== 'production') {\n console.debug('AgentShield: Agent detected', {\n ipAddress: input.ipAddress,\n userAgent: input.userAgent,\n confidence: result.confidence,\n agent: result.detectedAgent?.name,\n pathname: request.nextUrl.pathname,\n });\n }\n break;\n\n default:\n break;\n }\n }\n }\n\n // Continue with request\n const response = NextResponse.next();\n\n // Add detection headers\n response.headers.set('kya-detected', result.isAgent.toString());\n response.headers.set('kya-confidence', result.confidence.toString());\n if (result.detectedAgent?.name) {\n response.headers.set('kya-agent', result.detectedAgent.name);\n }\n\n return response;\n } catch (error) {\n console.error('AgentShield middleware error:', error);\n return NextResponse.next();\n }\n };\n}\n\n// Re-export types for convenience\nexport type { DetectionResult };\n"]}
@@ -1,4 +1,23 @@
1
1
  import { NextResponse } from 'next/server';
2
+ import { evaluateEnforcement } from '@kya-os/agentshield-shared';
3
+
4
+ // src/edge/index.ts
5
+
6
+ // src/utils.ts
7
+ function getClientIp(request) {
8
+ const forwardedFor = request.headers.get("x-forwarded-for");
9
+ if (forwardedFor) {
10
+ const ip = forwardedFor.split(",")[0]?.trim();
11
+ if (ip) return ip;
12
+ }
13
+ const realIp = request.headers.get("x-real-ip");
14
+ if (realIp) return realIp;
15
+ const cfIp = request.headers.get("cf-connecting-ip");
16
+ if (cfIp) return cfIp;
17
+ const clientIp = request.headers.get("x-client-ip");
18
+ if (clientIp) return clientIp;
19
+ return void 0;
20
+ }
2
21
 
3
22
  // src/edge/index.ts
4
23
  async function patternDetect(input) {
@@ -80,6 +99,7 @@ async function createDetector(config) {
80
99
  const result = await wasmDetector.detect(input);
81
100
  return {
82
101
  isAgent: result.isAgent,
102
+ isAiCrawler: result.isAiCrawler,
83
103
  confidence: result.confidence,
84
104
  detectionClass: result.detectionClass,
85
105
  detectedAgent: result.detectedAgent,
@@ -147,11 +167,9 @@ function createEdgeMiddleware(config = {}) {
147
167
  return NextResponse.next();
148
168
  }
149
169
  const url = new URL(request.url);
150
- const xForwardedFor = request.headers.get("x-forwarded-for");
151
- const clientIp = xForwardedFor?.split(",")[0]?.trim();
152
170
  const input = {
153
171
  userAgent: request.headers.get("user-agent") || void 0,
154
- ipAddress: request.ip || clientIp || void 0,
172
+ ipAddress: getClientIp(request),
155
173
  headers: Object.fromEntries(request.headers.entries()),
156
174
  url: url.pathname + url.search,
157
175
  method: request.method
@@ -187,56 +205,61 @@ function createEdgeMiddleware(config = {}) {
187
205
  }
188
206
  );
189
207
  }
190
- if (result.shouldBlock !== false && result.isAgent && result.confidence >= confidenceThreshold) {
191
- if (onDetection) {
192
- const customResponse = await onDetection(request, result);
193
- if (customResponse) {
194
- return customResponse;
195
- }
196
- }
197
- switch (onAgentDetected) {
198
- case "block": {
199
- const response2 = NextResponse.json(
200
- {
201
- error: blockedResponse.message,
202
- detected: true,
203
- confidence: result.confidence,
204
- timestamp: result.timestamp
205
- },
206
- { status: blockedResponse.status }
207
- );
208
- if (blockedResponse.headers) {
209
- Object.entries(blockedResponse.headers).forEach(([key, value]) => {
210
- response2.headers.set(key, value);
211
- });
208
+ if (result.shouldBlock !== false) {
209
+ const decision = evaluateEnforcement(result, {
210
+ confidenceThreshold,
211
+ defaultAction: onAgentDetected
212
+ });
213
+ if (decision.shouldNotify) {
214
+ if (onDetection) {
215
+ const customResponse = await onDetection(request, result);
216
+ if (customResponse) {
217
+ return customResponse;
212
218
  }
213
- return response2;
214
219
  }
215
- case "redirect":
216
- return NextResponse.redirect(new URL(redirectUrl, request.url));
217
- case "rewrite":
218
- return NextResponse.rewrite(new URL(rewriteUrl, request.url));
219
- case "log":
220
- if (debug || process.env.NODE_ENV !== "production") {
221
- console.debug("AgentShield: Agent detected", {
222
- ipAddress: input.ipAddress,
223
- userAgent: input.userAgent,
224
- confidence: result.confidence,
225
- agent: result.detectedAgent?.name,
226
- pathname: request.nextUrl.pathname
227
- });
220
+ switch (decision.action) {
221
+ case "block": {
222
+ const response2 = NextResponse.json(
223
+ {
224
+ error: blockedResponse.message,
225
+ detected: true,
226
+ confidence: result.confidence,
227
+ timestamp: result.timestamp
228
+ },
229
+ { status: blockedResponse.status }
230
+ );
231
+ if (blockedResponse.headers) {
232
+ Object.entries(blockedResponse.headers).forEach(([key, value]) => {
233
+ response2.headers.set(key, value);
234
+ });
235
+ }
236
+ return response2;
228
237
  }
229
- break;
230
- case "allow":
231
- default:
232
- break;
238
+ case "redirect":
239
+ return NextResponse.redirect(new URL(redirectUrl, request.url));
240
+ case "rewrite":
241
+ return NextResponse.rewrite(new URL(rewriteUrl, request.url));
242
+ case "log":
243
+ if (debug || process.env.NODE_ENV !== "production") {
244
+ console.debug("AgentShield: Agent detected", {
245
+ ipAddress: input.ipAddress,
246
+ userAgent: input.userAgent,
247
+ confidence: result.confidence,
248
+ agent: result.detectedAgent?.name,
249
+ pathname: request.nextUrl.pathname
250
+ });
251
+ }
252
+ break;
253
+ default:
254
+ break;
255
+ }
233
256
  }
234
257
  }
235
258
  const response = NextResponse.next();
236
- response.headers.set("x-agentshield-detected", result.isAgent.toString());
237
- response.headers.set("x-agentshield-confidence", result.confidence.toString());
259
+ response.headers.set("kya-detected", result.isAgent.toString());
260
+ response.headers.set("kya-confidence", result.confidence.toString());
238
261
  if (result.detectedAgent?.name) {
239
- response.headers.set("x-agentshield-agent", result.detectedAgent.name);
262
+ response.headers.set("kya-agent", result.detectedAgent.name);
240
263
  }
241
264
  return response;
242
265
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/edge/index.ts"],"names":["response"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Edge Runtime Middleware with WASM Support\n *\n * This module provides Next.js middleware that uses the unified WASM runtime.\n * For Edge Runtime, consumers must provide a pre-compiled WASM module via static import:\n *\n * ```typescript\n * // In your middleware.ts\n * import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module';\n * import { createEdgeMiddleware } from '@kya-os/agentshield-nextjs/edge';\n *\n * export const middleware = createEdgeMiddleware({\n * wasmModule,\n * apiKey: process.env.AGENTSHIELD_API_KEY,\n * onAgentDetected: 'block',\n * });\n * ```\n *\n * When an API key is provided, the middleware will:\n * 1. Load customer policies (deny lists, allow lists, thresholds) from AgentShield API\n * 2. Automatically block agents that match deny list entries\n * 3. Cache policies for 5 minutes with background refresh\n */\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport type { DetectionResult } from '@kya-os/agentshield-shared';\n\n/**\n * Edge middleware configuration\n */\nexport interface EdgeMiddlewareConfig {\n /**\n * Pre-compiled WebAssembly.Module for Edge Runtime\n * Use static import: `import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module'`\n */\n wasmModule?: WebAssembly.Module;\n\n /**\n * API key for loading customer policies from AgentShield dashboard\n * When provided, enables policy enforcement (deny lists, allow lists, thresholds)\n */\n apiKey?: string;\n\n /**\n * Custom URL for the policy API\n * @default 'https://api.agentshield.io'\n */\n policyApiUrl?: string;\n\n /**\n * Action to take when an agent is detected\n * @default 'log'\n */\n onAgentDetected?: 'block' | 'redirect' | 'rewrite' | 'log' | 'allow';\n\n /**\n * Custom handler called when an agent is detected\n * Return a NextResponse to override default behavior\n */\n onDetection?: (\n request: NextRequest,\n result: DetectionResult\n ) => Promise<NextResponse | void> | NextResponse | void;\n\n /**\n * Paths to skip detection (can be string prefixes or RegExp)\n */\n skipPaths?: (string | RegExp)[];\n\n /**\n * Minimum confidence threshold (0-100 scale) for considering a request as agent traffic\n * @default 70\n */\n confidenceThreshold?: number;\n\n /**\n * Response to send when blocking agents\n */\n blockedResponse?: {\n status?: number;\n message?: string;\n headers?: Record<string, string>;\n };\n\n /**\n * URL to redirect to when redirecting agents\n */\n redirectUrl?: string;\n\n /**\n * URL to rewrite to when rewriting agent requests\n */\n rewriteUrl?: string;\n\n /**\n * Enable debug logging\n */\n debug?: boolean;\n}\n\n/**\n * Detection input extracted from request\n */\ninterface DetectionInput {\n userAgent?: string;\n ipAddress?: string;\n headers: Record<string, string>;\n url?: string;\n method?: string;\n}\n\n/**\n * Simple edge-safe detector that uses pattern matching\n * Falls back to this when WASM is not available\n */\nasync function patternDetect(input: DetectionInput): Promise<DetectionResult> {\n const userAgent = input.userAgent?.toLowerCase() || '';\n const reasons: string[] = [];\n let confidence = 0;\n let detectedAgent: { type: string; name: string } | undefined;\n\n // AI agent patterns with confidence scores (0-100 scale)\n const patterns = [\n { pattern: /chatgpt-user/i, name: 'ChatGPT', confidence: 95 },\n { pattern: /claude-web/i, name: 'Claude', confidence: 95 },\n { pattern: /claude-?bot/i, name: 'ClaudeBot', confidence: 95 },\n { pattern: /anthropic/i, name: 'Claude', confidence: 90 },\n { pattern: /gptbot/i, name: 'GPTBot', confidence: 90 },\n { pattern: /perplexity/i, name: 'Perplexity', confidence: 90 },\n { pattern: /openai/i, name: 'OpenAI', confidence: 85 },\n { pattern: /copilot/i, name: 'Copilot', confidence: 85 },\n { pattern: /gemini/i, name: 'Gemini', confidence: 85 },\n { pattern: /bard/i, name: 'Bard', confidence: 85 },\n // HTTP client libraries (often used by AI agents/scrapers)\n { pattern: /^axios\\//i, name: 'HTTP Client (axios)', confidence: 75 },\n { pattern: /^node-fetch\\//i, name: 'HTTP Client (node-fetch)', confidence: 75 },\n { pattern: /^got\\//i, name: 'HTTP Client (got)', confidence: 75 },\n { pattern: /^python-requests\\//i, name: 'HTTP Client (requests)', confidence: 75 },\n { pattern: /^python-urllib/i, name: 'HTTP Client (urllib)', confidence: 75 },\n { pattern: /^curl\\//i, name: 'HTTP Client (curl)', confidence: 60 },\n { pattern: /^wget\\//i, name: 'HTTP Client (wget)', confidence: 60 },\n { pattern: /^httpie\\//i, name: 'HTTP Client (httpie)', confidence: 70 },\n { pattern: /^undici\\//i, name: 'HTTP Client (undici)', confidence: 75 },\n ];\n\n for (const { pattern, name, confidence: patternConfidence } of patterns) {\n if (pattern.test(userAgent)) {\n confidence = patternConfidence;\n detectedAgent = { type: name.toLowerCase(), name };\n reasons.push(`known_pattern:${name.toLowerCase()}`);\n break;\n }\n }\n\n // Check for AI-specific headers\n const aiHeaders = ['x-openai-', 'x-anthropic-', 'x-ai-', 'signature-agent'];\n for (const [key] of Object.entries(input.headers)) {\n if (aiHeaders.some((prefix) => key.toLowerCase().startsWith(prefix))) {\n confidence = Math.max(confidence, 45);\n reasons.push(`ai_headers:${key}`);\n break;\n }\n }\n\n return {\n isAgent: confidence > 30,\n confidence,\n detectionClass:\n confidence > 30 && detectedAgent\n ? { type: 'AiAgent' as const, agentType: detectedAgent.name }\n : confidence > 30\n ? { type: 'Unknown' as const }\n : { type: 'Human' as const },\n signals: [],\n ...(detectedAgent && { detectedAgent }),\n reasons,\n verificationMethod: 'pattern' as const,\n forgeabilityRisk: confidence > 80 ? 'medium' : 'high',\n timestamp: new Date(),\n };\n}\n\n/**\n * Extended detection result with policy enforcement\n */\ninterface PolicyEnforcedResult extends DetectionResult {\n shouldBlock?: boolean;\n blockReason?: 'deny_list' | 'not_in_allow_list' | 'threshold';\n}\n\n/**\n * Create detector using WASM runtime or fallback\n * When apiKey is provided, policy enforcement is enabled\n */\nasync function createDetector(config: EdgeMiddlewareConfig) {\n let detector: {\n detect: (input: DetectionInput) => Promise<PolicyEnforcedResult>;\n isReady: () => boolean;\n };\n\n // Try to use wasm-runtime if WASM module is provided\n if (config.wasmModule) {\n try {\n const { StaticWasmLoader, WasmDetector, PolicyLoader } = await import(\n '@kya-os/agentshield-wasm-runtime/edge'\n );\n const loader = new StaticWasmLoader(config.wasmModule);\n\n // Create policy loader if API key is provided\n const policyLoader = config.apiKey\n ? new PolicyLoader({\n apiUrl: config.policyApiUrl,\n })\n : undefined;\n\n // Create detector with policy support\n const wasmDetector = new WasmDetector(loader, policyLoader, {\n apiKey: config.apiKey,\n debug: config.debug,\n });\n\n await wasmDetector.ensureReady();\n\n if (config.debug) {\n console.debug('[AgentShield] WASM detector initialized in Edge Runtime', {\n policyEnabled: !!config.apiKey,\n });\n }\n\n detector = {\n detect: async (input: DetectionInput): Promise<PolicyEnforcedResult> => {\n const result = await wasmDetector.detect(input);\n // Convert IDetectionResult to DetectionResult by adding required fields\n // Type assertion needed due to different DetectionClass definitions\n return {\n isAgent: result.isAgent,\n confidence: result.confidence,\n detectionClass: result.detectionClass as DetectionResult['detectionClass'],\n detectedAgent: result.detectedAgent,\n verificationMethod: result.verificationMethod,\n forgeabilityRisk: result.forgeabilityRisk,\n reasons: result.reasons,\n timestamp: result.timestamp,\n signals: [], // Required by DetectionResult, empty for WASM results\n shouldBlock: result.shouldBlock,\n blockReason: result.blockReason as PolicyEnforcedResult['blockReason'],\n };\n },\n isReady: () => wasmDetector.isReady(),\n };\n } catch (error) {\n if (config.debug) {\n console.warn('[AgentShield] WASM runtime not available, using pattern detection:', error);\n }\n detector = {\n detect: patternDetect,\n isReady: () => true,\n };\n }\n } else {\n // Use pattern-based detection\n if (config.debug) {\n console.debug('[AgentShield] No WASM module provided, using pattern detection');\n }\n detector = {\n detect: patternDetect,\n isReady: () => true,\n };\n }\n\n return detector;\n}\n\n/**\n * Create Edge middleware with WASM support\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module';\n * import { createEdgeMiddleware } from '@kya-os/agentshield-nextjs/edge';\n *\n * export const middleware = createEdgeMiddleware({\n * wasmModule,\n * onAgentDetected: 'block',\n * confidenceThreshold: 70,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\n * };\n * ```\n */\nexport function createEdgeMiddleware(config: EdgeMiddlewareConfig = {}) {\n let detectorPromise: Promise<Awaited<ReturnType<typeof createDetector>>> | null = null;\n\n const {\n onAgentDetected = 'log',\n onDetection,\n skipPaths = [],\n confidenceThreshold = 70,\n blockedResponse = {\n status: 403,\n message: 'Access denied: Automated agent detected',\n headers: { 'Content-Type': 'application/json' },\n },\n redirectUrl = '/blocked',\n rewriteUrl = '/blocked',\n debug = false,\n } = config;\n\n return async (request: NextRequest): Promise<NextResponse> => {\n try {\n // Initialize detector lazily\n if (!detectorPromise) {\n detectorPromise = createDetector({ ...config, debug });\n }\n const detector = await detectorPromise;\n\n // Check if path should be skipped\n const shouldSkip = skipPaths.some((pattern) => {\n if (typeof pattern === 'string') {\n return request.nextUrl.pathname.startsWith(pattern);\n }\n return pattern.test(request.nextUrl.pathname);\n });\n\n if (shouldSkip) {\n return NextResponse.next();\n }\n\n // Extract request context\n const url = new URL(request.url);\n // Parse X-Forwarded-For header to get client IP (first in comma-separated list)\n const xForwardedFor = request.headers.get('x-forwarded-for');\n const clientIp = xForwardedFor?.split(',')[0]?.trim();\n const input: DetectionInput = {\n userAgent: request.headers.get('user-agent') || undefined,\n ipAddress: request.ip || clientIp || undefined,\n headers: Object.fromEntries(request.headers.entries()),\n url: url.pathname + url.search,\n method: request.method,\n };\n\n // Run detection\n const result = await detector.detect(input);\n\n // POLICY ENFORCEMENT: Check shouldBlock from policy rules (deny list, allow list, threshold)\n // This takes precedence over the general threshold check below\n if (result.shouldBlock) {\n if (debug) {\n console.debug('[AgentShield] Blocked by policy', {\n reason: result.blockReason,\n agent: result.detectedAgent?.name,\n confidence: result.confidence,\n pathname: request.nextUrl.pathname,\n });\n }\n\n // Call custom detection handler if provided\n if (onDetection) {\n const customResponse = await onDetection(request, result);\n if (customResponse) {\n return customResponse;\n }\n }\n\n // Return 403 with policy block reason\n return NextResponse.json(\n {\n error: 'Access denied by policy',\n reason: result.blockReason,\n detected: true,\n confidence: result.confidence,\n agent: result.detectedAgent?.name,\n timestamp: result.timestamp,\n },\n {\n status: 403,\n headers: { 'Content-Type': 'application/json' },\n }\n );\n }\n\n // Check if agent detected above threshold (when policy doesn't block)\n // Skip if shouldBlock === false (agent explicitly allowed by policy)\n if (\n result.shouldBlock !== false &&\n result.isAgent &&\n result.confidence >= confidenceThreshold\n ) {\n // Call custom detection handler if provided\n if (onDetection) {\n const customResponse = await onDetection(request, result);\n if (customResponse) {\n return customResponse;\n }\n }\n\n // Handle based on configuration\n switch (onAgentDetected) {\n case 'block': {\n const response = NextResponse.json(\n {\n error: blockedResponse.message,\n detected: true,\n confidence: result.confidence,\n timestamp: result.timestamp,\n },\n { status: blockedResponse.status }\n );\n\n if (blockedResponse.headers) {\n Object.entries(blockedResponse.headers).forEach(([key, value]) => {\n response.headers.set(key, value);\n });\n }\n\n return response;\n }\n\n case 'redirect':\n return NextResponse.redirect(new URL(redirectUrl, request.url));\n\n case 'rewrite':\n return NextResponse.rewrite(new URL(rewriteUrl, request.url));\n\n case 'log':\n if (debug || process.env.NODE_ENV !== 'production') {\n console.debug('AgentShield: Agent detected', {\n ipAddress: input.ipAddress,\n userAgent: input.userAgent,\n confidence: result.confidence,\n agent: result.detectedAgent?.name,\n pathname: request.nextUrl.pathname,\n });\n }\n break;\n\n case 'allow':\n default:\n break;\n }\n }\n\n // Continue with request\n const response = NextResponse.next();\n\n // Add detection headers\n response.headers.set('x-agentshield-detected', result.isAgent.toString());\n response.headers.set('x-agentshield-confidence', result.confidence.toString());\n if (result.detectedAgent?.name) {\n response.headers.set('x-agentshield-agent', result.detectedAgent.name);\n }\n\n return response;\n } catch (error) {\n console.error('AgentShield middleware error:', error);\n return NextResponse.next();\n }\n };\n}\n\n// Re-export types for convenience\nexport type { DetectionResult };\n"]}
1
+ {"version":3,"sources":["../../src/utils.ts","../../src/edge/index.ts"],"names":["response"],"mappings":";;;;;;AAcO,SAAS,YAAY,OAAA,EAA0C;AAEpE,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,iBAAiB,CAAA;AAC1D,EAAA,IAAI,YAAA,EAAc;AAEhB,IAAA,MAAM,KAAK,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,GAAG,IAAA,EAAK;AAC5C,IAAA,IAAI,IAAI,OAAO,EAAA;AAAA,EACjB;AAGA,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA;AAC9C,EAAA,IAAI,QAAQ,OAAO,MAAA;AAGnB,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,kBAAkB,CAAA;AACnD,EAAA,IAAI,MAAM,OAAO,IAAA;AAGjB,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAA;AAClD,EAAA,IAAI,UAAU,OAAO,QAAA;AAErB,EAAA,OAAO,MAAA;AACT;;;ACiFA,eAAe,cAAc,KAAA,EAAiD;AAC5E,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,SAAA,EAAW,WAAA,EAAY,IAAK,EAAA;AACpD,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,IAAI,aAAA;AAGJ,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,EAAE,OAAA,EAAS,eAAA,EAAiB,IAAA,EAAM,SAAA,EAAW,YAAY,EAAA,EAAG;AAAA,IAC5D,EAAE,OAAA,EAAS,aAAA,EAAe,IAAA,EAAM,QAAA,EAAU,YAAY,EAAA,EAAG;AAAA,IACzD,EAAE,OAAA,EAAS,cAAA,EAAgB,IAAA,EAAM,WAAA,EAAa,YAAY,EAAA,EAAG;AAAA,IAC7D,EAAE,OAAA,EAAS,YAAA,EAAc,IAAA,EAAM,QAAA,EAAU,YAAY,EAAA,EAAG;AAAA,IACxD,EAAE,OAAA,EAAS,SAAA,EAAW,IAAA,EAAM,QAAA,EAAU,YAAY,EAAA,EAAG;AAAA,IACrD,EAAE,OAAA,EAAS,aAAA,EAAe,IAAA,EAAM,YAAA,EAAc,YAAY,EAAA,EAAG;AAAA,IAC7D,EAAE,OAAA,EAAS,SAAA,EAAW,IAAA,EAAM,QAAA,EAAU,YAAY,EAAA,EAAG;AAAA,IACrD,EAAE,OAAA,EAAS,UAAA,EAAY,IAAA,EAAM,SAAA,EAAW,YAAY,EAAA,EAAG;AAAA,IACvD,EAAE,OAAA,EAAS,SAAA,EAAW,IAAA,EAAM,QAAA,EAAU,YAAY,EAAA,EAAG;AAAA,IACrD,EAAE,OAAA,EAAS,OAAA,EAAS,IAAA,EAAM,MAAA,EAAQ,YAAY,EAAA,EAAG;AAAA;AAAA,IAEjD,EAAE,OAAA,EAAS,WAAA,EAAa,IAAA,EAAM,qBAAA,EAAuB,YAAY,EAAA,EAAG;AAAA,IACpE,EAAE,OAAA,EAAS,gBAAA,EAAkB,IAAA,EAAM,0BAAA,EAA4B,YAAY,EAAA,EAAG;AAAA,IAC9E,EAAE,OAAA,EAAS,SAAA,EAAW,IAAA,EAAM,mBAAA,EAAqB,YAAY,EAAA,EAAG;AAAA,IAChE,EAAE,OAAA,EAAS,qBAAA,EAAuB,IAAA,EAAM,wBAAA,EAA0B,YAAY,EAAA,EAAG;AAAA,IACjF,EAAE,OAAA,EAAS,iBAAA,EAAmB,IAAA,EAAM,sBAAA,EAAwB,YAAY,EAAA,EAAG;AAAA,IAC3E,EAAE,OAAA,EAAS,UAAA,EAAY,IAAA,EAAM,oBAAA,EAAsB,YAAY,EAAA,EAAG;AAAA,IAClE,EAAE,OAAA,EAAS,UAAA,EAAY,IAAA,EAAM,oBAAA,EAAsB,YAAY,EAAA,EAAG;AAAA,IAClE,EAAE,OAAA,EAAS,YAAA,EAAc,IAAA,EAAM,sBAAA,EAAwB,YAAY,EAAA,EAAG;AAAA,IACtE,EAAE,OAAA,EAAS,YAAA,EAAc,IAAA,EAAM,sBAAA,EAAwB,YAAY,EAAA;AAAG,GACxE;AAEA,EAAA,KAAA,MAAW,EAAE,OAAA,EAAS,IAAA,EAAM,UAAA,EAAY,iBAAA,MAAuB,QAAA,EAAU;AACvE,IAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,SAAS,CAAA,EAAG;AAC3B,MAAA,UAAA,GAAa,iBAAA;AACb,MAAA,aAAA,GAAgB,EAAE,IAAA,EAAM,IAAA,CAAK,WAAA,IAAe,IAAA,EAAK;AACjD,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,cAAA,EAAiB,IAAA,CAAK,WAAA,EAAa,CAAA,CAAE,CAAA;AAClD,MAAA;AAAA,IACF;AAAA,EACF;AAGA,EAAA,MAAM,SAAA,GAAY,CAAC,WAAA,EAAa,cAAA,EAAgB,SAAS,iBAAiB,CAAA;AAC1E,EAAA,KAAA,MAAW,CAAC,GAAG,CAAA,IAAK,OAAO,OAAA,CAAQ,KAAA,CAAM,OAAO,CAAA,EAAG;AACjD,IAAA,IAAI,SAAA,CAAU,IAAA,CAAK,CAAC,MAAA,KAAW,GAAA,CAAI,aAAY,CAAE,UAAA,CAAW,MAAM,CAAC,CAAA,EAAG;AACpE,MAAA,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,UAAA,EAAY,EAAE,CAAA;AACpC,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,WAAA,EAAc,GAAG,CAAA,CAAE,CAAA;AAChC,MAAA;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,SAAS,UAAA,GAAa,EAAA;AAAA,IACtB,UAAA;AAAA,IACA,gBACE,UAAA,GAAa,EAAA,IAAM,gBACf,EAAE,IAAA,EAAM,WAAoB,SAAA,EAAW,aAAA,CAAc,MAAK,GAC1D,UAAA,GAAa,KACX,EAAE,IAAA,EAAM,WAAmB,GAC3B,EAAE,MAAM,OAAA,EAAiB;AAAA,IACjC,SAAS,EAAC;AAAA,IACV,GAAI,aAAA,IAAiB,EAAE,aAAA,EAAc;AAAA,IACrC,OAAA;AAAA,IACA,kBAAA,EAAoB,SAAA;AAAA,IACpB,gBAAA,EAAkB,UAAA,GAAa,EAAA,GAAK,QAAA,GAAW,MAAA;AAAA,IAC/C,SAAA,sBAAe,IAAA;AAAK,GACtB;AACF;AAcA,eAAe,eAAe,MAAA,EAA8B;AAC1D,EAAA,IAAI,QAAA;AAMJ,EAAA,IAAI,OAAO,UAAA,EAAY;AACrB,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,gBAAA,EAAkB,YAAA,EAAc,cAAa,GAAI,MAAM,OAC7D,uCACF,CAAA;AACA,MAAA,MAAM,MAAA,GAAS,IAAI,gBAAA,CAAiB,MAAA,CAAO,UAAU,CAAA;AAGrD,MAAA,MAAM,YAAA,GAAe,MAAA,CAAO,MAAA,GACxB,IAAI,YAAA,CAAa;AAAA,QACf,QAAQ,MAAA,CAAO;AAAA,OAChB,CAAA,GACD,KAAA,CAAA;AAGJ,MAAA,MAAM,YAAA,GAAe,IAAI,YAAA,CAAa,MAAA,EAAQ,YAAA,EAAc;AAAA,QAC1D,QAAQ,MAAA,CAAO,MAAA;AAAA,QACf,OAAO,MAAA,CAAO;AAAA,OACf,CAAA;AAED,MAAA,MAAM,aAAa,WAAA,EAAY;AAE/B,MAAA,IAAI,OAAO,KAAA,EAAO;AAChB,QAAA,OAAA,CAAQ,MAAM,yDAAA,EAA2D;AAAA,UACvE,aAAA,EAAe,CAAC,CAAC,MAAA,CAAO;AAAA,SACzB,CAAA;AAAA,MACH;AAEA,MAAA,QAAA,GAAW;AAAA,QACT,MAAA,EAAQ,OAAO,KAAA,KAAyD;AACtE,UAAA,MAAM,MAAA,GAAS,MAAM,YAAA,CAAa,MAAA,CAAO,KAAK,CAAA;AAG9C,UAAA,OAAO;AAAA,YACL,SAAS,MAAA,CAAO,OAAA;AAAA,YAChB,aAAc,MAAA,CAA8C,WAAA;AAAA,YAG5D,YAAY,MAAA,CAAO,UAAA;AAAA,YACnB,gBAAgB,MAAA,CAAO,cAAA;AAAA,YACvB,eAAe,MAAA,CAAO,aAAA;AAAA,YACtB,oBAAoB,MAAA,CAAO,kBAAA;AAAA,YAC3B,kBAAkB,MAAA,CAAO,gBAAA;AAAA,YACzB,SAAS,MAAA,CAAO,OAAA;AAAA,YAChB,WAAW,MAAA,CAAO,SAAA;AAAA,YAClB,SAAS,EAAC;AAAA;AAAA,YACV,aAAa,MAAA,CAAO,WAAA;AAAA,YACpB,aAAa,MAAA,CAAO;AAAA,WACtB;AAAA,QACF,CAAA;AAAA,QACA,OAAA,EAAS,MAAM,YAAA,CAAa,OAAA;AAAQ,OACtC;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,OAAO,KAAA,EAAO;AAChB,QAAA,OAAA,CAAQ,IAAA,CAAK,sEAAsE,KAAK,CAAA;AAAA,MAC1F;AACA,MAAA,QAAA,GAAW;AAAA,QACT,MAAA,EAAQ,aAAA;AAAA,QACR,SAAS,MAAM;AAAA,OACjB;AAAA,IACF;AAAA,EACF,CAAA,MAAO;AAEL,IAAA,IAAI,OAAO,KAAA,EAAO;AAChB,MAAA,OAAA,CAAQ,MAAM,gEAAgE,CAAA;AAAA,IAChF;AACA,IAAA,QAAA,GAAW;AAAA,MACT,MAAA,EAAQ,aAAA;AAAA,MACR,SAAS,MAAM;AAAA,KACjB;AAAA,EACF;AAEA,EAAA,OAAO,QAAA;AACT;AAsBO,SAAS,oBAAA,CAAqB,MAAA,GAA+B,EAAC,EAAG;AACtE,EAAA,IAAI,eAAA,GAA8E,IAAA;AAElF,EAAA,MAAM;AAAA,IACJ,eAAA,GAAkB,KAAA;AAAA,IAClB,WAAA;AAAA,IACA,YAAY,EAAC;AAAA,IACb,mBAAA,GAAsB,EAAA;AAAA,IACtB,eAAA,GAAkB;AAAA,MAChB,MAAA,EAAQ,GAAA;AAAA,MACR,OAAA,EAAS,yCAAA;AAAA,MACT,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAAmB,KAChD;AAAA,IACA,WAAA,GAAc,UAAA;AAAA,IACd,UAAA,GAAa,UAAA;AAAA,IACb,KAAA,GAAQ;AAAA,GACV,GAAI,MAAA;AAEJ,EAAA,OAAO,OAAO,OAAA,KAAgD;AAC5D,IAAA,IAAI;AAEF,MAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,QAAA,eAAA,GAAkB,cAAA,CAAe,EAAE,GAAG,MAAA,EAAQ,OAAO,CAAA;AAAA,MACvD;AACA,MAAA,MAAM,WAAW,MAAM,eAAA;AAGvB,MAAA,MAAM,UAAA,GAAa,SAAA,CAAU,IAAA,CAAK,CAAC,OAAA,KAAY;AAC7C,QAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,UAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,QAAA,CAAS,UAAA,CAAW,OAAO,CAAA;AAAA,QACpD;AACA,QAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA;AAAA,MAC9C,CAAC,CAAA;AAED,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,OAAO,aAAa,IAAA,EAAK;AAAA,MAC3B;AAGA,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,MAAA,MAAM,KAAA,GAAwB;AAAA,QAC5B,SAAA,EAAW,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,YAAY,CAAA,IAAK,KAAA,CAAA;AAAA,QAChD,SAAA,EAAW,YAAY,OAAO,CAAA;AAAA,QAC9B,SAAS,MAAA,CAAO,WAAA,CAAY,OAAA,CAAQ,OAAA,CAAQ,SAAS,CAAA;AAAA,QACrD,GAAA,EAAK,GAAA,CAAI,QAAA,GAAW,GAAA,CAAI,MAAA;AAAA,QACxB,QAAQ,OAAA,CAAQ;AAAA,OAClB;AAGA,MAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,MAAA,CAAO,KAAK,CAAA;AAI1C,MAAA,IAAI,OAAO,WAAA,EAAa;AACtB,QAAA,IAAI,KAAA,EAAO;AACT,UAAA,OAAA,CAAQ,MAAM,iCAAA,EAAmC;AAAA,YAC/C,QAAQ,MAAA,CAAO,WAAA;AAAA,YACf,KAAA,EAAO,OAAO,aAAA,EAAe,IAAA;AAAA,YAC7B,YAAY,MAAA,CAAO,UAAA;AAAA,YACnB,QAAA,EAAU,QAAQ,OAAA,CAAQ;AAAA,WAC3B,CAAA;AAAA,QACH;AAGA,QAAA,IAAI,WAAA,EAAa;AACf,UAAA,MAAM,cAAA,GAAiB,MAAM,WAAA,CAAY,OAAA,EAAS,MAAM,CAAA;AACxD,UAAA,IAAI,cAAA,EAAgB;AAClB,YAAA,OAAO,cAAA;AAAA,UACT;AAAA,QACF;AAGA,QAAA,OAAO,YAAA,CAAa,IAAA;AAAA,UAClB;AAAA,YACE,KAAA,EAAO,yBAAA;AAAA,YACP,QAAQ,MAAA,CAAO,WAAA;AAAA,YACf,QAAA,EAAU,IAAA;AAAA,YACV,YAAY,MAAA,CAAO,UAAA;AAAA,YACnB,KAAA,EAAO,OAAO,aAAA,EAAe,IAAA;AAAA,YAC7B,WAAW,MAAA,CAAO;AAAA,WACpB;AAAA,UACA;AAAA,YACE,MAAA,EAAQ,GAAA;AAAA,YACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAAmB;AAChD,SACF;AAAA,MACF;AAIA,MAAA,IAAI,MAAA,CAAO,gBAAgB,KAAA,EAAO;AAChC,QAAA,MAAM,QAAA,GAAW,oBAAoB,MAAA,EAAQ;AAAA,UAC3C,mBAAA;AAAA,UACA,aAAA,EAAe;AAAA,SAChB,CAAA;AAED,QAAA,IAAI,SAAS,YAAA,EAAc;AAEzB,UAAA,IAAI,WAAA,EAAa;AACf,YAAA,MAAM,cAAA,GAAiB,MAAM,WAAA,CAAY,OAAA,EAAS,MAAM,CAAA;AACxD,YAAA,IAAI,cAAA,EAAgB;AAClB,cAAA,OAAO,cAAA;AAAA,YACT;AAAA,UACF;AAGA,UAAA,QAAQ,SAAS,MAAA;AAAQ,YACvB,KAAK,OAAA,EAAS;AACZ,cAAA,MAAMA,YAAW,YAAA,CAAa,IAAA;AAAA,gBAC5B;AAAA,kBACE,OAAO,eAAA,CAAgB,OAAA;AAAA,kBACvB,QAAA,EAAU,IAAA;AAAA,kBACV,YAAY,MAAA,CAAO,UAAA;AAAA,kBACnB,WAAW,MAAA,CAAO;AAAA,iBACpB;AAAA,gBACA,EAAE,MAAA,EAAQ,eAAA,CAAgB,MAAA;AAAO,eACnC;AAEA,cAAA,IAAI,gBAAgB,OAAA,EAAS;AAC3B,gBAAA,MAAA,CAAO,OAAA,CAAQ,gBAAgB,OAAO,CAAA,CAAE,QAAQ,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM;AAChE,kBAAAA,SAAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,gBACjC,CAAC,CAAA;AAAA,cACH;AAEA,cAAA,OAAOA,SAAAA;AAAA,YACT;AAAA,YAEA,KAAK,UAAA;AACH,cAAA,OAAO,aAAa,QAAA,CAAS,IAAI,IAAI,WAAA,EAAa,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,YAEhE,KAAK,SAAA;AACH,cAAA,OAAO,aAAa,OAAA,CAAQ,IAAI,IAAI,UAAA,EAAY,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,YAE9D,KAAK,KAAA;AACH,cAAA,IAAI,KAAA,IAAS,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,YAAA,EAAc;AAClD,gBAAA,OAAA,CAAQ,MAAM,6BAAA,EAA+B;AAAA,kBAC3C,WAAW,KAAA,CAAM,SAAA;AAAA,kBACjB,WAAW,KAAA,CAAM,SAAA;AAAA,kBACjB,YAAY,MAAA,CAAO,UAAA;AAAA,kBACnB,KAAA,EAAO,OAAO,aAAA,EAAe,IAAA;AAAA,kBAC7B,QAAA,EAAU,QAAQ,OAAA,CAAQ;AAAA,iBAC3B,CAAA;AAAA,cACH;AACA,cAAA;AAAA,YAEF;AACE,cAAA;AAAA;AACJ,QACF;AAAA,MACF;AAGA,MAAA,MAAM,QAAA,GAAW,aAAa,IAAA,EAAK;AAGnC,MAAA,QAAA,CAAS,QAAQ,GAAA,CAAI,cAAA,EAAgB,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA;AAC9D,MAAA,QAAA,CAAS,QAAQ,GAAA,CAAI,gBAAA,EAAkB,MAAA,CAAO,UAAA,CAAW,UAAU,CAAA;AACnE,MAAA,IAAI,MAAA,CAAO,eAAe,IAAA,EAAM;AAC9B,QAAA,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,WAAA,EAAa,MAAA,CAAO,cAAc,IAAI,CAAA;AAAA,MAC7D;AAEA,MAAA,OAAO,QAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,CAAM,iCAAiC,KAAK,CAAA;AACpD,MAAA,OAAO,aAAa,IAAA,EAAK;AAAA,IAC3B;AAAA,EACF,CAAA;AACF","file":"index.mjs","sourcesContent":["/**\n * Utility functions for agentshield-nextjs\n */\n\nimport type { NextRequest } from 'next/server';\n\n/**\n * Extract client IP address from a NextRequest.\n * In Next.js 15+, the `ip` property was removed from NextRequest.\n * This function uses headers to determine the client IP.\n *\n * @param request - The NextRequest object\n * @returns The client IP address or undefined if not available\n */\nexport function getClientIp(request: NextRequest): string | undefined {\n // Check x-forwarded-for header (standard for proxies/load balancers)\n const forwardedFor = request.headers.get('x-forwarded-for');\n if (forwardedFor) {\n // Take the first IP in the chain (original client)\n const ip = forwardedFor.split(',')[0]?.trim();\n if (ip) return ip;\n }\n\n // Check x-real-ip header (commonly used by nginx)\n const realIp = request.headers.get('x-real-ip');\n if (realIp) return realIp;\n\n // Check cf-connecting-ip header (Cloudflare)\n const cfIp = request.headers.get('cf-connecting-ip');\n if (cfIp) return cfIp;\n\n // Check x-client-ip header (some proxies use this)\n const clientIp = request.headers.get('x-client-ip');\n if (clientIp) return clientIp;\n\n return undefined;\n}\n\n/**\n * Safely extract the hostname from a URL string.\n * Returns a friendly fallback when parsing fails so UX copy doesn't leak\n * \"undefined\" or similar to end users.\n */\nexport function safeHostname(url: string): string {\n try {\n return new URL(url).hostname;\n } catch {\n return 'this site';\n }\n}\n","/**\n * Edge Runtime Middleware with WASM Support\n *\n * This module provides Next.js middleware that uses the unified WASM runtime.\n * For Edge Runtime, consumers must provide a pre-compiled WASM module via static import:\n *\n * ```typescript\n * // In your middleware.ts\n * import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module';\n * import { createEdgeMiddleware } from '@kya-os/agentshield-nextjs/edge';\n *\n * export const middleware = createEdgeMiddleware({\n * wasmModule,\n * apiKey: process.env.AGENTSHIELD_API_KEY,\n * onAgentDetected: 'block',\n * });\n * ```\n *\n * When an API key is provided, the middleware will:\n * 1. Load customer policies (deny lists, allow lists, thresholds) from AgentShield API\n * 2. Automatically block agents that match deny list entries\n * 3. Cache policies for 5 minutes with background refresh\n */\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport type { DetectionResult } from '@kya-os/agentshield-shared';\nimport { evaluateEnforcement } from '@kya-os/agentshield-shared';\nimport { getClientIp } from '../utils';\n\n/**\n * Edge middleware configuration\n */\nexport interface EdgeMiddlewareConfig {\n /**\n * Pre-compiled WebAssembly.Module for Edge Runtime\n * Use static import: `import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module'`\n */\n wasmModule?: WebAssembly.Module;\n\n /**\n * API key for loading customer policies from AgentShield dashboard\n * When provided, enables policy enforcement (deny lists, allow lists, thresholds)\n */\n apiKey?: string;\n\n /**\n * Custom URL for the policy API\n * @default 'https://api.agentshield.io'\n */\n policyApiUrl?: string;\n\n /**\n * Action to take when an agent is detected\n * @default 'log'\n */\n onAgentDetected?: 'block' | 'redirect' | 'rewrite' | 'log' | 'allow';\n\n /**\n * Custom handler called when an agent is detected\n * Return a NextResponse to override default behavior\n */\n onDetection?: (\n request: NextRequest,\n result: DetectionResult\n ) => Promise<NextResponse | void> | NextResponse | void;\n\n /**\n * Paths to skip detection (can be string prefixes or RegExp)\n */\n skipPaths?: (string | RegExp)[];\n\n /**\n * Minimum confidence threshold (0-100 scale) for considering a request as agent traffic\n * @default 70\n */\n confidenceThreshold?: number;\n\n /**\n * Response to send when blocking agents\n */\n blockedResponse?: {\n status?: number;\n message?: string;\n headers?: Record<string, string>;\n };\n\n /**\n * URL to redirect to when redirecting agents\n */\n redirectUrl?: string;\n\n /**\n * URL to rewrite to when rewriting agent requests\n */\n rewriteUrl?: string;\n\n /**\n * Enable debug logging\n */\n debug?: boolean;\n}\n\n/**\n * Detection input extracted from request\n */\ninterface DetectionInput {\n userAgent?: string;\n ipAddress?: string;\n headers: Record<string, string>;\n url?: string;\n method?: string;\n}\n\n/**\n * Simple edge-safe detector that uses pattern matching\n * Falls back to this when WASM is not available\n */\nasync function patternDetect(input: DetectionInput): Promise<DetectionResult> {\n const userAgent = input.userAgent?.toLowerCase() || '';\n const reasons: string[] = [];\n let confidence = 0;\n let detectedAgent: { type: string; name: string } | undefined;\n\n // AI agent patterns with confidence scores (0-100 scale)\n const patterns = [\n { pattern: /chatgpt-user/i, name: 'ChatGPT', confidence: 95 },\n { pattern: /claude-web/i, name: 'Claude', confidence: 95 },\n { pattern: /claude-?bot/i, name: 'ClaudeBot', confidence: 95 },\n { pattern: /anthropic/i, name: 'Claude', confidence: 90 },\n { pattern: /gptbot/i, name: 'GPTBot', confidence: 90 },\n { pattern: /perplexity/i, name: 'Perplexity', confidence: 90 },\n { pattern: /openai/i, name: 'OpenAI', confidence: 85 },\n { pattern: /copilot/i, name: 'Copilot', confidence: 85 },\n { pattern: /gemini/i, name: 'Gemini', confidence: 85 },\n { pattern: /bard/i, name: 'Bard', confidence: 85 },\n // HTTP client libraries (often used by AI agents/scrapers)\n { pattern: /^axios\\//i, name: 'HTTP Client (axios)', confidence: 75 },\n { pattern: /^node-fetch\\//i, name: 'HTTP Client (node-fetch)', confidence: 75 },\n { pattern: /^got\\//i, name: 'HTTP Client (got)', confidence: 75 },\n { pattern: /^python-requests\\//i, name: 'HTTP Client (requests)', confidence: 75 },\n { pattern: /^python-urllib/i, name: 'HTTP Client (urllib)', confidence: 75 },\n { pattern: /^curl\\//i, name: 'HTTP Client (curl)', confidence: 60 },\n { pattern: /^wget\\//i, name: 'HTTP Client (wget)', confidence: 60 },\n { pattern: /^httpie\\//i, name: 'HTTP Client (httpie)', confidence: 70 },\n { pattern: /^undici\\//i, name: 'HTTP Client (undici)', confidence: 75 },\n ];\n\n for (const { pattern, name, confidence: patternConfidence } of patterns) {\n if (pattern.test(userAgent)) {\n confidence = patternConfidence;\n detectedAgent = { type: name.toLowerCase(), name };\n reasons.push(`known_pattern:${name.toLowerCase()}`);\n break;\n }\n }\n\n // Check for AI-specific headers\n const aiHeaders = ['x-openai-', 'x-anthropic-', 'x-ai-', 'signature-agent'];\n for (const [key] of Object.entries(input.headers)) {\n if (aiHeaders.some((prefix) => key.toLowerCase().startsWith(prefix))) {\n confidence = Math.max(confidence, 45);\n reasons.push(`ai_headers:${key}`);\n break;\n }\n }\n\n return {\n isAgent: confidence > 30,\n confidence,\n detectionClass:\n confidence > 30 && detectedAgent\n ? { type: 'AiAgent' as const, agentType: detectedAgent.name }\n : confidence > 30\n ? { type: 'Unknown' as const }\n : { type: 'Human' as const },\n signals: [],\n ...(detectedAgent && { detectedAgent }),\n reasons,\n verificationMethod: 'pattern' as const,\n forgeabilityRisk: confidence > 80 ? 'medium' : 'high',\n timestamp: new Date(),\n };\n}\n\n/**\n * Extended detection result with policy enforcement\n */\ninterface PolicyEnforcedResult extends DetectionResult {\n shouldBlock?: boolean;\n blockReason?: 'deny_list' | 'not_in_allow_list' | 'threshold';\n}\n\n/**\n * Create detector using WASM runtime or fallback\n * When apiKey is provided, policy enforcement is enabled\n */\nasync function createDetector(config: EdgeMiddlewareConfig) {\n let detector: {\n detect: (input: DetectionInput) => Promise<PolicyEnforcedResult>;\n isReady: () => boolean;\n };\n\n // Try to use wasm-runtime if WASM module is provided\n if (config.wasmModule) {\n try {\n const { StaticWasmLoader, WasmDetector, PolicyLoader } = await import(\n '@kya-os/agentshield-wasm-runtime/edge'\n );\n const loader = new StaticWasmLoader(config.wasmModule);\n\n // Create policy loader if API key is provided\n const policyLoader = config.apiKey\n ? new PolicyLoader({\n apiUrl: config.policyApiUrl,\n })\n : undefined;\n\n // Create detector with policy support\n const wasmDetector = new WasmDetector(loader, policyLoader, {\n apiKey: config.apiKey,\n debug: config.debug,\n });\n\n await wasmDetector.ensureReady();\n\n if (config.debug) {\n console.debug('[AgentShield] WASM detector initialized in Edge Runtime', {\n policyEnabled: !!config.apiKey,\n });\n }\n\n detector = {\n detect: async (input: DetectionInput): Promise<PolicyEnforcedResult> => {\n const result = await wasmDetector.detect(input);\n // Convert IDetectionResult to DetectionResult by adding required fields\n // Type assertion needed due to different DetectionClass definitions\n return {\n isAgent: result.isAgent,\n isAiCrawler: (result as unknown as Record<string, unknown>).isAiCrawler as\n | boolean\n | undefined,\n confidence: result.confidence,\n detectionClass: result.detectionClass as DetectionResult['detectionClass'],\n detectedAgent: result.detectedAgent,\n verificationMethod: result.verificationMethod,\n forgeabilityRisk: result.forgeabilityRisk,\n reasons: result.reasons,\n timestamp: result.timestamp,\n signals: [], // Required by DetectionResult, empty for WASM results\n shouldBlock: result.shouldBlock,\n blockReason: result.blockReason as PolicyEnforcedResult['blockReason'],\n };\n },\n isReady: () => wasmDetector.isReady(),\n };\n } catch (error) {\n if (config.debug) {\n console.warn('[AgentShield] WASM runtime not available, using pattern detection:', error);\n }\n detector = {\n detect: patternDetect,\n isReady: () => true,\n };\n }\n } else {\n // Use pattern-based detection\n if (config.debug) {\n console.debug('[AgentShield] No WASM module provided, using pattern detection');\n }\n detector = {\n detect: patternDetect,\n isReady: () => true,\n };\n }\n\n return detector;\n}\n\n/**\n * Create Edge middleware with WASM support\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module';\n * import { createEdgeMiddleware } from '@kya-os/agentshield-nextjs/edge';\n *\n * export const middleware = createEdgeMiddleware({\n * wasmModule,\n * onAgentDetected: 'block',\n * confidenceThreshold: 70,\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\n * };\n * ```\n */\nexport function createEdgeMiddleware(config: EdgeMiddlewareConfig = {}) {\n let detectorPromise: Promise<Awaited<ReturnType<typeof createDetector>>> | null = null;\n\n const {\n onAgentDetected = 'log',\n onDetection,\n skipPaths = [],\n confidenceThreshold = 70,\n blockedResponse = {\n status: 403,\n message: 'Access denied: Automated agent detected',\n headers: { 'Content-Type': 'application/json' },\n },\n redirectUrl = '/blocked',\n rewriteUrl = '/blocked',\n debug = false,\n } = config;\n\n return async (request: NextRequest): Promise<NextResponse> => {\n try {\n // Initialize detector lazily\n if (!detectorPromise) {\n detectorPromise = createDetector({ ...config, debug });\n }\n const detector = await detectorPromise;\n\n // Check if path should be skipped\n const shouldSkip = skipPaths.some((pattern) => {\n if (typeof pattern === 'string') {\n return request.nextUrl.pathname.startsWith(pattern);\n }\n return pattern.test(request.nextUrl.pathname);\n });\n\n if (shouldSkip) {\n return NextResponse.next();\n }\n\n // Extract request context\n const url = new URL(request.url);\n const input: DetectionInput = {\n userAgent: request.headers.get('user-agent') || undefined,\n ipAddress: getClientIp(request),\n headers: Object.fromEntries(request.headers.entries()),\n url: url.pathname + url.search,\n method: request.method,\n };\n\n // Run detection\n const result = await detector.detect(input);\n\n // POLICY ENFORCEMENT: Check shouldBlock from policy rules (deny list, allow list, threshold)\n // This takes precedence over the general threshold check below\n if (result.shouldBlock) {\n if (debug) {\n console.debug('[AgentShield] Blocked by policy', {\n reason: result.blockReason,\n agent: result.detectedAgent?.name,\n confidence: result.confidence,\n pathname: request.nextUrl.pathname,\n });\n }\n\n // Call custom detection handler if provided\n if (onDetection) {\n const customResponse = await onDetection(request, result);\n if (customResponse) {\n return customResponse;\n }\n }\n\n // Return 403 with policy block reason\n return NextResponse.json(\n {\n error: 'Access denied by policy',\n reason: result.blockReason,\n detected: true,\n confidence: result.confidence,\n agent: result.detectedAgent?.name,\n timestamp: result.timestamp,\n },\n {\n status: 403,\n headers: { 'Content-Type': 'application/json' },\n }\n );\n }\n\n // Check if agent detected above threshold (when policy doesn't block)\n // Skip if shouldBlock === false (agent explicitly allowed by policy)\n if (result.shouldBlock !== false) {\n const decision = evaluateEnforcement(result, {\n confidenceThreshold,\n defaultAction: onAgentDetected,\n });\n\n if (decision.shouldNotify) {\n // Call custom detection handler if provided\n if (onDetection) {\n const customResponse = await onDetection(request, result);\n if (customResponse) {\n return customResponse;\n }\n }\n\n // Handle based on decision action\n switch (decision.action) {\n case 'block': {\n const response = NextResponse.json(\n {\n error: blockedResponse.message,\n detected: true,\n confidence: result.confidence,\n timestamp: result.timestamp,\n },\n { status: blockedResponse.status }\n );\n\n if (blockedResponse.headers) {\n Object.entries(blockedResponse.headers).forEach(([key, value]) => {\n response.headers.set(key, value);\n });\n }\n\n return response;\n }\n\n case 'redirect':\n return NextResponse.redirect(new URL(redirectUrl, request.url));\n\n case 'rewrite':\n return NextResponse.rewrite(new URL(rewriteUrl, request.url));\n\n case 'log':\n if (debug || process.env.NODE_ENV !== 'production') {\n console.debug('AgentShield: Agent detected', {\n ipAddress: input.ipAddress,\n userAgent: input.userAgent,\n confidence: result.confidence,\n agent: result.detectedAgent?.name,\n pathname: request.nextUrl.pathname,\n });\n }\n break;\n\n default:\n break;\n }\n }\n }\n\n // Continue with request\n const response = NextResponse.next();\n\n // Add detection headers\n response.headers.set('kya-detected', result.isAgent.toString());\n response.headers.set('kya-confidence', result.confidence.toString());\n if (result.detectedAgent?.name) {\n response.headers.set('kya-agent', result.detectedAgent.name);\n }\n\n return response;\n } catch (error) {\n console.error('AgentShield middleware error:', error);\n return NextResponse.next();\n }\n };\n}\n\n// Re-export types for convenience\nexport type { DetectionResult };\n"]}
@@ -285,7 +285,15 @@ async function verifyAgentSignature(method, path, headers) {
285
285
  }
286
286
  let agent;
287
287
  let agentKey;
288
- if (signatureAgent === '"https://chatgpt.com"' || signatureAgent?.includes("chatgpt.com")) {
288
+ const isChatGPT = signatureAgent === '"https://chatgpt.com"' || (() => {
289
+ try {
290
+ const url = new URL(signatureAgent?.replace(/^"|"$/g, "") || "");
291
+ return url.hostname === "chatgpt.com" || url.hostname.endsWith(".chatgpt.com");
292
+ } catch {
293
+ return false;
294
+ }
295
+ })();
296
+ if (isChatGPT) {
289
297
  agent = "ChatGPT";
290
298
  agentKey = "chatgpt";
291
299
  }