@kya-os/agentshield-nextjs 0.1.41 → 0.1.43
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 +821 -233
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +797 -234
- 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 +42 -22
- package/wasm/agentshield_wasm.js +209 -152
- package/wasm/agentshield_wasm_bg.wasm +0 -0
- package/wasm/package.json +30 -0
|
@@ -1,21 +1,151 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var server = require('next/server');
|
|
4
|
+
var ed25519 = require('@noble/ed25519');
|
|
5
|
+
var sha2_js = require('@noble/hashes/sha2.js');
|
|
6
|
+
var agentshieldShared = require('@kya-os/agentshield-shared');
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
function _interopNamespace(e) {
|
|
9
|
+
if (e && e.__esModule) return e;
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var ed25519__namespace = /*#__PURE__*/_interopNamespace(ed25519);
|
|
6
27
|
|
|
7
|
-
// src/
|
|
28
|
+
// src/create-middleware.ts
|
|
29
|
+
ed25519__namespace.etc.sha512Sync = (...m) => sha2_js.sha512(ed25519__namespace.etc.concatBytes(...m));
|
|
8
30
|
var KNOWN_KEYS = {
|
|
9
31
|
chatgpt: [
|
|
10
32
|
{
|
|
11
33
|
kid: "otMqcjr17mGyruktGvJU8oojQTSMHlVm7uO-lrcqbdg",
|
|
12
34
|
// ChatGPT's current Ed25519 public key (base64)
|
|
35
|
+
// Source: https://chatgpt.com/.well-known/http-message-signatures-directory
|
|
13
36
|
publicKey: "7F_3jDlxaquwh291MiACkcS3Opq88NksyHiakzS-Y1g",
|
|
14
|
-
validFrom:
|
|
15
|
-
|
|
37
|
+
validFrom: 1735689600,
|
|
38
|
+
// Jan 1, 2025 (from OpenAI's nbf)
|
|
39
|
+
// Extended expiration as fallback safety - API fetch should provide fresh keys
|
|
40
|
+
// Check OpenAI's well-known endpoint for actual expiration dates
|
|
41
|
+
validUntil: 1799625600
|
|
42
|
+
// Jan 1, 2027 (extended for fallback safety)
|
|
16
43
|
}
|
|
17
44
|
]
|
|
18
45
|
};
|
|
46
|
+
var keyCache = /* @__PURE__ */ new Map();
|
|
47
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
48
|
+
var CACHE_MAX_SIZE = 100;
|
|
49
|
+
function getApiBaseUrl() {
|
|
50
|
+
if (typeof window !== "undefined") {
|
|
51
|
+
return "/api/internal";
|
|
52
|
+
}
|
|
53
|
+
const baseUrl2 = process.env.NEXT_PUBLIC_APP_URL || process.env.NEXT_PUBLIC_API_URL || process.env.API_URL || (process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : null);
|
|
54
|
+
if (baseUrl2) {
|
|
55
|
+
return baseUrl2.replace(/\/$/, "") + "/api/internal";
|
|
56
|
+
}
|
|
57
|
+
if (process.env.NODE_ENV !== "production") {
|
|
58
|
+
console.warn(
|
|
59
|
+
"[Signature] No base URL configured for server-side fetch. Using localhost fallback."
|
|
60
|
+
);
|
|
61
|
+
return "http://localhost:3000/api/internal";
|
|
62
|
+
}
|
|
63
|
+
console.error(
|
|
64
|
+
"[Signature] CRITICAL: No base URL configured for server-side fetch in production!"
|
|
65
|
+
);
|
|
66
|
+
return "/api/internal";
|
|
67
|
+
}
|
|
68
|
+
function cleanupExpiredCache() {
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
const entriesToDelete = [];
|
|
71
|
+
for (const [agent, cached] of keyCache.entries()) {
|
|
72
|
+
if (now - cached.cachedAt > CACHE_TTL_MS) {
|
|
73
|
+
entriesToDelete.push(agent);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const agent of entriesToDelete) {
|
|
77
|
+
keyCache.delete(agent);
|
|
78
|
+
}
|
|
79
|
+
if (keyCache.size > CACHE_MAX_SIZE) {
|
|
80
|
+
const entries = Array.from(keyCache.entries()).map(([agent, cached]) => ({
|
|
81
|
+
agent,
|
|
82
|
+
cachedAt: cached.cachedAt
|
|
83
|
+
}));
|
|
84
|
+
entries.sort((a, b) => a.cachedAt - b.cachedAt);
|
|
85
|
+
const toRemove = entries.slice(0, keyCache.size - CACHE_MAX_SIZE);
|
|
86
|
+
for (const entry of toRemove) {
|
|
87
|
+
keyCache.delete(entry.agent);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async function fetchKeysFromApi(agent) {
|
|
92
|
+
if (keyCache.size > CACHE_MAX_SIZE) {
|
|
93
|
+
cleanupExpiredCache();
|
|
94
|
+
}
|
|
95
|
+
const cached = keyCache.get(agent);
|
|
96
|
+
if (cached && Date.now() - cached.cachedAt < CACHE_TTL_MS) {
|
|
97
|
+
return cached.keys;
|
|
98
|
+
}
|
|
99
|
+
if (typeof fetch === "undefined") {
|
|
100
|
+
console.warn("[Signature] fetch() not available in this environment");
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const apiBaseUrl = getApiBaseUrl();
|
|
105
|
+
const url = `${apiBaseUrl}/signature-keys?agent=${encodeURIComponent(agent)}`;
|
|
106
|
+
const response = await fetch(url, {
|
|
107
|
+
method: "GET",
|
|
108
|
+
headers: {
|
|
109
|
+
"Content-Type": "application/json"
|
|
110
|
+
},
|
|
111
|
+
// 5 second timeout
|
|
112
|
+
signal: AbortSignal.timeout(5e3)
|
|
113
|
+
});
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
console.warn(`[Signature] Failed to fetch keys from API: ${response.status}`);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const data = await response.json();
|
|
119
|
+
if (!data.keys || !Array.isArray(data.keys) || data.keys.length === 0) {
|
|
120
|
+
console.warn(`[Signature] No keys returned from API for agent: ${agent}`);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
keyCache.set(agent, {
|
|
124
|
+
keys: data.keys,
|
|
125
|
+
cachedAt: Date.now()
|
|
126
|
+
});
|
|
127
|
+
return data.keys;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.warn("[Signature] Error fetching keys from API, using fallback", {
|
|
130
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
131
|
+
agent
|
|
132
|
+
});
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function isValidAgent(agent) {
|
|
137
|
+
return agent in KNOWN_KEYS;
|
|
138
|
+
}
|
|
139
|
+
async function getKeysForAgent(agent) {
|
|
140
|
+
const apiKeys = await fetchKeysFromApi(agent);
|
|
141
|
+
if (apiKeys && apiKeys.length > 0) {
|
|
142
|
+
return apiKeys;
|
|
143
|
+
}
|
|
144
|
+
if (isValidAgent(agent)) {
|
|
145
|
+
return KNOWN_KEYS[agent];
|
|
146
|
+
}
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
19
149
|
function parseSignatureInput(signatureInput) {
|
|
20
150
|
try {
|
|
21
151
|
const match = signatureInput.match(/sig1=\((.*?)\);(.+)/);
|
|
@@ -51,21 +181,29 @@ function buildSignatureBase(method, path, headers, signedHeaders) {
|
|
|
51
181
|
case "@authority":
|
|
52
182
|
value = headers["host"] || headers["Host"] || "";
|
|
53
183
|
break;
|
|
54
|
-
default:
|
|
55
|
-
const key = Object.keys(headers).find(
|
|
56
|
-
(k) => k.toLowerCase() === headerName.toLowerCase()
|
|
57
|
-
);
|
|
184
|
+
default: {
|
|
185
|
+
const key = Object.keys(headers).find((k) => k.toLowerCase() === headerName.toLowerCase());
|
|
58
186
|
value = key ? headers[key] || "" : "";
|
|
59
187
|
break;
|
|
188
|
+
}
|
|
60
189
|
}
|
|
61
190
|
components.push(`"${headerName}": ${value}`);
|
|
62
191
|
}
|
|
63
192
|
return components.join("\n");
|
|
64
193
|
}
|
|
194
|
+
function base64ToBytes(base64) {
|
|
195
|
+
let standardBase64 = base64.replace(/-/g, "+").replace(/_/g, "/");
|
|
196
|
+
const padding = standardBase64.length % 4;
|
|
197
|
+
if (padding) {
|
|
198
|
+
standardBase64 += "=".repeat(4 - padding);
|
|
199
|
+
}
|
|
200
|
+
const binaryString = atob(standardBase64);
|
|
201
|
+
return Uint8Array.from(binaryString, (c) => c.charCodeAt(0));
|
|
202
|
+
}
|
|
65
203
|
async function verifyEd25519Signature(publicKeyBase64, signatureBase64, message) {
|
|
66
204
|
try {
|
|
67
|
-
const publicKeyBytes =
|
|
68
|
-
const signatureBytes =
|
|
205
|
+
const publicKeyBytes = base64ToBytes(publicKeyBase64);
|
|
206
|
+
const signatureBytes = base64ToBytes(signatureBase64);
|
|
69
207
|
const messageBytes = new TextEncoder().encode(message);
|
|
70
208
|
if (publicKeyBytes.length !== 32) {
|
|
71
209
|
console.error("[Signature] Invalid public key length:", publicKeyBytes.length);
|
|
@@ -75,34 +213,36 @@ async function verifyEd25519Signature(publicKeyBase64, signatureBase64, message)
|
|
|
75
213
|
console.error("[Signature] Invalid signature length:", signatureBytes.length);
|
|
76
214
|
return false;
|
|
77
215
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
216
|
+
return ed25519__namespace.verify(signatureBytes, messageBytes, publicKeyBytes);
|
|
217
|
+
} catch (nobleError) {
|
|
218
|
+
console.warn("[Signature] @noble/ed25519 failed, trying Web Crypto fallback:", nobleError);
|
|
219
|
+
try {
|
|
220
|
+
const publicKeyBytes = base64ToBytes(publicKeyBase64);
|
|
221
|
+
const signatureBytes = base64ToBytes(signatureBase64);
|
|
222
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
223
|
+
const publicKey = await crypto.subtle.importKey(
|
|
224
|
+
"raw",
|
|
225
|
+
publicKeyBytes.buffer,
|
|
226
|
+
{
|
|
227
|
+
name: "Ed25519",
|
|
228
|
+
namedCurve: "Ed25519"
|
|
229
|
+
},
|
|
230
|
+
false,
|
|
231
|
+
["verify"]
|
|
232
|
+
);
|
|
233
|
+
return await crypto.subtle.verify(
|
|
234
|
+
"Ed25519",
|
|
235
|
+
publicKey,
|
|
236
|
+
signatureBytes.buffer,
|
|
237
|
+
messageBytes
|
|
238
|
+
);
|
|
239
|
+
} catch (cryptoError) {
|
|
240
|
+
console.error("[Signature] Both @noble/ed25519 and Web Crypto failed:", {
|
|
241
|
+
nobleError: nobleError instanceof Error ? nobleError.message : "Unknown",
|
|
242
|
+
cryptoError: cryptoError instanceof Error ? cryptoError.message : "Unknown"
|
|
243
|
+
});
|
|
244
|
+
return false;
|
|
104
245
|
}
|
|
105
|
-
return false;
|
|
106
246
|
}
|
|
107
247
|
}
|
|
108
248
|
async function verifyAgentSignature(method, path, headers) {
|
|
@@ -147,12 +287,12 @@ async function verifyAgentSignature(method, path, headers) {
|
|
|
147
287
|
}
|
|
148
288
|
}
|
|
149
289
|
let agent;
|
|
150
|
-
let
|
|
290
|
+
let agentKey;
|
|
151
291
|
if (signatureAgent === '"https://chatgpt.com"' || signatureAgent?.includes("chatgpt.com")) {
|
|
152
292
|
agent = "ChatGPT";
|
|
153
|
-
|
|
293
|
+
agentKey = "chatgpt";
|
|
154
294
|
}
|
|
155
|
-
if (!agent || !
|
|
295
|
+
if (!agent || !agentKey) {
|
|
156
296
|
return {
|
|
157
297
|
isValid: false,
|
|
158
298
|
confidence: 0,
|
|
@@ -160,6 +300,15 @@ async function verifyAgentSignature(method, path, headers) {
|
|
|
160
300
|
verificationMethod: "none"
|
|
161
301
|
};
|
|
162
302
|
}
|
|
303
|
+
const knownKeys = await getKeysForAgent(agentKey);
|
|
304
|
+
if (knownKeys.length === 0) {
|
|
305
|
+
return {
|
|
306
|
+
isValid: false,
|
|
307
|
+
confidence: 0,
|
|
308
|
+
reason: "No keys available for agent",
|
|
309
|
+
verificationMethod: "none"
|
|
310
|
+
};
|
|
311
|
+
}
|
|
163
312
|
const key = knownKeys.find((k) => k.kid === parsed.keyid);
|
|
164
313
|
if (!key) {
|
|
165
314
|
return {
|
|
@@ -186,11 +335,7 @@ async function verifyAgentSignature(method, path, headers) {
|
|
|
186
335
|
if (signatureValue.endsWith(":")) {
|
|
187
336
|
signatureValue = signatureValue.slice(0, -1);
|
|
188
337
|
}
|
|
189
|
-
const isValid = await verifyEd25519Signature(
|
|
190
|
-
key.publicKey,
|
|
191
|
-
signatureValue,
|
|
192
|
-
signatureBase
|
|
193
|
-
);
|
|
338
|
+
const isValid = await verifyEd25519Signature(key.publicKey, signatureValue, signatureBase);
|
|
194
339
|
if (isValid) {
|
|
195
340
|
return {
|
|
196
341
|
isValid: true,
|
|
@@ -214,27 +359,27 @@ function hasSignatureHeaders(headers) {
|
|
|
214
359
|
}
|
|
215
360
|
function isChatGPTSignature(headers) {
|
|
216
361
|
const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
|
|
217
|
-
|
|
362
|
+
if (!signatureAgent) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
const agentUrlStr = signatureAgent.replace(/^"+|"+$/g, "");
|
|
366
|
+
if (agentUrlStr === "https://chatgpt.com") {
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
try {
|
|
370
|
+
const agentUrl = new URL(agentUrlStr);
|
|
371
|
+
const allowedHosts = ["chatgpt.com", "www.chatgpt.com"];
|
|
372
|
+
return allowedHosts.includes(agentUrl.host);
|
|
373
|
+
} catch {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
218
376
|
}
|
|
219
|
-
|
|
220
|
-
// src/edge-detector-wrapper.ts
|
|
221
|
-
var AI_AGENT_PATTERNS = [
|
|
222
|
-
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
223
|
-
{ pattern: /claude-web/i, type: "claude", name: "Claude" },
|
|
224
|
-
{ pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
|
|
225
|
-
{ pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
|
|
226
|
-
{ pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
|
|
227
|
-
{ pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
|
|
228
|
-
// Fallback
|
|
229
|
-
{ pattern: /bingbot/i, type: "bing", name: "Bing AI" },
|
|
230
|
-
{ pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
|
|
231
|
-
];
|
|
232
|
-
var CLOUD_PROVIDERS = {
|
|
233
|
-
aws: ["54.", "52.", "35.", "18.", "3."],
|
|
234
|
-
gcp: ["35.", "34.", "104.", "107.", "108."],
|
|
235
|
-
azure: ["13.", "20.", "40.", "52.", "104."]
|
|
236
|
-
};
|
|
377
|
+
var rules = agentshieldShared.loadRulesSync();
|
|
237
378
|
var EdgeAgentDetector = class {
|
|
379
|
+
rules;
|
|
380
|
+
constructor() {
|
|
381
|
+
this.rules = rules;
|
|
382
|
+
}
|
|
238
383
|
async analyze(input) {
|
|
239
384
|
const reasons = [];
|
|
240
385
|
let detectedAgent;
|
|
@@ -253,7 +398,7 @@ var EdgeAgentDetector = class {
|
|
|
253
398
|
headers
|
|
254
399
|
);
|
|
255
400
|
if (signatureResult.isValid) {
|
|
256
|
-
confidence = signatureResult.confidence;
|
|
401
|
+
confidence = signatureResult.confidence * 100;
|
|
257
402
|
reasons.push(`verified_signature:${signatureResult.agent?.toLowerCase() || "unknown"}`);
|
|
258
403
|
if (signatureResult.agent) {
|
|
259
404
|
detectedAgent = {
|
|
@@ -266,7 +411,13 @@ var EdgeAgentDetector = class {
|
|
|
266
411
|
reasons.push(`keyid:${signatureResult.keyid}`);
|
|
267
412
|
}
|
|
268
413
|
} else {
|
|
269
|
-
|
|
414
|
+
console.warn("[EdgeAgentDetector] Signature verification failed:", {
|
|
415
|
+
reason: signatureResult.reason,
|
|
416
|
+
agent: signatureResult.agent,
|
|
417
|
+
hasSignatureAgent: !!headers["signature-agent"] || !!headers["Signature-Agent"],
|
|
418
|
+
signatureAgentValue: headers["signature-agent"] || headers["Signature-Agent"]
|
|
419
|
+
});
|
|
420
|
+
confidence = Math.max(confidence, 30);
|
|
270
421
|
reasons.push("invalid_signature");
|
|
271
422
|
if (signatureResult.reason) {
|
|
272
423
|
reasons.push(`signature_error:${signatureResult.reason}`);
|
|
@@ -278,68 +429,133 @@ var EdgeAgentDetector = class {
|
|
|
278
429
|
}
|
|
279
430
|
} catch (error) {
|
|
280
431
|
console.error("[EdgeAgentDetector] Signature verification error:", error);
|
|
281
|
-
confidence = Math.max(confidence,
|
|
432
|
+
confidence = Math.max(confidence, 20);
|
|
282
433
|
reasons.push("signature_verification_error");
|
|
283
434
|
}
|
|
284
435
|
}
|
|
285
436
|
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
286
437
|
if (userAgent) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
438
|
+
const userAgentEntries = Object.entries(this.rules.rules.userAgents);
|
|
439
|
+
const genericKeys = ["generic_bot", "dev_tools", "automation_tools"];
|
|
440
|
+
const sortedEntries = userAgentEntries.sort((a, b) => {
|
|
441
|
+
const aIsGeneric = genericKeys.includes(a[0]);
|
|
442
|
+
const bIsGeneric = genericKeys.includes(b[0]);
|
|
443
|
+
if (aIsGeneric && !bIsGeneric) return 1;
|
|
444
|
+
if (!aIsGeneric && bIsGeneric) return -1;
|
|
445
|
+
return 0;
|
|
446
|
+
});
|
|
447
|
+
for (const [agentKey, agentRule] of sortedEntries) {
|
|
448
|
+
const rule = agentRule;
|
|
449
|
+
const matched = rule.patterns.some((pattern) => {
|
|
450
|
+
const regex = new RegExp(pattern, "i");
|
|
451
|
+
return regex.test(userAgent);
|
|
452
|
+
});
|
|
453
|
+
if (matched) {
|
|
454
|
+
const agentType = this.getAgentType(agentKey);
|
|
455
|
+
const agentName = this.getAgentName(agentKey);
|
|
456
|
+
confidence = Math.max(confidence, rule.confidence * 100);
|
|
457
|
+
reasons.push(`known_pattern:${agentType}`);
|
|
298
458
|
if (!detectedAgent) {
|
|
299
|
-
detectedAgent = { type, name };
|
|
459
|
+
detectedAgent = { type: agentType, name: agentName };
|
|
300
460
|
verificationMethod = "pattern";
|
|
301
461
|
}
|
|
302
462
|
break;
|
|
303
463
|
}
|
|
304
464
|
}
|
|
305
465
|
}
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
"anthropic-client-id",
|
|
310
|
-
"x-goog-api-client",
|
|
311
|
-
"x-ms-copilot-id"
|
|
312
|
-
];
|
|
313
|
-
const foundAiHeaders = aiHeaders.filter(
|
|
314
|
-
(header) => normalizedHeaders[header]
|
|
466
|
+
const suspiciousHeaders = this.rules.rules.headers.suspicious;
|
|
467
|
+
const foundAiHeaders = suspiciousHeaders.filter(
|
|
468
|
+
(headerRule) => normalizedHeaders[headerRule.name.toLowerCase()]
|
|
315
469
|
);
|
|
316
470
|
if (foundAiHeaders.length > 0) {
|
|
317
|
-
|
|
471
|
+
const maxConfidence = Math.max(...foundAiHeaders.map((h) => h.confidence * 100));
|
|
472
|
+
confidence = Math.max(confidence, maxConfidence);
|
|
318
473
|
reasons.push(`ai_headers:${foundAiHeaders.length}`);
|
|
319
474
|
}
|
|
320
475
|
const ip = input.ip || input.ipAddress;
|
|
321
476
|
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
477
|
+
const ipRanges = "providers" in this.rules.rules.ipRanges ? this.rules.rules.ipRanges.providers : this.rules.rules.ipRanges;
|
|
478
|
+
for (const [provider, ipRule] of Object.entries(ipRanges)) {
|
|
479
|
+
if (!ipRule || typeof ipRule !== "object" || !("ranges" in ipRule) || !Array.isArray(ipRule.ranges))
|
|
480
|
+
continue;
|
|
481
|
+
const matched = ipRule.ranges.some((range) => {
|
|
482
|
+
const prefix = range.split("/")[0];
|
|
483
|
+
const prefixParts = prefix.split(".");
|
|
484
|
+
const ipParts = ip.split(".");
|
|
485
|
+
for (let i = 0; i < Math.min(prefixParts.length - 1, 2); i++) {
|
|
486
|
+
if (prefixParts[i] !== ipParts[i] && prefixParts[i] !== "0") {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return true;
|
|
491
|
+
});
|
|
492
|
+
if (matched) {
|
|
493
|
+
const rule = ipRule;
|
|
494
|
+
confidence = Math.max(confidence, rule.confidence * 40);
|
|
325
495
|
reasons.push(`cloud_provider:${provider}`);
|
|
326
496
|
break;
|
|
327
497
|
}
|
|
328
498
|
}
|
|
329
499
|
}
|
|
330
|
-
if (reasons.length > 2 && confidence <
|
|
331
|
-
confidence = Math.min(confidence * 1.2,
|
|
500
|
+
if (reasons.length > 2 && confidence < 100) {
|
|
501
|
+
confidence = Math.min(confidence * 1.2, 95);
|
|
332
502
|
}
|
|
503
|
+
confidence = Math.min(Math.max(confidence, 0), 100);
|
|
333
504
|
return {
|
|
334
|
-
isAgent: confidence >
|
|
505
|
+
isAgent: confidence > 30,
|
|
506
|
+
// Updated to 0-100 scale (was 0.3)
|
|
335
507
|
confidence,
|
|
508
|
+
detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
|
|
509
|
+
signals: [],
|
|
510
|
+
// Will be populated by enhanced detection engine in future tasks
|
|
336
511
|
...detectedAgent && { detectedAgent },
|
|
337
512
|
reasons,
|
|
338
|
-
...verificationMethod && {
|
|
339
|
-
|
|
513
|
+
...verificationMethod && {
|
|
514
|
+
verificationMethod
|
|
515
|
+
},
|
|
516
|
+
forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 80 ? "medium" : "high",
|
|
517
|
+
// Updated to 0-100 scale
|
|
340
518
|
timestamp: /* @__PURE__ */ new Date()
|
|
341
519
|
};
|
|
342
520
|
}
|
|
521
|
+
/**
|
|
522
|
+
* Get agent type from rule key
|
|
523
|
+
*/
|
|
524
|
+
getAgentType(agentKey) {
|
|
525
|
+
const typeMap = {
|
|
526
|
+
openai_gptbot: "openai",
|
|
527
|
+
anthropic_claude: "anthropic",
|
|
528
|
+
perplexity_bot: "perplexity",
|
|
529
|
+
google_ai: "google",
|
|
530
|
+
microsoft_ai: "microsoft",
|
|
531
|
+
meta_ai: "meta",
|
|
532
|
+
cohere_bot: "cohere",
|
|
533
|
+
huggingface_bot: "huggingface",
|
|
534
|
+
generic_bot: "generic",
|
|
535
|
+
dev_tools: "dev",
|
|
536
|
+
automation_tools: "automation"
|
|
537
|
+
};
|
|
538
|
+
return typeMap[agentKey] || agentKey;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Get agent name from rule key
|
|
542
|
+
*/
|
|
543
|
+
getAgentName(agentKey) {
|
|
544
|
+
const nameMap = {
|
|
545
|
+
openai_gptbot: "ChatGPT/GPTBot",
|
|
546
|
+
anthropic_claude: "Claude",
|
|
547
|
+
perplexity_bot: "Perplexity",
|
|
548
|
+
google_ai: "Google AI",
|
|
549
|
+
microsoft_ai: "Microsoft Copilot",
|
|
550
|
+
meta_ai: "Meta AI",
|
|
551
|
+
cohere_bot: "Cohere",
|
|
552
|
+
huggingface_bot: "HuggingFace",
|
|
553
|
+
generic_bot: "Generic Bot",
|
|
554
|
+
dev_tools: "Development Tool",
|
|
555
|
+
automation_tools: "Automation Tool"
|
|
556
|
+
};
|
|
557
|
+
return nameMap[agentKey] || agentKey;
|
|
558
|
+
}
|
|
343
559
|
};
|
|
344
560
|
var EdgeAgentDetectorWrapper = class {
|
|
345
561
|
detector;
|
|
@@ -410,26 +626,31 @@ async function initWasm() {
|
|
|
410
626
|
throw new Error(`Failed to fetch WASM: ${response2.status}`);
|
|
411
627
|
}
|
|
412
628
|
const streamResponse = response2.clone();
|
|
413
|
-
const { instance } = await WebAssembly.instantiateStreaming(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
console.log("WASM:", ptr, len);
|
|
419
|
-
},
|
|
420
|
-
__wbindgen_throw: (ptr, len) => {
|
|
421
|
-
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
629
|
+
const { instance } = await WebAssembly.instantiateStreaming(streamResponse, {
|
|
630
|
+
wbg: {
|
|
631
|
+
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
632
|
+
if (process.env.NODE_ENV !== "production") {
|
|
633
|
+
console.debug("WASM:", ptr, len);
|
|
422
634
|
}
|
|
635
|
+
},
|
|
636
|
+
__wbindgen_throw: (ptr, len) => {
|
|
637
|
+
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
423
638
|
}
|
|
424
639
|
}
|
|
425
|
-
);
|
|
640
|
+
});
|
|
426
641
|
wasmInstance = instance;
|
|
427
642
|
wasmExports = instance.exports;
|
|
428
|
-
|
|
643
|
+
if (process.env.NODE_ENV !== "production") {
|
|
644
|
+
console.debug("[AgentShield] \u2705 WASM module initialized with streaming");
|
|
645
|
+
}
|
|
429
646
|
return;
|
|
430
647
|
} catch (streamError) {
|
|
431
648
|
if (!controller.signal.aborted) {
|
|
432
|
-
|
|
649
|
+
if (process.env.NODE_ENV !== "production") {
|
|
650
|
+
console.debug(
|
|
651
|
+
"[AgentShield] Streaming compilation failed, falling back to standard compilation"
|
|
652
|
+
);
|
|
653
|
+
}
|
|
433
654
|
} else {
|
|
434
655
|
throw streamError;
|
|
435
656
|
}
|
|
@@ -445,7 +666,9 @@ async function initWasm() {
|
|
|
445
666
|
const imports = {
|
|
446
667
|
wbg: {
|
|
447
668
|
__wbg_log_1d3ae13c3d5e6b8e: (ptr, len) => {
|
|
448
|
-
|
|
669
|
+
if (process.env.NODE_ENV !== "production") {
|
|
670
|
+
console.debug("WASM:", ptr, len);
|
|
671
|
+
}
|
|
449
672
|
},
|
|
450
673
|
__wbindgen_throw: (ptr, len) => {
|
|
451
674
|
throw new Error(`WASM Error at ${ptr}, length ${len}`);
|
|
@@ -454,12 +677,20 @@ async function initWasm() {
|
|
|
454
677
|
};
|
|
455
678
|
wasmInstance = await WebAssembly.instantiate(compiledModule, imports);
|
|
456
679
|
wasmExports = wasmInstance.exports;
|
|
457
|
-
|
|
680
|
+
if (process.env.NODE_ENV !== "production") {
|
|
681
|
+
console.debug("[AgentShield] \u2705 WASM module initialized via fallback");
|
|
682
|
+
}
|
|
458
683
|
} catch (fetchError) {
|
|
459
|
-
|
|
460
|
-
|
|
684
|
+
const error = fetchError;
|
|
685
|
+
if (error.name === "AbortError") {
|
|
686
|
+
console.warn(
|
|
687
|
+
"[AgentShield] WASM fetch timed out after 3 seconds - using pattern detection"
|
|
688
|
+
);
|
|
461
689
|
} else {
|
|
462
|
-
console.warn(
|
|
690
|
+
console.warn(
|
|
691
|
+
"[AgentShield] Failed to fetch WASM file:",
|
|
692
|
+
error.message || "Unknown error"
|
|
693
|
+
);
|
|
463
694
|
}
|
|
464
695
|
wasmExports = null;
|
|
465
696
|
}
|
|
@@ -520,30 +751,16 @@ async function isWasmAvailable() {
|
|
|
520
751
|
return false;
|
|
521
752
|
}
|
|
522
753
|
}
|
|
523
|
-
|
|
524
|
-
// src/edge-detector-with-wasm.ts
|
|
525
|
-
var AI_AGENT_PATTERNS2 = [
|
|
526
|
-
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
527
|
-
{ pattern: /claude-web/i, type: "claude", name: "Claude" },
|
|
528
|
-
{ pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
|
|
529
|
-
{ pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
|
|
530
|
-
{ pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
|
|
531
|
-
{ pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
|
|
532
|
-
{ pattern: /bingbot/i, type: "bing", name: "Bing AI" },
|
|
533
|
-
{ pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
|
|
534
|
-
];
|
|
535
|
-
var CLOUD_PROVIDERS2 = {
|
|
536
|
-
aws: ["54.", "52.", "35.", "18.", "3."],
|
|
537
|
-
gcp: ["35.", "34.", "104.", "107.", "108."],
|
|
538
|
-
azure: ["13.", "20.", "40.", "52.", "104."]
|
|
539
|
-
};
|
|
754
|
+
var rules2 = agentshieldShared.loadRulesSync();
|
|
540
755
|
var EdgeAgentDetectorWithWasm = class {
|
|
541
756
|
constructor(enableWasm = true) {
|
|
542
757
|
this.enableWasm = enableWasm;
|
|
758
|
+
this.rules = rules2;
|
|
543
759
|
}
|
|
544
760
|
wasmEnabled = false;
|
|
545
761
|
initPromise = null;
|
|
546
762
|
baseUrl = null;
|
|
763
|
+
rules;
|
|
547
764
|
/**
|
|
548
765
|
* Set the base URL for WASM loading in Edge Runtime
|
|
549
766
|
*/
|
|
@@ -566,11 +783,14 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
566
783
|
this.initPromise = (async () => {
|
|
567
784
|
try {
|
|
568
785
|
const wasmAvailable = await isWasmAvailable();
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
786
|
+
if (wasmAvailable) {
|
|
787
|
+
if (this.baseUrl) {
|
|
788
|
+
setWasmBaseUrl(this.baseUrl);
|
|
789
|
+
}
|
|
790
|
+
await initWasm();
|
|
791
|
+
this.wasmEnabled = true;
|
|
572
792
|
} else {
|
|
573
|
-
|
|
793
|
+
this.wasmEnabled = false;
|
|
574
794
|
}
|
|
575
795
|
} catch (error) {
|
|
576
796
|
console.error("[AgentShield] Failed to initialize WASM:", error);
|
|
@@ -595,69 +815,84 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
595
815
|
const signaturePresent = !!(normalizedHeaders["signature"] || normalizedHeaders["signature-input"]);
|
|
596
816
|
const signatureAgent = normalizedHeaders["signature-agent"];
|
|
597
817
|
if (signatureAgent?.includes("chatgpt.com")) {
|
|
598
|
-
confidence =
|
|
818
|
+
confidence = 85;
|
|
599
819
|
reasons.push("signature_agent:chatgpt");
|
|
600
820
|
detectedAgent = { type: "chatgpt", name: "ChatGPT" };
|
|
601
821
|
verificationMethod = "signature";
|
|
602
822
|
} else if (signaturePresent) {
|
|
603
|
-
confidence = Math.max(confidence,
|
|
823
|
+
confidence = Math.max(confidence, 40);
|
|
604
824
|
reasons.push("signature_present");
|
|
605
825
|
}
|
|
606
826
|
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
607
827
|
if (userAgent) {
|
|
608
|
-
for (const
|
|
609
|
-
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
reasons.push(`known_pattern:${type}`);
|
|
828
|
+
for (const [agentKey, agentRule] of Object.entries(this.rules.rules.userAgents)) {
|
|
829
|
+
const matched = agentRule.patterns.some((pattern) => {
|
|
830
|
+
const regex = new RegExp(pattern, "i");
|
|
831
|
+
return regex.test(userAgent);
|
|
832
|
+
});
|
|
833
|
+
if (matched) {
|
|
834
|
+
const agentType = this.getAgentType(agentKey);
|
|
835
|
+
const agentName = this.getAgentName(agentKey);
|
|
836
|
+
confidence = Math.max(confidence, Math.round(agentRule.confidence * 0.85 * 100));
|
|
837
|
+
reasons.push(`known_pattern:${agentType}`);
|
|
619
838
|
if (!detectedAgent) {
|
|
620
|
-
detectedAgent = { type, name };
|
|
839
|
+
detectedAgent = { type: agentType, name: agentName };
|
|
621
840
|
verificationMethod = "pattern";
|
|
622
841
|
}
|
|
623
842
|
break;
|
|
624
843
|
}
|
|
625
844
|
}
|
|
626
845
|
}
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
"anthropic-client-id",
|
|
631
|
-
"x-goog-api-client",
|
|
632
|
-
"x-ms-copilot-id"
|
|
633
|
-
];
|
|
634
|
-
const foundAiHeaders = aiHeaders.filter(
|
|
635
|
-
(header) => normalizedHeaders[header]
|
|
846
|
+
const suspiciousHeaders = this.rules.rules.headers.suspicious;
|
|
847
|
+
const foundAiHeaders = suspiciousHeaders.filter(
|
|
848
|
+
(headerRule) => normalizedHeaders[headerRule.name.toLowerCase()]
|
|
636
849
|
);
|
|
637
850
|
if (foundAiHeaders.length > 0) {
|
|
638
|
-
|
|
851
|
+
const maxConfidence = Math.max(...foundAiHeaders.map((h) => h.confidence));
|
|
852
|
+
confidence = Math.max(confidence, maxConfidence);
|
|
639
853
|
reasons.push(`ai_headers:${foundAiHeaders.length}`);
|
|
640
854
|
}
|
|
641
855
|
const ip = input.ip || input.ipAddress;
|
|
642
856
|
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
857
|
+
const ipRanges = "providers" in this.rules.rules.ipRanges ? this.rules.rules.ipRanges.providers : this.rules.rules.ipRanges;
|
|
858
|
+
for (const [provider, ipRule] of Object.entries(ipRanges)) {
|
|
859
|
+
if (!ipRule || typeof ipRule !== "object" || !("ranges" in ipRule) || !Array.isArray(ipRule.ranges))
|
|
860
|
+
continue;
|
|
861
|
+
const matched = ipRule.ranges.some((range) => {
|
|
862
|
+
const prefix = range.split("/")[0];
|
|
863
|
+
const prefixParts = prefix.split(".");
|
|
864
|
+
const ipParts = ip.split(".");
|
|
865
|
+
for (let i = 0; i < Math.min(prefixParts.length - 1, 2); i++) {
|
|
866
|
+
if (prefixParts[i] !== ipParts[i] && prefixParts[i] !== "0") {
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return true;
|
|
871
|
+
});
|
|
872
|
+
if (matched) {
|
|
873
|
+
confidence = Math.max(confidence, Math.round(ipRule.confidence * 0.4 * 100));
|
|
646
874
|
reasons.push(`cloud_provider:${provider}`);
|
|
647
875
|
break;
|
|
648
876
|
}
|
|
649
877
|
}
|
|
650
878
|
}
|
|
651
879
|
if (reasons.length > 2) {
|
|
652
|
-
confidence = Math.min(confidence * 1.2,
|
|
880
|
+
confidence = Math.min(Math.round(confidence * 1.2), 95);
|
|
653
881
|
}
|
|
882
|
+
confidence = Math.min(Math.max(confidence, 0), 100);
|
|
654
883
|
return {
|
|
655
|
-
isAgent: confidence >
|
|
884
|
+
isAgent: confidence > 30,
|
|
885
|
+
// 30% threshold
|
|
656
886
|
confidence,
|
|
887
|
+
detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
|
|
888
|
+
signals: [],
|
|
889
|
+
// Will be populated by enhanced detection engine in future tasks
|
|
657
890
|
...detectedAgent && { detectedAgent },
|
|
658
891
|
reasons,
|
|
659
|
-
...verificationMethod && {
|
|
660
|
-
|
|
892
|
+
...verificationMethod && {
|
|
893
|
+
verificationMethod: agentshieldShared.mapVerificationMethod(verificationMethod)
|
|
894
|
+
},
|
|
895
|
+
forgeabilityRisk: confidence > 80 ? "medium" : "high",
|
|
661
896
|
timestamp: /* @__PURE__ */ new Date()
|
|
662
897
|
};
|
|
663
898
|
}
|
|
@@ -674,15 +909,17 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
674
909
|
input.ip || input.ipAddress
|
|
675
910
|
);
|
|
676
911
|
if (wasmResult) {
|
|
677
|
-
console.log("[AgentShield] Using WASM detection result");
|
|
678
912
|
const detectedAgent = wasmResult.agent ? this.mapAgentName(wasmResult.agent) : void 0;
|
|
679
913
|
return {
|
|
680
914
|
isAgent: wasmResult.isAgent,
|
|
681
915
|
confidence: wasmResult.confidence,
|
|
916
|
+
detectionClass: wasmResult.isAgent && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : wasmResult.isAgent ? { type: "Unknown" } : { type: "Human" },
|
|
917
|
+
signals: [],
|
|
918
|
+
// Will be populated by enhanced detection engine in future tasks
|
|
682
919
|
...detectedAgent && { detectedAgent },
|
|
683
920
|
reasons: [`wasm:${wasmResult.verificationMethod}`],
|
|
684
|
-
verificationMethod: wasmResult.verificationMethod,
|
|
685
|
-
forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence >
|
|
921
|
+
verificationMethod: agentshieldShared.mapVerificationMethod(wasmResult.verificationMethod),
|
|
922
|
+
forgeabilityRisk: wasmResult.verificationMethod === "signature" ? "low" : wasmResult.confidence > 90 ? "medium" : "high",
|
|
686
923
|
timestamp: /* @__PURE__ */ new Date()
|
|
687
924
|
};
|
|
688
925
|
}
|
|
@@ -691,15 +928,50 @@ var EdgeAgentDetectorWithWasm = class {
|
|
|
691
928
|
}
|
|
692
929
|
}
|
|
693
930
|
const patternResult = await this.patternDetection(input);
|
|
694
|
-
if (this.wasmEnabled && patternResult.confidence >=
|
|
695
|
-
patternResult.confidence = Math.min(
|
|
931
|
+
if (this.wasmEnabled && patternResult.confidence >= 85) {
|
|
932
|
+
patternResult.confidence = Math.min(95, patternResult.confidence + 10);
|
|
696
933
|
patternResult.reasons.push("wasm_enhanced");
|
|
697
|
-
if (!patternResult.verificationMethod) {
|
|
698
|
-
patternResult.verificationMethod = "wasm-enhanced";
|
|
699
|
-
}
|
|
700
934
|
}
|
|
701
935
|
return patternResult;
|
|
702
936
|
}
|
|
937
|
+
/**
|
|
938
|
+
* Get agent type from rule key
|
|
939
|
+
*/
|
|
940
|
+
getAgentType(agentKey) {
|
|
941
|
+
const typeMap = {
|
|
942
|
+
openai_gptbot: "openai",
|
|
943
|
+
anthropic_claude: "anthropic",
|
|
944
|
+
perplexity_bot: "perplexity",
|
|
945
|
+
google_ai: "google",
|
|
946
|
+
microsoft_ai: "microsoft",
|
|
947
|
+
meta_ai: "meta",
|
|
948
|
+
cohere_bot: "cohere",
|
|
949
|
+
huggingface_bot: "huggingface",
|
|
950
|
+
generic_bot: "generic",
|
|
951
|
+
dev_tools: "dev",
|
|
952
|
+
automation_tools: "automation"
|
|
953
|
+
};
|
|
954
|
+
return typeMap[agentKey] || agentKey;
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Get agent name from rule key
|
|
958
|
+
*/
|
|
959
|
+
getAgentName(agentKey) {
|
|
960
|
+
const nameMap = {
|
|
961
|
+
openai_gptbot: "ChatGPT/GPTBot",
|
|
962
|
+
anthropic_claude: "Claude",
|
|
963
|
+
perplexity_bot: "Perplexity",
|
|
964
|
+
google_ai: "Google AI",
|
|
965
|
+
microsoft_ai: "Microsoft Copilot",
|
|
966
|
+
meta_ai: "Meta AI",
|
|
967
|
+
cohere_bot: "Cohere",
|
|
968
|
+
huggingface_bot: "HuggingFace",
|
|
969
|
+
generic_bot: "Generic Bot",
|
|
970
|
+
dev_tools: "Development Tool",
|
|
971
|
+
automation_tools: "Automation Tool"
|
|
972
|
+
};
|
|
973
|
+
return nameMap[agentKey] || agentKey;
|
|
974
|
+
}
|
|
703
975
|
/**
|
|
704
976
|
* Map agent names from WASM to consistent format
|
|
705
977
|
*/
|
|
@@ -880,7 +1152,9 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
880
1152
|
}) : null;
|
|
881
1153
|
if (config.events) {
|
|
882
1154
|
Object.entries(config.events).forEach(([event, handler]) => {
|
|
883
|
-
|
|
1155
|
+
if (handler) {
|
|
1156
|
+
detector.on(event, handler);
|
|
1157
|
+
}
|
|
884
1158
|
});
|
|
885
1159
|
}
|
|
886
1160
|
const {
|
|
@@ -912,19 +1186,22 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
912
1186
|
const response2 = server.NextResponse.next();
|
|
913
1187
|
response2.headers.set("x-agentshield-detected", "true");
|
|
914
1188
|
response2.headers.set("x-agentshield-agent", existingSession.agent);
|
|
915
|
-
response2.headers.set(
|
|
916
|
-
"x-agentshield-confidence",
|
|
917
|
-
existingSession.confidence.toString()
|
|
918
|
-
);
|
|
1189
|
+
response2.headers.set("x-agentshield-confidence", existingSession.confidence.toString());
|
|
919
1190
|
response2.headers.set("x-agentshield-session", "continued");
|
|
920
1191
|
response2.headers.set("x-agentshield-session-id", existingSession.id);
|
|
921
1192
|
request.agentShield = {
|
|
922
1193
|
result: {
|
|
923
1194
|
isAgent: true,
|
|
924
1195
|
confidence: existingSession.confidence,
|
|
925
|
-
|
|
1196
|
+
detectionClass: { type: "AiAgent" },
|
|
1197
|
+
detectedAgent: {
|
|
1198
|
+
type: "ai_agent",
|
|
1199
|
+
name: existingSession.agent
|
|
1200
|
+
},
|
|
926
1201
|
timestamp: /* @__PURE__ */ new Date(),
|
|
927
|
-
verificationMethod: "
|
|
1202
|
+
verificationMethod: "behavioral",
|
|
1203
|
+
reasons: ["Session continued"],
|
|
1204
|
+
signals: []
|
|
928
1205
|
},
|
|
929
1206
|
session: existingSession,
|
|
930
1207
|
skipped: false
|
|
@@ -954,7 +1231,7 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
954
1231
|
timestamp: /* @__PURE__ */ new Date()
|
|
955
1232
|
};
|
|
956
1233
|
const result = await detector.analyze(context);
|
|
957
|
-
if (result.isAgent && result.confidence >= (config.confidenceThreshold ??
|
|
1234
|
+
if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
|
|
958
1235
|
if (onDetection) {
|
|
959
1236
|
const customResponse = await onDetection(request, result);
|
|
960
1237
|
if (customResponse) {
|
|
@@ -973,11 +1250,9 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
973
1250
|
{ status: blockedResponse.status }
|
|
974
1251
|
);
|
|
975
1252
|
if (blockedResponse.headers) {
|
|
976
|
-
Object.entries(blockedResponse.headers).forEach(
|
|
977
|
-
(
|
|
978
|
-
|
|
979
|
-
}
|
|
980
|
-
);
|
|
1253
|
+
Object.entries(blockedResponse.headers).forEach(([key, value]) => {
|
|
1254
|
+
response2.headers.set(key, value);
|
|
1255
|
+
});
|
|
981
1256
|
}
|
|
982
1257
|
detector.emit("agent.blocked", result, context);
|
|
983
1258
|
return response2;
|
|
@@ -987,17 +1262,19 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
987
1262
|
case "rewrite":
|
|
988
1263
|
return server.NextResponse.rewrite(new URL(rewriteUrl, request.url));
|
|
989
1264
|
case "log":
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1265
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1266
|
+
console.debug("AgentShield: Agent detected", {
|
|
1267
|
+
ipAddress: context.ipAddress,
|
|
1268
|
+
userAgent: context.userAgent,
|
|
1269
|
+
confidence: result.confidence,
|
|
1270
|
+
reasons: result.reasons,
|
|
1271
|
+
pathname: request.nextUrl.pathname
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
997
1274
|
break;
|
|
998
1275
|
case "allow":
|
|
999
1276
|
default:
|
|
1000
|
-
if (result.isAgent && result.confidence >= (config.confidenceThreshold ??
|
|
1277
|
+
if (result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
|
|
1001
1278
|
detector.emit("agent.allowed", result, context);
|
|
1002
1279
|
}
|
|
1003
1280
|
break;
|
|
@@ -1009,14 +1286,11 @@ function createAgentShieldMiddleware(config = {}) {
|
|
|
1009
1286
|
};
|
|
1010
1287
|
let response = server.NextResponse.next();
|
|
1011
1288
|
response.headers.set("x-agentshield-detected", result.isAgent.toString());
|
|
1012
|
-
response.headers.set(
|
|
1013
|
-
"x-agentshield-confidence",
|
|
1014
|
-
result.confidence.toString()
|
|
1015
|
-
);
|
|
1289
|
+
response.headers.set("x-agentshield-confidence", result.confidence.toString());
|
|
1016
1290
|
if (result.detectedAgent?.name) {
|
|
1017
1291
|
response.headers.set("x-agentshield-agent", result.detectedAgent.name);
|
|
1018
1292
|
}
|
|
1019
|
-
if (sessionTracker && result.isAgent && result.confidence >= (config.confidenceThreshold ??
|
|
1293
|
+
if (sessionTracker && result.isAgent && result.confidence >= (config.confidenceThreshold ?? 70)) {
|
|
1020
1294
|
response = await sessionTracker.track(request, response, result);
|
|
1021
1295
|
response.headers.set("x-agentshield-session", "new");
|
|
1022
1296
|
detector.emit("agent.session.started", result, context);
|