@kya-os/agentshield-nextjs 0.1.40 → 0.1.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/api-client.d.mts +145 -0
  3. package/dist/api-client.d.ts +145 -0
  4. package/dist/api-client.js +130 -0
  5. package/dist/api-client.js.map +1 -0
  6. package/dist/api-client.mjs +126 -0
  7. package/dist/api-client.mjs.map +1 -0
  8. package/dist/api-middleware.d.mts +118 -0
  9. package/dist/api-middleware.d.ts +118 -0
  10. package/dist/api-middleware.js +295 -0
  11. package/dist/api-middleware.js.map +1 -0
  12. package/dist/api-middleware.mjs +292 -0
  13. package/dist/api-middleware.mjs.map +1 -0
  14. package/dist/create-middleware.d.mts +2 -1
  15. package/dist/create-middleware.d.ts +2 -1
  16. package/dist/create-middleware.js +474 -200
  17. package/dist/create-middleware.js.map +1 -1
  18. package/dist/create-middleware.mjs +454 -200
  19. package/dist/create-middleware.mjs.map +1 -1
  20. package/dist/edge/index.d.mts +110 -0
  21. package/dist/edge/index.d.ts +110 -0
  22. package/dist/edge/index.js +253 -0
  23. package/dist/edge/index.js.map +1 -0
  24. package/dist/edge/index.mjs +251 -0
  25. package/dist/edge/index.mjs.map +1 -0
  26. package/dist/edge-detector-wrapper.d.mts +6 -15
  27. package/dist/edge-detector-wrapper.d.ts +6 -15
  28. package/dist/edge-detector-wrapper.js +314 -95
  29. package/dist/edge-detector-wrapper.js.map +1 -1
  30. package/dist/edge-detector-wrapper.mjs +294 -95
  31. package/dist/edge-detector-wrapper.mjs.map +1 -1
  32. package/dist/edge-runtime-loader.d.mts +1 -1
  33. package/dist/edge-runtime-loader.d.ts +1 -1
  34. package/dist/edge-runtime-loader.js +10 -25
  35. package/dist/edge-runtime-loader.js.map +1 -1
  36. package/dist/edge-runtime-loader.mjs +11 -23
  37. package/dist/edge-runtime-loader.mjs.map +1 -1
  38. package/dist/edge-wasm-middleware.js +2 -1
  39. package/dist/edge-wasm-middleware.js.map +1 -1
  40. package/dist/edge-wasm-middleware.mjs +2 -1
  41. package/dist/edge-wasm-middleware.mjs.map +1 -1
  42. package/dist/enhanced-middleware.d.mts +153 -0
  43. package/dist/enhanced-middleware.d.ts +153 -0
  44. package/dist/enhanced-middleware.js +1074 -0
  45. package/dist/enhanced-middleware.js.map +1 -0
  46. package/dist/enhanced-middleware.mjs +1072 -0
  47. package/dist/enhanced-middleware.mjs.map +1 -0
  48. package/dist/index.d.mts +8 -153
  49. package/dist/index.d.ts +8 -153
  50. package/dist/index.js +1390 -680
  51. package/dist/index.js.map +1 -1
  52. package/dist/index.mjs +1370 -685
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/middleware.d.mts +2 -1
  55. package/dist/middleware.d.ts +2 -1
  56. package/dist/middleware.js +474 -200
  57. package/dist/middleware.js.map +1 -1
  58. package/dist/middleware.mjs +454 -200
  59. package/dist/middleware.mjs.map +1 -1
  60. package/dist/session-tracker.d.mts +1 -1
  61. package/dist/session-tracker.d.ts +1 -1
  62. package/dist/session-tracker.js.map +1 -1
  63. package/dist/session-tracker.mjs.map +1 -1
  64. package/dist/signature-verifier.d.mts +1 -0
  65. package/dist/signature-verifier.d.ts +1 -0
  66. package/dist/signature-verifier.js +204 -44
  67. package/dist/signature-verifier.js.map +1 -1
  68. package/dist/signature-verifier.mjs +184 -44
  69. package/dist/signature-verifier.mjs.map +1 -1
  70. package/dist/{types-BJTEUa4T.d.mts → types-DVmy9NE3.d.mts} +19 -2
  71. package/dist/{types-BJTEUa4T.d.ts → types-DVmy9NE3.d.ts} +19 -2
  72. package/dist/wasm-middleware.js +15 -6
  73. package/dist/wasm-middleware.js.map +1 -1
  74. package/dist/wasm-middleware.mjs +15 -6
  75. package/dist/wasm-middleware.mjs.map +1 -1
  76. package/package.json +43 -21
  77. package/wasm/agentshield_wasm.js +209 -152
  78. package/wasm/agentshield_wasm_bg.wasm +0 -0
  79. package/wasm/package.json +30 -0
