@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,15 +1,128 @@
|
|
|
1
|
+
import * as ed25519 from '@noble/ed25519';
|
|
2
|
+
import { sha512 } from '@noble/hashes/sha2.js';
|
|
3
|
+
import { loadRulesSync } from '@kya-os/agentshield-shared';
|
|
4
|
+
|
|
1
5
|
// src/signature-verifier.ts
|
|
6
|
+
ed25519.etc.sha512Sync = (...m) => sha512(ed25519.etc.concatBytes(...m));
|
|
2
7
|
var KNOWN_KEYS = {
|
|
3
8
|
chatgpt: [
|
|
4
9
|
{
|
|
5
10
|
kid: "otMqcjr17mGyruktGvJU8oojQTSMHlVm7uO-lrcqbdg",
|
|
6
11
|
// ChatGPT's current Ed25519 public key (base64)
|
|
12
|
+
// Source: https://chatgpt.com/.well-known/http-message-signatures-directory
|
|
7
13
|
publicKey: "7F_3jDlxaquwh291MiACkcS3Opq88NksyHiakzS-Y1g",
|
|
8
|
-
validFrom:
|
|
9
|
-
|
|
14
|
+
validFrom: 1735689600,
|
|
15
|
+
// Jan 1, 2025 (from OpenAI's nbf)
|
|
16
|
+
// Extended expiration as fallback safety - API fetch should provide fresh keys
|
|
17
|
+
// Check OpenAI's well-known endpoint for actual expiration dates
|
|
18
|
+
validUntil: 1799625600
|
|
19
|
+
// Jan 1, 2027 (extended for fallback safety)
|
|
10
20
|
}
|
|
11
21
|
]
|
|
12
22
|
};
|
|
23
|
+
var keyCache = /* @__PURE__ */ new Map();
|
|
24
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
25
|
+
var CACHE_MAX_SIZE = 100;
|
|
26
|
+
function getApiBaseUrl() {
|
|
27
|
+
if (typeof window !== "undefined") {
|
|
28
|
+
return "/api/internal";
|
|
29
|
+
}
|
|
30
|
+
const baseUrl = 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);
|
|
31
|
+
if (baseUrl) {
|
|
32
|
+
return baseUrl.replace(/\/$/, "") + "/api/internal";
|
|
33
|
+
}
|
|
34
|
+
if (process.env.NODE_ENV !== "production") {
|
|
35
|
+
console.warn(
|
|
36
|
+
"[Signature] No base URL configured for server-side fetch. Using localhost fallback."
|
|
37
|
+
);
|
|
38
|
+
return "http://localhost:3000/api/internal";
|
|
39
|
+
}
|
|
40
|
+
console.error(
|
|
41
|
+
"[Signature] CRITICAL: No base URL configured for server-side fetch in production!"
|
|
42
|
+
);
|
|
43
|
+
return "/api/internal";
|
|
44
|
+
}
|
|
45
|
+
function cleanupExpiredCache() {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const entriesToDelete = [];
|
|
48
|
+
for (const [agent, cached] of keyCache.entries()) {
|
|
49
|
+
if (now - cached.cachedAt > CACHE_TTL_MS) {
|
|
50
|
+
entriesToDelete.push(agent);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const agent of entriesToDelete) {
|
|
54
|
+
keyCache.delete(agent);
|
|
55
|
+
}
|
|
56
|
+
if (keyCache.size > CACHE_MAX_SIZE) {
|
|
57
|
+
const entries = Array.from(keyCache.entries()).map(([agent, cached]) => ({
|
|
58
|
+
agent,
|
|
59
|
+
cachedAt: cached.cachedAt
|
|
60
|
+
}));
|
|
61
|
+
entries.sort((a, b) => a.cachedAt - b.cachedAt);
|
|
62
|
+
const toRemove = entries.slice(0, keyCache.size - CACHE_MAX_SIZE);
|
|
63
|
+
for (const entry of toRemove) {
|
|
64
|
+
keyCache.delete(entry.agent);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function fetchKeysFromApi(agent) {
|
|
69
|
+
if (keyCache.size > CACHE_MAX_SIZE) {
|
|
70
|
+
cleanupExpiredCache();
|
|
71
|
+
}
|
|
72
|
+
const cached = keyCache.get(agent);
|
|
73
|
+
if (cached && Date.now() - cached.cachedAt < CACHE_TTL_MS) {
|
|
74
|
+
return cached.keys;
|
|
75
|
+
}
|
|
76
|
+
if (typeof fetch === "undefined") {
|
|
77
|
+
console.warn("[Signature] fetch() not available in this environment");
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const apiBaseUrl = getApiBaseUrl();
|
|
82
|
+
const url = `${apiBaseUrl}/signature-keys?agent=${encodeURIComponent(agent)}`;
|
|
83
|
+
const response = await fetch(url, {
|
|
84
|
+
method: "GET",
|
|
85
|
+
headers: {
|
|
86
|
+
"Content-Type": "application/json"
|
|
87
|
+
},
|
|
88
|
+
// 5 second timeout
|
|
89
|
+
signal: AbortSignal.timeout(5e3)
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
console.warn(`[Signature] Failed to fetch keys from API: ${response.status}`);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const data = await response.json();
|
|
96
|
+
if (!data.keys || !Array.isArray(data.keys) || data.keys.length === 0) {
|
|
97
|
+
console.warn(`[Signature] No keys returned from API for agent: ${agent}`);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
keyCache.set(agent, {
|
|
101
|
+
keys: data.keys,
|
|
102
|
+
cachedAt: Date.now()
|
|
103
|
+
});
|
|
104
|
+
return data.keys;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.warn("[Signature] Error fetching keys from API, using fallback", {
|
|
107
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
108
|
+
agent
|
|
109
|
+
});
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function isValidAgent(agent) {
|
|
114
|
+
return agent in KNOWN_KEYS;
|
|
115
|
+
}
|
|
116
|
+
async function getKeysForAgent(agent) {
|
|
117
|
+
const apiKeys = await fetchKeysFromApi(agent);
|
|
118
|
+
if (apiKeys && apiKeys.length > 0) {
|
|
119
|
+
return apiKeys;
|
|
120
|
+
}
|
|
121
|
+
if (isValidAgent(agent)) {
|
|
122
|
+
return KNOWN_KEYS[agent];
|
|
123
|
+
}
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
13
126
|
function parseSignatureInput(signatureInput) {
|
|
14
127
|
try {
|
|
15
128
|
const match = signatureInput.match(/sig1=\((.*?)\);(.+)/);
|
|
@@ -45,21 +158,29 @@ function buildSignatureBase(method, path, headers, signedHeaders) {
|
|
|
45
158
|
case "@authority":
|
|
46
159
|
value = headers["host"] || headers["Host"] || "";
|
|
47
160
|
break;
|
|
48
|
-
default:
|
|
49
|
-
const key = Object.keys(headers).find(
|
|
50
|
-
(k) => k.toLowerCase() === headerName.toLowerCase()
|
|
51
|
-
);
|
|
161
|
+
default: {
|
|
162
|
+
const key = Object.keys(headers).find((k) => k.toLowerCase() === headerName.toLowerCase());
|
|
52
163
|
value = key ? headers[key] || "" : "";
|
|
53
164
|
break;
|
|
165
|
+
}
|
|
54
166
|
}
|
|
55
167
|
components.push(`"${headerName}": ${value}`);
|
|
56
168
|
}
|
|
57
169
|
return components.join("\n");
|
|
58
170
|
}
|
|
171
|
+
function base64ToBytes(base64) {
|
|
172
|
+
let standardBase64 = base64.replace(/-/g, "+").replace(/_/g, "/");
|
|
173
|
+
const padding = standardBase64.length % 4;
|
|
174
|
+
if (padding) {
|
|
175
|
+
standardBase64 += "=".repeat(4 - padding);
|
|
176
|
+
}
|
|
177
|
+
const binaryString = atob(standardBase64);
|
|
178
|
+
return Uint8Array.from(binaryString, (c) => c.charCodeAt(0));
|
|
179
|
+
}
|
|
59
180
|
async function verifyEd25519Signature(publicKeyBase64, signatureBase64, message) {
|
|
60
181
|
try {
|
|
61
|
-
const publicKeyBytes =
|
|
62
|
-
const signatureBytes =
|
|
182
|
+
const publicKeyBytes = base64ToBytes(publicKeyBase64);
|
|
183
|
+
const signatureBytes = base64ToBytes(signatureBase64);
|
|
63
184
|
const messageBytes = new TextEncoder().encode(message);
|
|
64
185
|
if (publicKeyBytes.length !== 32) {
|
|
65
186
|
console.error("[Signature] Invalid public key length:", publicKeyBytes.length);
|
|
@@ -69,34 +190,36 @@ async function verifyEd25519Signature(publicKeyBase64, signatureBase64, message)
|
|
|
69
190
|
console.error("[Signature] Invalid signature length:", signatureBytes.length);
|
|
70
191
|
return false;
|
|
71
192
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
193
|
+
return ed25519.verify(signatureBytes, messageBytes, publicKeyBytes);
|
|
194
|
+
} catch (nobleError) {
|
|
195
|
+
console.warn("[Signature] @noble/ed25519 failed, trying Web Crypto fallback:", nobleError);
|
|
196
|
+
try {
|
|
197
|
+
const publicKeyBytes = base64ToBytes(publicKeyBase64);
|
|
198
|
+
const signatureBytes = base64ToBytes(signatureBase64);
|
|
199
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
200
|
+
const publicKey = await crypto.subtle.importKey(
|
|
201
|
+
"raw",
|
|
202
|
+
publicKeyBytes.buffer,
|
|
203
|
+
{
|
|
204
|
+
name: "Ed25519",
|
|
205
|
+
namedCurve: "Ed25519"
|
|
206
|
+
},
|
|
207
|
+
false,
|
|
208
|
+
["verify"]
|
|
209
|
+
);
|
|
210
|
+
return await crypto.subtle.verify(
|
|
211
|
+
"Ed25519",
|
|
212
|
+
publicKey,
|
|
213
|
+
signatureBytes.buffer,
|
|
214
|
+
messageBytes
|
|
215
|
+
);
|
|
216
|
+
} catch (cryptoError) {
|
|
217
|
+
console.error("[Signature] Both @noble/ed25519 and Web Crypto failed:", {
|
|
218
|
+
nobleError: nobleError instanceof Error ? nobleError.message : "Unknown",
|
|
219
|
+
cryptoError: cryptoError instanceof Error ? cryptoError.message : "Unknown"
|
|
220
|
+
});
|
|
221
|
+
return false;
|
|
98
222
|
}
|
|
99
|
-
return false;
|
|
100
223
|
}
|
|
101
224
|
}
|
|
102
225
|
async function verifyAgentSignature(method, path, headers) {
|
|
@@ -141,12 +264,12 @@ async function verifyAgentSignature(method, path, headers) {
|
|
|
141
264
|
}
|
|
142
265
|
}
|
|
143
266
|
let agent;
|
|
144
|
-
let
|
|
267
|
+
let agentKey;
|
|
145
268
|
if (signatureAgent === '"https://chatgpt.com"' || signatureAgent?.includes("chatgpt.com")) {
|
|
146
269
|
agent = "ChatGPT";
|
|
147
|
-
|
|
270
|
+
agentKey = "chatgpt";
|
|
148
271
|
}
|
|
149
|
-
if (!agent || !
|
|
272
|
+
if (!agent || !agentKey) {
|
|
150
273
|
return {
|
|
151
274
|
isValid: false,
|
|
152
275
|
confidence: 0,
|
|
@@ -154,6 +277,15 @@ async function verifyAgentSignature(method, path, headers) {
|
|
|
154
277
|
verificationMethod: "none"
|
|
155
278
|
};
|
|
156
279
|
}
|
|
280
|
+
const knownKeys = await getKeysForAgent(agentKey);
|
|
281
|
+
if (knownKeys.length === 0) {
|
|
282
|
+
return {
|
|
283
|
+
isValid: false,
|
|
284
|
+
confidence: 0,
|
|
285
|
+
reason: "No keys available for agent",
|
|
286
|
+
verificationMethod: "none"
|
|
287
|
+
};
|
|
288
|
+
}
|
|
157
289
|
const key = knownKeys.find((k) => k.kid === parsed.keyid);
|
|
158
290
|
if (!key) {
|
|
159
291
|
return {
|
|
@@ -180,11 +312,7 @@ async function verifyAgentSignature(method, path, headers) {
|
|
|
180
312
|
if (signatureValue.endsWith(":")) {
|
|
181
313
|
signatureValue = signatureValue.slice(0, -1);
|
|
182
314
|
}
|
|
183
|
-
const isValid = await verifyEd25519Signature(
|
|
184
|
-
key.publicKey,
|
|
185
|
-
signatureValue,
|
|
186
|
-
signatureBase
|
|
187
|
-
);
|
|
315
|
+
const isValid = await verifyEd25519Signature(key.publicKey, signatureValue, signatureBase);
|
|
188
316
|
if (isValid) {
|
|
189
317
|
return {
|
|
190
318
|
isValid: true,
|
|
@@ -208,27 +336,27 @@ function hasSignatureHeaders(headers) {
|
|
|
208
336
|
}
|
|
209
337
|
function isChatGPTSignature(headers) {
|
|
210
338
|
const signatureAgent = headers["signature-agent"] || headers["Signature-Agent"];
|
|
211
|
-
|
|
339
|
+
if (!signatureAgent) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
const agentUrlStr = signatureAgent.replace(/^"+|"+$/g, "");
|
|
343
|
+
if (agentUrlStr === "https://chatgpt.com") {
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
const agentUrl = new URL(agentUrlStr);
|
|
348
|
+
const allowedHosts = ["chatgpt.com", "www.chatgpt.com"];
|
|
349
|
+
return allowedHosts.includes(agentUrl.host);
|
|
350
|
+
} catch {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
212
353
|
}
|
|
213
|
-
|
|
214
|
-
// src/edge-detector-wrapper.ts
|
|
215
|
-
var AI_AGENT_PATTERNS = [
|
|
216
|
-
{ pattern: /chatgpt-user/i, type: "chatgpt", name: "ChatGPT" },
|
|
217
|
-
{ pattern: /claude-web/i, type: "claude", name: "Claude" },
|
|
218
|
-
{ pattern: /perplexitybot/i, type: "perplexity", name: "Perplexity" },
|
|
219
|
-
{ pattern: /perplexity-user/i, type: "perplexity", name: "Perplexity" },
|
|
220
|
-
{ pattern: /perplexity-ai/i, type: "perplexity", name: "Perplexity" },
|
|
221
|
-
{ pattern: /perplexity/i, type: "perplexity", name: "Perplexity" },
|
|
222
|
-
// Fallback
|
|
223
|
-
{ pattern: /bingbot/i, type: "bing", name: "Bing AI" },
|
|
224
|
-
{ pattern: /anthropic-ai/i, type: "anthropic", name: "Anthropic" }
|
|
225
|
-
];
|
|
226
|
-
var CLOUD_PROVIDERS = {
|
|
227
|
-
aws: ["54.", "52.", "35.", "18.", "3."],
|
|
228
|
-
gcp: ["35.", "34.", "104.", "107.", "108."],
|
|
229
|
-
azure: ["13.", "20.", "40.", "52.", "104."]
|
|
230
|
-
};
|
|
354
|
+
var rules = loadRulesSync();
|
|
231
355
|
var EdgeAgentDetector = class {
|
|
356
|
+
rules;
|
|
357
|
+
constructor() {
|
|
358
|
+
this.rules = rules;
|
|
359
|
+
}
|
|
232
360
|
async analyze(input) {
|
|
233
361
|
const reasons = [];
|
|
234
362
|
let detectedAgent;
|
|
@@ -247,7 +375,7 @@ var EdgeAgentDetector = class {
|
|
|
247
375
|
headers
|
|
248
376
|
);
|
|
249
377
|
if (signatureResult.isValid) {
|
|
250
|
-
confidence = signatureResult.confidence;
|
|
378
|
+
confidence = signatureResult.confidence * 100;
|
|
251
379
|
reasons.push(`verified_signature:${signatureResult.agent?.toLowerCase() || "unknown"}`);
|
|
252
380
|
if (signatureResult.agent) {
|
|
253
381
|
detectedAgent = {
|
|
@@ -260,7 +388,13 @@ var EdgeAgentDetector = class {
|
|
|
260
388
|
reasons.push(`keyid:${signatureResult.keyid}`);
|
|
261
389
|
}
|
|
262
390
|
} else {
|
|
263
|
-
|
|
391
|
+
console.warn("[EdgeAgentDetector] Signature verification failed:", {
|
|
392
|
+
reason: signatureResult.reason,
|
|
393
|
+
agent: signatureResult.agent,
|
|
394
|
+
hasSignatureAgent: !!headers["signature-agent"] || !!headers["Signature-Agent"],
|
|
395
|
+
signatureAgentValue: headers["signature-agent"] || headers["Signature-Agent"]
|
|
396
|
+
});
|
|
397
|
+
confidence = Math.max(confidence, 30);
|
|
264
398
|
reasons.push("invalid_signature");
|
|
265
399
|
if (signatureResult.reason) {
|
|
266
400
|
reasons.push(`signature_error:${signatureResult.reason}`);
|
|
@@ -272,68 +406,133 @@ var EdgeAgentDetector = class {
|
|
|
272
406
|
}
|
|
273
407
|
} catch (error) {
|
|
274
408
|
console.error("[EdgeAgentDetector] Signature verification error:", error);
|
|
275
|
-
confidence = Math.max(confidence,
|
|
409
|
+
confidence = Math.max(confidence, 20);
|
|
276
410
|
reasons.push("signature_verification_error");
|
|
277
411
|
}
|
|
278
412
|
}
|
|
279
413
|
const userAgent = input.userAgent || input.headers?.["user-agent"] || "";
|
|
280
414
|
if (userAgent) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
415
|
+
const userAgentEntries = Object.entries(this.rules.rules.userAgents);
|
|
416
|
+
const genericKeys = ["generic_bot", "dev_tools", "automation_tools"];
|
|
417
|
+
const sortedEntries = userAgentEntries.sort((a, b) => {
|
|
418
|
+
const aIsGeneric = genericKeys.includes(a[0]);
|
|
419
|
+
const bIsGeneric = genericKeys.includes(b[0]);
|
|
420
|
+
if (aIsGeneric && !bIsGeneric) return 1;
|
|
421
|
+
if (!aIsGeneric && bIsGeneric) return -1;
|
|
422
|
+
return 0;
|
|
423
|
+
});
|
|
424
|
+
for (const [agentKey, agentRule] of sortedEntries) {
|
|
425
|
+
const rule = agentRule;
|
|
426
|
+
const matched = rule.patterns.some((pattern) => {
|
|
427
|
+
const regex = new RegExp(pattern, "i");
|
|
428
|
+
return regex.test(userAgent);
|
|
429
|
+
});
|
|
430
|
+
if (matched) {
|
|
431
|
+
const agentType = this.getAgentType(agentKey);
|
|
432
|
+
const agentName = this.getAgentName(agentKey);
|
|
433
|
+
confidence = Math.max(confidence, rule.confidence * 100);
|
|
434
|
+
reasons.push(`known_pattern:${agentType}`);
|
|
292
435
|
if (!detectedAgent) {
|
|
293
|
-
detectedAgent = { type, name };
|
|
436
|
+
detectedAgent = { type: agentType, name: agentName };
|
|
294
437
|
verificationMethod = "pattern";
|
|
295
438
|
}
|
|
296
439
|
break;
|
|
297
440
|
}
|
|
298
441
|
}
|
|
299
442
|
}
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
"anthropic-client-id",
|
|
304
|
-
"x-goog-api-client",
|
|
305
|
-
"x-ms-copilot-id"
|
|
306
|
-
];
|
|
307
|
-
const foundAiHeaders = aiHeaders.filter(
|
|
308
|
-
(header) => normalizedHeaders[header]
|
|
443
|
+
const suspiciousHeaders = this.rules.rules.headers.suspicious;
|
|
444
|
+
const foundAiHeaders = suspiciousHeaders.filter(
|
|
445
|
+
(headerRule) => normalizedHeaders[headerRule.name.toLowerCase()]
|
|
309
446
|
);
|
|
310
447
|
if (foundAiHeaders.length > 0) {
|
|
311
|
-
|
|
448
|
+
const maxConfidence = Math.max(...foundAiHeaders.map((h) => h.confidence * 100));
|
|
449
|
+
confidence = Math.max(confidence, maxConfidence);
|
|
312
450
|
reasons.push(`ai_headers:${foundAiHeaders.length}`);
|
|
313
451
|
}
|
|
314
452
|
const ip = input.ip || input.ipAddress;
|
|
315
453
|
if (ip && !normalizedHeaders["x-forwarded-for"] && !normalizedHeaders["x-real-ip"]) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
454
|
+
const ipRanges = "providers" in this.rules.rules.ipRanges ? this.rules.rules.ipRanges.providers : this.rules.rules.ipRanges;
|
|
455
|
+
for (const [provider, ipRule] of Object.entries(ipRanges)) {
|
|
456
|
+
if (!ipRule || typeof ipRule !== "object" || !("ranges" in ipRule) || !Array.isArray(ipRule.ranges))
|
|
457
|
+
continue;
|
|
458
|
+
const matched = ipRule.ranges.some((range) => {
|
|
459
|
+
const prefix = range.split("/")[0];
|
|
460
|
+
const prefixParts = prefix.split(".");
|
|
461
|
+
const ipParts = ip.split(".");
|
|
462
|
+
for (let i = 0; i < Math.min(prefixParts.length - 1, 2); i++) {
|
|
463
|
+
if (prefixParts[i] !== ipParts[i] && prefixParts[i] !== "0") {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return true;
|
|
468
|
+
});
|
|
469
|
+
if (matched) {
|
|
470
|
+
const rule = ipRule;
|
|
471
|
+
confidence = Math.max(confidence, rule.confidence * 40);
|
|
319
472
|
reasons.push(`cloud_provider:${provider}`);
|
|
320
473
|
break;
|
|
321
474
|
}
|
|
322
475
|
}
|
|
323
476
|
}
|
|
324
|
-
if (reasons.length > 2 && confidence <
|
|
325
|
-
confidence = Math.min(confidence * 1.2,
|
|
477
|
+
if (reasons.length > 2 && confidence < 100) {
|
|
478
|
+
confidence = Math.min(confidence * 1.2, 95);
|
|
326
479
|
}
|
|
480
|
+
confidence = Math.min(Math.max(confidence, 0), 100);
|
|
327
481
|
return {
|
|
328
|
-
isAgent: confidence >
|
|
482
|
+
isAgent: confidence > 30,
|
|
483
|
+
// Updated to 0-100 scale (was 0.3)
|
|
329
484
|
confidence,
|
|
485
|
+
detectionClass: confidence > 30 && detectedAgent ? { type: "AiAgent", agentType: detectedAgent.name } : confidence > 30 ? { type: "Unknown" } : { type: "Human" },
|
|
486
|
+
signals: [],
|
|
487
|
+
// Will be populated by enhanced detection engine in future tasks
|
|
330
488
|
...detectedAgent && { detectedAgent },
|
|
331
489
|
reasons,
|
|
332
|
-
...verificationMethod && {
|
|
333
|
-
|
|
490
|
+
...verificationMethod && {
|
|
491
|
+
verificationMethod
|
|
492
|
+
},
|
|
493
|
+
forgeabilityRisk: verificationMethod === "signature" ? "low" : confidence > 80 ? "medium" : "high",
|
|
494
|
+
// Updated to 0-100 scale
|
|
334
495
|
timestamp: /* @__PURE__ */ new Date()
|
|
335
496
|
};
|
|
336
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* Get agent type from rule key
|
|
500
|
+
*/
|
|
501
|
+
getAgentType(agentKey) {
|
|
502
|
+
const typeMap = {
|
|
503
|
+
openai_gptbot: "openai",
|
|
504
|
+
anthropic_claude: "anthropic",
|
|
505
|
+
perplexity_bot: "perplexity",
|
|
506
|
+
google_ai: "google",
|
|
507
|
+
microsoft_ai: "microsoft",
|
|
508
|
+
meta_ai: "meta",
|
|
509
|
+
cohere_bot: "cohere",
|
|
510
|
+
huggingface_bot: "huggingface",
|
|
511
|
+
generic_bot: "generic",
|
|
512
|
+
dev_tools: "dev",
|
|
513
|
+
automation_tools: "automation"
|
|
514
|
+
};
|
|
515
|
+
return typeMap[agentKey] || agentKey;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Get agent name from rule key
|
|
519
|
+
*/
|
|
520
|
+
getAgentName(agentKey) {
|
|
521
|
+
const nameMap = {
|
|
522
|
+
openai_gptbot: "ChatGPT/GPTBot",
|
|
523
|
+
anthropic_claude: "Claude",
|
|
524
|
+
perplexity_bot: "Perplexity",
|
|
525
|
+
google_ai: "Google AI",
|
|
526
|
+
microsoft_ai: "Microsoft Copilot",
|
|
527
|
+
meta_ai: "Meta AI",
|
|
528
|
+
cohere_bot: "Cohere",
|
|
529
|
+
huggingface_bot: "HuggingFace",
|
|
530
|
+
generic_bot: "Generic Bot",
|
|
531
|
+
dev_tools: "Development Tool",
|
|
532
|
+
automation_tools: "Automation Tool"
|
|
533
|
+
};
|
|
534
|
+
return nameMap[agentKey] || agentKey;
|
|
535
|
+
}
|
|
337
536
|
};
|
|
338
537
|
var EdgeAgentDetectorWrapper = class {
|
|
339
538
|
detector;
|