@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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/api-client.d.mts +145 -0
- package/dist/api-client.d.ts +145 -0
- package/dist/api-client.js +130 -0
- package/dist/api-client.js.map +1 -0
- package/dist/api-client.mjs +126 -0
- package/dist/api-client.mjs.map +1 -0
- package/dist/api-middleware.d.mts +118 -0
- package/dist/api-middleware.d.ts +118 -0
- package/dist/api-middleware.js +295 -0
- package/dist/api-middleware.js.map +1 -0
- package/dist/api-middleware.mjs +292 -0
- package/dist/api-middleware.mjs.map +1 -0
- package/dist/create-middleware.d.mts +2 -1
- package/dist/create-middleware.d.ts +2 -1
- package/dist/create-middleware.js +474 -200
- package/dist/create-middleware.js.map +1 -1
- package/dist/create-middleware.mjs +454 -200
- package/dist/create-middleware.mjs.map +1 -1
- package/dist/edge/index.d.mts +110 -0
- package/dist/edge/index.d.ts +110 -0
- package/dist/edge/index.js +253 -0
- package/dist/edge/index.js.map +1 -0
- package/dist/edge/index.mjs +251 -0
- package/dist/edge/index.mjs.map +1 -0
- package/dist/edge-detector-wrapper.d.mts +6 -15
- package/dist/edge-detector-wrapper.d.ts +6 -15
- package/dist/edge-detector-wrapper.js +314 -95
- package/dist/edge-detector-wrapper.js.map +1 -1
- package/dist/edge-detector-wrapper.mjs +294 -95
- package/dist/edge-detector-wrapper.mjs.map +1 -1
- package/dist/edge-runtime-loader.d.mts +1 -1
- package/dist/edge-runtime-loader.d.ts +1 -1
- package/dist/edge-runtime-loader.js +10 -25
- package/dist/edge-runtime-loader.js.map +1 -1
- package/dist/edge-runtime-loader.mjs +11 -23
- package/dist/edge-runtime-loader.mjs.map +1 -1
- package/dist/edge-wasm-middleware.js +2 -1
- package/dist/edge-wasm-middleware.js.map +1 -1
- package/dist/edge-wasm-middleware.mjs +2 -1
- package/dist/edge-wasm-middleware.mjs.map +1 -1
- package/dist/enhanced-middleware.d.mts +153 -0
- package/dist/enhanced-middleware.d.ts +153 -0
- package/dist/enhanced-middleware.js +1074 -0
- package/dist/enhanced-middleware.js.map +1 -0
- package/dist/enhanced-middleware.mjs +1072 -0
- package/dist/enhanced-middleware.mjs.map +1 -0
- package/dist/index.d.mts +8 -153
- package/dist/index.d.ts +8 -153
- package/dist/index.js +1390 -680
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1370 -685
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.d.mts +2 -1
- package/dist/middleware.d.ts +2 -1
- package/dist/middleware.js +474 -200
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +454 -200
- package/dist/middleware.mjs.map +1 -1
- package/dist/session-tracker.d.mts +1 -1
- package/dist/session-tracker.d.ts +1 -1
- package/dist/session-tracker.js.map +1 -1
- package/dist/session-tracker.mjs.map +1 -1
- package/dist/signature-verifier.d.mts +1 -0
- package/dist/signature-verifier.d.ts +1 -0
- package/dist/signature-verifier.js +204 -44
- package/dist/signature-verifier.js.map +1 -1
- package/dist/signature-verifier.mjs +184 -44
- package/dist/signature-verifier.mjs.map +1 -1
- package/dist/{types-BJTEUa4T.d.mts → types-DVmy9NE3.d.mts} +19 -2
- package/dist/{types-BJTEUa4T.d.ts → types-DVmy9NE3.d.ts} +19 -2
- package/dist/wasm-middleware.js +15 -6
- package/dist/wasm-middleware.js.map +1 -1
- package/dist/wasm-middleware.mjs +15 -6
- package/dist/wasm-middleware.mjs.map +1 -1
- package/package.json +43 -21
- package/wasm/agentshield_wasm.js +209 -152
- package/wasm/agentshield_wasm_bg.wasm +0 -0
- package/wasm/package.json +30 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { DetectionResult } from '@kya-os/agentshield-shared';
|
|
3
|
+
export { DetectionResult } from '@kya-os/agentshield-shared';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Edge Runtime Middleware with WASM Support
|
|
7
|
+
*
|
|
8
|
+
* This module provides Next.js middleware that uses the unified WASM runtime.
|
|
9
|
+
* For Edge Runtime, consumers must provide a pre-compiled WASM module via static import:
|
|
10
|
+
*
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // In your middleware.ts
|
|
13
|
+
* import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module';
|
|
14
|
+
* import { createEdgeMiddleware } from '@kya-os/agentshield-nextjs/edge';
|
|
15
|
+
*
|
|
16
|
+
* export const middleware = createEdgeMiddleware({
|
|
17
|
+
* wasmModule,
|
|
18
|
+
* apiKey: process.env.AGENTSHIELD_API_KEY,
|
|
19
|
+
* onAgentDetected: 'block',
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* When an API key is provided, the middleware will:
|
|
24
|
+
* 1. Load customer policies (deny lists, allow lists, thresholds) from AgentShield API
|
|
25
|
+
* 2. Automatically block agents that match deny list entries
|
|
26
|
+
* 3. Cache policies for 5 minutes with background refresh
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Edge middleware configuration
|
|
31
|
+
*/
|
|
32
|
+
interface EdgeMiddlewareConfig {
|
|
33
|
+
/**
|
|
34
|
+
* Pre-compiled WebAssembly.Module for Edge Runtime
|
|
35
|
+
* Use static import: `import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module'`
|
|
36
|
+
*/
|
|
37
|
+
wasmModule?: WebAssembly.Module;
|
|
38
|
+
/**
|
|
39
|
+
* API key for loading customer policies from AgentShield dashboard
|
|
40
|
+
* When provided, enables policy enforcement (deny lists, allow lists, thresholds)
|
|
41
|
+
*/
|
|
42
|
+
apiKey?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Custom URL for the policy API
|
|
45
|
+
* @default 'https://api.agentshield.io'
|
|
46
|
+
*/
|
|
47
|
+
policyApiUrl?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Action to take when an agent is detected
|
|
50
|
+
* @default 'log'
|
|
51
|
+
*/
|
|
52
|
+
onAgentDetected?: 'block' | 'redirect' | 'rewrite' | 'log' | 'allow';
|
|
53
|
+
/**
|
|
54
|
+
* Custom handler called when an agent is detected
|
|
55
|
+
* Return a NextResponse to override default behavior
|
|
56
|
+
*/
|
|
57
|
+
onDetection?: (request: NextRequest, result: DetectionResult) => Promise<NextResponse | void> | NextResponse | void;
|
|
58
|
+
/**
|
|
59
|
+
* Paths to skip detection (can be string prefixes or RegExp)
|
|
60
|
+
*/
|
|
61
|
+
skipPaths?: (string | RegExp)[];
|
|
62
|
+
/**
|
|
63
|
+
* Minimum confidence threshold (0-100 scale) for considering a request as agent traffic
|
|
64
|
+
* @default 70
|
|
65
|
+
*/
|
|
66
|
+
confidenceThreshold?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Response to send when blocking agents
|
|
69
|
+
*/
|
|
70
|
+
blockedResponse?: {
|
|
71
|
+
status?: number;
|
|
72
|
+
message?: string;
|
|
73
|
+
headers?: Record<string, string>;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* URL to redirect to when redirecting agents
|
|
77
|
+
*/
|
|
78
|
+
redirectUrl?: string;
|
|
79
|
+
/**
|
|
80
|
+
* URL to rewrite to when rewriting agent requests
|
|
81
|
+
*/
|
|
82
|
+
rewriteUrl?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Enable debug logging
|
|
85
|
+
*/
|
|
86
|
+
debug?: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create Edge middleware with WASM support
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* // middleware.ts
|
|
94
|
+
* import wasmModule from '@kya-os/agentshield-wasm-runtime/wasm?module';
|
|
95
|
+
* import { createEdgeMiddleware } from '@kya-os/agentshield-nextjs/edge';
|
|
96
|
+
*
|
|
97
|
+
* export const middleware = createEdgeMiddleware({
|
|
98
|
+
* wasmModule,
|
|
99
|
+
* onAgentDetected: 'block',
|
|
100
|
+
* confidenceThreshold: 70,
|
|
101
|
+
* });
|
|
102
|
+
*
|
|
103
|
+
* export const config = {
|
|
104
|
+
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
105
|
+
* };
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare function createEdgeMiddleware(config?: EdgeMiddlewareConfig): (request: NextRequest) => Promise<NextResponse>;
|
|
109
|
+
|
|
110
|
+
export { type EdgeMiddlewareConfig, createEdgeMiddleware };
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var server = require('next/server');
|
|
4
|
+
|
|
5
|
+
// src/edge/index.ts
|
|
6
|
+
async function patternDetect(input) {
|
|
7
|
+
const userAgent = input.userAgent?.toLowerCase() || "";
|
|
8
|
+
const reasons = [];
|
|
9
|
+
let confidence = 0;
|
|
10
|
+
let detectedAgent;
|
|
11
|
+
const patterns = [
|
|
12
|
+
{ pattern: /chatgpt-user/i, name: "ChatGPT", confidence: 95 },
|
|
13
|
+
{ pattern: /claude-web/i, name: "Claude", confidence: 95 },
|
|
14
|
+
{ pattern: /claude-?bot/i, name: "ClaudeBot", confidence: 95 },
|
|
15
|
+
{ pattern: /anthropic/i, name: "Claude", confidence: 90 },
|
|
16
|
+
{ pattern: /gptbot/i, name: "GPTBot", confidence: 90 },
|
|
17
|
+
{ pattern: /perplexity/i, name: "Perplexity", confidence: 90 },
|
|
18
|
+
{ pattern: /openai/i, name: "OpenAI", confidence: 85 },
|
|
19
|
+
{ pattern: /copilot/i, name: "Copilot", confidence: 85 },
|
|
20
|
+
{ pattern: /gemini/i, name: "Gemini", confidence: 85 },
|
|
21
|
+
{ pattern: /bard/i, name: "Bard", confidence: 85 },
|
|
22
|
+
// HTTP client libraries (often used by AI agents/scrapers)
|
|
23
|
+
{ pattern: /^axios\//i, name: "HTTP Client (axios)", confidence: 75 },
|
|
24
|
+
{ pattern: /^node-fetch\//i, name: "HTTP Client (node-fetch)", confidence: 75 },
|
|
25
|
+
{ pattern: /^got\//i, name: "HTTP Client (got)", confidence: 75 },
|
|
26
|
+
{ pattern: /^python-requests\//i, name: "HTTP Client (requests)", confidence: 75 },
|
|
27
|
+
{ pattern: /^python-urllib/i, name: "HTTP Client (urllib)", confidence: 75 },
|
|
28
|
+
{ pattern: /^curl\//i, name: "HTTP Client (curl)", confidence: 60 },
|
|
29
|
+
{ pattern: /^wget\//i, name: "HTTP Client (wget)", confidence: 60 },
|
|
30
|
+
{ pattern: /^httpie\//i, name: "HTTP Client (httpie)", confidence: 70 },
|
|
31
|
+
{ pattern: /^undici\//i, name: "HTTP Client (undici)", confidence: 75 }
|
|
32
|
+
];
|
|
33
|
+
for (const { pattern, name, confidence: patternConfidence } of patterns) {
|
|
34
|
+
if (pattern.test(userAgent)) {
|
|
35
|
+
confidence = patternConfidence;
|
|
36
|
+
detectedAgent = { type: name.toLowerCase(), name };
|
|
37
|
+
reasons.push(`known_pattern:${name.toLowerCase()}`);
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const aiHeaders = ["x-openai-", "x-anthropic-", "x-ai-", "signature-agent"];
|
|
42
|
+
for (const [key] of Object.entries(input.headers)) {
|
|
43
|
+
if (aiHeaders.some((prefix) => key.toLowerCase().startsWith(prefix))) {
|
|
44
|
+
confidence = Math.max(confidence, 45);
|
|
45
|
+
reasons.push(`ai_headers:${key}`);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
isAgent: confidence > 30,
|
|
51
|
+
confidence,
|
|
52
|
+
detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
|
|
53
|
+
signals: [],
|
|
54
|
+
...detectedAgent && { detectedAgent },
|
|
55
|
+
reasons,
|
|
56
|
+
verificationMethod: "pattern",
|
|
57
|
+
forgeabilityRisk: confidence > 80 ? "medium" : "high",
|
|
58
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async function createDetector(config) {
|
|
62
|
+
let detector;
|
|
63
|
+
if (config.wasmModule) {
|
|
64
|
+
try {
|
|
65
|
+
const { StaticWasmLoader, WasmDetector, PolicyLoader } = await import('@kya-os/agentshield-wasm-runtime/edge');
|
|
66
|
+
const loader = new StaticWasmLoader(config.wasmModule);
|
|
67
|
+
const policyLoader = config.apiKey ? new PolicyLoader({
|
|
68
|
+
apiUrl: config.policyApiUrl
|
|
69
|
+
}) : void 0;
|
|
70
|
+
const wasmDetector = new WasmDetector(loader, policyLoader, {
|
|
71
|
+
apiKey: config.apiKey,
|
|
72
|
+
debug: config.debug
|
|
73
|
+
});
|
|
74
|
+
await wasmDetector.ensureReady();
|
|
75
|
+
if (config.debug) {
|
|
76
|
+
console.debug("[AgentShield] WASM detector initialized in Edge Runtime", {
|
|
77
|
+
policyEnabled: !!config.apiKey
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
detector = {
|
|
81
|
+
detect: async (input) => {
|
|
82
|
+
const result = await wasmDetector.detect(input);
|
|
83
|
+
return {
|
|
84
|
+
isAgent: result.isAgent,
|
|
85
|
+
confidence: result.confidence,
|
|
86
|
+
detectionClass: result.detectionClass,
|
|
87
|
+
detectedAgent: result.detectedAgent,
|
|
88
|
+
verificationMethod: result.verificationMethod,
|
|
89
|
+
forgeabilityRisk: result.forgeabilityRisk,
|
|
90
|
+
reasons: result.reasons,
|
|
91
|
+
timestamp: result.timestamp,
|
|
92
|
+
signals: [],
|
|
93
|
+
// Required by DetectionResult, empty for WASM results
|
|
94
|
+
shouldBlock: result.shouldBlock,
|
|
95
|
+
blockReason: result.blockReason
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
isReady: () => wasmDetector.isReady()
|
|
99
|
+
};
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (config.debug) {
|
|
102
|
+
console.warn("[AgentShield] WASM runtime not available, using pattern detection:", error);
|
|
103
|
+
}
|
|
104
|
+
detector = {
|
|
105
|
+
detect: patternDetect,
|
|
106
|
+
isReady: () => true
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
if (config.debug) {
|
|
111
|
+
console.debug("[AgentShield] No WASM module provided, using pattern detection");
|
|
112
|
+
}
|
|
113
|
+
detector = {
|
|
114
|
+
detect: patternDetect,
|
|
115
|
+
isReady: () => true
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return detector;
|
|
119
|
+
}
|
|
120
|
+
function createEdgeMiddleware(config = {}) {
|
|
121
|
+
let detectorPromise = null;
|
|
122
|
+
const {
|
|
123
|
+
onAgentDetected = "log",
|
|
124
|
+
onDetection,
|
|
125
|
+
skipPaths = [],
|
|
126
|
+
confidenceThreshold = 70,
|
|
127
|
+
blockedResponse = {
|
|
128
|
+
status: 403,
|
|
129
|
+
message: "Access denied: Automated agent detected",
|
|
130
|
+
headers: { "Content-Type": "application/json" }
|
|
131
|
+
},
|
|
132
|
+
redirectUrl = "/blocked",
|
|
133
|
+
rewriteUrl = "/blocked",
|
|
134
|
+
debug = false
|
|
135
|
+
} = config;
|
|
136
|
+
return async (request) => {
|
|
137
|
+
try {
|
|
138
|
+
if (!detectorPromise) {
|
|
139
|
+
detectorPromise = createDetector({ ...config, debug });
|
|
140
|
+
}
|
|
141
|
+
const detector = await detectorPromise;
|
|
142
|
+
const shouldSkip = skipPaths.some((pattern) => {
|
|
143
|
+
if (typeof pattern === "string") {
|
|
144
|
+
return request.nextUrl.pathname.startsWith(pattern);
|
|
145
|
+
}
|
|
146
|
+
return pattern.test(request.nextUrl.pathname);
|
|
147
|
+
});
|
|
148
|
+
if (shouldSkip) {
|
|
149
|
+
return server.NextResponse.next();
|
|
150
|
+
}
|
|
151
|
+
const url = new URL(request.url);
|
|
152
|
+
const xForwardedFor = request.headers.get("x-forwarded-for");
|
|
153
|
+
const clientIp = xForwardedFor?.split(",")[0]?.trim();
|
|
154
|
+
const input = {
|
|
155
|
+
userAgent: request.headers.get("user-agent") || void 0,
|
|
156
|
+
ipAddress: request.ip || clientIp || void 0,
|
|
157
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
158
|
+
url: url.pathname + url.search,
|
|
159
|
+
method: request.method
|
|
160
|
+
};
|
|
161
|
+
const result = await detector.detect(input);
|
|
162
|
+
if (result.shouldBlock) {
|
|
163
|
+
if (debug) {
|
|
164
|
+
console.debug("[AgentShield] Blocked by policy", {
|
|
165
|
+
reason: result.blockReason,
|
|
166
|
+
agent: result.detectedAgent?.name,
|
|
167
|
+
confidence: result.confidence,
|
|
168
|
+
pathname: request.nextUrl.pathname
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (onDetection) {
|
|
172
|
+
const customResponse = await onDetection(request, result);
|
|
173
|
+
if (customResponse) {
|
|
174
|
+
return customResponse;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return server.NextResponse.json(
|
|
178
|
+
{
|
|
179
|
+
error: "Access denied by policy",
|
|
180
|
+
reason: result.blockReason,
|
|
181
|
+
detected: true,
|
|
182
|
+
confidence: result.confidence,
|
|
183
|
+
agent: result.detectedAgent?.name,
|
|
184
|
+
timestamp: result.timestamp
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
status: 403,
|
|
188
|
+
headers: { "Content-Type": "application/json" }
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
}
|
|
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
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return response2;
|
|
216
|
+
}
|
|
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
|
+
});
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
case "allow":
|
|
233
|
+
default:
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
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());
|
|
240
|
+
if (result.detectedAgent?.name) {
|
|
241
|
+
response.headers.set("x-agentshield-agent", result.detectedAgent.name);
|
|
242
|
+
}
|
|
243
|
+
return response;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error("AgentShield middleware error:", error);
|
|
246
|
+
return server.NextResponse.next();
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
exports.createEdgeMiddleware = createEdgeMiddleware;
|
|
252
|
+
//# sourceMappingURL=index.js.map
|
|
253
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/edge/index.ts"],"names":["NextResponse","response"],"mappings":";;;;;AAmHA,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,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,OAAOA,oBAAa,IAAA,EAAK;AAAA,MAC3B;AAGA,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAE/B,MAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,iBAAiB,CAAA;AAC3D,MAAA,MAAM,WAAW,aAAA,EAAe,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,GAAG,IAAA,EAAK;AACpD,MAAA,MAAM,KAAA,GAAwB;AAAA,QAC5B,SAAA,EAAW,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,YAAY,CAAA,IAAK,KAAA,CAAA;AAAA,QAChD,SAAA,EAAW,OAAA,CAAQ,EAAA,IAAM,QAAA,IAAY,KAAA,CAAA;AAAA,QACrC,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,OAAOA,mBAAA,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,IACE,OAAO,WAAA,KAAgB,KAAA,IACvB,OAAO,OAAA,IACP,MAAA,CAAO,cAAc,mBAAA,EACrB;AAEA,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,QAAQ,eAAA;AAAiB,UACvB,KAAK,OAAA,EAAS;AACZ,YAAA,MAAMC,YAAWD,mBAAA,CAAa,IAAA;AAAA,cAC5B;AAAA,gBACE,OAAO,eAAA,CAAgB,OAAA;AAAA,gBACvB,QAAA,EAAU,IAAA;AAAA,gBACV,YAAY,MAAA,CAAO,UAAA;AAAA,gBACnB,WAAW,MAAA,CAAO;AAAA,eACpB;AAAA,cACA,EAAE,MAAA,EAAQ,eAAA,CAAgB,MAAA;AAAO,aACnC;AAEA,YAAA,IAAI,gBAAgB,OAAA,EAAS;AAC3B,cAAA,MAAA,CAAO,OAAA,CAAQ,gBAAgB,OAAO,CAAA,CAAE,QAAQ,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM;AAChE,gBAAAC,SAAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,cACjC,CAAC,CAAA;AAAA,YACH;AAEA,YAAA,OAAOA,SAAAA;AAAA,UACT;AAAA,UAEA,KAAK,UAAA;AACH,YAAA,OAAOD,oBAAa,QAAA,CAAS,IAAI,IAAI,WAAA,EAAa,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,UAEhE,KAAK,SAAA;AACH,YAAA,OAAOA,oBAAa,OAAA,CAAQ,IAAI,IAAI,UAAA,EAAY,OAAA,CAAQ,GAAG,CAAC,CAAA;AAAA,UAE9D,KAAK,KAAA;AACH,YAAA,IAAI,KAAA,IAAS,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,YAAA,EAAc;AAClD,cAAA,OAAA,CAAQ,MAAM,6BAAA,EAA+B;AAAA,gBAC3C,WAAW,KAAA,CAAM,SAAA;AAAA,gBACjB,WAAW,KAAA,CAAM,SAAA;AAAA,gBACjB,YAAY,MAAA,CAAO,UAAA;AAAA,gBACnB,KAAA,EAAO,OAAO,aAAA,EAAe,IAAA;AAAA,gBAC7B,QAAA,EAAU,QAAQ,OAAA,CAAQ;AAAA,eAC3B,CAAA;AAAA,YACH;AACA,YAAA;AAAA,UAEF,KAAK,OAAA;AAAA,UACL;AACE,YAAA;AAAA;AACJ,MACF;AAGA,MAAA,MAAM,QAAA,GAAWA,oBAAa,IAAA,EAAK;AAGnC,MAAA,QAAA,CAAS,QAAQ,GAAA,CAAI,wBAAA,EAA0B,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA;AACxE,MAAA,QAAA,CAAS,QAAQ,GAAA,CAAI,0BAAA,EAA4B,MAAA,CAAO,UAAA,CAAW,UAAU,CAAA;AAC7E,MAAA,IAAI,MAAA,CAAO,eAAe,IAAA,EAAM;AAC9B,QAAA,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,qBAAA,EAAuB,MAAA,CAAO,cAAc,IAAI,CAAA;AAAA,MACvE;AAEA,MAAA,OAAO,QAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,CAAM,iCAAiC,KAAK,CAAA;AACpD,MAAA,OAAOA,oBAAa,IAAA,EAAK;AAAA,IAC3B;AAAA,EACF,CAAA;AACF","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"]}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
|
|
3
|
+
// src/edge/index.ts
|
|
4
|
+
async function patternDetect(input) {
|
|
5
|
+
const userAgent = input.userAgent?.toLowerCase() || "";
|
|
6
|
+
const reasons = [];
|
|
7
|
+
let confidence = 0;
|
|
8
|
+
let detectedAgent;
|
|
9
|
+
const patterns = [
|
|
10
|
+
{ pattern: /chatgpt-user/i, name: "ChatGPT", confidence: 95 },
|
|
11
|
+
{ pattern: /claude-web/i, name: "Claude", confidence: 95 },
|
|
12
|
+
{ pattern: /claude-?bot/i, name: "ClaudeBot", confidence: 95 },
|
|
13
|
+
{ pattern: /anthropic/i, name: "Claude", confidence: 90 },
|
|
14
|
+
{ pattern: /gptbot/i, name: "GPTBot", confidence: 90 },
|
|
15
|
+
{ pattern: /perplexity/i, name: "Perplexity", confidence: 90 },
|
|
16
|
+
{ pattern: /openai/i, name: "OpenAI", confidence: 85 },
|
|
17
|
+
{ pattern: /copilot/i, name: "Copilot", confidence: 85 },
|
|
18
|
+
{ pattern: /gemini/i, name: "Gemini", confidence: 85 },
|
|
19
|
+
{ pattern: /bard/i, name: "Bard", confidence: 85 },
|
|
20
|
+
// HTTP client libraries (often used by AI agents/scrapers)
|
|
21
|
+
{ pattern: /^axios\//i, name: "HTTP Client (axios)", confidence: 75 },
|
|
22
|
+
{ pattern: /^node-fetch\//i, name: "HTTP Client (node-fetch)", confidence: 75 },
|
|
23
|
+
{ pattern: /^got\//i, name: "HTTP Client (got)", confidence: 75 },
|
|
24
|
+
{ pattern: /^python-requests\//i, name: "HTTP Client (requests)", confidence: 75 },
|
|
25
|
+
{ pattern: /^python-urllib/i, name: "HTTP Client (urllib)", confidence: 75 },
|
|
26
|
+
{ pattern: /^curl\//i, name: "HTTP Client (curl)", confidence: 60 },
|
|
27
|
+
{ pattern: /^wget\//i, name: "HTTP Client (wget)", confidence: 60 },
|
|
28
|
+
{ pattern: /^httpie\//i, name: "HTTP Client (httpie)", confidence: 70 },
|
|
29
|
+
{ pattern: /^undici\//i, name: "HTTP Client (undici)", confidence: 75 }
|
|
30
|
+
];
|
|
31
|
+
for (const { pattern, name, confidence: patternConfidence } of patterns) {
|
|
32
|
+
if (pattern.test(userAgent)) {
|
|
33
|
+
confidence = patternConfidence;
|
|
34
|
+
detectedAgent = { type: name.toLowerCase(), name };
|
|
35
|
+
reasons.push(`known_pattern:${name.toLowerCase()}`);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const aiHeaders = ["x-openai-", "x-anthropic-", "x-ai-", "signature-agent"];
|
|
40
|
+
for (const [key] of Object.entries(input.headers)) {
|
|
41
|
+
if (aiHeaders.some((prefix) => key.toLowerCase().startsWith(prefix))) {
|
|
42
|
+
confidence = Math.max(confidence, 45);
|
|
43
|
+
reasons.push(`ai_headers:${key}`);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
isAgent: confidence > 30,
|
|
49
|
+
confidence,
|
|
50
|
+
detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
|
|
51
|
+
signals: [],
|
|
52
|
+
...detectedAgent && { detectedAgent },
|
|
53
|
+
reasons,
|
|
54
|
+
verificationMethod: "pattern",
|
|
55
|
+
forgeabilityRisk: confidence > 80 ? "medium" : "high",
|
|
56
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async function createDetector(config) {
|
|
60
|
+
let detector;
|
|
61
|
+
if (config.wasmModule) {
|
|
62
|
+
try {
|
|
63
|
+
const { StaticWasmLoader, WasmDetector, PolicyLoader } = await import('@kya-os/agentshield-wasm-runtime/edge');
|
|
64
|
+
const loader = new StaticWasmLoader(config.wasmModule);
|
|
65
|
+
const policyLoader = config.apiKey ? new PolicyLoader({
|
|
66
|
+
apiUrl: config.policyApiUrl
|
|
67
|
+
}) : void 0;
|
|
68
|
+
const wasmDetector = new WasmDetector(loader, policyLoader, {
|
|
69
|
+
apiKey: config.apiKey,
|
|
70
|
+
debug: config.debug
|
|
71
|
+
});
|
|
72
|
+
await wasmDetector.ensureReady();
|
|
73
|
+
if (config.debug) {
|
|
74
|
+
console.debug("[AgentShield] WASM detector initialized in Edge Runtime", {
|
|
75
|
+
policyEnabled: !!config.apiKey
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
detector = {
|
|
79
|
+
detect: async (input) => {
|
|
80
|
+
const result = await wasmDetector.detect(input);
|
|
81
|
+
return {
|
|
82
|
+
isAgent: result.isAgent,
|
|
83
|
+
confidence: result.confidence,
|
|
84
|
+
detectionClass: result.detectionClass,
|
|
85
|
+
detectedAgent: result.detectedAgent,
|
|
86
|
+
verificationMethod: result.verificationMethod,
|
|
87
|
+
forgeabilityRisk: result.forgeabilityRisk,
|
|
88
|
+
reasons: result.reasons,
|
|
89
|
+
timestamp: result.timestamp,
|
|
90
|
+
signals: [],
|
|
91
|
+
// Required by DetectionResult, empty for WASM results
|
|
92
|
+
shouldBlock: result.shouldBlock,
|
|
93
|
+
blockReason: result.blockReason
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
isReady: () => wasmDetector.isReady()
|
|
97
|
+
};
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (config.debug) {
|
|
100
|
+
console.warn("[AgentShield] WASM runtime not available, using pattern detection:", error);
|
|
101
|
+
}
|
|
102
|
+
detector = {
|
|
103
|
+
detect: patternDetect,
|
|
104
|
+
isReady: () => true
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
if (config.debug) {
|
|
109
|
+
console.debug("[AgentShield] No WASM module provided, using pattern detection");
|
|
110
|
+
}
|
|
111
|
+
detector = {
|
|
112
|
+
detect: patternDetect,
|
|
113
|
+
isReady: () => true
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return detector;
|
|
117
|
+
}
|
|
118
|
+
function createEdgeMiddleware(config = {}) {
|
|
119
|
+
let detectorPromise = null;
|
|
120
|
+
const {
|
|
121
|
+
onAgentDetected = "log",
|
|
122
|
+
onDetection,
|
|
123
|
+
skipPaths = [],
|
|
124
|
+
confidenceThreshold = 70,
|
|
125
|
+
blockedResponse = {
|
|
126
|
+
status: 403,
|
|
127
|
+
message: "Access denied: Automated agent detected",
|
|
128
|
+
headers: { "Content-Type": "application/json" }
|
|
129
|
+
},
|
|
130
|
+
redirectUrl = "/blocked",
|
|
131
|
+
rewriteUrl = "/blocked",
|
|
132
|
+
debug = false
|
|
133
|
+
} = config;
|
|
134
|
+
return async (request) => {
|
|
135
|
+
try {
|
|
136
|
+
if (!detectorPromise) {
|
|
137
|
+
detectorPromise = createDetector({ ...config, debug });
|
|
138
|
+
}
|
|
139
|
+
const detector = await detectorPromise;
|
|
140
|
+
const shouldSkip = skipPaths.some((pattern) => {
|
|
141
|
+
if (typeof pattern === "string") {
|
|
142
|
+
return request.nextUrl.pathname.startsWith(pattern);
|
|
143
|
+
}
|
|
144
|
+
return pattern.test(request.nextUrl.pathname);
|
|
145
|
+
});
|
|
146
|
+
if (shouldSkip) {
|
|
147
|
+
return NextResponse.next();
|
|
148
|
+
}
|
|
149
|
+
const url = new URL(request.url);
|
|
150
|
+
const xForwardedFor = request.headers.get("x-forwarded-for");
|
|
151
|
+
const clientIp = xForwardedFor?.split(",")[0]?.trim();
|
|
152
|
+
const input = {
|
|
153
|
+
userAgent: request.headers.get("user-agent") || void 0,
|
|
154
|
+
ipAddress: request.ip || clientIp || void 0,
|
|
155
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
156
|
+
url: url.pathname + url.search,
|
|
157
|
+
method: request.method
|
|
158
|
+
};
|
|
159
|
+
const result = await detector.detect(input);
|
|
160
|
+
if (result.shouldBlock) {
|
|
161
|
+
if (debug) {
|
|
162
|
+
console.debug("[AgentShield] Blocked by policy", {
|
|
163
|
+
reason: result.blockReason,
|
|
164
|
+
agent: result.detectedAgent?.name,
|
|
165
|
+
confidence: result.confidence,
|
|
166
|
+
pathname: request.nextUrl.pathname
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (onDetection) {
|
|
170
|
+
const customResponse = await onDetection(request, result);
|
|
171
|
+
if (customResponse) {
|
|
172
|
+
return customResponse;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return NextResponse.json(
|
|
176
|
+
{
|
|
177
|
+
error: "Access denied by policy",
|
|
178
|
+
reason: result.blockReason,
|
|
179
|
+
detected: true,
|
|
180
|
+
confidence: result.confidence,
|
|
181
|
+
agent: result.detectedAgent?.name,
|
|
182
|
+
timestamp: result.timestamp
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
status: 403,
|
|
186
|
+
headers: { "Content-Type": "application/json" }
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
}
|
|
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
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return response2;
|
|
214
|
+
}
|
|
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
|
+
});
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
230
|
+
case "allow":
|
|
231
|
+
default:
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const response = NextResponse.next();
|
|
236
|
+
response.headers.set("x-agentshield-detected", result.isAgent.toString());
|
|
237
|
+
response.headers.set("x-agentshield-confidence", result.confidence.toString());
|
|
238
|
+
if (result.detectedAgent?.name) {
|
|
239
|
+
response.headers.set("x-agentshield-agent", result.detectedAgent.name);
|
|
240
|
+
}
|
|
241
|
+
return response;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error("AgentShield middleware error:", error);
|
|
244
|
+
return NextResponse.next();
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export { createEdgeMiddleware };
|
|
250
|
+
//# sourceMappingURL=index.mjs.map
|
|
251
|
+
//# sourceMappingURL=index.mjs.map
|