@@ -0,0 +1,295 @@
1
+ 'use strict';
2
+
3
+ var server = require('next/server');
4
+
5
+ // src/api-middleware.ts
6
+
7
+ // src/api-client.ts
8
+ var DEFAULT_BASE_URL = "https://api.agentshield.ai";
9
+ var DEFAULT_TIMEOUT = 5e3;
10
+ var AgentShieldClient = class {
11
+ apiKey;
12
+ baseUrl;
13
+ timeout;
14
+ debug;
15
+ constructor(config) {
16
+ if (!config.apiKey) {
17
+ throw new Error("AgentShield API key is required");
18
+ }
19
+ this.apiKey = config.apiKey;
20
+ this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
21
+ this.timeout = config.timeout || DEFAULT_TIMEOUT;
22
+ this.debug = config.debug || false;
23
+ }
24
+ /**
25
+ * Call the enforce API to check if a request should be allowed
26
+ */
27
+ async enforce(input) {
28
+ const startTime = Date.now();
29
+ try {
30
+ const controller = new AbortController();
31
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
32
+ try {
33
+ const response = await fetch(`${this.baseUrl}/api/v1/enforce`, {
34
+ method: "POST",
35
+ headers: {
36
+ "Content-Type": "application/json",
37
+ Authorization: `Bearer ${this.apiKey}`,
38
+ "X-Request-ID": input.requestId || crypto.randomUUID()
39
+ },
40
+ body: JSON.stringify(input),
41
+ signal: controller.signal
42
+ });
43
+ clearTimeout(timeoutId);
44
+ const data = await response.json();
45
+ if (this.debug) {
46
+ console.log("[AgentShield] Enforce response:", {
47
+ status: response.status,
48
+ action: data.data?.decision.action,
49
+ processingTimeMs: Date.now() - startTime
50
+ });
51
+ }
52
+ if (!response.ok) {
53
+ return {
54
+ success: false,
55
+ error: {
56
+ code: `HTTP_${response.status}`,
57
+ message: data.error?.message || `HTTP error: ${response.status}`
58
+ }
59
+ };
60
+ }
61
+ return data;
62
+ } catch (error) {
63
+ clearTimeout(timeoutId);
64
+ throw error;
65
+ }
66
+ } catch (error) {
67
+ if (error instanceof Error && error.name === "AbortError") {
68
+ if (this.debug) {
69
+ console.warn("[AgentShield] Request timed out");
70
+ }
71
+ return {
72
+ success: false,
73
+ error: {
74
+ code: "TIMEOUT",
75
+ message: `Request timed out after ${this.timeout}ms`
76
+ }
77
+ };
78
+ }
79
+ if (this.debug) {
80
+ console.error("[AgentShield] Request failed:", error);
81
+ }
82
+ return {
83
+ success: false,
84
+ error: {
85
+ code: "NETWORK_ERROR",
86
+ message: error instanceof Error ? error.message : "Network request failed"
87
+ }
88
+ };
89
+ }
90
+ }
91
+ /**
92
+ * Quick check - returns just the action without full response parsing
93
+ * Useful for very fast middleware that just needs allow/block
94
+ */
95
+ async quickCheck(input) {
96
+ const result = await this.enforce(input);
97
+ if (!result.success || !result.data) {
98
+ return {
99
+ action: "allow",
100
+ error: result.error?.message
101
+ };
102
+ }
103
+ return {
104
+ action: result.data.decision.action
105
+ };
106
+ }
107
+ };
108
+ var clientInstance = null;
109
+ function getAgentShieldClient(config) {
110
+ if (!clientInstance) {
111
+ const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;
112
+ if (!apiKey) {
113
+ throw new Error(
114
+ "AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config."
115
+ );
116
+ }
117
+ clientInstance = new AgentShieldClient({
118
+ apiKey,
119
+ baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,
120
+ timeout: config?.timeout,
121
+ debug: config?.debug || process.env.AGENTSHIELD_DEBUG === "true"
122
+ });
123
+ }
124
+ return clientInstance;
125
+ }
126
+
127
+ // src/api-middleware.ts
128
+ function matchPath(path, pattern) {
129
+ if (pattern === path) return true;
130
+ if (pattern.includes("*")) {
131
+ const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
132
+ return new RegExp(`^${regexPattern}$`).test(path);
133
+ }
134
+ if (pattern.endsWith("/")) {
135
+ return path.startsWith(pattern) || path === pattern.slice(0, -1);
136
+ }
137
+ return path.startsWith(pattern);
138
+ }
139
+ function shouldSkipPath(path, skipPaths) {
140
+ return skipPaths.some((pattern) => matchPath(path, pattern));
141
+ }
142
+ function shouldIncludePath(path, includePaths) {
143
+ if (!includePaths || includePaths.length === 0) return true;
144
+ return includePaths.some((pattern) => matchPath(path, pattern));
145
+ }
146
+ function buildBlockedResponse(decision, config) {
147
+ const status = config.blockedResponse?.status ?? 403;
148
+ const message = config.blockedResponse?.message ?? decision.message ?? "Access denied";
149
+ const response = server.NextResponse.json(
150
+ {
151
+ error: message,
152
+ code: "AGENT_BLOCKED",
153
+ reason: decision.reason,
154
+ agentType: decision.agentType
155
+ },
156
+ { status }
157
+ );
158
+ if (config.blockedResponse?.headers) {
159
+ for (const [key, value] of Object.entries(config.blockedResponse.headers)) {
160
+ response.headers.set(key, value);
161
+ }
162
+ }
163
+ response.headers.set("X-AgentShield-Action", decision.action);
164
+ response.headers.set("X-AgentShield-Reason", decision.reason);
165
+ return response;
166
+ }
167
+ function buildRedirectResponse(request, decision, config) {
168
+ const redirectUrl = config.redirectUrl || decision.redirectUrl || "/blocked";
169
+ const url = new URL(redirectUrl, request.url);
170
+ url.searchParams.set("reason", decision.reason);
171
+ if (decision.agentType) {
172
+ url.searchParams.set("agent", decision.agentType);
173
+ }
174
+ return server.NextResponse.redirect(url);
175
+ }
176
+ function withAgentShield(config = {}) {
177
+ let client = null;
178
+ const getClient = () => {
179
+ if (!client) {
180
+ client = getAgentShieldClient({
181
+ apiKey: config.apiKey,
182
+ baseUrl: config.apiUrl,
183
+ timeout: config.timeout,
184
+ debug: config.debug
185
+ });
186
+ }
187
+ return client;
188
+ };
189
+ const defaultSkipPaths = [
190
+ "/_next/static/*",
191
+ "/_next/image/*",
192
+ "/favicon.ico",
193
+ "/robots.txt",
194
+ "/sitemap.xml"
195
+ ];
196
+ const skipPaths = [...defaultSkipPaths, ...config.skipPaths || []];
197
+ const failOpen = config.failOpen ?? true;
198
+ return async function middleware(request) {
199
+ const path = request.nextUrl.pathname;
200
+ const startTime = Date.now();
201
+ if (shouldSkipPath(path, skipPaths)) {
202
+ return server.NextResponse.next();
203
+ }
204
+ if (!shouldIncludePath(path, config.includePaths)) {
205
+ return server.NextResponse.next();
206
+ }
207
+ try {
208
+ const result = await getClient().enforce({
209
+ headers: Object.fromEntries(request.headers.entries()),
210
+ userAgent: request.headers.get("user-agent") || void 0,
211
+ ipAddress: request.ip || request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip") || void 0,
212
+ path,
213
+ url: request.url,
214
+ method: request.method,
215
+ requestId: request.headers.get("x-request-id") || void 0,
216
+ options: {
217
+ includeDetectionResult: config.debug
218
+ }
219
+ });
220
+ if (!result.success || !result.data) {
221
+ if (config.debug) {
222
+ console.warn("[AgentShield] API error:", result.error);
223
+ }
224
+ if (failOpen) {
225
+ return server.NextResponse.next();
226
+ }
227
+ return server.NextResponse.json(
228
+ { error: "Security check failed", code: "API_ERROR" },
229
+ { status: 503 }
230
+ );
231
+ }
232
+ const decision = result.data.decision;
233
+ if (config.debug) {
234
+ console.log("[AgentShield] Decision:", {
235
+ path,
236
+ action: decision.action,
237
+ isAgent: decision.isAgent,
238
+ confidence: decision.confidence,
239
+ agentName: decision.agentName,
240
+ processingTimeMs: Date.now() - startTime
241
+ });
242
+ }
243
+ if (decision.isAgent && config.onAgentDetected) {
244
+ await config.onAgentDetected(request, decision);
245
+ }
246
+ switch (decision.action) {
247
+ case "block": {
248
+ if (config.customBlockedResponse) {
249
+ return config.customBlockedResponse(request, decision);
250
+ }
251
+ if (config.onBlock === "redirect") {
252
+ return buildRedirectResponse(request, decision, config);
253
+ }
254
+ return buildBlockedResponse(decision, config);
255
+ }
256
+ case "redirect": {
257
+ return buildRedirectResponse(request, decision, config);
258
+ }
259
+ case "challenge": {
260
+ return buildRedirectResponse(request, decision, config);
261
+ }
262
+ case "log":
263
+ case "allow":
264
+ default: {
265
+ const response = server.NextResponse.next();
266
+ if (decision.isAgent) {
267
+ response.headers.set("X-AgentShield-Detected", "true");
268
+ response.headers.set("X-AgentShield-Confidence", decision.confidence.toString());
269
+ if (decision.agentName) {
270
+ response.headers.set("X-AgentShield-Agent", decision.agentName);
271
+ }
272
+ }
273
+ return response;
274
+ }
275
+ }
276
+ } catch (error) {
277
+ if (config.debug) {
278
+ console.error("[AgentShield] Middleware error:", error);
279
+ }
280
+ if (failOpen) {
281
+ return server.NextResponse.next();
282
+ }
283
+ return server.NextResponse.json(
284
+ { error: "Security check failed", code: "MIDDLEWARE_ERROR" },
285
+ { status: 503 }
286
+ );
287
+ }
288
+ };
289
+ }
290
+ var agentShieldMiddleware = withAgentShield();
291
+
292
+ exports.agentShieldMiddleware = agentShieldMiddleware;
293
+ exports.withAgentShield = withAgentShield;
294
+ //# sourceMappingURL=api-middleware.js.map
295
+ //# sourceMappingURL=api-middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api-client.ts","../src/api-middleware.ts"],"names":["NextResponse"],"mappings":"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","file":"api-middleware.js","sourcesContent":["/**\n * AgentShield API Client\n *\n * Lightweight client for calling the AgentShield enforce API from middleware.\n * Designed for Edge Runtime compatibility (no Node.js-specific APIs).\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * API client configuration\n */\nexport interface AgentShieldClientConfig {\n /** API key for authentication */\n apiKey: string;\n /** API base URL (defaults to production) */\n baseUrl?: string;\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n/**\n * Enforcement action\n */\nexport type EnforcementAction = 'allow' | 'block' | 'redirect' | 'challenge' | 'log';\n\n/**\n * Enforcement decision from the API\n */\nexport interface EnforcementDecision {\n action: EnforcementAction;\n reason: string;\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n redirectUrl?: string;\n message?: string;\n metadata?: {\n policyVersion?: string;\n signatureVerified?: boolean;\n denyListMatch?: {\n clientDid?: string;\n agentDid?: string;\n clientName?: string;\n reason?: string;\n };\n };\n}\n\n/**\n * Detection result (optional in response)\n */\nexport interface DetectionResult {\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n verificationMethod?: string;\n reasons?: string[];\n}\n\n/**\n * Enforce API response\n */\nexport interface EnforceResponse {\n success: boolean;\n data?: {\n decision: EnforcementDecision;\n processingTimeMs: number;\n requestId: string;\n detection?: DetectionResult;\n };\n error?: {\n code: string;\n message: string;\n };\n}\n\n/**\n * Request input for enforce API\n */\nexport interface EnforceInput {\n /** HTTP headers from the incoming request */\n headers?: Record<string, string>;\n /** User-Agent header */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Request path */\n path?: string;\n /** Request URL */\n url?: string;\n /** HTTP method */\n method?: string;\n /** Request ID for tracing */\n requestId?: string;\n /** Options */\n options?: {\n /** Include full detection result */\n includeDetectionResult?: boolean;\n /** Cache TTL override */\n cacheTTL?: number;\n };\n}\n\n// ============================================================================\n// Client Implementation\n// ============================================================================\n\nconst DEFAULT_BASE_URL = 'https://api.agentshield.ai';\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * AgentShield API Client\n *\n * @example\n * ```typescript\n * const client = new AgentShieldClient({\n * apiKey: process.env.AGENTSHIELD_API_KEY!,\n * });\n *\n * const result = await client.enforce({\n * headers: Object.fromEntries(request.headers),\n * path: request.nextUrl.pathname,\n * method: request.method,\n * });\n *\n * if (result.decision.action === 'block') {\n * return new Response('Access denied', { status: 403 });\n * }\n * ```\n */\nexport class AgentShieldClient {\n private apiKey: string;\n private baseUrl: string;\n private timeout: number;\n private debug: boolean;\n\n constructor(config: AgentShieldClientConfig) {\n if (!config.apiKey) {\n throw new Error('AgentShield API key is required');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n this.debug = config.debug || false;\n }\n\n /**\n * Call the enforce API to check if a request should be allowed\n */\n async enforce(input: EnforceInput): Promise<EnforceResponse> {\n const startTime = Date.now();\n\n try {\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(`${this.baseUrl}/api/v1/enforce`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'X-Request-ID': input.requestId || crypto.randomUUID(),\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = (await response.json()) as EnforceResponse;\n\n if (this.debug) {\n console.log('[AgentShield] Enforce response:', {\n status: response.status,\n action: data.data?.decision.action,\n processingTimeMs: Date.now() - startTime,\n });\n }\n\n // Handle non-2xx responses\n if (!response.ok) {\n return {\n success: false,\n error: {\n code: `HTTP_${response.status}`,\n message: data.error?.message || `HTTP error: ${response.status}`,\n },\n };\n }\n\n return data;\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Handle timeout\n if (error instanceof Error && error.name === 'AbortError') {\n if (this.debug) {\n console.warn('[AgentShield] Request timed out');\n }\n return {\n success: false,\n error: {\n code: 'TIMEOUT',\n message: `Request timed out after ${this.timeout}ms`,\n },\n };\n }\n\n // Handle network errors\n if (this.debug) {\n console.error('[AgentShield] Request failed:', error);\n }\n\n return {\n success: false,\n error: {\n code: 'NETWORK_ERROR',\n message: error instanceof Error ? error.message : 'Network request failed',\n },\n };\n }\n }\n\n /**\n * Quick check - returns just the action without full response parsing\n * Useful for very fast middleware that just needs allow/block\n */\n async quickCheck(input: EnforceInput): Promise<{\n action: EnforcementAction;\n error?: string;\n }> {\n const result = await this.enforce(input);\n\n if (!result.success || !result.data) {\n // On error, default to allow (fail-open)\n return {\n action: 'allow',\n error: result.error?.message,\n };\n }\n\n return {\n action: result.data.decision.action,\n };\n }\n}\n\n/**\n * Create a singleton client instance\n *\n * @example\n * ```typescript\n * // In middleware.ts\n * import { getAgentShieldClient } from '@kya-os/agentshield-nextjs';\n *\n * const client = getAgentShieldClient();\n * ```\n */\nlet clientInstance: AgentShieldClient | null = null;\n\nexport function getAgentShieldClient(config?: Partial<AgentShieldClientConfig>): AgentShieldClient {\n if (!clientInstance) {\n const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;\n\n if (!apiKey) {\n throw new Error(\n 'AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config.'\n );\n }\n\n clientInstance = new AgentShieldClient({\n apiKey,\n baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,\n timeout: config?.timeout,\n debug: config?.debug || process.env.AGENTSHIELD_DEBUG === 'true',\n });\n }\n\n return clientInstance;\n}\n\n/**\n * Reset the singleton client (useful for testing)\n */\nexport function resetAgentShieldClient(): void {\n clientInstance = null;\n}\n","/**\n * API-based AgentShield Middleware for Next.js\n *\n * This middleware uses the AgentShield API for detection and enforcement,\n * instead of running detection locally. This approach:\n *\n * 1. Works reliably in Edge Runtime (no WASM loading issues)\n * 2. Ensures consistent detection across all platforms\n * 3. Applies centralized policies from the dashboard\n * 4. Supports deny lists, thresholds, and path rules\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import { withAgentShield } from '@kya-os/agentshield-nextjs/api-middleware';\n *\n * export default withAgentShield({\n * apiKey: process.env.AGENTSHIELD_API_KEY!,\n * // Optional overrides:\n * onBlock: 'redirect', // 'block' | 'redirect' | 'challenge'\n * redirectUrl: '/blocked',\n * skipPaths: ['/api/health', '/_next/*'],\n * });\n *\n * export const config = {\n * matcher: ['/((?!_next/static|favicon.ico).*)'],\n * };\n * ```\n */\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport {\n AgentShieldClient,\n getAgentShieldClient,\n type EnforcementAction,\n type EnforcementDecision,\n} from './api-client';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Middleware configuration\n */\nexport interface AgentShieldMiddlewareConfig {\n /** API key (or use AGENTSHIELD_API_KEY env var) */\n apiKey?: string;\n /** API base URL (defaults to production) */\n apiUrl?: string;\n /** Request timeout in ms (default: 5000) */\n timeout?: number;\n\n /**\n * Action to take when an agent should be blocked\n * - 'block': Return 403 response\n * - 'redirect': Redirect to redirectUrl\n * - 'challenge': Show a challenge page (future)\n * Default: uses policy from dashboard\n */\n onBlock?: 'block' | 'redirect' | 'challenge';\n\n /**\n * URL to redirect to when blocking (if onBlock is 'redirect')\n * Default: uses redirectUrl from dashboard policy\n */\n redirectUrl?: string;\n\n /**\n * Custom blocked response\n */\n blockedResponse?: {\n status?: number;\n message?: string;\n headers?: Record<string, string>;\n };\n\n /**\n * Paths to skip (in addition to dashboard policy)\n * Supports glob patterns: '/api/*', '/_next/*'\n */\n skipPaths?: string[];\n\n /**\n * Only enforce on these paths (overrides dashboard policy)\n */\n includePaths?: string[];\n\n /**\n * Callback when an agent is detected\n */\n onAgentDetected?: (\n request: NextRequest,\n decision: EnforcementDecision\n ) => void | Promise<void>;\n\n /**\n * Callback to customize the blocked response\n */\n customBlockedResponse?: (\n request: NextRequest,\n decision: EnforcementDecision\n ) => NextResponse | Promise<NextResponse>;\n\n /**\n * Whether to fail open (allow) on API errors\n * Default: true (recommended for production)\n */\n failOpen?: boolean;\n\n /**\n * Enable debug logging\n */\n debug?: boolean;\n}\n\n// ============================================================================\n// Path Matching\n// ============================================================================\n\n/**\n * Check if a path matches a pattern\n */\nfunction matchPath(path: string, pattern: string): boolean {\n // Handle exact match\n if (pattern === path) return true;\n\n // Handle glob patterns\n if (pattern.includes('*')) {\n const regexPattern = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&') // Escape regex chars\n .replace(/\\*/g, '.*'); // Convert * to .*\n return new RegExp(`^${regexPattern}$`).test(path);\n }\n\n // Handle prefix match\n if (pattern.endsWith('/')) {\n return path.startsWith(pattern) || path === pattern.slice(0, -1);\n }\n\n return path.startsWith(pattern);\n}\n\n/**\n * Check if path should be skipped\n */\nfunction shouldSkipPath(path: string, skipPaths: string[]): boolean {\n return skipPaths.some((pattern) => matchPath(path, pattern));\n}\n\n/**\n * Check if path should be included (if includePaths is set)\n */\nfunction shouldIncludePath(path: string, includePaths?: string[]): boolean {\n if (!includePaths || includePaths.length === 0) return true;\n return includePaths.some((pattern) => matchPath(path, pattern));\n}\n\n// ============================================================================\n// Response Builders\n// ============================================================================\n\n/**\n * Build blocked response\n */\nfunction buildBlockedResponse(\n decision: EnforcementDecision,\n config: AgentShieldMiddlewareConfig\n): NextResponse {\n const status = config.blockedResponse?.status ?? 403;\n const message = config.blockedResponse?.message ?? decision.message ?? 'Access denied';\n\n const response = NextResponse.json(\n {\n error: message,\n code: 'AGENT_BLOCKED',\n reason: decision.reason,\n agentType: decision.agentType,\n },\n { status }\n );\n\n // Add custom headers\n if (config.blockedResponse?.headers) {\n for (const [key, value] of Object.entries(config.blockedResponse.headers)) {\n response.headers.set(key, value);\n }\n }\n\n // Add AgentShield headers\n response.headers.set('X-AgentShield-Action', decision.action);\n response.headers.set('X-AgentShield-Reason', decision.reason);\n\n return response;\n}\n\n/**\n * Build redirect response\n */\nfunction buildRedirectResponse(\n request: NextRequest,\n decision: EnforcementDecision,\n config: AgentShieldMiddlewareConfig\n): NextResponse {\n const redirectUrl = config.redirectUrl || decision.redirectUrl || '/blocked';\n const url = new URL(redirectUrl, request.url);\n\n // Add query params with detection info\n url.searchParams.set('reason', decision.reason);\n if (decision.agentType) {\n url.searchParams.set('agent', decision.agentType);\n }\n\n return NextResponse.redirect(url);\n}\n\n// ============================================================================\n// Middleware Factory\n// ============================================================================\n\n/**\n * Create AgentShield middleware with API-based detection\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import { withAgentShield } from '@kya-os/agentshield-nextjs/api-middleware';\n *\n * export default withAgentShield({\n * onBlock: 'block',\n * skipPaths: ['/api/health'],\n * });\n * ```\n */\nexport function withAgentShield(config: AgentShieldMiddlewareConfig = {}) {\n // Initialize client (will use AGENTSHIELD_API_KEY env var if not provided)\n let client: AgentShieldClient | null = null;\n\n const getClient = () => {\n if (!client) {\n client = getAgentShieldClient({\n apiKey: config.apiKey,\n baseUrl: config.apiUrl,\n timeout: config.timeout,\n debug: config.debug,\n });\n }\n return client;\n };\n\n // Default skip paths (static assets, etc.)\n const defaultSkipPaths = [\n '/_next/static/*',\n '/_next/image/*',\n '/favicon.ico',\n '/robots.txt',\n '/sitemap.xml',\n ];\n\n const skipPaths = [...defaultSkipPaths, ...(config.skipPaths || [])];\n const failOpen = config.failOpen ?? true;\n\n return async function middleware(request: NextRequest): Promise<NextResponse> {\n const path = request.nextUrl.pathname;\n const startTime = Date.now();\n\n // Check skip paths\n if (shouldSkipPath(path, skipPaths)) {\n return NextResponse.next();\n }\n\n // Check include paths\n if (!shouldIncludePath(path, config.includePaths)) {\n return NextResponse.next();\n }\n\n try {\n // Call enforce API\n const result = await getClient().enforce({\n headers: Object.fromEntries(request.headers.entries()),\n userAgent: request.headers.get('user-agent') || undefined,\n ipAddress:\n request.ip ||\n request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||\n request.headers.get('x-real-ip') ||\n undefined,\n path,\n url: request.url,\n method: request.method,\n requestId: request.headers.get('x-request-id') || undefined,\n options: {\n includeDetectionResult: config.debug,\n },\n });\n\n // Handle API error\n if (!result.success || !result.data) {\n if (config.debug) {\n console.warn('[AgentShield] API error:', result.error);\n }\n\n if (failOpen) {\n return NextResponse.next();\n }\n\n // Fail closed - block on error\n return NextResponse.json(\n { error: 'Security check failed', code: 'API_ERROR' },\n { status: 503 }\n );\n }\n\n const decision = result.data.decision;\n\n // Log if debug enabled\n if (config.debug) {\n console.log('[AgentShield] Decision:', {\n path,\n action: decision.action,\n isAgent: decision.isAgent,\n confidence: decision.confidence,\n agentName: decision.agentName,\n processingTimeMs: Date.now() - startTime,\n });\n }\n\n // Handle agent detection callback\n if (decision.isAgent && config.onAgentDetected) {\n await config.onAgentDetected(request, decision);\n }\n\n // Handle enforcement action\n switch (decision.action) {\n case 'block': {\n // Use custom response if provided\n if (config.customBlockedResponse) {\n return config.customBlockedResponse(request, decision);\n }\n\n // Check if config overrides to redirect\n if (config.onBlock === 'redirect') {\n return buildRedirectResponse(request, decision, config);\n }\n\n return buildBlockedResponse(decision, config);\n }\n\n case 'redirect': {\n return buildRedirectResponse(request, decision, config);\n }\n\n case 'challenge': {\n // Future: implement challenge page\n // For now, treat as redirect\n return buildRedirectResponse(request, decision, config);\n }\n\n case 'log':\n case 'allow':\n default: {\n // Allow the request to proceed\n const response = NextResponse.next();\n\n // Add detection headers for downstream use\n if (decision.isAgent) {\n response.headers.set('X-AgentShield-Detected', 'true');\n response.headers.set('X-AgentShield-Confidence', decision.confidence.toString());\n if (decision.agentName) {\n response.headers.set('X-AgentShield-Agent', decision.agentName);\n }\n }\n\n return response;\n }\n }\n } catch (error) {\n // Unexpected error\n if (config.debug) {\n console.error('[AgentShield] Middleware error:', error);\n }\n\n if (failOpen) {\n return NextResponse.next();\n }\n\n return NextResponse.json(\n { error: 'Security check failed', code: 'MIDDLEWARE_ERROR' },\n { status: 503 }\n );\n }\n };\n}\n\n/**\n * Convenience export for simple setup\n *\n * @example\n * ```typescript\n * // middleware.ts\n * export { agentShieldMiddleware as default } from '@kya-os/agentshield-nextjs/api-middleware';\n * ```\n */\nexport const agentShieldMiddleware = withAgentShield();\n"]}
@@ -0,0 +1,292 @@
1
+ import { NextResponse } from 'next/server';
2
+
3
+ // src/api-middleware.ts
4
+
5
+ // src/api-client.ts
6
+ var DEFAULT_BASE_URL = "https://api.agentshield.ai";
7
+ var DEFAULT_TIMEOUT = 5e3;
8
+ var AgentShieldClient = class {
9
+ apiKey;
10
+ baseUrl;
11
+ timeout;
12
+ debug;
13
+ constructor(config) {
14
+ if (!config.apiKey) {
15
+ throw new Error("AgentShield API key is required");
16
+ }
17
+ this.apiKey = config.apiKey;
18
+ this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
19
+ this.timeout = config.timeout || DEFAULT_TIMEOUT;
20
+ this.debug = config.debug || false;
21
+ }
22
+ /**
23
+ * Call the enforce API to check if a request should be allowed
24
+ */
25
+ async enforce(input) {
26
+ const startTime = Date.now();
27
+ try {
28
+ const controller = new AbortController();
29
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
30
+ try {
31
+ const response = await fetch(`${this.baseUrl}/api/v1/enforce`, {
32
+ method: "POST",
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ Authorization: `Bearer ${this.apiKey}`,
36
+ "X-Request-ID": input.requestId || crypto.randomUUID()
37
+ },
38
+ body: JSON.stringify(input),
39
+ signal: controller.signal
40
+ });
41
+ clearTimeout(timeoutId);
42
+ const data = await response.json();
43
+ if (this.debug) {
44
+ console.log("[AgentShield] Enforce response:", {
45
+ status: response.status,
46
+ action: data.data?.decision.action,
47
+ processingTimeMs: Date.now() - startTime
48
+ });
49
+ }
50
+ if (!response.ok) {
51
+ return {
52
+ success: false,
53
+ error: {
54
+ code: `HTTP_${response.status}`,
55
+ message: data.error?.message || `HTTP error: ${response.status}`
56
+ }
57
+ };
58
+ }
59
+ return data;
60
+ } catch (error) {
61
+ clearTimeout(timeoutId);
62
+ throw error;
63
+ }
64
+ } catch (error) {
65
+ if (error instanceof Error && error.name === "AbortError") {
66
+ if (this.debug) {
67
+ console.warn("[AgentShield] Request timed out");
68
+ }
69
+ return {
70
+ success: false,
71
+ error: {
72
+ code: "TIMEOUT",
73
+ message: `Request timed out after ${this.timeout}ms`
74
+ }
75
+ };
76
+ }
77
+ if (this.debug) {
78
+ console.error("[AgentShield] Request failed:", error);
79
+ }
80
+ return {
81
+ success: false,
82
+ error: {
83
+ code: "NETWORK_ERROR",
84
+ message: error instanceof Error ? error.message : "Network request failed"
85
+ }
86
+ };
87
+ }
88
+ }
89
+ /**
90
+ * Quick check - returns just the action without full response parsing
91
+ * Useful for very fast middleware that just needs allow/block
92
+ */
93
+ async quickCheck(input) {
94
+ const result = await this.enforce(input);
95
+ if (!result.success || !result.data) {
96
+ return {
97
+ action: "allow",
98
+ error: result.error?.message
99
+ };
100
+ }
101
+ return {
102
+ action: result.data.decision.action
103
+ };
104
+ }
105
+ };
106
+ var clientInstance = null;
107
+ function getAgentShieldClient(config) {
108
+ if (!clientInstance) {
109
+ const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;
110
+ if (!apiKey) {
111
+ throw new Error(
112
+ "AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config."
113
+ );
114
+ }
115
+ clientInstance = new AgentShieldClient({
116
+ apiKey,
117
+ baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,
118
+ timeout: config?.timeout,
119
+ debug: config?.debug || process.env.AGENTSHIELD_DEBUG === "true"
120
+ });
121
+ }
122
+ return clientInstance;
123
+ }
124
+
125
+ // src/api-middleware.ts
126
+ function matchPath(path, pattern) {
127
+ if (pattern === path) return true;
128
+ if (pattern.includes("*")) {
129
+ const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
130
+ return new RegExp(`^${regexPattern}$`).test(path);
131
+ }
132
+ if (pattern.endsWith("/")) {
133
+ return path.startsWith(pattern) || path === pattern.slice(0, -1);
134
+ }
135
+ return path.startsWith(pattern);
136
+ }
137
+ function shouldSkipPath(path, skipPaths) {
138
+ return skipPaths.some((pattern) => matchPath(path, pattern));
139
+ }
140
+ function shouldIncludePath(path, includePaths) {
141
+ if (!includePaths || includePaths.length === 0) return true;
142
+ return includePaths.some((pattern) => matchPath(path, pattern));
143
+ }
144
+ function buildBlockedResponse(decision, config) {
145
+ const status = config.blockedResponse?.status ?? 403;
146
+ const message = config.blockedResponse?.message ?? decision.message ?? "Access denied";
147
+ const response = NextResponse.json(
148
+ {
149
+ error: message,
150
+ code: "AGENT_BLOCKED",
151
+ reason: decision.reason,
152
+ agentType: decision.agentType
153
+ },
154
+ { status }
155
+ );
156
+ if (config.blockedResponse?.headers) {
157
+ for (const [key, value] of Object.entries(config.blockedResponse.headers)) {
158
+ response.headers.set(key, value);
159
+ }
160
+ }
161
+ response.headers.set("X-AgentShield-Action", decision.action);
162
+ response.headers.set("X-AgentShield-Reason", decision.reason);
163
+ return response;
164
+ }
165
+ function buildRedirectResponse(request, decision, config) {
166
+ const redirectUrl = config.redirectUrl || decision.redirectUrl || "/blocked";
167
+ const url = new URL(redirectUrl, request.url);
168
+ url.searchParams.set("reason", decision.reason);
169
+ if (decision.agentType) {
170
+ url.searchParams.set("agent", decision.agentType);
171
+ }
172
+ return NextResponse.redirect(url);
173
+ }
174
+ function withAgentShield(config = {}) {
175
+ let client = null;
176
+ const getClient = () => {
177
+ if (!client) {
178
+ client = getAgentShieldClient({
179
+ apiKey: config.apiKey,
180
+ baseUrl: config.apiUrl,
181
+ timeout: config.timeout,
182
+ debug: config.debug
183
+ });
184
+ }
185
+ return client;
186
+ };
187
+ const defaultSkipPaths = [
188
+ "/_next/static/*",
189
+ "/_next/image/*",
190
+ "/favicon.ico",
191
+ "/robots.txt",
192
+ "/sitemap.xml"
193
+ ];
194
+ const skipPaths = [...defaultSkipPaths, ...config.skipPaths || []];
195
+ const failOpen = config.failOpen ?? true;
196
+ return async function middleware(request) {
197
+ const path = request.nextUrl.pathname;
198
+ const startTime = Date.now();
199
+ if (shouldSkipPath(path, skipPaths)) {
200
+ return NextResponse.next();
201
+ }
202
+ if (!shouldIncludePath(path, config.includePaths)) {
203
+ return NextResponse.next();
204
+ }
205
+ try {
206
+ const result = await getClient().enforce({
207
+ headers: Object.fromEntries(request.headers.entries()),
208
+ userAgent: request.headers.get("user-agent") || void 0,
209
+ ipAddress: request.ip || request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip") || void 0,
210
+ path,
211
+ url: request.url,
212
+ method: request.method,
213
+ requestId: request.headers.get("x-request-id") || void 0,
214
+ options: {
215
+ includeDetectionResult: config.debug
216
+ }
217
+ });
218
+ if (!result.success || !result.data) {
219
+ if (config.debug) {
220
+ console.warn("[AgentShield] API error:", result.error);
221
+ }
222
+ if (failOpen) {
223
+ return NextResponse.next();
224
+ }
225
+ return NextResponse.json(
226
+ { error: "Security check failed", code: "API_ERROR" },
227
+ { status: 503 }
228
+ );
229
+ }
230
+ const decision = result.data.decision;
231
+ if (config.debug) {
232
+ console.log("[AgentShield] Decision:", {
233
+ path,
234
+ action: decision.action,
235
+ isAgent: decision.isAgent,
236
+ confidence: decision.confidence,
237
+ agentName: decision.agentName,
238
+ processingTimeMs: Date.now() - startTime
239
+ });
240
+ }
241
+ if (decision.isAgent && config.onAgentDetected) {
242
+ await config.onAgentDetected(request, decision);
243
+ }
244
+ switch (decision.action) {
245
+ case "block": {
246
+ if (config.customBlockedResponse) {
247
+ return config.customBlockedResponse(request, decision);
248
+ }
249
+ if (config.onBlock === "redirect") {
250
+ return buildRedirectResponse(request, decision, config);
251
+ }
252
+ return buildBlockedResponse(decision, config);
253
+ }
254
+ case "redirect": {
255
+ return buildRedirectResponse(request, decision, config);
256
+ }
257
+ case "challenge": {
258
+ return buildRedirectResponse(request, decision, config);
259
+ }
260
+ case "log":
261
+ case "allow":
262
+ default: {
263
+ const response = NextResponse.next();
264
+ if (decision.isAgent) {
265
+ response.headers.set("X-AgentShield-Detected", "true");
266
+ response.headers.set("X-AgentShield-Confidence", decision.confidence.toString());
267
+ if (decision.agentName) {
268
+ response.headers.set("X-AgentShield-Agent", decision.agentName);
269
+ }
270
+ }
271
+ return response;
272
+ }
273
+ }
274
+ } catch (error) {
275
+ if (config.debug) {
276
+ console.error("[AgentShield] Middleware error:", error);
277
+ }
278
+ if (failOpen) {
279
+ return NextResponse.next();
280
+ }
281
+ return NextResponse.json(
282
+ { error: "Security check failed", code: "MIDDLEWARE_ERROR" },
283
+ { status: 503 }
284
+ );
285
+ }
286
+ };
287
+ }
288
+ var agentShieldMiddleware = withAgentShield();
289
+
290
+ export { agentShieldMiddleware, withAgentShield };
291
+ //# sourceMappingURL=api-middleware.mjs.map
292
+ //# sourceMappingURL=api-middleware.mjs.map