@kya-os/agentshield-nextjs 0.3.2 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +21 -369
  2. package/index.js +9 -0
  3. package/package.json +6 -141
  4. package/EDGE_RUNTIME_WASM_SETUP.md +0 -348
  5. package/bin/setup-edge-wasm.js +0 -525
  6. package/dist/.tsbuildinfo +0 -1
  7. package/dist/api-client.d.mts +0 -196
  8. package/dist/api-client.d.ts +0 -196
  9. package/dist/api-client.js +0 -200
  10. package/dist/api-client.js.map +0 -1
  11. package/dist/api-client.mjs +0 -196
  12. package/dist/api-client.mjs.map +0 -1
  13. package/dist/api-middleware.d.mts +0 -140
  14. package/dist/api-middleware.d.ts +0 -140
  15. package/dist/api-middleware.js +0 -511
  16. package/dist/api-middleware.js.map +0 -1
  17. package/dist/api-middleware.mjs +0 -508
  18. package/dist/api-middleware.mjs.map +0 -1
  19. package/dist/create-middleware.d.mts +0 -17
  20. package/dist/create-middleware.d.ts +0 -17
  21. package/dist/create-middleware.js +0 -1381
  22. package/dist/create-middleware.js.map +0 -1
  23. package/dist/create-middleware.mjs +0 -1358
  24. package/dist/create-middleware.mjs.map +0 -1
  25. package/dist/edge/index.d.mts +0 -110
  26. package/dist/edge/index.d.ts +0 -110
  27. package/dist/edge/index.js +0 -277
  28. package/dist/edge/index.js.map +0 -1
  29. package/dist/edge/index.mjs +0 -275
  30. package/dist/edge/index.mjs.map +0 -1
  31. package/dist/edge-detector-wrapper.d.mts +0 -34
  32. package/dist/edge-detector-wrapper.d.ts +0 -34
  33. package/dist/edge-detector-wrapper.js +0 -596
  34. package/dist/edge-detector-wrapper.js.map +0 -1
  35. package/dist/edge-detector-wrapper.mjs +0 -574
  36. package/dist/edge-detector-wrapper.mjs.map +0 -1
  37. package/dist/edge-runtime-loader.d.mts +0 -50
  38. package/dist/edge-runtime-loader.d.ts +0 -50
  39. package/dist/edge-runtime-loader.js +0 -204
  40. package/dist/edge-runtime-loader.js.map +0 -1
  41. package/dist/edge-runtime-loader.mjs +0 -201
  42. package/dist/edge-runtime-loader.mjs.map +0 -1
  43. package/dist/edge-wasm-middleware.d.mts +0 -68
  44. package/dist/edge-wasm-middleware.d.ts +0 -68
  45. package/dist/edge-wasm-middleware.js +0 -318
  46. package/dist/edge-wasm-middleware.js.map +0 -1
  47. package/dist/edge-wasm-middleware.mjs +0 -315
  48. package/dist/edge-wasm-middleware.mjs.map +0 -1
  49. package/dist/enhanced-middleware.d.mts +0 -153
  50. package/dist/enhanced-middleware.d.ts +0 -153
  51. package/dist/enhanced-middleware.js +0 -1082
  52. package/dist/enhanced-middleware.js.map +0 -1
  53. package/dist/enhanced-middleware.mjs +0 -1080
  54. package/dist/enhanced-middleware.mjs.map +0 -1
  55. package/dist/index.d.mts +0 -24
  56. package/dist/index.d.ts +0 -24
  57. package/dist/index.js +0 -2717
  58. package/dist/index.js.map +0 -1
  59. package/dist/index.mjs +0 -2662
  60. package/dist/index.mjs.map +0 -1
  61. package/dist/middleware.d.mts +0 -21
  62. package/dist/middleware.d.ts +0 -21
  63. package/dist/middleware.js +0 -1362
  64. package/dist/middleware.js.map +0 -1
  65. package/dist/middleware.mjs +0 -1339
  66. package/dist/middleware.mjs.map +0 -1
  67. package/dist/nodejs-wasm-loader.d.mts +0 -25
  68. package/dist/nodejs-wasm-loader.d.ts +0 -25
  69. package/dist/nodejs-wasm-loader.js +0 -78
  70. package/dist/nodejs-wasm-loader.js.map +0 -1
  71. package/dist/nodejs-wasm-loader.mjs +0 -68
  72. package/dist/nodejs-wasm-loader.mjs.map +0 -1
  73. package/dist/policy.d.mts +0 -162
  74. package/dist/policy.d.ts +0 -162
  75. package/dist/policy.js +0 -189
  76. package/dist/policy.js.map +0 -1
  77. package/dist/policy.mjs +0 -165
  78. package/dist/policy.mjs.map +0 -1
  79. package/dist/session-tracker.d.mts +0 -55
  80. package/dist/session-tracker.d.ts +0 -55
  81. package/dist/session-tracker.js +0 -170
  82. package/dist/session-tracker.js.map +0 -1
  83. package/dist/session-tracker.mjs +0 -167
  84. package/dist/session-tracker.mjs.map +0 -1
  85. package/dist/signature-verifier.d.mts +0 -33
  86. package/dist/signature-verifier.d.ts +0 -33
  87. package/dist/signature-verifier.js +0 -386
  88. package/dist/signature-verifier.js.map +0 -1
  89. package/dist/signature-verifier.mjs +0 -362
  90. package/dist/signature-verifier.mjs.map +0 -1
  91. package/dist/types-DVmy9NE3.d.mts +0 -105
  92. package/dist/types-DVmy9NE3.d.ts +0 -105
  93. package/dist/wasm-middleware.d.mts +0 -63
  94. package/dist/wasm-middleware.d.ts +0 -63
  95. package/dist/wasm-middleware.js +0 -98
  96. package/dist/wasm-middleware.js.map +0 -1
  97. package/dist/wasm-middleware.mjs +0 -95
  98. package/dist/wasm-middleware.mjs.map +0 -1
  99. package/dist/wasm-setup.d.mts +0 -46
  100. package/dist/wasm-setup.d.ts +0 -46
  101. package/dist/wasm-setup.js +0 -157
  102. package/dist/wasm-setup.js.map +0 -1
  103. package/dist/wasm-setup.mjs +0 -148
  104. package/dist/wasm-setup.mjs.map +0 -1
  105. package/templates/middleware-wasm-100.ts +0 -151
  106. package/wasm/agentshield_wasm.d.ts +0 -479
  107. package/wasm/agentshield_wasm.js +0 -1536
  108. package/wasm/agentshield_wasm_bg.wasm +0 -0
  109. package/wasm/package.json +0 -30
  110. package/wasm.d.ts +0 -21
