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