@kya-os/agentshield-nextjs 0.1.29 → 0.1.31
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/create-middleware.js +591 -12
- package/dist/create-middleware.js.map +1 -1
- package/dist/create-middleware.mjs +591 -12
- package/dist/create-middleware.mjs.map +1 -1
- package/dist/edge-detector-wrapper.js +251 -12
- package/dist/edge-detector-wrapper.js.map +1 -1
- package/dist/edge-detector-wrapper.mjs +251 -12
- package/dist/edge-detector-wrapper.mjs.map +1 -1
- package/dist/index.js +591 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +591 -12
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.js +587 -8
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +587 -8
- package/dist/middleware.mjs.map +1 -1
- package/dist/signature-verifier.js +220 -0
- package/dist/signature-verifier.js.map +1 -0
- package/dist/signature-verifier.mjs +216 -0
- package/dist/signature-verifier.mjs.map +1 -0
- package/package.json +2 -2
- package/wasm/agentshield_wasm.d.ts +121 -0
- package/wasm/agentshield_wasm.js +576 -0
- package/wasm/agentshield_wasm_bg.wasm +0 -0
- package/dist/create-middleware.d.mts +0 -16
- package/dist/create-middleware.d.ts +0 -16
- package/dist/edge-detector-wrapper.d.mts +0 -42
- package/dist/edge-detector-wrapper.d.ts +0 -42
- package/dist/edge-runtime-loader.d.mts +0 -49
- package/dist/edge-runtime-loader.d.ts +0 -49
- package/dist/edge-wasm-middleware.d.mts +0 -58
- package/dist/edge-wasm-middleware.d.ts +0 -58
- package/dist/index.d.mts +0 -19
- package/dist/index.d.ts +0 -19
- package/dist/middleware.d.mts +0 -20
- package/dist/middleware.d.ts +0 -20
- package/dist/nodejs-wasm-loader.d.mts +0 -25
- package/dist/nodejs-wasm-loader.d.ts +0 -25
- package/dist/session-tracker.d.mts +0 -55
- package/dist/session-tracker.d.ts +0 -55
- package/dist/types-BJTEUa4T.d.mts +0 -88
- package/dist/types-BJTEUa4T.d.ts +0 -88
- package/dist/wasm-middleware.d.mts +0 -62
- package/dist/wasm-middleware.d.ts +0 -62
- package/dist/wasm-setup.d.mts +0 -46
- package/dist/wasm-setup.d.ts +0 -46
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,219 @@ import { NextResponse } from 'next/server';
|
|
|
2
2
|
|
|
3
3
|
// src/create-middleware.ts
|
|
4
4
|
|
|
5
|
+
// src/signature-verifier.ts
|
|
6
|
+
var KNOWN_KEYS = {
|
|
7
|
+
chatgpt: [
|
|
8
|
+
{
|
|
9
|
+
kid: "otMqcjr17mGyruktGvJU8oojQTSMHlVm7uO-lrcqbdg",
|
|
10
|
+
// ChatGPT's current Ed25519 public key (base64)
|
|
11
|
+
publicKey: "7F_3jDlxaquwh291MiACkcS3Opq88NksyHiakzS-Y1g",
|
|
12
|
+
validFrom: (/* @__PURE__ */ new Date("2025-01-01")).getTime() / 1e3,
|
|
13
|
+
validUntil: (/* @__PURE__ */ new Date("2025-04-11")).getTime() / 1e3
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
};
|
|
17
|
+
function parseSignatureInput(signatureInput) {
|
|
18
|
+
try {
|
|
19
|
+
const match = signatureInput.match(/sig1=\((.*?)\);(.+)/);
|
|
20
|
+
if (!match) return null;
|
|
21
|
+
const [, headersList, params] = match;
|
|
22
|
+
const signedHeaders = headersList.split(" ").map((h) => h.replace(/"/g, "").trim()).filter((h) => h.length > 0);
|
|
23
|
+
const keyidMatch = params.match(/keyid="([^"]+)"/);
|
|
24
|
+
const createdMatch = params.match(/created=(\d+)/);
|
|
25
|
+
const expiresMatch = params.match(/expires=(\d+)/);
|
|
26
|
+
if (!keyidMatch) return null;
|
|
27
|
+
return {
|
|
28
|
+
keyid: keyidMatch[1],
|
|
29
|
+
created: createdMatch ? parseInt(createdMatch[1]) : void 0,
|
|
30
|
+
expires: expiresMatch ? parseInt(expiresMatch[1]) : void 0,
|
|
31
|
+
signedHeaders
|
|
32
|
+
};
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error("[Signature] Failed to parse Signature-Input:", error);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function buildSignatureBase(method, path, headers, signedHeaders) {
|
|
39
|
+
const components = [];
|
|
40
|
+
for (const headerName of signedHeaders) {
|
|
41
|
+
let value;
|
|
42
|
+
switch (headerName) {
|
|
43
|
+
case "@method":
|
|
44
|
+
value = method.toUpperCase();
|
|
45
|
+
break;
|
|
46
|
+
case "@path":
|
|
47
|
+
value = path;
|
|
48
|
+
break;
|
|
49
|
+
case "@authority":
|
|
50
|
+
value = headers["host"] || headers["Host"] || "";
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
const key = Object.keys(headers).find(
|
|
54
|
+
(k) => k.toLowerCase() === headerName.toLowerCase()
|
|
55
|
+
);
|
|
56
|
+
value = key ? headers[key] : "";
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
components.push(`"${headerName}": ${value}`);
|
|
60
|
+
}
|
|
61
|
+
return components.join("\n");
|
|
62
|
+
}
|
|
63
|
+
async function verifyEd25519Signature(publicKeyBase64, signatureBase64, message) {
|
|
64
|
+
try {
|
|
65
|
+
const publicKeyBytes = Uint8Array.from(atob(publicKeyBase64), (c) => c.charCodeAt(0));
|
|
66
|
+
const signatureBytes = Uint8Array.from(atob(signatureBase64), (c) => c.charCodeAt(0));
|
|
67
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
68
|
+
if (publicKeyBytes.length !== 32) {
|
|
69
|
+
console.error("[Signature] Invalid public key length:", publicKeyBytes.length);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
if (signatureBytes.length !== 64) {
|
|
73
|
+
console.error("[Signature] Invalid signature length:", signatureBytes.length);
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const publicKey = await crypto.subtle.importKey(
|
|
77
|
+
"raw",
|
|
78
|
+
publicKeyBytes,
|
|
79
|
+
{
|
|
80
|
+
name: "Ed25519",
|
|
81
|
+
namedCurve: "Ed25519"
|
|
82
|
+
},
|
|
83
|
+
false,
|
|
84
|
+
["verify"]
|
|
85
|
+
);
|
|
86
|
+
const isValid = await crypto.subtle.verify(
|
|
87
|
+
"Ed25519",
|
|
88
|
+
publicKey,
|
|
89
|
+
signatureBytes,
|
|
90
|
+
messageBytes
|
|
91
|
+
);
|
|
92
|
+
return isValid;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error("[Signature] Ed25519 verification failed:", error);
|
|
95
|
+
if (typeof window === "undefined") {
|
|
96
|
+
try {
|
|
97
|
+
console.warn("[Signature] Ed25519 not supported in this environment");
|
|
98
|
+
return false;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function verifyAgentSignature(method, path, headers) {
|
|
107
|
+
const signature = headers["signature"] || headers["Signature"];
|
|
108
|
+
const signatureInput = headers["signature-input"] || headers["Signature-Input"];
|
|
109
|
+
const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
|
|
110
|
+
if (!signature || !signatureInput) {
|
|
111
|
+
return {
|
|
112
|
+
isValid: false,
|
|
113
|
+
confidence: 0,
|
|
114
|
+
reason: "No signature headers present",
|
|
115
|
+
verificationMethod: "none"
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const parsed = parseSignatureInput(signatureInput);
|
|
119
|
+
if (!parsed) {
|
|
120
|
+
return {
|
|
121
|
+
isValid: false,
|
|
122
|
+
confidence: 0,
|
|
123
|
+
reason: "Invalid Signature-Input header",
|
|
124
|
+
verificationMethod: "none"
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (parsed.created) {
|
|
128
|
+
const now2 = Math.floor(Date.now() / 1e3);
|
|
129
|
+
const age = now2 - parsed.created;
|
|
130
|
+
if (age > 300) {
|
|
131
|
+
return {
|
|
132
|
+
isValid: false,
|
|
133
|
+
confidence: 0,
|
|
134
|
+
reason: "Signature expired (older than 5 minutes)",
|
|
135
|
+
verificationMethod: "none"
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (age < -30) {
|
|
139
|
+
return {
|
|
140
|
+
isValid: false,
|
|
141
|
+
confidence: 0,
|
|
142
|
+
reason: "Signature timestamp is in the future",
|
|
143
|
+
verificationMethod: "none"
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
let agent;
|
|
148
|
+
let knownKeys;
|
|
149
|
+
if (signatureAgent === '"https://chatgpt.com"' || signatureAgent?.includes("chatgpt.com")) {
|
|
150
|
+
agent = "ChatGPT";
|
|
151
|
+
knownKeys = KNOWN_KEYS.chatgpt;
|
|
152
|
+
}
|
|
153
|
+
if (!agent || !knownKeys) {
|
|
154
|
+
return {
|
|
155
|
+
isValid: false,
|
|
156
|
+
confidence: 0,
|
|
157
|
+
reason: "Unknown signature agent",
|
|
158
|
+
verificationMethod: "none"
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const key = knownKeys.find((k) => k.kid === parsed.keyid);
|
|
162
|
+
if (!key) {
|
|
163
|
+
return {
|
|
164
|
+
isValid: false,
|
|
165
|
+
confidence: 0,
|
|
166
|
+
reason: `Unknown key ID: ${parsed.keyid}`,
|
|
167
|
+
verificationMethod: "none"
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
171
|
+
if (now < key.validFrom || now > key.validUntil) {
|
|
172
|
+
return {
|
|
173
|
+
isValid: false,
|
|
174
|
+
confidence: 0,
|
|
175
|
+
reason: "Key is not valid at current time",
|
|
176
|
+
verificationMethod: "none"
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const signatureBase = buildSignatureBase(method, path, headers, parsed.signedHeaders);
|
|
180
|
+
let signatureValue = signature;
|
|
181
|
+
if (signatureValue.startsWith("sig1=:")) {
|
|
182
|
+
signatureValue = signatureValue.substring(6);
|
|
183
|
+
}
|
|
184
|
+
if (signatureValue.endsWith(":")) {
|
|
185
|
+
signatureValue = signatureValue.slice(0, -1);
|
|
186
|
+
}
|
|
187
|
+
const isValid = await verifyEd25519Signature(
|
|
188
|
+
key.publicKey,
|
|
189
|
+
signatureValue,
|
|
190
|
+
signatureBase
|
|
191
|
+
);
|
|
192
|
+
if (isValid) {
|
|
193
|
+
return {
|
|
194
|
+
isValid: true,
|
|
195
|
+
agent,
|
|
196
|
+
keyid: parsed.keyid,
|
|
197
|
+
confidence: 1,
|
|
198
|
+
// 100% confidence for valid signature
|
|
199
|
+
verificationMethod: "signature"
|
|
200
|
+
};
|
|
201
|
+
} else {
|
|
202
|
+
return {
|
|
203
|
+
isValid: false,
|
|
204
|
+
confidence: 0,
|
|
205
|
+
reason: "Signature verification failed",
|
|
206
|
+
verificationMethod: "none"
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function hasSignatureHeaders(headers) {
|
|
211
|
+
return !!((headers["signature"] || headers["Signature"]) && (headers["signature-input"] || headers["Signature-Input"]));
|
|
212
|
+
}
|
|
213
|
+
function isChatGPTSignature(headers) {
|
|
214
|
+
const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
|
|
215
|
+
return signatureAgent === '"https://chatgpt.com"' || (signatureAgent?.includes("chatgpt.com") || false);
|
|
216
|
+
}
|
|
217
|
+
|
|
5
218
|
// src/edge-detector-wrapper.ts
|
|
6
219
|
var AI_AGENT_PATTERNS = [
|
|
7
220
|
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
@@ -21,6 +234,312 @@ var CLOUD_PROVIDERS = {
|
|
|
21
234
|
};
|
|
22
235
|
var EdgeAgentDetector = class {
|
|
23
236
|
async analyze(input) {
|
|
237
|
+
const reasons = [];
|
|
238
|
+
let detectedAgent;
|
|
239
|
+
let verificationMethod;
|
|
240
|
+
let confidence = 0;
|
|
241
|
+
const headers = input.headers || {};
|
|
242
|
+
const normalizedHeaders = {};
|
|
243
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
244
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
245
|
+
}
|
|
246
|
+
if (hasSignatureHeaders(headers)) {
|
|
247
|
+
try {
|
|
248
|
+
const signatureResult = await verifyAgentSignature(
|
|
249
|
+
input.method || "GET",
|
|
250
|
+
input.url || "/",
|
|
251
|
+
headers
|
|
252
|
+
);
|
|
253
|
+
if (signatureResult.isValid) {
|
|
254
|
+
confidence = signatureResult.confidence;
|
|
255
|
+
reasons.push(`verified_signature:${signatureResult.agent?.toLowerCase() || "unknown"}`);
|
|
256
|
+
if (signatureResult.agent) {
|
|
257
|
+
detectedAgent = {
|
|
258
|
+
type: signatureResult.agent.toLowerCase(),
|
|
259
|
+
name: signatureResult.agent
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
verificationMethod = signatureResult.verificationMethod;
|
|
263
|
+
if (signatureResult.keyid) {
|
|
264
|
+
reasons.push(`keyid:${signatureResult.keyid}`);
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
confidence = Math.max(confidence, 0.3);
|
|
268
|
+
reasons.push("invalid_signature");
|
|
269
|
+
if (signatureResult.reason) {
|
|
270
|
+
reasons.push(`signature_error:${signatureResult.reason}`);
|
|
271
|
+
}
|
|
272
|
+
if (isChatGPTSignature(headers)) {
|
|
273
|
+
reasons.push("claims_chatgpt");
|
|
274
|
+
detectedAgent = { type: "chatgpt", name: "ChatGPT (unverified)" };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error("[EdgeAgentDetector] Signature verification error:", error);
|
|
279
|
+
confidence = Math.max(confidence, 0.2);
|
|
280
|
+
reasons.push("signature_verification_error");
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
284
|
+
if (userAgent) {
|
|
285
|
+
for (const { pattern, type, name } of AI_AGENT_PATTERNS) {
|
|
286
|
+
if (pattern.test(userAgent)) {
|
|
287
|
+
const highConfidenceAgents = [
|
|
288
|
+
"chatgpt",
|
|
289
|
+
"claude",
|
|
290
|
+
"perplexity",
|
|
291
|
+
"anthropic"
|
|
292
|
+
];
|
|
293
|
+
const patternConfidence = highConfidenceAgents.includes(type) ? 0.85 : 0.5;
|
|
294
|
+
confidence = Math.max(confidence, patternConfidence);
|
|
295
|
+
reasons.push(`known_pattern:${type}`);
|
|
296
|
+
if (!detectedAgent) {
|
|
297
|
+
detectedAgent = { type, name };
|
|
298
|
+
verificationMethod = "pattern";
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const aiHeaders = [
|
|
305
|
+
"openai-conversation-id",
|
|
306
|
+
"openai-ephemeral-user-id",
|
|
307
|
+
"anthropic-client-id",
|
|
308
|
+
"x-goog-api-client",
|
|
309
|
+
"x-ms-copilot-id"
|
|
310
|
+
];
|
|
311
|
+
const foundAiHeaders = aiHeaders.filter(
|
|
312
|
+
(header) => normalizedHeaders[header]
|
|
313
|
+
);
|
|
314
|
+
if (foundAiHeaders.length > 0) {
|
|
315
|
+
confidence = Math.max(confidence, 0.6);
|
|
316
|
+
reasons.push(`ai_headers:${foundAiHeaders.length}`);
|
|
317
|
+
}
|
|
318
|
+
const ip = input.ip || input.ipAddress;
|
|
319
|
+
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
320
|
+
for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS)) {
|
|
321
|
+
if (prefixes.some((prefix) => ip.startsWith(prefix))) {
|
|
322
|
+
confidence = Math.max(confidence, 0.4);
|
|
323
|
+
reasons.push(`cloud_provider:${provider}`);
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (reasons.length > 2 && confidence < 1) {
|
|
329
|
+
confidence = Math.min(confidence * 1.2, 0.95);
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
isAgent: confidence > 0.3,
|
|
333
|
+
confidence,
|
|
334
|
+
...detectedAgent && { detectedAgent },
|
|
335
|
+
reasons,
|
|
336
|
+
...verificationMethod && { verificationMethod },
|
|
337
|
+
forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 0.8 ? "medium" : "high",
|
|
338
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
var EdgeAgentDetectorWrapper = class {
|
|
343
|
+
detector;
|
|
344
|
+
events = /* @__PURE__ */ new Map();
|
|
345
|
+
constructor(_config) {
|
|
346
|
+
this.detector = new EdgeAgentDetector();
|
|
347
|
+
}
|
|
348
|
+
async analyze(input) {
|
|
349
|
+
const result = await this.detector.analyze(input);
|
|
350
|
+
if (result.isAgent && this.events.has("agent.detected")) {
|
|
351
|
+
const handlers = this.events.get("agent.detected") || [];
|
|
352
|
+
handlers.forEach((handler) => handler(result, input));
|
|
353
|
+
}
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
on(event, handler) {
|
|
357
|
+
if (!this.events.has(event)) {
|
|
358
|
+
this.events.set(event, []);
|
|
359
|
+
}
|
|
360
|
+
this.events.get(event).push(handler);
|
|
361
|
+
}
|
|
362
|
+
emit(event, ...args) {
|
|
363
|
+
const handlers = this.events.get(event) || [];
|
|
364
|
+
handlers.forEach((handler) => handler(...args));
|
|
365
|
+
}
|
|
366
|
+
async init() {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// src/wasm-loader.ts
|
|
372
|
+
var wasmInstance = null;
|
|
373
|
+
var wasmExports = null;
|
|
374
|
+
var initPromise = null;
|
|
375
|
+
var WASM_PATH = "/wasm/agentshield_wasm_bg.wasm";
|
|
376
|
+
async function initWasm() {
|
|
377
|
+
if (wasmExports) return true;
|
|
378
|
+
if (initPromise) {
|
|
379
|
+
await initPromise;
|
|
380
|
+
return !!wasmExports;
|
|
381
|
+
}
|
|
382
|
+
initPromise = (async () => {
|
|
383
|
+
try {
|
|
384
|
+
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
385
|
+
try {
|
|
386
|
+
const response2 = await fetch(WASM_PATH);
|
|
387
|
+
if (!response2.ok) {
|
|
388
|
+
throw new Error(`Failed to fetch WASM: ${response2.status}`);
|
|
389
|
+
}
|
|
390
|
+
const streamResponse = response2.clone();
|
|
391
|
+
const { instance } = await WebAssembly.instantiateStreaming(
|
|
392
|
+
streamResponse,
|
|
393
|
+
{
|
|
394
|
+
wbg: {
|
|
395
|
+
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
396
|
+
console.log("WASM:", ptr, len);
|
|
397
|
+
},
|
|
398
|
+
__wbindgen_throw: (ptr, len) => {
|
|
399
|
+
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
);
|
|
404
|
+
wasmInstance = instance;
|
|
405
|
+
wasmExports = instance.exports;
|
|
406
|
+
console.log("[AgentShield] \u2705 WASM module initialized with streaming");
|
|
407
|
+
return;
|
|
408
|
+
} catch (streamError) {
|
|
409
|
+
console.log("[AgentShield] Streaming compilation failed, falling back to standard compilation");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const response = await fetch(WASM_PATH);
|
|
413
|
+
if (!response.ok) {
|
|
414
|
+
throw new Error(`Failed to fetch WASM: ${response.status}`);
|
|
415
|
+
}
|
|
416
|
+
const wasmArrayBuffer = await response.arrayBuffer();
|
|
417
|
+
const compiledModule = await WebAssembly.compile(wasmArrayBuffer);
|
|
418
|
+
const imports = {
|
|
419
|
+
wbg: {
|
|
420
|
+
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
421
|
+
console.log("WASM:", ptr, len);
|
|
422
|
+
},
|
|
423
|
+
__wbindgen_throw: (ptr, len) => {
|
|
424
|
+
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
wasmInstance = await WebAssembly.instantiate(compiledModule, imports);
|
|
429
|
+
wasmExports = wasmInstance.exports;
|
|
430
|
+
console.log("[AgentShield] \u2705 WASM module initialized via fallback");
|
|
431
|
+
} catch (error) {
|
|
432
|
+
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
433
|
+
wasmExports = null;
|
|
434
|
+
}
|
|
435
|
+
})();
|
|
436
|
+
await initPromise;
|
|
437
|
+
return !!wasmExports;
|
|
438
|
+
}
|
|
439
|
+
async function detectAgentWithWasm(userAgent, headers, ipAddress) {
|
|
440
|
+
const initialized = await initWasm();
|
|
441
|
+
if (!initialized || !wasmExports) {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
const headersJson = JSON.stringify(headers);
|
|
446
|
+
if (typeof wasmExports.detect_agent === "function") {
|
|
447
|
+
const result = wasmExports.detect_agent(
|
|
448
|
+
userAgent || "",
|
|
449
|
+
headersJson,
|
|
450
|
+
ipAddress || "",
|
|
451
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
452
|
+
);
|
|
453
|
+
return {
|
|
454
|
+
isAgent: result.is_agent || false,
|
|
455
|
+
confidence: result.confidence || 0,
|
|
456
|
+
agent: result.agent,
|
|
457
|
+
verificationMethod: result.verification_method || "wasm",
|
|
458
|
+
riskLevel: result.risk_level || "low"
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
console.warn("[AgentShield] WASM exports do not include detect_agent function");
|
|
462
|
+
return null;
|
|
463
|
+
} catch (error) {
|
|
464
|
+
console.error("[AgentShield] WASM detection failed:", error);
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
async function getWasmVersion() {
|
|
469
|
+
const initialized = await initWasm();
|
|
470
|
+
if (!initialized || !wasmExports) {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
if (typeof wasmExports.version === "function") {
|
|
474
|
+
return wasmExports.version();
|
|
475
|
+
}
|
|
476
|
+
return "unknown";
|
|
477
|
+
}
|
|
478
|
+
async function isWasmAvailable() {
|
|
479
|
+
try {
|
|
480
|
+
const initialized = await initWasm();
|
|
481
|
+
if (!initialized) return false;
|
|
482
|
+
const version = await getWasmVersion();
|
|
483
|
+
return version !== null;
|
|
484
|
+
} catch {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// src/edge-detector-with-wasm.ts
|
|
490
|
+
var AI_AGENT_PATTERNS2 = [
|
|
491
|
+
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
492
|
+
{ pattern: /claude-web/i, type: "claude", name: "Claude" },
|
|
493
|
+
{ pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
|
|
494
|
+
{ pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
|
|
495
|
+
{ pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
|
|
496
|
+
{ pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
|
|
497
|
+
{ pattern: /bingbot/i, type: "bing", name: "Bing AI" },
|
|
498
|
+
{ pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
|
|
499
|
+
];
|
|
500
|
+
var CLOUD_PROVIDERS2 = {
|
|
501
|
+
aws: ["54.", "52.", "35.", "18.", "3."],
|
|
502
|
+
gcp: ["35.", "34.", "104.", "107.", "108."],
|
|
503
|
+
azure: ["13.", "20.", "40.", "52.", "104."]
|
|
504
|
+
};
|
|
505
|
+
var EdgeAgentDetectorWithWasm = class {
|
|
506
|
+
constructor(enableWasm = true) {
|
|
507
|
+
this.enableWasm = enableWasm;
|
|
508
|
+
}
|
|
509
|
+
wasmEnabled = false;
|
|
510
|
+
initPromise = null;
|
|
511
|
+
/**
|
|
512
|
+
* Initialize the detector (including WASM if enabled)
|
|
513
|
+
*/
|
|
514
|
+
async init() {
|
|
515
|
+
if (!this.enableWasm) {
|
|
516
|
+
this.wasmEnabled = false;
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (this.initPromise) {
|
|
520
|
+
await this.initPromise;
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
this.initPromise = (async () => {
|
|
524
|
+
try {
|
|
525
|
+
const wasmAvailable = await isWasmAvailable();
|
|
526
|
+
this.wasmEnabled = wasmAvailable;
|
|
527
|
+
if (this.wasmEnabled) {
|
|
528
|
+
console.log("[AgentShield] WASM detection enabled");
|
|
529
|
+
} else {
|
|
530
|
+
console.log("[AgentShield] WASM not available, using pattern detection");
|
|
531
|
+
}
|
|
532
|
+
} catch (error) {
|
|
533
|
+
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
534
|
+
this.wasmEnabled = false;
|
|
535
|
+
}
|
|
536
|
+
})();
|
|
537
|
+
await this.initPromise;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Pattern-based detection (fallback)
|
|
541
|
+
*/
|
|
542
|
+
async patternDetection(input) {
|
|
24
543
|
const reasons = [];
|
|
25
544
|
let detectedAgent;
|
|
26
545
|
let verificationMethod;
|
|
@@ -43,7 +562,7 @@ var EdgeAgentDetector = class {
|
|
|
43
562
|
}
|
|
44
563
|
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
45
564
|
if (userAgent) {
|
|
46
|
-
for (const { pattern, type, name } of
|
|
565
|
+
for (const { pattern, type, name } of AI_AGENT_PATTERNS2) {
|
|
47
566
|
if (pattern.test(userAgent)) {
|
|
48
567
|
const highConfidenceAgents = [
|
|
49
568
|
"chatgpt",
|
|
@@ -78,7 +597,7 @@ var EdgeAgentDetector = class {
|
|
|
78
597
|
}
|
|
79
598
|
const ip = input.ip || input.ipAddress;
|
|
80
599
|
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
81
|
-
for (const [provider, prefixes] of Object.entries(
|
|
600
|
+
for (const [provider, prefixes] of Object.entries(CLOUD_PROVIDERS2)) {
|
|
82
601
|
if (prefixes.some((prefix) => ip.startsWith(prefix))) {
|
|
83
602
|
confidence = Math.max(confidence, 0.4);
|
|
84
603
|
reasons.push(`cloud_provider:${provider}`);
|
|
@@ -99,12 +618,69 @@ var EdgeAgentDetector = class {
|
|
|
99
618
|
timestamp: /* @__PURE__ */ new Date()
|
|
100
619
|
};
|
|
101
620
|
}
|
|
621
|
+
/**
|
|
622
|
+
* Analyze request with WASM enhancement when available
|
|
623
|
+
*/
|
|
624
|
+
async analyze(input) {
|
|
625
|
+
await this.init();
|
|
626
|
+
if (this.wasmEnabled) {
|
|
627
|
+
try {
|
|
628
|
+
const wasmResult = await detectAgentWithWasm(
|
|
629
|
+
input.userAgent || input.headers?.["user-agent"],
|
|
630
|
+
input.headers || {},
|
|
631
|
+
input.ip || input.ipAddress
|
|
632
|
+
);
|
|
633
|
+
if (wasmResult) {
|
|
634
|
+
console.log("[AgentShield] Using WASM detection result");
|
|
635
|
+
const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
|
|
636
|
+
return {
|
|
637
|
+
isAgent: wasmResult.isAgent,
|
|
638
|
+
confidence: wasmResult.confidence,
|
|
639
|
+
...detectedAgent && { detectedAgent },
|
|
640
|
+
reasons: [`wasm:${wasmResult.verificationMethod}`],
|
|
641
|
+
verificationMethod: wasmResult.verificationMethod,
|
|
642
|
+
forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 0.9 ? "medium" : "high",
|
|
643
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
} catch (error) {
|
|
647
|
+
console.error("[AgentShield] WASM detection error:", error);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const patternResult = await this.patternDetection(input);
|
|
651
|
+
if (this.wasmEnabled && patternResult.confidence >= 0.85) {
|
|
652
|
+
patternResult.confidence = Math.min(0.95, patternResult.confidence + 0.1);
|
|
653
|
+
patternResult.reasons.push("wasm_enhanced");
|
|
654
|
+
if (!patternResult.verificationMethod) {
|
|
655
|
+
patternResult.verificationMethod = "wasm-enhanced";
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return patternResult;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Map agent names from WASM to consistent format
|
|
662
|
+
*/
|
|
663
|
+
mapAgentName(agent) {
|
|
664
|
+
const lowerAgent = agent.toLowerCase();
|
|
665
|
+
if (lowerAgent.includes("chatgpt")) {
|
|
666
|
+
return { type: "chatgpt", name: "ChatGPT" };
|
|
667
|
+
} else if (lowerAgent.includes("claude")) {
|
|
668
|
+
return { type: "claude", name: "Claude" };
|
|
669
|
+
} else if (lowerAgent.includes("perplexity")) {
|
|
670
|
+
return { type: "perplexity", name: "Perplexity" };
|
|
671
|
+
} else if (lowerAgent.includes("bing")) {
|
|
672
|
+
return { type: "bing", name: "Bing AI" };
|
|
673
|
+
} else if (lowerAgent.includes("anthropic")) {
|
|
674
|
+
return { type: "anthropic", name: "Anthropic" };
|
|
675
|
+
}
|
|
676
|
+
return { type: "unknown", name: agent };
|
|
677
|
+
}
|
|
102
678
|
};
|
|
103
|
-
var
|
|
679
|
+
var EdgeAgentDetectorWrapperWithWasm = class {
|
|
104
680
|
detector;
|
|
105
681
|
events = /* @__PURE__ */ new Map();
|
|
106
|
-
constructor(
|
|
107
|
-
this.detector = new
|
|
682
|
+
constructor(config) {
|
|
683
|
+
this.detector = new EdgeAgentDetectorWithWasm(config?.enableWasm ?? true);
|
|
108
684
|
}
|
|
109
685
|
async analyze(input) {
|
|
110
686
|
const result = await this.detector.analyze(input);
|
|
@@ -125,7 +701,7 @@ var EdgeAgentDetectorWrapper = class {
|
|
|
125
701
|
handlers.forEach((handler) => handler(...args));
|
|
126
702
|
}
|
|
127
703
|
async init() {
|
|
128
|
-
|
|
704
|
+
await this.detector.init();
|
|
129
705
|
}
|
|
130
706
|
};
|
|
131
707
|
|
|
@@ -301,7 +877,7 @@ var StatelessSessionChecker = class {
|
|
|
301
877
|
|
|
302
878
|
// src/middleware.ts
|
|
303
879
|
function createAgentShieldMiddleware(config = {}) {
|
|
304
|
-
const detector = new EdgeAgentDetectorWrapper(config);
|
|
880
|
+
const detector = config.enableWasm ? new EdgeAgentDetectorWrapperWithWasm({ enableWasm: true }) : new EdgeAgentDetectorWrapper(config);
|
|
305
881
|
const sessionTracker = config.sessionTracking?.enabled || config.enableWasm ? new EdgeSessionTracker({
|
|
306
882
|
enabled: true,
|
|
307
883
|
...config.sessionTracking
|
|
@@ -370,11 +946,14 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
370
946
|
}
|
|
371
947
|
const userAgent = request.headers.get("user-agent");
|
|
372
948
|
const ipAddress = request.ip ?? request.headers.get("x-forwarded-for");
|
|
949
|
+
const url = new URL(request.url);
|
|
950
|
+
const pathWithQuery = url.pathname + url.search;
|
|
373
951
|
const context = {
|
|
374
952
|
...userAgent && { userAgent },
|
|
375
953
|
...ipAddress && { ipAddress },
|
|
376
954
|
headers: Object.fromEntries(request.headers.entries()),
|
|
377
|
-
url:
|
|
955
|
+
url: pathWithQuery,
|
|
956
|
+
// Use path instead of full URL for signature verification
|
|
378
957
|
method: request.method,
|
|
379
958
|
timestamp: /* @__PURE__ */ new Date()
|
|
380
959
|
};
|
|
@@ -457,19 +1036,19 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
457
1036
|
// src/create-middleware.ts
|
|
458
1037
|
var middlewareInstance = null;
|
|
459
1038
|
var isInitializing = false;
|
|
460
|
-
var
|
|
1039
|
+
var initPromise2 = null;
|
|
461
1040
|
function createAgentShieldMiddleware2(config) {
|
|
462
1041
|
return async function agentShieldMiddleware(request) {
|
|
463
1042
|
if (!middlewareInstance) {
|
|
464
1043
|
if (!isInitializing) {
|
|
465
1044
|
isInitializing = true;
|
|
466
|
-
|
|
1045
|
+
initPromise2 = (async () => {
|
|
467
1046
|
middlewareInstance = createAgentShieldMiddleware(config);
|
|
468
1047
|
return middlewareInstance;
|
|
469
1048
|
})();
|
|
470
1049
|
}
|
|
471
|
-
if (
|
|
472
|
-
middlewareInstance = await
|
|
1050
|
+
if (initPromise2) {
|
|
1051
|
+
middlewareInstance = await initPromise2;
|
|
473
1052
|
}
|
|
474
1053
|
}
|
|
475
1054
|
return middlewareInstance ? middlewareInstance(request) : NextResponse.next();
|