@@ -1,277 +0,0 @@
1
- 'use strict';
2
-
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
- }
23
-
24
- // src/edge/index.ts
25
- async function patternDetect(input) {
26
- const userAgent = input.userAgent?.toLowerCase() || "";
27
- const reasons = [];
28
- let confidence = 0;
29
- let detectedAgent;
30
- const patterns = [
31
- { pattern: /chatgpt-user/i, name: "ChatGPT", confidence: 95 },
32
- { pattern: /claude-web/i, name: "Claude", confidence: 95 },
33
- { pattern: /claude-user/i, name: "Claude", confidence: 95 },
34
- { pattern: /claude-?bot/i, name: "ClaudeBot", confidence: 95 },
35
- { pattern: /anthropic/i, name: "Claude", confidence: 90 },
36
- { pattern: /gptbot/i, name: "GPTBot", confidence: 90 },
37
- { pattern: /perplexity/i, name: "Perplexity", confidence: 90 },
38
- { pattern: /openai/i, name: "OpenAI", confidence: 85 },
39
- { pattern: /copilot/i, name: "Copilot", confidence: 85 },
40
- { pattern: /gemini/i, name: "Gemini", confidence: 85 },
41
- { pattern: /bard/i, name: "Bard", confidence: 85 },
42
- // HTTP client libraries (often used by AI agents/scrapers)
43
- { pattern: /^axios\//i, name: "HTTP Client (axios)", confidence: 75 },
44
- { pattern: /^node-fetch\//i, name: "HTTP Client (node-fetch)", confidence: 75 },
45
- { pattern: /^got\//i, name: "HTTP Client (got)", confidence: 75 },
46
- { pattern: /^python-requests\//i, name: "HTTP Client (requests)", confidence: 75 },
47
- { pattern: /^python-urllib/i, name: "HTTP Client (urllib)", confidence: 75 },
48
- { pattern: /^curl\//i, name: "HTTP Client (curl)", confidence: 60 },
49
- { pattern: /^wget\//i, name: "HTTP Client (wget)", confidence: 60 },
50
- { pattern: /^httpie\//i, name: "HTTP Client (httpie)", confidence: 70 },
51
- { pattern: /^undici\//i, name: "HTTP Client (undici)", confidence: 75 }
52
- ];
53
- for (const { pattern, name, confidence: patternConfidence } of patterns) {
54
- if (pattern.test(userAgent)) {
55
- confidence = patternConfidence;
56
- detectedAgent = { type: name.toLowerCase(), name };
57
- reasons.push(`known_pattern:${name.toLowerCase()}`);
58
- break;
59
- }
60
- }
61
- const aiHeaders = ["x-openai-", "x-anthropic-", "x-ai-", "signature-agent"];
62
- for (const [key] of Object.entries(input.headers)) {
63
- if (aiHeaders.some((prefix) => key.toLowerCase().startsWith(prefix))) {
64
- confidence = Math.max(confidence, 45);
65
- reasons.push(`ai_headers:${key}`);
66
- break;
67
- }
68
- }
69
- return {
70
- isAgent: confidence > 30,
71
- confidence,
72
- detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
73
- signals: [],
74
- ...detectedAgent && { detectedAgent },
75
- reasons,
76
- verificationMethod: "pattern",
77
- forgeabilityRisk: confidence > 80 ? "medium" : "high",
78
- timestamp: /* @__PURE__ */ new Date()
79
- };
80
- }
81
- async function createDetector(config) {
82
- let detector;
83
- if (config.wasmModule) {
84
- try {
85
- const { StaticWasmLoader, WasmDetector, PolicyLoader } = await import('@kya-os/agentshield-wasm-runtime/edge');
86
- const loader = new StaticWasmLoader(config.wasmModule);
87
- const policyLoader = config.apiKey ? new PolicyLoader({
88
- apiUrl: config.policyApiUrl
89
- }) : void 0;
90
- const wasmDetector = new WasmDetector(loader, policyLoader, {
91
- apiKey: config.apiKey,
92
- debug: config.debug
93
- });
94
- await wasmDetector.ensureReady();
95
- if (config.debug) {
96
- console.debug("[AgentShield] WASM detector initialized in Edge Runtime", {
97
- policyEnabled: !!config.apiKey
98
- });
99
- }
100
- detector = {
101
- detect: async (input) => {
102
- const result = await wasmDetector.detect(input);
103
- return {
104
- isAgent: result.isAgent,
105
- isAiCrawler: result.isAiCrawler,
106
- confidence: result.confidence,
107
- detectionClass: result.detectionClass,
108
- detectedAgent: result.detectedAgent,
109
- verificationMethod: result.verificationMethod,
110
- forgeabilityRisk: result.forgeabilityRisk,
111
- reasons: result.reasons,
112
- timestamp: result.timestamp,
113
- signals: [],
114
- // Required by DetectionResult, empty for WASM results
115
- shouldBlock: result.shouldBlock,
116
- blockReason: result.blockReason
117
- };
118
- },
119
- isReady: () => wasmDetector.isReady()
120
- };
121
- } catch (error) {
122
- if (config.debug) {
123
- console.warn("[AgentShield] WASM runtime not available, using pattern detection:", error);
124
- }
125
- detector = {
126
- detect: patternDetect,
127
- isReady: () => true
128
- };
129
- }
130
- } else {
131
- if (config.debug) {
132
- console.debug("[AgentShield] No WASM module provided, using pattern detection");
133
- }
134
- detector = {
135
- detect: patternDetect,
136
- isReady: () => true
137
- };
138
- }
139
- return detector;
140
- }
141
- function createEdgeMiddleware(config = {}) {
142
- let detectorPromise = null;
143
- const {
144
- onAgentDetected = "log",
145
- onDetection,
146
- skipPaths = [],
147
- confidenceThreshold = 70,
148
- blockedResponse = {
149
- status: 403,
150
- message: "Access denied: Automated agent detected",
151
- headers: { "Content-Type": "application/json" }
152
- },
153
- redirectUrl = "/blocked",
154
- rewriteUrl = "/blocked",
155
- debug = false
156
- } = config;
157
- return async (request) => {
158
- try {
159
- if (!detectorPromise) {
160
- detectorPromise = createDetector({ ...config, debug });
161
- }
162
- const detector = await detectorPromise;
163
- const shouldSkip = skipPaths.some((pattern) => {
164
- if (typeof pattern === "string") {
165
- return request.nextUrl.pathname.startsWith(pattern);
166
- }
167
- return pattern.test(request.nextUrl.pathname);
168
- });
169
- if (shouldSkip) {
170
- return server.NextResponse.next();
171
- }
172
- const url = new URL(request.url);
173
- const input = {
174
- userAgent: request.headers.get("user-agent") || void 0,
175
- ipAddress: getClientIp(request),
176
- headers: Object.fromEntries(request.headers.entries()),
177
- url: url.pathname + url.search,
178
- method: request.method
179
- };
180
- const result = await detector.detect(input);
181
- if (result.shouldBlock) {
182
- if (debug) {
183
- console.debug("[AgentShield] Blocked by policy", {
184
- reason: result.blockReason,
185
- agent: result.detectedAgent?.name,
186
- confidence: result.confidence,
187
- pathname: request.nextUrl.pathname
188
- });
189
- }
190
- if (onDetection) {
191
- const customResponse = await onDetection(request, result);
192
- if (customResponse) {
193
- return customResponse;
194
- }
195
- }
196
- return server.NextResponse.json(
197
- {
198
- error: "Access denied by policy",
199
- reason: result.blockReason,
200
- detected: true,
201
- confidence: result.confidence,
202
- agent: result.detectedAgent?.name,
203
- timestamp: result.timestamp
204
- },
205
- {
206
- status: 403,
207
- headers: { "Content-Type": "application/json" }
208
- }
209
- );
210
- }
211
- if (result.shouldBlock !== false) {
212
- const decision = agentshieldShared.evaluateEnforcement(result, {
213
- confidenceThreshold,
214
- defaultAction: onAgentDetected
215
- });
216
- if (decision.shouldNotify) {
217
- if (onDetection) {
218
- const customResponse = await onDetection(request, result);
219
- if (customResponse) {
220
- return customResponse;
221
- }
222
- }
223
- switch (decision.action) {
224
- case "block": {
225
- const response2 = server.NextResponse.json(
226
- {
227
- error: blockedResponse.message,
228
- detected: true,
229
- confidence: result.confidence,
230
- timestamp: result.timestamp
231
- },
232
- { status: blockedResponse.status }
233
- );
234
- if (blockedResponse.headers) {
235
- Object.entries(blockedResponse.headers).forEach(([key, value]) => {
236
- response2.headers.set(key, value);
237
- });
238
- }
239
- return response2;
240
- }
241
- case "redirect":
242
- return server.NextResponse.redirect(new URL(redirectUrl, request.url));
243
- case "rewrite":
244
- return server.NextResponse.rewrite(new URL(rewriteUrl, request.url));
245
- case "log":
246
- if (debug || process.env.NODE_ENV !== "production") {
247
- console.debug("AgentShield: Agent detected", {
248
- ipAddress: input.ipAddress,
249
- userAgent: input.userAgent,
250
- confidence: result.confidence,
251
- agent: result.detectedAgent?.name,
252
- pathname: request.nextUrl.pathname
253
- });
254
- }
255
- break;
256
- default:
257
- break;
258
- }
259
- }
260
- }
261
- const response = server.NextResponse.next();
262
- response.headers.set("kya-detected", result.isAgent.toString());
263
- response.headers.set("kya-confidence", result.confidence.toString());
264
- if (result.detectedAgent?.name) {
265
- response.headers.set("kya-agent", result.detectedAgent.name);
266
- }
267
- return response;
268
- } catch (error) {
269
- console.error("AgentShield middleware error:", error);
270
- return server.NextResponse.next();
271
- }
272
- };
273
- }
274
-
275
- exports.createEdgeMiddleware = createEdgeMiddleware;
276
- //# sourceMappingURL=index.js.map
277
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
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-user/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,275 +0,0 @@
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
- }
21
-
22
- // src/edge/index.ts
23
- async function patternDetect(input) {
24
- const userAgent = input.userAgent?.toLowerCase() || "";
25
- const reasons = [];
26
- let confidence = 0;
27
- let detectedAgent;
28
- const patterns = [
29
- { pattern: /chatgpt-user/i, name: "ChatGPT", confidence: 95 },
30
- { pattern: /claude-web/i, name: "Claude", confidence: 95 },
31
- { pattern: /claude-user/i, name: "Claude", confidence: 95 },
32
- { pattern: /claude-?bot/i, name: "ClaudeBot", confidence: 95 },
33
- { pattern: /anthropic/i, name: "Claude", confidence: 90 },
34
- { pattern: /gptbot/i, name: "GPTBot", confidence: 90 },
35
- { pattern: /perplexity/i, name: "Perplexity", confidence: 90 },
36
- { pattern: /openai/i, name: "OpenAI", confidence: 85 },
37
- { pattern: /copilot/i, name: "Copilot", confidence: 85 },
38
- { pattern: /gemini/i, name: "Gemini", confidence: 85 },
39
- { pattern: /bard/i, name: "Bard", confidence: 85 },
40
- // HTTP client libraries (often used by AI agents/scrapers)
41
- { pattern: /^axios\//i, name: "HTTP Client (axios)", confidence: 75 },
42
- { pattern: /^node-fetch\//i, name: "HTTP Client (node-fetch)", confidence: 75 },
43
- { pattern: /^got\//i, name: "HTTP Client (got)", confidence: 75 },
44
- { pattern: /^python-requests\//i, name: "HTTP Client (requests)", confidence: 75 },
45
- { pattern: /^python-urllib/i, name: "HTTP Client (urllib)", confidence: 75 },
46
- { pattern: /^curl\//i, name: "HTTP Client (curl)", confidence: 60 },
47
- { pattern: /^wget\//i, name: "HTTP Client (wget)", confidence: 60 },
48
- { pattern: /^httpie\//i, name: "HTTP Client (httpie)", confidence: 70 },
49
- { pattern: /^undici\//i, name: "HTTP Client (undici)", confidence: 75 }
50
- ];
51
- for (const { pattern, name, confidence: patternConfidence } of patterns) {
52
- if (pattern.test(userAgent)) {
53
- confidence = patternConfidence;
54
- detectedAgent = { type: name.toLowerCase(), name };
55
- reasons.push(`known_pattern:${name.toLowerCase()}`);
56
- break;
57
- }
58
- }
59
- const aiHeaders = ["x-openai-", "x-anthropic-", "x-ai-", "signature-agent"];
60
- for (const [key] of Object.entries(input.headers)) {
61
- if (aiHeaders.some((prefix) => key.toLowerCase().startsWith(prefix))) {
62
- confidence = Math.max(confidence, 45);
63
- reasons.push(`ai_headers:${key}`);
64
- break;
65
- }
66
- }
67
- return {
68
- isAgent: confidence > 30,
69
- confidence,
70
- detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
71
- signals: [],
72
- ...detectedAgent && { detectedAgent },
73
- reasons,
74
- verificationMethod: "pattern",
75
- forgeabilityRisk: confidence > 80 ? "medium" : "high",
76
- timestamp: /* @__PURE__ */ new Date()
77
- };
78
- }
79
- async function createDetector(config) {
80
- let detector;
81
- if (config.wasmModule) {
82
- try {
83
- const { StaticWasmLoader, WasmDetector, PolicyLoader } = await import('@kya-os/agentshield-wasm-runtime/edge');
84
- const loader = new StaticWasmLoader(config.wasmModule);
85
- const policyLoader = config.apiKey ? new PolicyLoader({
86
- apiUrl: config.policyApiUrl
87
- }) : void 0;
88
- const wasmDetector = new WasmDetector(loader, policyLoader, {
89
- apiKey: config.apiKey,
90
- debug: config.debug
91
- });
92
- await wasmDetector.ensureReady();
93
- if (config.debug) {
94
- console.debug("[AgentShield] WASM detector initialized in Edge Runtime", {
95
- policyEnabled: !!config.apiKey
96
- });
97
- }
98
- detector = {
99
- detect: async (input) => {
100
- const result = await wasmDetector.detect(input);
101
- return {
102
- isAgent: result.isAgent,
103
- isAiCrawler: result.isAiCrawler,
104
- confidence: result.confidence,
105
- detectionClass: result.detectionClass,
106
- detectedAgent: result.detectedAgent,
107
- verificationMethod: result.verificationMethod,
108
- forgeabilityRisk: result.forgeabilityRisk,
109
- reasons: result.reasons,
110
- timestamp: result.timestamp,
111
- signals: [],
112
- // Required by DetectionResult, empty for WASM results
113
- shouldBlock: result.shouldBlock,
114
- blockReason: result.blockReason
115
- };
116
- },
117
- isReady: () => wasmDetector.isReady()
118
- };
119
- } catch (error) {
120
- if (config.debug) {
121
- console.warn("[AgentShield] WASM runtime not available, using pattern detection:", error);
122
- }
123
- detector = {
124
- detect: patternDetect,
125
- isReady: () => true
126
- };
127
- }
128
- } else {
129
- if (config.debug) {
130
- console.debug("[AgentShield] No WASM module provided, using pattern detection");
131
- }
132
- detector = {
133
- detect: patternDetect,
134
- isReady: () => true
135
- };
136
- }
137
- return detector;
138
- }
139
- function createEdgeMiddleware(config = {}) {
140
- let detectorPromise = null;
141
- const {
142
- onAgentDetected = "log",
143
- onDetection,
144
- skipPaths = [],
145
- confidenceThreshold = 70,
146
- blockedResponse = {
147
- status: 403,
148
- message: "Access denied: Automated agent detected",
149
- headers: { "Content-Type": "application/json" }
150
- },
151
- redirectUrl = "/blocked",
152
- rewriteUrl = "/blocked",
153
- debug = false
154
- } = config;
155
- return async (request) => {
156
- try {
157
- if (!detectorPromise) {
158
- detectorPromise = createDetector({ ...config, debug });
159
- }
160
- const detector = await detectorPromise;
161
- const shouldSkip = skipPaths.some((pattern) => {
162
- if (typeof pattern === "string") {
163
- return request.nextUrl.pathname.startsWith(pattern);
164
- }
165
- return pattern.test(request.nextUrl.pathname);
166
- });
167
- if (shouldSkip) {
168
- return NextResponse.next();
169
- }
170
- const url = new URL(request.url);
171
- const input = {
172
- userAgent: request.headers.get("user-agent") || void 0,
173
- ipAddress: getClientIp(request),
174
- headers: Object.fromEntries(request.headers.entries()),
175
- url: url.pathname + url.search,
176
- method: request.method
177
- };
178
- const result = await detector.detect(input);
179
- if (result.shouldBlock) {
180
- if (debug) {
181
- console.debug("[AgentShield] Blocked by policy", {
182
- reason: result.blockReason,
183
- agent: result.detectedAgent?.name,
184
- confidence: result.confidence,
185
- pathname: request.nextUrl.pathname
186
- });
187
- }
188
- if (onDetection) {
189
- const customResponse = await onDetection(request, result);
190
- if (customResponse) {
191
- return customResponse;
192
- }
193
- }
194
- return NextResponse.json(
195
- {
196
- error: "Access denied by policy",
197
- reason: result.blockReason,
198
- detected: true,
199
- confidence: result.confidence,
200
- agent: result.detectedAgent?.name,
201
- timestamp: result.timestamp
202
- },
203
- {
204
- status: 403,
205
- headers: { "Content-Type": "application/json" }
206
- }
207
- );
208
- }
209
- if (result.shouldBlock !== false) {
210
- const decision = evaluateEnforcement(result, {
211
- confidenceThreshold,
212
- defaultAction: onAgentDetected
213
- });
214
- if (decision.shouldNotify) {
215
- if (onDetection) {
216
- const customResponse = await onDetection(request, result);
217
- if (customResponse) {
218
- return customResponse;
219
- }
220
- }
221
- switch (decision.action) {
222
- case "block": {
223
- const response2 = NextResponse.json(
224
- {
225
- error: blockedResponse.message,
226
- detected: true,
227
- confidence: result.confidence,
228
- timestamp: result.timestamp
229
- },
230
- { status: blockedResponse.status }
231
- );
232
- if (blockedResponse.headers) {
233
- Object.entries(blockedResponse.headers).forEach(([key, value]) => {
234
- response2.headers.set(key, value);
235
- });
236
- }
237
- return response2;
238
- }
239
- case "redirect":
240
- return NextResponse.redirect(new URL(redirectUrl, request.url));
241
- case "rewrite":
242
- return NextResponse.rewrite(new URL(rewriteUrl, request.url));
243
- case "log":
244
- if (debug || process.env.NODE_ENV !== "production") {
245
- console.debug("AgentShield: Agent detected", {
246
- ipAddress: input.ipAddress,
247
- userAgent: input.userAgent,
248
- confidence: result.confidence,
249
- agent: result.detectedAgent?.name,
250
- pathname: request.nextUrl.pathname
251
- });
252
- }
253
- break;
254
- default:
255
- break;
256
- }
257
- }
258
- }
259
- const response = NextResponse.next();
260
- response.headers.set("kya-detected", result.isAgent.toString());
261
- response.headers.set("kya-confidence", result.confidence.toString());
262
- if (result.detectedAgent?.name) {
263
- response.headers.set("kya-agent", result.detectedAgent.name);
264
- }
265
- return response;
266
- } catch (error) {
267
- console.error("AgentShield middleware error:", error);
268
- return NextResponse.next();
269
- }
270
- };
271
- }
272
-
273
- export { createEdgeMiddleware };
274
- //# sourceMappingURL=index.mjs.map
275
- //# sourceMappingURL=index.mjs.map