@kya-os/agentshield-nextjs 0.1.33 → 0.1.35

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/index.mjs CHANGED
@@ -1055,6 +1055,488 @@ function createAgentShieldMiddleware2(config) {
1055
1055
  };
1056
1056
  }
1057
1057
 
1058
+ // src/wasm-confidence.ts
1059
+ async function checkWasmAvailability() {
1060
+ try {
1061
+ if (typeof WebAssembly === "undefined") {
1062
+ return false;
1063
+ }
1064
+ const wasmCode = new Uint8Array([
1065
+ 0,
1066
+ 97,
1067
+ 115,
1068
+ 109,
1069
+ 1,
1070
+ 0,
1071
+ 0,
1072
+ 0
1073
+ ]);
1074
+ const module = await WebAssembly.compile(wasmCode);
1075
+ await WebAssembly.instantiate(module);
1076
+ return true;
1077
+ } catch {
1078
+ return false;
1079
+ }
1080
+ }
1081
+ function shouldIndicateWasmVerification(confidence) {
1082
+ return confidence >= 0.85 && confidence < 1;
1083
+ }
1084
+ function getWasmConfidenceBoost(baseConfidence, reasons = []) {
1085
+ if (reasons.some(
1086
+ (r) => r.includes("signature_agent") && !r.includes("signature_headers_present")
1087
+ )) {
1088
+ return 1;
1089
+ }
1090
+ if (baseConfidence >= 0.85) {
1091
+ return 0.95;
1092
+ }
1093
+ if (baseConfidence >= 0.7) {
1094
+ return Math.min(baseConfidence * 1.1, 0.9);
1095
+ }
1096
+ return baseConfidence;
1097
+ }
1098
+ function getVerificationMethod(confidence, reasons = []) {
1099
+ if (reasons.some(
1100
+ (r) => r.includes("signature_agent") && !r.includes("signature_headers_present")
1101
+ )) {
1102
+ return "signature";
1103
+ }
1104
+ if (shouldIndicateWasmVerification(confidence)) {
1105
+ return "wasm-enhanced";
1106
+ }
1107
+ return "pattern";
1108
+ }
1109
+
1110
+ // src/storage/memory-adapter.ts
1111
+ var MemoryStorageAdapter = class {
1112
+ events = /* @__PURE__ */ new Map();
1113
+ sessions = /* @__PURE__ */ new Map();
1114
+ eventTimeline = [];
1115
+ maxEvents = 1e3;
1116
+ maxSessions = 100;
1117
+ async storeEvent(event) {
1118
+ const eventKey = `${event.timestamp}:${event.eventId}`;
1119
+ this.events.set(eventKey, event);
1120
+ this.eventTimeline.push({
1121
+ timestamp: Date.parse(event.timestamp),
1122
+ eventId: eventKey
1123
+ });
1124
+ this.eventTimeline.sort((a, b) => b.timestamp - a.timestamp);
1125
+ if (this.eventTimeline.length > this.maxEvents) {
1126
+ const removed = this.eventTimeline.splice(this.maxEvents);
1127
+ removed.forEach((item) => this.events.delete(item.eventId));
1128
+ }
1129
+ }
1130
+ async getRecentEvents(limit = 100) {
1131
+ const recent = this.eventTimeline.slice(0, limit);
1132
+ return recent.map((item) => this.events.get(item.eventId)).filter((event) => event !== void 0);
1133
+ }
1134
+ async getSessionEvents(sessionId) {
1135
+ const events = [];
1136
+ for (const event of this.events.values()) {
1137
+ if (event.sessionId === sessionId) {
1138
+ events.push(event);
1139
+ }
1140
+ }
1141
+ return events.sort(
1142
+ (a, b) => Date.parse(b.timestamp) - Date.parse(a.timestamp)
1143
+ );
1144
+ }
1145
+ async storeSession(session) {
1146
+ this.sessions.set(session.sessionId, session);
1147
+ if (this.sessions.size > this.maxSessions) {
1148
+ const sortedSessions = Array.from(this.sessions.entries()).sort((a, b) => Date.parse(b[1].lastSeen) - Date.parse(a[1].lastSeen));
1149
+ const toRemove = sortedSessions.slice(this.maxSessions);
1150
+ toRemove.forEach(([id]) => this.sessions.delete(id));
1151
+ }
1152
+ }
1153
+ async getSession(sessionId) {
1154
+ return this.sessions.get(sessionId) || null;
1155
+ }
1156
+ async getRecentSessions(limit = 10) {
1157
+ const sorted = Array.from(this.sessions.values()).sort((a, b) => Date.parse(b.lastSeen) - Date.parse(a.lastSeen));
1158
+ return sorted.slice(0, limit);
1159
+ }
1160
+ async cleanup(olderThan) {
1161
+ const cutoff = olderThan.getTime();
1162
+ this.eventTimeline = this.eventTimeline.filter((item) => {
1163
+ if (item.timestamp < cutoff) {
1164
+ this.events.delete(item.eventId);
1165
+ return false;
1166
+ }
1167
+ return true;
1168
+ });
1169
+ for (const [id, session] of this.sessions.entries()) {
1170
+ if (Date.parse(session.lastSeen) < cutoff) {
1171
+ this.sessions.delete(id);
1172
+ }
1173
+ }
1174
+ }
1175
+ };
1176
+
1177
+ // src/storage/redis-adapter.ts
1178
+ var RedisStorageAdapter = class {
1179
+ redis;
1180
+ ttl;
1181
+ keyPrefix = "agent-shield";
1182
+ constructor(redis, ttl = 86400) {
1183
+ this.redis = redis;
1184
+ this.ttl = ttl;
1185
+ }
1186
+ eventKey(timestamp, eventId) {
1187
+ return `${this.keyPrefix}:events:${timestamp}:${eventId}`;
1188
+ }
1189
+ sessionKey(sessionId) {
1190
+ return `${this.keyPrefix}:sessions:${sessionId}`;
1191
+ }
1192
+ timelineKey() {
1193
+ return `${this.keyPrefix}:events:timeline`;
1194
+ }
1195
+ async storeEvent(event) {
1196
+ const key = this.eventKey(event.timestamp, event.eventId);
1197
+ await this.redis.setex(key, this.ttl, JSON.stringify(event));
1198
+ await this.redis.zadd(this.timelineKey(), {
1199
+ score: Date.parse(event.timestamp),
1200
+ member: key
1201
+ });
1202
+ const count = await this.redis.zcard(this.timelineKey());
1203
+ if (count && count > 1e3) {
1204
+ await this.redis.zremrangebyrank(this.timelineKey(), 0, -1001);
1205
+ }
1206
+ }
1207
+ async getRecentEvents(limit = 100) {
1208
+ const keys = await this.redis.zrevrange(this.timelineKey(), 0, limit - 1);
1209
+ if (!keys || keys.length === 0) {
1210
+ return [];
1211
+ }
1212
+ const events = [];
1213
+ for (const key of keys) {
1214
+ const data = await this.redis.get(key);
1215
+ if (data) {
1216
+ try {
1217
+ const event = typeof data === "string" ? JSON.parse(data) : data;
1218
+ events.push(event);
1219
+ } catch (e) {
1220
+ console.error(`Failed to parse event ${key}:`, e);
1221
+ }
1222
+ }
1223
+ }
1224
+ return events;
1225
+ }
1226
+ async getSessionEvents(sessionId) {
1227
+ const keys = await this.redis.zrevrange(this.timelineKey(), 0, -1);
1228
+ if (!keys || keys.length === 0) {
1229
+ return [];
1230
+ }
1231
+ const events = [];
1232
+ for (const key of keys) {
1233
+ const data = await this.redis.get(key);
1234
+ if (data) {
1235
+ try {
1236
+ const event = typeof data === "string" ? JSON.parse(data) : data;
1237
+ if (event.sessionId === sessionId) {
1238
+ events.push(event);
1239
+ }
1240
+ } catch (e) {
1241
+ console.error(`Failed to parse event ${key}:`, e);
1242
+ }
1243
+ }
1244
+ }
1245
+ return events;
1246
+ }
1247
+ async storeSession(session) {
1248
+ const key = this.sessionKey(session.sessionId);
1249
+ const existing = await this.redis.get(key);
1250
+ if (existing) {
1251
+ const existingSession = typeof existing === "string" ? JSON.parse(existing) : existing;
1252
+ const methods = /* @__PURE__ */ new Set([
1253
+ ...existingSession.verificationMethods || [],
1254
+ ...session.verificationMethods
1255
+ ]);
1256
+ const updatedSession = {
1257
+ ...existingSession,
1258
+ lastSeen: session.lastSeen,
1259
+ eventCount: session.eventCount,
1260
+ paths: Array.from(/* @__PURE__ */ new Set([...existingSession.paths, ...session.paths])),
1261
+ averageConfidence: session.averageConfidence,
1262
+ verificationMethods: Array.from(methods)
1263
+ };
1264
+ await this.redis.setex(key, this.ttl, JSON.stringify(updatedSession));
1265
+ } else {
1266
+ await this.redis.setex(key, this.ttl, JSON.stringify(session));
1267
+ }
1268
+ }
1269
+ async getSession(sessionId) {
1270
+ const key = this.sessionKey(sessionId);
1271
+ const data = await this.redis.get(key);
1272
+ if (!data) {
1273
+ return null;
1274
+ }
1275
+ try {
1276
+ return typeof data === "string" ? JSON.parse(data) : data;
1277
+ } catch (e) {
1278
+ console.error(`Failed to parse session ${sessionId}:`, e);
1279
+ return null;
1280
+ }
1281
+ }
1282
+ async getRecentSessions(limit = 10) {
1283
+ const pattern = `${this.keyPrefix}:sessions:*`;
1284
+ const sessions = [];
1285
+ let cursor = 0;
1286
+ do {
1287
+ const [nextCursor, keys] = await this.redis.scan(cursor, {
1288
+ match: pattern,
1289
+ count: 100
1290
+ });
1291
+ cursor = parseInt(nextCursor);
1292
+ for (const key of keys) {
1293
+ const data = await this.redis.get(key);
1294
+ if (data) {
1295
+ try {
1296
+ const session = typeof data === "string" ? JSON.parse(data) : data;
1297
+ sessions.push(session);
1298
+ } catch (e) {
1299
+ console.error(`Failed to parse session from ${key}:`, e);
1300
+ }
1301
+ }
1302
+ }
1303
+ } while (cursor !== 0 && sessions.length < limit * 2);
1304
+ return sessions.sort((a, b) => Date.parse(b.lastSeen) - Date.parse(a.lastSeen)).slice(0, limit);
1305
+ }
1306
+ async cleanup(olderThan) {
1307
+ const cutoff = olderThan.getTime();
1308
+ await this.redis.zremrangebyrank(
1309
+ this.timelineKey(),
1310
+ 0,
1311
+ Math.floor(cutoff / 1e3)
1312
+ );
1313
+ }
1314
+ };
1315
+
1316
+ // src/storage/index.ts
1317
+ async function createStorageAdapter(config) {
1318
+ if (!config || config.type === "memory") {
1319
+ return new MemoryStorageAdapter();
1320
+ }
1321
+ if (config.type === "custom" && config.custom) {
1322
+ return config.custom;
1323
+ }
1324
+ if (config.type === "redis" && config.redis) {
1325
+ try {
1326
+ const { Redis } = await import('@upstash/redis');
1327
+ const redis = new Redis({
1328
+ url: config.redis.url,
1329
+ token: config.redis.token
1330
+ });
1331
+ return new RedisStorageAdapter(redis, config.ttl);
1332
+ } catch (error) {
1333
+ console.warn("[AgentShield] Failed to initialize Redis storage, falling back to memory:", error);
1334
+ return new MemoryStorageAdapter();
1335
+ }
1336
+ }
1337
+ return new MemoryStorageAdapter();
1338
+ }
1339
+
1340
+ // src/enhanced-middleware.ts
1341
+ var SessionManager = class {
1342
+ sessionLastActivity = /* @__PURE__ */ new Map();
1343
+ generateSessionId(ipAddress, userAgent) {
1344
+ const now = Date.now();
1345
+ const timeWindow = Math.floor(now / (5 * 60 * 1e3));
1346
+ const baseKey = `${ipAddress || "unknown"}:${userAgent || "unknown"}`;
1347
+ const windowKey = `${baseKey}:${timeWindow}`;
1348
+ const lastActivity = this.sessionLastActivity.get(windowKey);
1349
+ const shouldCreateNewSession = !lastActivity || now - lastActivity > 3e4;
1350
+ this.sessionLastActivity.set(windowKey, now);
1351
+ if (this.sessionLastActivity.size > 100) {
1352
+ const cutoff = now - 6e5;
1353
+ for (const [key, time] of this.sessionLastActivity.entries()) {
1354
+ if (time < cutoff) {
1355
+ this.sessionLastActivity.delete(key);
1356
+ }
1357
+ }
1358
+ }
1359
+ const data = shouldCreateNewSession ? `${windowKey}:${now}` : `${windowKey}:${lastActivity}`;
1360
+ let hash = 0;
1361
+ for (let i = 0; i < data.length; i++) {
1362
+ const char = data.charCodeAt(i);
1363
+ hash = (hash << 5) - hash + char;
1364
+ hash = hash & hash;
1365
+ }
1366
+ return Math.abs(hash).toString(16).padStart(12, "0").substring(0, 12);
1367
+ }
1368
+ };
1369
+ function createEnhancedAgentShieldMiddleware(config = {}) {
1370
+ let storageAdapter = null;
1371
+ let storageInitPromise = null;
1372
+ const getStorage = async () => {
1373
+ if (storageAdapter) return storageAdapter;
1374
+ if (storageInitPromise) return storageInitPromise;
1375
+ storageInitPromise = createStorageAdapter(config.storage).then((adapter) => {
1376
+ storageAdapter = adapter;
1377
+ return adapter;
1378
+ });
1379
+ return storageInitPromise;
1380
+ };
1381
+ let detector = null;
1382
+ let detectorInitPromise = null;
1383
+ const getDetector = async () => {
1384
+ if (detector) return detector;
1385
+ if (detectorInitPromise) {
1386
+ await detectorInitPromise;
1387
+ return detector;
1388
+ }
1389
+ detectorInitPromise = (async () => {
1390
+ try {
1391
+ const wasmAvailable = await checkWasmAvailability();
1392
+ if (wasmAvailable) {
1393
+ console.log("[AgentShield] \u2705 WASM support detected - enhanced detection enabled");
1394
+ detector = new EdgeAgentDetectorWrapperWithWasm({ enableWasm: true });
1395
+ } else {
1396
+ console.log("[AgentShield] \u2139\uFE0F Using pattern-based detection (WASM not available)");
1397
+ detector = new EdgeAgentDetectorWrapper({});
1398
+ }
1399
+ } catch (error) {
1400
+ console.warn("[AgentShield] Failed to initialize WASM, using fallback:", error);
1401
+ detector = new EdgeAgentDetectorWrapper({});
1402
+ }
1403
+ })();
1404
+ await detectorInitPromise;
1405
+ return detector;
1406
+ };
1407
+ const sessionManager = new SessionManager();
1408
+ const sessionTrackingEnabled = config.sessionTracking?.enabled !== false;
1409
+ return async (request) => {
1410
+ const { pathname } = request.nextUrl;
1411
+ if (config.skipPaths?.some((path) => pathname.startsWith(path))) {
1412
+ return NextResponse.next();
1413
+ }
1414
+ const userAgent = request.headers.get("user-agent");
1415
+ const ipAddress = request.ip ?? request.headers.get("x-forwarded-for");
1416
+ const url = new URL(request.url);
1417
+ const pathWithQuery = url.pathname + url.search;
1418
+ const context = {
1419
+ userAgent: userAgent || "",
1420
+ ipAddress: ipAddress || "",
1421
+ headers: Object.fromEntries(request.headers.entries()),
1422
+ url: pathWithQuery,
1423
+ method: request.method,
1424
+ timestamp: /* @__PURE__ */ new Date()
1425
+ };
1426
+ const activeDetector = await getDetector();
1427
+ const result = await activeDetector.analyze(context);
1428
+ let finalConfidence = result.confidence;
1429
+ let verificationMethod = result.verificationMethod || "pattern";
1430
+ if (result.isAgent) {
1431
+ const reasons = result.reasons || [];
1432
+ if (shouldIndicateWasmVerification(result.confidence)) {
1433
+ finalConfidence = getWasmConfidenceBoost(result.confidence, reasons);
1434
+ verificationMethod = getVerificationMethod(finalConfidence, reasons);
1435
+ }
1436
+ }
1437
+ if (result.isAgent && finalConfidence >= (config.confidenceThreshold ?? 0.7)) {
1438
+ if (sessionTrackingEnabled) {
1439
+ const storage = await getStorage();
1440
+ const sessionId = sessionManager.generateSessionId(ipAddress || void 0, userAgent || void 0);
1441
+ const event = {
1442
+ eventId: `agent_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
1443
+ sessionId,
1444
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1445
+ agentType: result.detectedAgent?.type || "unknown",
1446
+ agentName: result.detectedAgent?.name || "Unknown",
1447
+ confidence: finalConfidence,
1448
+ path: pathWithQuery,
1449
+ ...userAgent && { userAgent },
1450
+ ...ipAddress && { ipAddress },
1451
+ method: request.method,
1452
+ detectionReasons: result.reasons || [],
1453
+ verificationMethod,
1454
+ detectionDetails: {
1455
+ patterns: result.detectedAgent?.patterns,
1456
+ behaviors: result.detectedAgent?.behaviors,
1457
+ fingerprintMatches: result.detectedAgent?.fingerprints
1458
+ }
1459
+ };
1460
+ try {
1461
+ await storage.storeEvent(event);
1462
+ let session = await storage.getSession(sessionId);
1463
+ if (session) {
1464
+ session.lastSeen = event.timestamp;
1465
+ session.eventCount++;
1466
+ if (!session.paths.includes(pathWithQuery)) {
1467
+ session.paths.push(pathWithQuery);
1468
+ }
1469
+ session.averageConfidence = (session.averageConfidence * (session.eventCount - 1) + finalConfidence) / session.eventCount;
1470
+ if (!session.verificationMethods.includes(verificationMethod)) {
1471
+ session.verificationMethods.push(verificationMethod);
1472
+ }
1473
+ } else {
1474
+ session = {
1475
+ sessionId,
1476
+ ...ipAddress && { ipAddress },
1477
+ ...userAgent && { userAgent },
1478
+ agentType: result.detectedAgent?.type || "unknown",
1479
+ agentName: result.detectedAgent?.name || "Unknown",
1480
+ firstSeen: event.timestamp,
1481
+ lastSeen: event.timestamp,
1482
+ eventCount: 1,
1483
+ paths: [pathWithQuery],
1484
+ averageConfidence: finalConfidence,
1485
+ verificationMethods: [verificationMethod]
1486
+ };
1487
+ }
1488
+ if (session) {
1489
+ await storage.storeSession(session);
1490
+ }
1491
+ } catch (error) {
1492
+ console.error("[AgentShield] Failed to store event:", error);
1493
+ }
1494
+ }
1495
+ if (config.onDetection) {
1496
+ await config.onDetection(result, context);
1497
+ }
1498
+ switch (config.onAgentDetected) {
1499
+ case "block": {
1500
+ const { status = 403, message = "Access denied: AI agent detected" } = config.blockedResponse || {};
1501
+ const response2 = NextResponse.json(
1502
+ { error: message, detected: true, confidence: finalConfidence },
1503
+ { status }
1504
+ );
1505
+ response2.headers.set("x-agentshield-detected", "true");
1506
+ response2.headers.set("x-agentshield-confidence", String(Math.round(finalConfidence * 100)));
1507
+ response2.headers.set("x-agentshield-agent", result.detectedAgent?.name || "Unknown");
1508
+ response2.headers.set("x-agentshield-verification", verificationMethod);
1509
+ return response2;
1510
+ }
1511
+ case "log":
1512
+ const isInteresting = finalConfidence >= 0.9 || result.detectedAgent?.name?.toLowerCase().includes("chatgpt") || result.detectedAgent?.name?.toLowerCase().includes("perplexity") || verificationMethod === "signature";
1513
+ if (isInteresting) {
1514
+ console.log(`[AgentShield] \u{1F916} AI Agent detected (${verificationMethod}):`, {
1515
+ agent: result.detectedAgent?.name,
1516
+ confidence: `${(finalConfidence * 100).toFixed(0)}%`,
1517
+ path: pathWithQuery,
1518
+ verification: verificationMethod
1519
+ });
1520
+ }
1521
+ break;
1522
+ }
1523
+ }
1524
+ const response = NextResponse.next();
1525
+ if (result.isAgent) {
1526
+ response.headers.set("x-agentshield-detected", "true");
1527
+ response.headers.set("x-agentshield-confidence", String(Math.round(finalConfidence * 100)));
1528
+ response.headers.set("x-agentshield-agent", result.detectedAgent?.name || "Unknown");
1529
+ response.headers.set("x-agentshield-verification", verificationMethod);
1530
+ if (finalConfidence > 0.9) {
1531
+ response.headers.set("x-ai-visitor", "true");
1532
+ response.headers.set("x-ai-confidence", finalConfidence.toString());
1533
+ response.headers.set("x-ai-verification", verificationMethod);
1534
+ }
1535
+ }
1536
+ return response;
1537
+ };
1538
+ }
1539
+
1058
1540
  // src/index.ts
1059
1541
  var VERSION = "0.1.0";
1060
1542
  /**
@@ -1063,6 +1545,6 @@ var VERSION = "0.1.0";
1063
1545
  * @license MIT OR Apache-2.0
1064
1546
  */
1065
1547
 
1066
- export { EdgeSessionTracker, StatelessSessionChecker, VERSION, createAgentShieldMiddleware2 as createAgentShieldMiddleware, createAgentShieldMiddleware as createAgentShieldMiddlewareBase, createAgentShieldMiddleware2 as createMiddleware };
1548
+ export { EdgeSessionTracker, StatelessSessionChecker, VERSION, createAgentShieldMiddleware2 as createAgentShieldMiddleware, createAgentShieldMiddleware as createAgentShieldMiddlewareBase, createEnhancedAgentShieldMiddleware, createAgentShieldMiddleware2 as createMiddleware };
1067
1549
  //# sourceMappingURL=index.mjs.map
1068
1550
  //# sourceMappingURL=index.mjs.map