@kya-os/checkpoint-wasm-runtime 1.0.0
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/CHANGELOG.md +58 -0
- package/dist/adapters.d.mts +257 -0
- package/dist/adapters.d.ts +257 -0
- package/dist/adapters.js +603 -0
- package/dist/adapters.js.map +1 -0
- package/dist/adapters.mjs +586 -0
- package/dist/adapters.mjs.map +1 -0
- package/dist/dynamic-loader-cS-pUisw.d.ts +65 -0
- package/dist/dynamic-loader-qGJacfEC.d.mts +65 -0
- package/dist/edge.d.mts +22 -0
- package/dist/edge.d.ts +22 -0
- package/dist/edge.js +1403 -0
- package/dist/edge.js.map +1 -0
- package/dist/edge.mjs +1391 -0
- package/dist/edge.mjs.map +1 -0
- package/dist/engine-edge.d.mts +58 -0
- package/dist/engine-edge.d.ts +58 -0
- package/dist/engine-edge.js +537 -0
- package/dist/engine-edge.js.map +1 -0
- package/dist/engine-edge.mjs +533 -0
- package/dist/engine-edge.mjs.map +1 -0
- package/dist/engine.d.mts +34 -0
- package/dist/engine.d.ts +34 -0
- package/dist/engine.js +11 -0
- package/dist/engine.js.map +1 -0
- package/dist/engine.mjs +9 -0
- package/dist/engine.mjs.map +1 -0
- package/dist/index.d.mts +58 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +1652 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1637 -0
- package/dist/index.mjs.map +1 -0
- package/dist/node.d.mts +26 -0
- package/dist/node.d.ts +26 -0
- package/dist/node.js +972 -0
- package/dist/node.js.map +1 -0
- package/dist/node.mjs +960 -0
- package/dist/node.mjs.map +1 -0
- package/dist/orchestrator-edge.d.mts +243 -0
- package/dist/orchestrator-edge.d.ts +243 -0
- package/dist/orchestrator-edge.js +1076 -0
- package/dist/orchestrator-edge.js.map +1 -0
- package/dist/orchestrator-edge.mjs +1065 -0
- package/dist/orchestrator-edge.mjs.map +1 -0
- package/dist/orchestrator.d.mts +50 -0
- package/dist/orchestrator.d.ts +50 -0
- package/dist/orchestrator.js +1185 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/orchestrator.mjs +1172 -0
- package/dist/orchestrator.mjs.map +1 -0
- package/dist/rules-detector-DjbTJ1-Q.d.mts +470 -0
- package/dist/rules-detector-DjbTJ1-Q.d.ts +470 -0
- package/dist/static-loader-C1hUlksK.d.ts +72 -0
- package/dist/static-loader-Ds4iNw7c.d.mts +72 -0
- package/dist/types-D0j85fF0.d.mts +163 -0
- package/dist/types-D0j85fF0.d.ts +163 -0
- package/package.json +141 -0
- package/wasm/agentshield_wasm.d.ts +485 -0
- package/wasm/agentshield_wasm.js +1551 -0
- package/wasm/agentshield_wasm_bg.wasm +0 -0
- package/wasm/agentshield_wasm_bg.wasm.d.ts +97 -0
- package/wasm/kya-os-engine/kya_os_engine.d.ts +24 -0
- package/wasm/kya-os-engine/kya_os_engine.js +517 -0
- package/wasm/kya-os-engine/kya_os_engine_bg.wasm +0 -0
- package/wasm/kya-os-engine/kya_os_engine_bg.wasm.d.ts +8 -0
- package/wasm/kya-os-engine-web/kya_os_engine.d.ts +56 -0
- package/wasm/kya-os-engine-web/kya_os_engine.js +574 -0
- package/wasm/kya-os-engine-web/kya_os_engine_bg.wasm +0 -0
- package/wasm/kya-os-engine-web/kya_os_engine_bg.wasm.d.ts +8 -0
- package/wasm/package.json +30 -0
package/dist/node.mjs
ADDED
|
@@ -0,0 +1,960 @@
|
|
|
1
|
+
import { loadRulesSync } from '@kya-os/checkpoint-shared';
|
|
2
|
+
|
|
3
|
+
// src/types.ts
|
|
4
|
+
var CONFIDENCE = {
|
|
5
|
+
/** Minimum confidence for isAgent=true */
|
|
6
|
+
THRESHOLD_AGENT: 30,
|
|
7
|
+
/** Cryptographic signature verified */
|
|
8
|
+
SIGNATURE_VERIFIED: 100,
|
|
9
|
+
/** Signature header present but not verified */
|
|
10
|
+
SIGNATURE_PRESENT: 85,
|
|
11
|
+
/** Strong pattern match */
|
|
12
|
+
PATTERN_HIGH: 85,
|
|
13
|
+
/** Moderate pattern match */
|
|
14
|
+
PATTERN_MEDIUM: 60,
|
|
15
|
+
/** Weak pattern match */
|
|
16
|
+
PATTERN_LOW: 40,
|
|
17
|
+
/** Cloud IP detection only */
|
|
18
|
+
CLOUD_IP: 30
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// src/wasm-detector.ts
|
|
22
|
+
function parseVerificationMethod(method) {
|
|
23
|
+
switch (method.toLowerCase()) {
|
|
24
|
+
case "signature":
|
|
25
|
+
return "signature";
|
|
26
|
+
case "pattern":
|
|
27
|
+
return "pattern";
|
|
28
|
+
case "behavioral":
|
|
29
|
+
return "behavioral";
|
|
30
|
+
case "network":
|
|
31
|
+
return "network";
|
|
32
|
+
case "mcp_i_handshake":
|
|
33
|
+
return "mcp_i_handshake";
|
|
34
|
+
default:
|
|
35
|
+
return "none";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function determineForgeabilityRisk(method, confidence) {
|
|
39
|
+
if (method === "signature" && confidence >= 90) {
|
|
40
|
+
return "low";
|
|
41
|
+
}
|
|
42
|
+
if (confidence >= 80) {
|
|
43
|
+
return "medium";
|
|
44
|
+
}
|
|
45
|
+
return "high";
|
|
46
|
+
}
|
|
47
|
+
function determineDetectionClass(isAgent, agentName, confidence) {
|
|
48
|
+
if (!isAgent || confidence < CONFIDENCE.THRESHOLD_AGENT) {
|
|
49
|
+
return { type: "Human" };
|
|
50
|
+
}
|
|
51
|
+
if (agentName) {
|
|
52
|
+
const aiAgentPatterns = [
|
|
53
|
+
"chatgpt",
|
|
54
|
+
"gpt",
|
|
55
|
+
"openai",
|
|
56
|
+
"claude",
|
|
57
|
+
"anthropic",
|
|
58
|
+
"perplexity",
|
|
59
|
+
"gemini",
|
|
60
|
+
"google ai",
|
|
61
|
+
"copilot",
|
|
62
|
+
"bing chat"
|
|
63
|
+
];
|
|
64
|
+
const lowerName = agentName.toLowerCase();
|
|
65
|
+
if (aiAgentPatterns.some((pattern) => lowerName.includes(pattern))) {
|
|
66
|
+
return { type: "AiAgent", agentType: agentName };
|
|
67
|
+
}
|
|
68
|
+
const botPatterns = ["bot", "crawler", "spider", "scraper"];
|
|
69
|
+
if (botPatterns.some((pattern) => lowerName.includes(pattern))) {
|
|
70
|
+
return { type: "Bot", botType: agentName };
|
|
71
|
+
}
|
|
72
|
+
const automationPatterns = ["selenium", "puppeteer", "playwright", "cypress"];
|
|
73
|
+
if (automationPatterns.some((pattern) => lowerName.includes(pattern))) {
|
|
74
|
+
return { type: "Automation", toolType: agentName };
|
|
75
|
+
}
|
|
76
|
+
return { type: "AiAgent", agentType: agentName };
|
|
77
|
+
}
|
|
78
|
+
return { type: "Unknown" };
|
|
79
|
+
}
|
|
80
|
+
var WasmDetector = class {
|
|
81
|
+
/**
|
|
82
|
+
* Create a new WasmDetector
|
|
83
|
+
* @param loader - WASM loader (static for Edge, dynamic for Node.js)
|
|
84
|
+
* @param policyLoader - Optional policy loader for API key support
|
|
85
|
+
* @param options - Detector configuration options
|
|
86
|
+
*/
|
|
87
|
+
constructor(loader, policyLoader, options = {}) {
|
|
88
|
+
this.loader = loader;
|
|
89
|
+
this.policyLoader = policyLoader;
|
|
90
|
+
this.options = options;
|
|
91
|
+
}
|
|
92
|
+
ready = false;
|
|
93
|
+
loadPromise = null;
|
|
94
|
+
/**
|
|
95
|
+
* Analyze a request and detect if it's from an agent
|
|
96
|
+
*/
|
|
97
|
+
async detect(input) {
|
|
98
|
+
await this.ensureReady();
|
|
99
|
+
try {
|
|
100
|
+
const bindings = this.loader.getBindings();
|
|
101
|
+
const metadata = {
|
|
102
|
+
user_agent: input.userAgent || null,
|
|
103
|
+
ip_address: input.ipAddress || null,
|
|
104
|
+
headers: JSON.stringify(input.headers || {}),
|
|
105
|
+
timestamp: (input.timestamp || /* @__PURE__ */ new Date()).toISOString(),
|
|
106
|
+
url: input.url || null,
|
|
107
|
+
method: input.method || null,
|
|
108
|
+
client_fingerprint: input.clientFingerprint || null,
|
|
109
|
+
free: () => {
|
|
110
|
+
}
|
|
111
|
+
// No-op for JS
|
|
112
|
+
};
|
|
113
|
+
const wasmResult = bindings.detect_agent(metadata);
|
|
114
|
+
const verificationMethod = parseVerificationMethod(wasmResult.verification_method);
|
|
115
|
+
const forgeabilityRisk = determineForgeabilityRisk(verificationMethod, wasmResult.confidence);
|
|
116
|
+
const detectionClass = determineDetectionClass(
|
|
117
|
+
wasmResult.is_agent,
|
|
118
|
+
wasmResult.agent,
|
|
119
|
+
wasmResult.confidence
|
|
120
|
+
);
|
|
121
|
+
let result = {
|
|
122
|
+
isAgent: wasmResult.is_agent,
|
|
123
|
+
confidence: wasmResult.confidence,
|
|
124
|
+
// Already 0-100, no conversion!
|
|
125
|
+
detectionClass,
|
|
126
|
+
detectedAgent: wasmResult.agent ? {
|
|
127
|
+
type: this.inferAgentType(wasmResult.agent),
|
|
128
|
+
name: wasmResult.agent
|
|
129
|
+
} : void 0,
|
|
130
|
+
verificationMethod,
|
|
131
|
+
forgeabilityRisk,
|
|
132
|
+
reasons: this.extractReasons(wasmResult),
|
|
133
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
134
|
+
};
|
|
135
|
+
if (this.policyLoader && this.options.apiKey) {
|
|
136
|
+
result = await this.applyPolicy(result);
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (this.options.debug) {
|
|
141
|
+
console.error("[WasmDetector] Detection error:", error);
|
|
142
|
+
}
|
|
143
|
+
return this.createDefaultResult();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Check if the detector is ready
|
|
148
|
+
*/
|
|
149
|
+
isReady() {
|
|
150
|
+
return this.ready;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Ensure the detector is initialized
|
|
154
|
+
*/
|
|
155
|
+
async ensureReady() {
|
|
156
|
+
if (this.ready) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (this.loadPromise) {
|
|
160
|
+
return this.loadPromise;
|
|
161
|
+
}
|
|
162
|
+
this.loadPromise = this.initialize();
|
|
163
|
+
return this.loadPromise;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get detector version
|
|
167
|
+
*/
|
|
168
|
+
async getVersion() {
|
|
169
|
+
await this.ensureReady();
|
|
170
|
+
return this.loader.getBindings().get_version();
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Initialize the detector
|
|
174
|
+
*/
|
|
175
|
+
async initialize() {
|
|
176
|
+
try {
|
|
177
|
+
await this.loader.load();
|
|
178
|
+
this.ready = true;
|
|
179
|
+
if (this.options.debug) {
|
|
180
|
+
const version = this.loader.getBindings().get_version();
|
|
181
|
+
console.log(
|
|
182
|
+
`[WasmDetector] Initialized with WASM v${version} (${this.loader.getStrategy()})`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
this.loadPromise = null;
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Apply customer policy to detection result
|
|
192
|
+
*/
|
|
193
|
+
async applyPolicy(result) {
|
|
194
|
+
if (!this.policyLoader || !this.options.apiKey) {
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
let policy = this.policyLoader.getCachedPolicy(this.options.apiKey);
|
|
199
|
+
if (!policy) {
|
|
200
|
+
policy = await this.policyLoader.loadPolicy(this.options.apiKey);
|
|
201
|
+
}
|
|
202
|
+
return this.applyPolicyRules(result, policy);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (this.options.debug) {
|
|
205
|
+
console.warn("[WasmDetector] Failed to load policy:", error);
|
|
206
|
+
}
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Check if agent name matches a policy list entry
|
|
212
|
+
* Uses exact match or word-boundary prefix match to avoid false positives
|
|
213
|
+
* e.g., "gpt" matches "ChatGPT" and "GPT-4" but not "EgyptBot"
|
|
214
|
+
*/
|
|
215
|
+
matchesPolicyEntry(agentName, policyEntry) {
|
|
216
|
+
const lowerAgent = agentName.toLowerCase();
|
|
217
|
+
const lowerEntry = policyEntry.toLowerCase();
|
|
218
|
+
if (lowerAgent === lowerEntry) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
const wordBoundaryRegex = new RegExp(
|
|
222
|
+
`(^|[^a-z0-9])${this.escapeRegex(lowerEntry)}($|[^a-z0-9])`,
|
|
223
|
+
"i"
|
|
224
|
+
);
|
|
225
|
+
return wordBoundaryRegex.test(lowerAgent);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Escape special regex characters in a string
|
|
229
|
+
*/
|
|
230
|
+
escapeRegex(str) {
|
|
231
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Apply policy rules to detection result
|
|
235
|
+
*/
|
|
236
|
+
applyPolicyRules(result, policy) {
|
|
237
|
+
if (policy.denyList && result.detectedAgent) {
|
|
238
|
+
const agentName = result.detectedAgent.name;
|
|
239
|
+
const isDenied = policy.denyList.some((denied) => this.matchesPolicyEntry(agentName, denied));
|
|
240
|
+
if (isDenied) {
|
|
241
|
+
return {
|
|
242
|
+
...result,
|
|
243
|
+
shouldBlock: true,
|
|
244
|
+
blockReason: "deny_list"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (policy.allowList && policy.allowList.length > 0 && result.isAgent) {
|
|
249
|
+
const agentName = result.detectedAgent?.name;
|
|
250
|
+
if (!agentName) ; else {
|
|
251
|
+
const isAllowed = policy.allowList.some(
|
|
252
|
+
(allowed) => this.matchesPolicyEntry(agentName, allowed)
|
|
253
|
+
);
|
|
254
|
+
if (!isAllowed) {
|
|
255
|
+
return {
|
|
256
|
+
...result,
|
|
257
|
+
shouldBlock: true,
|
|
258
|
+
blockReason: "not_in_allow_list"
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
...result,
|
|
263
|
+
shouldBlock: false
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (policy.blockThreshold !== void 0 && result.isAgent && result.confidence >= policy.blockThreshold) {
|
|
268
|
+
return {
|
|
269
|
+
...result,
|
|
270
|
+
shouldBlock: true,
|
|
271
|
+
blockReason: "threshold"
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Infer agent type from name
|
|
278
|
+
*/
|
|
279
|
+
inferAgentType(agentName) {
|
|
280
|
+
const lowerName = agentName.toLowerCase();
|
|
281
|
+
if (lowerName.includes("chatgpt") || lowerName.includes("openai") || lowerName.includes("gpt")) {
|
|
282
|
+
return "openai";
|
|
283
|
+
}
|
|
284
|
+
if (lowerName.includes("claude") || lowerName.includes("anthropic")) {
|
|
285
|
+
return "anthropic";
|
|
286
|
+
}
|
|
287
|
+
if (lowerName.includes("perplexity")) {
|
|
288
|
+
return "perplexity";
|
|
289
|
+
}
|
|
290
|
+
if (lowerName.includes("gemini") || lowerName.includes("google")) {
|
|
291
|
+
return "google";
|
|
292
|
+
}
|
|
293
|
+
if (lowerName.includes("copilot") || lowerName.includes("bing")) {
|
|
294
|
+
return "microsoft";
|
|
295
|
+
}
|
|
296
|
+
return "unknown";
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Extract reasons from WASM result
|
|
300
|
+
*/
|
|
301
|
+
extractReasons(wasmResult) {
|
|
302
|
+
const reasons = [];
|
|
303
|
+
if (wasmResult.verification_method) {
|
|
304
|
+
reasons.push(`method:${wasmResult.verification_method}`);
|
|
305
|
+
}
|
|
306
|
+
if (wasmResult.agent) {
|
|
307
|
+
reasons.push(`agent:${wasmResult.agent.toLowerCase()}`);
|
|
308
|
+
}
|
|
309
|
+
return reasons;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Create default result (assumed human)
|
|
313
|
+
*/
|
|
314
|
+
createDefaultResult() {
|
|
315
|
+
return {
|
|
316
|
+
isAgent: false,
|
|
317
|
+
confidence: 0,
|
|
318
|
+
detectionClass: { type: "Human" },
|
|
319
|
+
verificationMethod: "none",
|
|
320
|
+
forgeabilityRisk: "high",
|
|
321
|
+
reasons: ["detection_failed"],
|
|
322
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// src/loaders/dynamic-loader.ts
|
|
328
|
+
async function findWasmModule() {
|
|
329
|
+
const wasmPaths = [
|
|
330
|
+
// Relative to this package
|
|
331
|
+
"../wasm/agentshield_wasm_bg.wasm",
|
|
332
|
+
"./wasm/agentshield_wasm_bg.wasm",
|
|
333
|
+
// From agentshield-core package
|
|
334
|
+
"@kya-os/checkpoint/wasm/agentshield_wasm_bg.wasm",
|
|
335
|
+
// From monorepo build output
|
|
336
|
+
"../../rust/target/wasm32-unknown-unknown/release/agentshield_wasm_bg.wasm"
|
|
337
|
+
];
|
|
338
|
+
for (const path of wasmPaths) {
|
|
339
|
+
try {
|
|
340
|
+
const module = await import(
|
|
341
|
+
/* webpackIgnore: true */
|
|
342
|
+
path
|
|
343
|
+
);
|
|
344
|
+
if (module.default && module.default instanceof ArrayBuffer) {
|
|
345
|
+
return module.default;
|
|
346
|
+
}
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
const fs = await import('fs/promises');
|
|
352
|
+
const nodePath = await import('path');
|
|
353
|
+
let moduleDir = null;
|
|
354
|
+
try {
|
|
355
|
+
const importMetaUrl = eval('typeof import.meta !== "undefined" && import.meta.url');
|
|
356
|
+
if (importMetaUrl) {
|
|
357
|
+
const url = await import('url');
|
|
358
|
+
moduleDir = nodePath.dirname(url.fileURLToPath(importMetaUrl));
|
|
359
|
+
}
|
|
360
|
+
} catch {
|
|
361
|
+
}
|
|
362
|
+
if (!moduleDir) {
|
|
363
|
+
try {
|
|
364
|
+
const cjsDirname = eval('typeof __dirname !== "undefined" && __dirname');
|
|
365
|
+
if (cjsDirname) {
|
|
366
|
+
moduleDir = cjsDirname;
|
|
367
|
+
}
|
|
368
|
+
} catch {
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const fsWasmPaths = [
|
|
372
|
+
nodePath.resolve(
|
|
373
|
+
process.cwd(),
|
|
374
|
+
"node_modules/@kya-os/checkpoint-wasm-runtime/wasm/agentshield_wasm_bg.wasm"
|
|
375
|
+
),
|
|
376
|
+
nodePath.resolve(
|
|
377
|
+
process.cwd(),
|
|
378
|
+
"node_modules/@kya-os/checkpoint/dist/wasm/agentshield_wasm_bg.wasm"
|
|
379
|
+
)
|
|
380
|
+
];
|
|
381
|
+
if (moduleDir) {
|
|
382
|
+
fsWasmPaths.unshift(
|
|
383
|
+
nodePath.resolve(moduleDir, "../wasm/agentshield_wasm_bg.wasm"),
|
|
384
|
+
nodePath.resolve(moduleDir, "../../wasm/agentshield_wasm_bg.wasm")
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
for (const wasmPath of fsWasmPaths) {
|
|
388
|
+
try {
|
|
389
|
+
const buffer = await fs.readFile(wasmPath);
|
|
390
|
+
const arrayBuffer = new ArrayBuffer(buffer.byteLength);
|
|
391
|
+
new Uint8Array(arrayBuffer).set(buffer);
|
|
392
|
+
return arrayBuffer;
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
398
|
+
throw new Error(
|
|
399
|
+
"Could not find WASM module. Ensure @kya-os/checkpoint-wasm-runtime/wasm is installed."
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
var JsRequestMetadata = class {
|
|
403
|
+
constructor(user_agent, ip_address, headers, timestamp, url, method, client_fingerprint) {
|
|
404
|
+
this.user_agent = user_agent;
|
|
405
|
+
this.ip_address = ip_address;
|
|
406
|
+
this.headers = headers;
|
|
407
|
+
this.timestamp = timestamp;
|
|
408
|
+
this.url = url;
|
|
409
|
+
this.method = method;
|
|
410
|
+
this.client_fingerprint = client_fingerprint;
|
|
411
|
+
}
|
|
412
|
+
free() {
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
var DynamicWasmLoader = class {
|
|
416
|
+
/**
|
|
417
|
+
* Create a new DynamicWasmLoader
|
|
418
|
+
* @param wasmPath - Optional custom path to WASM file
|
|
419
|
+
*/
|
|
420
|
+
constructor(wasmPath) {
|
|
421
|
+
this.wasmPath = wasmPath;
|
|
422
|
+
}
|
|
423
|
+
bindings = null;
|
|
424
|
+
instance = null;
|
|
425
|
+
loadPromise = null;
|
|
426
|
+
/**
|
|
427
|
+
* Load and compile the WASM module
|
|
428
|
+
*/
|
|
429
|
+
async load() {
|
|
430
|
+
if (this.bindings) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (this.loadPromise) {
|
|
434
|
+
return this.loadPromise;
|
|
435
|
+
}
|
|
436
|
+
this.loadPromise = this.doLoad();
|
|
437
|
+
return this.loadPromise;
|
|
438
|
+
}
|
|
439
|
+
async doLoad() {
|
|
440
|
+
try {
|
|
441
|
+
let wasmBuffer;
|
|
442
|
+
if (this.wasmPath) {
|
|
443
|
+
const fs2 = await import('fs/promises');
|
|
444
|
+
const buffer = await fs2.readFile(this.wasmPath);
|
|
445
|
+
wasmBuffer = new ArrayBuffer(buffer.byteLength);
|
|
446
|
+
new Uint8Array(wasmBuffer).set(buffer);
|
|
447
|
+
} else {
|
|
448
|
+
wasmBuffer = await findWasmModule();
|
|
449
|
+
}
|
|
450
|
+
const compiled = await WebAssembly.compile(wasmBuffer);
|
|
451
|
+
const imports = {
|
|
452
|
+
wbg: this.createWasmBindgenImports()
|
|
453
|
+
};
|
|
454
|
+
this.instance = await WebAssembly.instantiate(compiled, imports);
|
|
455
|
+
const exports$1 = this.instance.exports;
|
|
456
|
+
this.bindings = this.createBindings(exports$1);
|
|
457
|
+
} catch (error) {
|
|
458
|
+
this.loadPromise = null;
|
|
459
|
+
throw new Error(
|
|
460
|
+
`Failed to load WASM module: ${error instanceof Error ? error.message : String(error)}`
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Get the WASM bindings after loading
|
|
466
|
+
*/
|
|
467
|
+
getBindings() {
|
|
468
|
+
if (!this.bindings) {
|
|
469
|
+
throw new Error("WASM not loaded. Call load() first.");
|
|
470
|
+
}
|
|
471
|
+
return this.bindings;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Check if WASM is loaded
|
|
475
|
+
*/
|
|
476
|
+
isLoaded() {
|
|
477
|
+
return this.bindings !== null;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Get the loading strategy name
|
|
481
|
+
*/
|
|
482
|
+
getStrategy() {
|
|
483
|
+
return "dynamic-compile";
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Create wasm-bindgen required imports
|
|
487
|
+
*/
|
|
488
|
+
createWasmBindgenImports() {
|
|
489
|
+
return {
|
|
490
|
+
__wbg_log_f740dc2253ea759b: (ptr, len) => {
|
|
491
|
+
console.log("[WASM]", ptr, len);
|
|
492
|
+
},
|
|
493
|
+
__wbg_error_f851667af71bcfc6: (ptr, len) => {
|
|
494
|
+
console.error("[WASM Error]", ptr, len);
|
|
495
|
+
},
|
|
496
|
+
__wbg_now_c644db5194be8437: () => Date.now(),
|
|
497
|
+
__wbindgen_throw: (ptr, len) => {
|
|
498
|
+
throw new Error(`WASM threw: ${ptr}, ${len}`);
|
|
499
|
+
},
|
|
500
|
+
__wbindgen_object_drop_ref: () => {
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Create bindings wrapper from WASM exports
|
|
506
|
+
*/
|
|
507
|
+
createBindings(exports$1) {
|
|
508
|
+
const detectAgent = exports$1["detect_agent"];
|
|
509
|
+
const getVersion = exports$1["get_version"];
|
|
510
|
+
const getBuildInfo = exports$1["get_build_info"];
|
|
511
|
+
if (!detectAgent) {
|
|
512
|
+
throw new Error("WASM module missing detect_agent export");
|
|
513
|
+
}
|
|
514
|
+
return {
|
|
515
|
+
detect_agent: (metadata) => {
|
|
516
|
+
const wasmMetadata = new JsRequestMetadata(
|
|
517
|
+
metadata.user_agent,
|
|
518
|
+
metadata.ip_address,
|
|
519
|
+
metadata.headers,
|
|
520
|
+
metadata.timestamp,
|
|
521
|
+
metadata.url,
|
|
522
|
+
metadata.method,
|
|
523
|
+
metadata.client_fingerprint
|
|
524
|
+
);
|
|
525
|
+
try {
|
|
526
|
+
return detectAgent(wasmMetadata);
|
|
527
|
+
} finally {
|
|
528
|
+
wasmMetadata.free();
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
get_version: () => {
|
|
532
|
+
if (getVersion) {
|
|
533
|
+
return getVersion();
|
|
534
|
+
}
|
|
535
|
+
return "0.1.0-dynamic";
|
|
536
|
+
},
|
|
537
|
+
get_build_info: () => {
|
|
538
|
+
if (getBuildInfo) {
|
|
539
|
+
return getBuildInfo();
|
|
540
|
+
}
|
|
541
|
+
return JSON.stringify({ version: "0.1.0", strategy: "dynamic-compile" });
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
function createDynamicLoader(wasmPath) {
|
|
547
|
+
return new DynamicWasmLoader(wasmPath);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/policy/policy-loader.ts
|
|
551
|
+
var DEFAULT_CONFIG = {
|
|
552
|
+
apiUrl: "https://api.agentshield.io",
|
|
553
|
+
cacheTTL: 5 * 60 * 1e3,
|
|
554
|
+
// 5 minutes
|
|
555
|
+
maxCacheSize: 100,
|
|
556
|
+
backgroundRefresh: true,
|
|
557
|
+
timeout: 5e3
|
|
558
|
+
};
|
|
559
|
+
var PolicyLoader = class {
|
|
560
|
+
cache = /* @__PURE__ */ new Map();
|
|
561
|
+
config;
|
|
562
|
+
constructor(config = {}) {
|
|
563
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Load policy for an API key
|
|
567
|
+
*/
|
|
568
|
+
async loadPolicy(apiKey) {
|
|
569
|
+
const cached = this.getCachedPolicy(apiKey);
|
|
570
|
+
if (cached) {
|
|
571
|
+
if (this.config.backgroundRefresh && this.shouldRefresh(apiKey)) {
|
|
572
|
+
this.refreshInBackground(apiKey);
|
|
573
|
+
}
|
|
574
|
+
return cached;
|
|
575
|
+
}
|
|
576
|
+
return this.fetchPolicy(apiKey);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Get cached policy if available and not expired
|
|
580
|
+
*/
|
|
581
|
+
getCachedPolicy(apiKey) {
|
|
582
|
+
const entry = this.cache.get(apiKey);
|
|
583
|
+
if (!entry) {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
if (this.isExpired(entry)) {
|
|
587
|
+
this.cache.delete(apiKey);
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
return entry.policy;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Invalidate cached policy
|
|
594
|
+
*/
|
|
595
|
+
invalidateCache(apiKey) {
|
|
596
|
+
this.cache.delete(apiKey);
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Fetch policy from API and cache it
|
|
600
|
+
*/
|
|
601
|
+
async fetchPolicy(apiKey) {
|
|
602
|
+
const policy = await this.fetchPolicyFromApi(apiKey);
|
|
603
|
+
this.cachePolicy(apiKey, policy);
|
|
604
|
+
return policy;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Fetch policy from API without caching
|
|
608
|
+
* Used internally for both direct fetches and background refreshes
|
|
609
|
+
*/
|
|
610
|
+
async fetchPolicyFromApi(apiKey) {
|
|
611
|
+
const controller = new AbortController();
|
|
612
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
613
|
+
try {
|
|
614
|
+
const response = await fetch(`${this.config.apiUrl}/api/v1/policy`, {
|
|
615
|
+
method: "GET",
|
|
616
|
+
headers: {
|
|
617
|
+
Authorization: `Bearer ${apiKey}`,
|
|
618
|
+
"Content-Type": "application/json",
|
|
619
|
+
"User-Agent": "agentshield-wasm-runtime/0.1.0"
|
|
620
|
+
},
|
|
621
|
+
signal: controller.signal
|
|
622
|
+
});
|
|
623
|
+
clearTimeout(timeoutId);
|
|
624
|
+
if (!response.ok) {
|
|
625
|
+
if (response.status === 401) {
|
|
626
|
+
throw new PolicyLoadError("Invalid API key", "INVALID_API_KEY");
|
|
627
|
+
}
|
|
628
|
+
if (response.status === 404) {
|
|
629
|
+
return this.getDefaultPolicy(apiKey);
|
|
630
|
+
}
|
|
631
|
+
throw new PolicyLoadError(`Failed to load policy: ${response.status}`, "API_ERROR");
|
|
632
|
+
}
|
|
633
|
+
return await response.json();
|
|
634
|
+
} catch (error) {
|
|
635
|
+
clearTimeout(timeoutId);
|
|
636
|
+
if (error instanceof PolicyLoadError) {
|
|
637
|
+
throw error;
|
|
638
|
+
}
|
|
639
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
640
|
+
throw new PolicyLoadError("Policy request timeout", "TIMEOUT");
|
|
641
|
+
}
|
|
642
|
+
throw new PolicyLoadError(
|
|
643
|
+
`Failed to load policy: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
644
|
+
"NETWORK_ERROR"
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Cache a policy
|
|
650
|
+
*/
|
|
651
|
+
cachePolicy(apiKey, policy) {
|
|
652
|
+
if (this.cache.size >= this.config.maxCacheSize) {
|
|
653
|
+
const oldestKey = this.cache.keys().next().value;
|
|
654
|
+
if (oldestKey) {
|
|
655
|
+
this.cache.delete(oldestKey);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
this.cache.set(apiKey, {
|
|
659
|
+
policy,
|
|
660
|
+
loadedAt: Date.now(),
|
|
661
|
+
refreshing: false
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Check if cached entry is expired
|
|
666
|
+
*/
|
|
667
|
+
isExpired(entry) {
|
|
668
|
+
return Date.now() - entry.loadedAt > this.config.cacheTTL;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Check if cache entry should be refreshed
|
|
672
|
+
*/
|
|
673
|
+
shouldRefresh(apiKey) {
|
|
674
|
+
const entry = this.cache.get(apiKey);
|
|
675
|
+
if (!entry) {
|
|
676
|
+
return false;
|
|
677
|
+
}
|
|
678
|
+
const refreshThreshold = this.config.cacheTTL * 0.8;
|
|
679
|
+
return Date.now() - entry.loadedAt > refreshThreshold && !entry.refreshing;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Refresh policy in background
|
|
683
|
+
*/
|
|
684
|
+
async refreshInBackground(apiKey) {
|
|
685
|
+
const entry = this.cache.get(apiKey);
|
|
686
|
+
if (!entry || entry.refreshing) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
entry.refreshing = true;
|
|
690
|
+
try {
|
|
691
|
+
const policy = await this.fetchPolicyFromApi(apiKey);
|
|
692
|
+
this.cachePolicy(apiKey, policy);
|
|
693
|
+
} catch {
|
|
694
|
+
} finally {
|
|
695
|
+
const currentEntry = this.cache.get(apiKey);
|
|
696
|
+
if (currentEntry && currentEntry.refreshing) {
|
|
697
|
+
currentEntry.refreshing = false;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Get default policy for a project
|
|
703
|
+
*/
|
|
704
|
+
getDefaultPolicy(apiKey) {
|
|
705
|
+
return {
|
|
706
|
+
projectId: apiKey.split("_")[1] || "unknown",
|
|
707
|
+
blockThreshold: 90
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
var PolicyLoadError = class extends Error {
|
|
712
|
+
constructor(message, code) {
|
|
713
|
+
super(message);
|
|
714
|
+
this.code = code;
|
|
715
|
+
this.name = "PolicyLoadError";
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
function createPolicyLoader(config) {
|
|
719
|
+
return new PolicyLoader(config);
|
|
720
|
+
}
|
|
721
|
+
var RulesDetector = class {
|
|
722
|
+
rules = null;
|
|
723
|
+
ready = false;
|
|
724
|
+
/**
|
|
725
|
+
* Analyze a request and detect if it's from an agent
|
|
726
|
+
*/
|
|
727
|
+
async detect(input) {
|
|
728
|
+
await this.ensureReady();
|
|
729
|
+
const reasons = [];
|
|
730
|
+
let confidence = 0;
|
|
731
|
+
let detectedAgentName = null;
|
|
732
|
+
let verificationMethod = "none";
|
|
733
|
+
const userAgent = input.userAgent || input.headers["user-agent"] || "";
|
|
734
|
+
const normalizedHeaders = this.normalizeHeaders(input.headers);
|
|
735
|
+
if (userAgent && this.rules) {
|
|
736
|
+
const uaMatch = this.matchUserAgent(userAgent);
|
|
737
|
+
if (uaMatch) {
|
|
738
|
+
confidence = Math.max(confidence, uaMatch.confidence);
|
|
739
|
+
detectedAgentName = uaMatch.agentName;
|
|
740
|
+
verificationMethod = "pattern";
|
|
741
|
+
reasons.push(`pattern:${uaMatch.agentKey}`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (this.rules) {
|
|
745
|
+
const headerMatch = this.matchHeaders(normalizedHeaders);
|
|
746
|
+
if (headerMatch.confidence > 0) {
|
|
747
|
+
confidence = Math.max(confidence, headerMatch.confidence);
|
|
748
|
+
reasons.push(`headers:${headerMatch.count}`);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
if (this.hasSignatureHeaders(normalizedHeaders)) {
|
|
752
|
+
confidence = Math.max(confidence, CONFIDENCE.SIGNATURE_PRESENT);
|
|
753
|
+
reasons.push("signature_headers_present");
|
|
754
|
+
verificationMethod = "pattern";
|
|
755
|
+
}
|
|
756
|
+
const isAgent = confidence > CONFIDENCE.THRESHOLD_AGENT;
|
|
757
|
+
const detectionClass = this.determineDetectionClass(isAgent, detectedAgentName, confidence);
|
|
758
|
+
return {
|
|
759
|
+
isAgent,
|
|
760
|
+
confidence,
|
|
761
|
+
detectionClass,
|
|
762
|
+
detectedAgent: detectedAgentName ? {
|
|
763
|
+
type: this.inferAgentType(detectedAgentName),
|
|
764
|
+
name: detectedAgentName
|
|
765
|
+
} : void 0,
|
|
766
|
+
verificationMethod,
|
|
767
|
+
forgeabilityRisk: "high",
|
|
768
|
+
// JS fallback is always high risk
|
|
769
|
+
reasons,
|
|
770
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Check if the detector is ready
|
|
775
|
+
*/
|
|
776
|
+
isReady() {
|
|
777
|
+
return this.ready;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Ensure the detector is initialized
|
|
781
|
+
*/
|
|
782
|
+
async ensureReady() {
|
|
783
|
+
if (this.ready) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
try {
|
|
787
|
+
this.rules = loadRulesSync();
|
|
788
|
+
this.ready = true;
|
|
789
|
+
} catch (error) {
|
|
790
|
+
console.warn("[RulesDetector] Failed to load rules:", error);
|
|
791
|
+
this.ready = true;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Get detector version
|
|
796
|
+
*/
|
|
797
|
+
async getVersion() {
|
|
798
|
+
return "0.1.0-js-fallback";
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Normalize headers to lowercase keys
|
|
802
|
+
*/
|
|
803
|
+
normalizeHeaders(headers) {
|
|
804
|
+
const normalized = {};
|
|
805
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
806
|
+
normalized[key.toLowerCase()] = value;
|
|
807
|
+
}
|
|
808
|
+
return normalized;
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Match user agent against rules
|
|
812
|
+
*/
|
|
813
|
+
matchUserAgent(userAgent) {
|
|
814
|
+
if (!this.rules) {
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
const userAgents = this.rules.rules.userAgents;
|
|
818
|
+
const genericKeys = ["generic_bot", "dev_tools", "automation_tools"];
|
|
819
|
+
const entries = Object.entries(userAgents).sort((a, b) => {
|
|
820
|
+
const aIsGeneric = genericKeys.includes(a[0]);
|
|
821
|
+
const bIsGeneric = genericKeys.includes(b[0]);
|
|
822
|
+
if (aIsGeneric && !bIsGeneric) return 1;
|
|
823
|
+
if (!aIsGeneric && bIsGeneric) return -1;
|
|
824
|
+
return 0;
|
|
825
|
+
});
|
|
826
|
+
for (const [agentKey, rule] of entries) {
|
|
827
|
+
for (const pattern of rule.patterns) {
|
|
828
|
+
try {
|
|
829
|
+
const regex = new RegExp(pattern, "i");
|
|
830
|
+
if (regex.test(userAgent)) {
|
|
831
|
+
return {
|
|
832
|
+
// Convert 0-1 rule confidence to 0-100 scale
|
|
833
|
+
confidence: rule.confidence * 100,
|
|
834
|
+
agentName: this.getAgentName(agentKey),
|
|
835
|
+
agentKey
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
} catch {
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Match headers against suspicious header rules
|
|
846
|
+
*/
|
|
847
|
+
matchHeaders(headers) {
|
|
848
|
+
if (!this.rules) {
|
|
849
|
+
return { confidence: 0, count: 0 };
|
|
850
|
+
}
|
|
851
|
+
const suspicious = this.rules.rules.headers.suspicious || [];
|
|
852
|
+
let maxConfidence = 0;
|
|
853
|
+
let count = 0;
|
|
854
|
+
for (const headerRule of suspicious) {
|
|
855
|
+
if (headers[headerRule.name.toLowerCase()]) {
|
|
856
|
+
maxConfidence = Math.max(maxConfidence, headerRule.confidence * 100);
|
|
857
|
+
count++;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return { confidence: maxConfidence, count };
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Check if signature headers are present
|
|
864
|
+
*/
|
|
865
|
+
hasSignatureHeaders(headers) {
|
|
866
|
+
return !!(headers["signature"] || headers["signature-input"] || headers["signature-agent"]);
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Get human-readable agent name from rule key
|
|
870
|
+
*/
|
|
871
|
+
getAgentName(agentKey) {
|
|
872
|
+
const nameMap = {
|
|
873
|
+
openai_gptbot: "ChatGPT/GPTBot",
|
|
874
|
+
anthropic_claude: "Claude",
|
|
875
|
+
perplexity_bot: "Perplexity",
|
|
876
|
+
google_ai: "Google AI",
|
|
877
|
+
microsoft_ai: "Microsoft Copilot",
|
|
878
|
+
meta_ai: "Meta AI",
|
|
879
|
+
cohere_bot: "Cohere",
|
|
880
|
+
huggingface_bot: "HuggingFace",
|
|
881
|
+
generic_bot: "Generic Bot",
|
|
882
|
+
dev_tools: "Development Tool",
|
|
883
|
+
automation_tools: "Automation Tool"
|
|
884
|
+
};
|
|
885
|
+
return nameMap[agentKey] || agentKey;
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Infer agent type from name
|
|
889
|
+
*/
|
|
890
|
+
inferAgentType(agentName) {
|
|
891
|
+
const lowerName = agentName.toLowerCase();
|
|
892
|
+
if (lowerName.includes("chatgpt") || lowerName.includes("gpt")) return "openai";
|
|
893
|
+
if (lowerName.includes("claude")) return "anthropic";
|
|
894
|
+
if (lowerName.includes("perplexity")) return "perplexity";
|
|
895
|
+
if (lowerName.includes("google")) return "google";
|
|
896
|
+
if (lowerName.includes("copilot") || lowerName.includes("bing")) return "microsoft";
|
|
897
|
+
return "unknown";
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Determine detection class
|
|
901
|
+
*/
|
|
902
|
+
determineDetectionClass(isAgent, agentName, confidence) {
|
|
903
|
+
if (!isAgent || confidence < CONFIDENCE.THRESHOLD_AGENT) {
|
|
904
|
+
return { type: "Human" };
|
|
905
|
+
}
|
|
906
|
+
if (agentName) {
|
|
907
|
+
const lowerName = agentName.toLowerCase();
|
|
908
|
+
const aiPatterns = ["chatgpt", "gpt", "claude", "perplexity", "gemini", "copilot"];
|
|
909
|
+
if (aiPatterns.some((p) => lowerName.includes(p))) {
|
|
910
|
+
return { type: "AiAgent", agentType: agentName };
|
|
911
|
+
}
|
|
912
|
+
if (lowerName.includes("bot") || lowerName.includes("crawler")) {
|
|
913
|
+
return { type: "Bot", botType: agentName };
|
|
914
|
+
}
|
|
915
|
+
if (lowerName.includes("automation") || lowerName.includes("tool")) {
|
|
916
|
+
return { type: "Automation", toolType: agentName };
|
|
917
|
+
}
|
|
918
|
+
return { type: "AiAgent", agentType: agentName };
|
|
919
|
+
}
|
|
920
|
+
return { type: "Unknown" };
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
function createRulesDetector() {
|
|
924
|
+
return new RulesDetector();
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// src/node.ts
|
|
928
|
+
function createNodeDetector(options = {}) {
|
|
929
|
+
const loader = new DynamicWasmLoader();
|
|
930
|
+
let policyLoader;
|
|
931
|
+
if (options.apiKey) {
|
|
932
|
+
policyLoader = new PolicyLoader({
|
|
933
|
+
apiUrl: options.policyApiUrl,
|
|
934
|
+
cacheTTL: options.policyTTL
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
return new WasmDetector(loader, policyLoader, options);
|
|
938
|
+
}
|
|
939
|
+
function createFallbackDetector() {
|
|
940
|
+
return new RulesDetector();
|
|
941
|
+
}
|
|
942
|
+
function extractInputFromExpressRequest(req) {
|
|
943
|
+
const headers = {};
|
|
944
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
945
|
+
if (value) {
|
|
946
|
+
headers[key] = Array.isArray(value) ? value[0] : value;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
userAgent: headers["user-agent"],
|
|
951
|
+
ipAddress: req.ip || headers["x-forwarded-for"]?.split(",")[0]?.trim() || headers["x-real-ip"],
|
|
952
|
+
headers,
|
|
953
|
+
url: req.url,
|
|
954
|
+
method: req.method
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
export { CONFIDENCE, DynamicWasmLoader, PolicyLoader, RulesDetector, WasmDetector, createDynamicLoader, createFallbackDetector, createNodeDetector, createPolicyLoader, createRulesDetector, extractInputFromExpressRequest };
|
|
959
|
+
//# sourceMappingURL=node.mjs.map
|
|
960
|
+
//# sourceMappingURL=node.mjs.map
|