UNPKG

@kya-os/agentshield-nextjs

Version:

Next.js middleware for AgentShield AI agent detection

185 lines (182 loc) 6.83 kB
'use strict'; var server = require('next/server'); var agentshieldShared = require('@kya-os/agentshield-shared'); // src/policy.ts function createContextFromDetection(detection, request) { return agentshieldShared.createEvaluationContext({ agentType: detection.detectedAgent?.type, agentName: detection.detectedAgent?.name, agentVendor: detection.detectedAgent?.vendor, confidence: detection.confidence, riskLevel: detection.riskLevel, path: request.nextUrl.pathname, method: request.method, signatureVerified: detection.verificationMethod === "signature", isAuthenticated: false, // TODO: integrate with auth userAgent: request.headers.get("user-agent") || void 0 }); } function evaluatePolicyForDetection(detection, request, policy) { const context = createContextFromDetection(detection, request); return agentshieldShared.evaluatePolicy(policy, context); } function buildBlockedResponse(decision, config) { const status = config.blockedResponse?.status ?? 403; const message = config.blockedResponse?.message ?? decision.message ?? "Access denied"; const response = server.NextResponse.json( { error: message, code: "POLICY_BLOCKED", reason: decision.reason, ruleId: decision.ruleId, matchType: decision.matchType }, { status } ); if (config.blockedResponse?.headers) { for (const [key, value] of Object.entries(config.blockedResponse.headers)) { response.headers.set(key, value); } } response.headers.set("X-AgentShield-Action", decision.action); response.headers.set("X-AgentShield-Reason", decision.reason); response.headers.set("X-AgentShield-MatchType", decision.matchType); return response; } function buildRedirectResponse(request, decision, config) { const redirectUrl = decision.redirectUrl || config.redirectUrl || "/blocked"; const url = new URL(redirectUrl, request.url); url.searchParams.set("reason", decision.reason); if (decision.ruleId) { url.searchParams.set("ruleId", decision.ruleId); } return server.NextResponse.redirect(url); } function buildChallengeResponse(request, decision, config) { return buildRedirectResponse(request, decision, config); } async function handlePolicyDecision(request, decision, config) { switch (decision.action) { case agentshieldShared.ENFORCEMENT_ACTIONS.BLOCK: if (config.customBlockedResponse) { return await config.customBlockedResponse(request, decision); } return buildBlockedResponse(decision, config); case agentshieldShared.ENFORCEMENT_ACTIONS.REDIRECT: return buildRedirectResponse(request, decision, config); case agentshieldShared.ENFORCEMENT_ACTIONS.CHALLENGE: return buildChallengeResponse(request, decision, config); case agentshieldShared.ENFORCEMENT_ACTIONS.LOG: console.log("[AgentShield] Policy decision (log):", { path: request.nextUrl.pathname, action: decision.action, reason: decision.reason, matchType: decision.matchType, ruleId: decision.ruleId }); return null; // Continue to allow case agentshieldShared.ENFORCEMENT_ACTIONS.ALLOW: default: return null; } } var fetcherCache = /* @__PURE__ */ new Map(); function getFetcherCacheKey(config) { return `${config.apiUrl ?? "default"}:${config.apiKey ?? ""}:${config.cacheTtlSeconds ?? "default"}`; } function getPolicyFetcher(config) { if (!config) { throw new Error("fetchPolicy config required"); } const cacheKey = getFetcherCacheKey(config); let fetcher = fetcherCache.get(cacheKey); if (!fetcher) { const fetcherConfig = { apiBaseUrl: config.apiUrl || "https://kya.vouched.id", apiKey: config.apiKey, cacheTtlSeconds: config.cacheTtlSeconds }; fetcher = agentshieldShared.createPolicyFetcher(fetcherConfig); fetcherCache.set(cacheKey, fetcher); } return fetcher; } async function getPolicy(config) { if (config.policy) { return agentshieldShared.PolicyConfigSchema.parse({ ...agentshieldShared.DEFAULT_POLICY, ...config.policy }); } if (config.fetchPolicy) { try { const fetcher = getPolicyFetcher(config.fetchPolicy); return await fetcher.getPolicy(config.fetchPolicy.projectId); } catch (error) { if (config.debug) { console.warn("[AgentShield] Policy fetch failed, using fallback:", error); } return agentshieldShared.PolicyConfigSchema.parse({ ...agentshieldShared.DEFAULT_POLICY, ...config.fallbackPolicy || {} }); } } return agentshieldShared.PolicyConfigSchema.parse(agentshieldShared.DEFAULT_POLICY); } async function applyPolicy(request, detection, config) { try { const path = request.nextUrl.pathname; if (config.skipPaths?.some((pattern) => agentshieldShared.matchPath(path, pattern))) { return null; } if (config.includePaths && config.includePaths.length > 0) { if (!config.includePaths.some((pattern) => agentshieldShared.matchPath(path, pattern))) { return null; } } const policy = await getPolicy(config); const context = createContextFromDetection(detection, request); const decision = agentshieldShared.evaluatePolicy(policy, context); if (config.onPolicyDecision) { await config.onPolicyDecision(request, decision, context); } return await handlePolicyDecision(request, decision, config); } catch (error) { if (config.debug) { console.error("[AgentShield] Policy evaluation error:", error); } if (config.failOpen !== false) { return null; } return server.NextResponse.json( { error: "Security check failed", code: "POLICY_ERROR" }, { status: 503 } ); } } Object.defineProperty(exports, "DEFAULT_POLICY", { enumerable: true, get: function () { return agentshieldShared.DEFAULT_POLICY; } }); Object.defineProperty(exports, "ENFORCEMENT_ACTIONS", { enumerable: true, get: function () { return agentshieldShared.ENFORCEMENT_ACTIONS; } }); Object.defineProperty(exports, "createEvaluationContext", { enumerable: true, get: function () { return agentshieldShared.createEvaluationContext; } }); Object.defineProperty(exports, "evaluatePolicy", { enumerable: true, get: function () { return agentshieldShared.evaluatePolicy; } }); exports.applyPolicy = applyPolicy; exports.buildBlockedResponse = buildBlockedResponse; exports.buildChallengeResponse = buildChallengeResponse; exports.buildRedirectResponse = buildRedirectResponse; exports.createContextFromDetection = createContextFromDetection; exports.evaluatePolicyForDetection = evaluatePolicyForDetection; exports.getPolicy = getPolicy; exports.handlePolicyDecision = handlePolicyDecision; //# sourceMappingURL=policy.js.map //# sourceMappingURL=policy.js.map