@kya-os/agentshield-nextjs
Version:
Next.js middleware for AgentShield AI agent detection
176 lines (174 loc) • 5.27 kB
JavaScript
;
// src/session-tracker.ts
var EdgeSessionTracker = class {
config;
constructor(config) {
this.config = {
enabled: config.enabled,
cookieName: config.cookieName || "__agentshield_session",
cookieMaxAge: config.cookieMaxAge || 3600,
// 1 hour default
encryptionKey: config.encryptionKey || process.env.AGENTSHIELD_SECRET || "agentshield-default-key"
};
}
/**
* Track a new AI agent session
*/
async track(_request, response, result) {
try {
if (!this.config.enabled || !result.isAgent) {
return response;
}
const sessionData = {
id: crypto.randomUUID(),
agent: result.detectedAgent?.name || "unknown",
confidence: result.confidence,
detectedAt: Date.now(),
expires: Date.now() + this.config.cookieMaxAge * 1e3
};
const encrypted = await this.encrypt(JSON.stringify(sessionData));
response.cookies.set(this.config.cookieName, encrypted, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: this.config.cookieMaxAge,
path: "/"
});
return response;
} catch (error) {
if (process.env.DEBUG_AGENTSHIELD) {
console.warn("AgentShield: Failed to track session:", error);
}
return response;
}
}
/**
* Check for existing AI agent session
*/
async check(request) {
try {
if (!this.config.enabled) {
return null;
}
const cookie = request.cookies.get(this.config.cookieName);
if (!cookie?.value) {
return null;
}
const decrypted = await this.decrypt(cookie.value);
const session = JSON.parse(decrypted);
if (session.expires < Date.now()) {
return null;
}
return session;
} catch (error) {
if (process.env.DEBUG_AGENTSHIELD) {
console.warn("AgentShield: Failed to check session:", error);
}
return null;
}
}
/**
* Clear an existing session
*/
clear(response) {
try {
response.cookies.delete(this.config.cookieName);
} catch (error) {
if (process.env.DEBUG_AGENTSHIELD) {
console.warn("AgentShield: Failed to clear session:", error);
}
}
return response;
}
/**
* Simple encryption using Web Crypto API (Edge-compatible)
*/
async encrypt(data) {
try {
const key = this.config.encryptionKey;
const encoded = new TextEncoder().encode(data);
const obfuscated = new Uint8Array(encoded.length);
for (let i = 0; i < encoded.length; i++) {
obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);
}
return btoa(
Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join("")
);
} catch (error) {
return btoa(data);
}
}
/**
* Simple decryption (Edge-compatible)
*/
async decrypt(data) {
try {
const key = this.config.encryptionKey;
const decoded = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
const deobfuscated = new Uint8Array(decoded.length);
for (let i = 0; i < decoded.length; i++) {
deobfuscated[i] = (decoded[i] || 0) ^ key.charCodeAt(i % key.length);
}
return new TextDecoder().decode(deobfuscated);
} catch (error) {
return atob(data);
}
}
};
var StatelessSessionChecker = class {
static check(headers) {
try {
const agent = headers["x-agentshield-session-agent"];
const confidence = headers["x-agentshield-session-confidence"];
const sessionId = headers["x-agentshield-session-id"];
if (agent && confidence && sessionId) {
return {
id: sessionId,
agent,
confidence: parseFloat(confidence),
detectedAt: Date.now(),
expires: Date.now() + 36e5
// 1 hour
};
}
const cookieHeader = headers["cookie"];
if (cookieHeader && cookieHeader.includes("__agentshield_session=")) {
const match = cookieHeader.match(/__agentshield_session=([^;]+)/);
if (match && match[1]) {
try {
const decoded = atob(match[1]);
return JSON.parse(decoded);
} catch {
}
}
}
return null;
} catch {
return null;
}
}
static setHeaders(response, session) {
try {
if (response.setHeader) {
response.setHeader("X-AgentShield-Session-Agent", session.agent);
response.setHeader(
"X-AgentShield-Session-Confidence",
session.confidence.toString()
);
response.setHeader("X-AgentShield-Session-Id", session.id);
} else if (response.headers && response.headers.set) {
response.headers.set("x-agentshield-session-agent", session.agent);
response.headers.set(
"x-agentshield-session-confidence",
session.confidence.toString()
);
response.headers.set("x-agentshield-session-id", session.id);
}
} catch {
}
}
};
exports.EdgeSessionTracker = EdgeSessionTracker;
exports.StatelessSessionChecker = StatelessSessionChecker;
//# sourceMappingURL=session-tracker.js.map
//# sourceMappingURL=session-tracker.js.map