@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.
Files changed (110) hide show
  1. package/README.md +21 -369
  2. package/index.js +9 -0
  3. package/package.json +7 -141
  4. package/EDGE_RUNTIME_WASM_SETUP.md +0 -348
  5. package/bin/setup-edge-wasm.js +0 -525
  6. package/dist/.tsbuildinfo +0 -1
  7. package/dist/api-client.d.mts +0 -196
  8. package/dist/api-client.d.ts +0 -196
  9. package/dist/api-client.js +0 -200
  10. package/dist/api-client.js.map +0 -1
  11. package/dist/api-client.mjs +0 -196
  12. package/dist/api-client.mjs.map +0 -1
  13. package/dist/api-middleware.d.mts +0 -140
  14. package/dist/api-middleware.d.ts +0 -140
  15. package/dist/api-middleware.js +0 -511
  16. package/dist/api-middleware.js.map +0 -1
  17. package/dist/api-middleware.mjs +0 -508
  18. package/dist/api-middleware.mjs.map +0 -1
  19. package/dist/create-middleware.d.mts +0 -17
  20. package/dist/create-middleware.d.ts +0 -17
  21. package/dist/create-middleware.js +0 -1381
  22. package/dist/create-middleware.js.map +0 -1
  23. package/dist/create-middleware.mjs +0 -1358
  24. package/dist/create-middleware.mjs.map +0 -1
  25. package/dist/edge/index.d.mts +0 -110
  26. package/dist/edge/index.d.ts +0 -110
  27. package/dist/edge/index.js +0 -277
  28. package/dist/edge/index.js.map +0 -1
  29. package/dist/edge/index.mjs +0 -275
  30. package/dist/edge/index.mjs.map +0 -1
  31. package/dist/edge-detector-wrapper.d.mts +0 -34
  32. package/dist/edge-detector-wrapper.d.ts +0 -34
  33. package/dist/edge-detector-wrapper.js +0 -596
  34. package/dist/edge-detector-wrapper.js.map +0 -1
  35. package/dist/edge-detector-wrapper.mjs +0 -574
  36. package/dist/edge-detector-wrapper.mjs.map +0 -1
  37. package/dist/edge-runtime-loader.d.mts +0 -50
  38. package/dist/edge-runtime-loader.d.ts +0 -50
  39. package/dist/edge-runtime-loader.js +0 -204
  40. package/dist/edge-runtime-loader.js.map +0 -1
  41. package/dist/edge-runtime-loader.mjs +0 -201
  42. package/dist/edge-runtime-loader.mjs.map +0 -1
  43. package/dist/edge-wasm-middleware.d.mts +0 -68
  44. package/dist/edge-wasm-middleware.d.ts +0 -68
  45. package/dist/edge-wasm-middleware.js +0 -318
  46. package/dist/edge-wasm-middleware.js.map +0 -1
  47. package/dist/edge-wasm-middleware.mjs +0 -315
  48. package/dist/edge-wasm-middleware.mjs.map +0 -1
  49. package/dist/enhanced-middleware.d.mts +0 -153
  50. package/dist/enhanced-middleware.d.ts +0 -153
  51. package/dist/enhanced-middleware.js +0 -1082
  52. package/dist/enhanced-middleware.js.map +0 -1
  53. package/dist/enhanced-middleware.mjs +0 -1080
  54. package/dist/enhanced-middleware.mjs.map +0 -1
  55. package/dist/index.d.mts +0 -24
  56. package/dist/index.d.ts +0 -24
  57. package/dist/index.js +0 -2717
  58. package/dist/index.js.map +0 -1
  59. package/dist/index.mjs +0 -2662
  60. package/dist/index.mjs.map +0 -1
  61. package/dist/middleware.d.mts +0 -21
  62. package/dist/middleware.d.ts +0 -21
  63. package/dist/middleware.js +0 -1362
  64. package/dist/middleware.js.map +0 -1
  65. package/dist/middleware.mjs +0 -1339
  66. package/dist/middleware.mjs.map +0 -1
  67. package/dist/nodejs-wasm-loader.d.mts +0 -25
  68. package/dist/nodejs-wasm-loader.d.ts +0 -25
  69. package/dist/nodejs-wasm-loader.js +0 -78
  70. package/dist/nodejs-wasm-loader.js.map +0 -1
  71. package/dist/nodejs-wasm-loader.mjs +0 -68
  72. package/dist/nodejs-wasm-loader.mjs.map +0 -1
  73. package/dist/policy.d.mts +0 -162
  74. package/dist/policy.d.ts +0 -162
  75. package/dist/policy.js +0 -189
  76. package/dist/policy.js.map +0 -1
  77. package/dist/policy.mjs +0 -165
  78. package/dist/policy.mjs.map +0 -1
  79. package/dist/session-tracker.d.mts +0 -55
  80. package/dist/session-tracker.d.ts +0 -55
  81. package/dist/session-tracker.js +0 -170
  82. package/dist/session-tracker.js.map +0 -1
  83. package/dist/session-tracker.mjs +0 -167
  84. package/dist/session-tracker.mjs.map +0 -1
  85. package/dist/signature-verifier.d.mts +0 -33
  86. package/dist/signature-verifier.d.ts +0 -33
  87. package/dist/signature-verifier.js +0 -386
  88. package/dist/signature-verifier.js.map +0 -1
  89. package/dist/signature-verifier.mjs +0 -362
  90. package/dist/signature-verifier.mjs.map +0 -1
  91. package/dist/types-DVmy9NE3.d.mts +0 -105
  92. package/dist/types-DVmy9NE3.d.ts +0 -105
  93. package/dist/wasm-middleware.d.mts +0 -63
  94. package/dist/wasm-middleware.d.ts +0 -63
  95. package/dist/wasm-middleware.js +0 -98
  96. package/dist/wasm-middleware.js.map +0 -1
  97. package/dist/wasm-middleware.mjs +0 -95
  98. package/dist/wasm-middleware.mjs.map +0 -1
  99. package/dist/wasm-setup.d.mts +0 -46
  100. package/dist/wasm-setup.d.ts +0 -46
  101. package/dist/wasm-setup.js +0 -157
  102. package/dist/wasm-setup.js.map +0 -1
  103. package/dist/wasm-setup.mjs +0 -148
  104. package/dist/wasm-setup.mjs.map +0 -1
  105. package/templates/middleware-wasm-100.ts +0 -151
  106. package/wasm/agentshield_wasm.d.ts +0 -479
  107. package/wasm/agentshield_wasm.js +0 -1536
  108. package/wasm/agentshield_wasm_bg.wasm +0 -0
  109. package/wasm/package.json +0 -30
  110. package/wasm.d.ts +0 -21
package/dist/policy.mjs DELETED
@@ -1,165 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { createEvaluationContext, evaluatePolicy, ENFORCEMENT_ACTIONS, PolicyConfigSchema, DEFAULT_POLICY, matchPath, createPolicyFetcher } from '@kya-os/agentshield-shared';
3
- export { DEFAULT_POLICY, ENFORCEMENT_ACTIONS, createEvaluationContext, evaluatePolicy } from '@kya-os/agentshield-shared';
4
-
5
- // src/policy.ts
6
- function createContextFromDetection(detection, request) {
7
- return createEvaluationContext({
8
- agentType: detection.detectedAgent?.type,
9
- agentName: detection.detectedAgent?.name,
10
- agentVendor: detection.detectedAgent?.vendor,
11
- confidence: detection.confidence,
12
- riskLevel: detection.riskLevel,
13
- path: request.nextUrl.pathname,
14
- method: request.method,
15
- signatureVerified: detection.verificationMethod === "signature",
16
- isAuthenticated: false,
17
- // TODO: integrate with auth
18
- userAgent: request.headers.get("user-agent") || void 0
19
- });
20
- }
21
- function evaluatePolicyForDetection(detection, request, policy) {
22
- const context = createContextFromDetection(detection, request);
23
- return evaluatePolicy(policy, context);
24
- }
25
- function buildBlockedResponse(decision, config) {
26
- const status = config.blockedResponse?.status ?? 403;
27
- const message = config.blockedResponse?.message ?? decision.message ?? "Access denied";
28
- const response = NextResponse.json(
29
- {
30
- error: message,
31
- code: "POLICY_BLOCKED",
32
- reason: decision.reason,
33
- ruleId: decision.ruleId,
34
- matchType: decision.matchType
35
- },
36
- { status }
37
- );
38
- if (config.blockedResponse?.headers) {
39
- for (const [key, value] of Object.entries(config.blockedResponse.headers)) {
40
- response.headers.set(key, value);
41
- }
42
- }
43
- response.headers.set("KYA-Action", decision.action);
44
- response.headers.set("KYA-Reason", decision.reason);
45
- response.headers.set("KYA-Match-Type", decision.matchType);
46
- return response;
47
- }
48
- function buildRedirectResponse(request, decision, config, detection) {
49
- const redirectUrl = decision.redirectUrl || config.redirectUrl || "/blocked";
50
- const url = new URL(redirectUrl, request.url);
51
- url.searchParams.set("reason", decision.reason);
52
- if (decision.ruleId) {
53
- url.searchParams.set("ruleId", decision.ruleId);
54
- }
55
- const agentName = detection?.detectedAgent?.name;
56
- if (agentName && !url.searchParams.has("agent")) {
57
- url.searchParams.set("agent", agentName.toLowerCase());
58
- }
59
- return NextResponse.redirect(url);
60
- }
61
- function buildChallengeResponse(request, decision, config, detection) {
62
- return buildRedirectResponse(request, decision, config, detection);
63
- }
64
- async function handlePolicyDecision(request, decision, config, detection) {
65
- switch (decision.action) {
66
- case ENFORCEMENT_ACTIONS.BLOCK:
67
- if (config.customBlockedResponse) {
68
- return await config.customBlockedResponse(request, decision);
69
- }
70
- return buildBlockedResponse(decision, config);
71
- case ENFORCEMENT_ACTIONS.REDIRECT:
72
- return buildRedirectResponse(request, decision, config, detection);
73
- case ENFORCEMENT_ACTIONS.CHALLENGE:
74
- return buildChallengeResponse(request, decision, config, detection);
75
- case ENFORCEMENT_ACTIONS.LOG:
76
- console.log("[AgentShield] Policy decision (log):", {
77
- path: request.nextUrl.pathname,
78
- action: decision.action,
79
- reason: decision.reason,
80
- matchType: decision.matchType,
81
- ruleId: decision.ruleId
82
- });
83
- return null;
84
- // Continue to allow
85
- case ENFORCEMENT_ACTIONS.ALLOW:
86
- default:
87
- return null;
88
- }
89
- }
90
- var fetcherCache = /* @__PURE__ */ new Map();
91
- function getFetcherCacheKey(config) {
92
- return `${config.apiUrl ?? "default"}:${config.apiKey ?? ""}:${config.cacheTtlSeconds ?? "default"}`;
93
- }
94
- function getPolicyFetcher(config) {
95
- if (!config) {
96
- throw new Error("fetchPolicy config required");
97
- }
98
- const cacheKey = getFetcherCacheKey(config);
99
- let fetcher = fetcherCache.get(cacheKey);
100
- if (!fetcher) {
101
- const fetcherConfig = {
102
- apiBaseUrl: config.apiUrl || "https://kya.vouched.id",
103
- apiKey: config.apiKey,
104
- cacheTtlSeconds: config.cacheTtlSeconds
105
- };
106
- fetcher = createPolicyFetcher(fetcherConfig);
107
- fetcherCache.set(cacheKey, fetcher);
108
- }
109
- return fetcher;
110
- }
111
- async function getPolicy(config) {
112
- if (config.policy) {
113
- return PolicyConfigSchema.parse({ ...DEFAULT_POLICY, ...config.policy });
114
- }
115
- if (config.fetchPolicy) {
116
- try {
117
- const fetcher = getPolicyFetcher(config.fetchPolicy);
118
- return await fetcher.getPolicy(config.fetchPolicy.projectId);
119
- } catch (error) {
120
- if (config.debug) {
121
- console.warn("[AgentShield] Policy fetch failed, using fallback:", error);
122
- }
123
- return PolicyConfigSchema.parse({
124
- ...DEFAULT_POLICY,
125
- ...config.fallbackPolicy || {}
126
- });
127
- }
128
- }
129
- return PolicyConfigSchema.parse(DEFAULT_POLICY);
130
- }
131
- async function applyPolicy(request, detection, config) {
132
- try {
133
- const path = request.nextUrl.pathname;
134
- if (config.skipPaths?.some((pattern) => matchPath(path, pattern))) {
135
- return null;
136
- }
137
- if (config.includePaths && config.includePaths.length > 0) {
138
- if (!config.includePaths.some((pattern) => matchPath(path, pattern))) {
139
- return null;
140
- }
141
- }
142
- const policy = await getPolicy(config);
143
- const context = createContextFromDetection(detection, request);
144
- const decision = evaluatePolicy(policy, context);
145
- if (config.onPolicyDecision) {
146
- await config.onPolicyDecision(request, decision, context);
147
- }
148
- return await handlePolicyDecision(request, decision, config, detection);
149
- } catch (error) {
150
- if (config.debug) {
151
- console.error("[AgentShield] Policy evaluation error:", error);
152
- }
153
- if (config.failOpen !== false) {
154
- return null;
155
- }
156
- return NextResponse.json(
157
- { error: "Security check failed", code: "POLICY_ERROR" },
158
- { status: 503 }
159
- );
160
- }
161
- }
162
-
163
- export { applyPolicy, buildBlockedResponse, buildChallengeResponse, buildRedirectResponse, createContextFromDetection, evaluatePolicyForDetection, getPolicy, handlePolicyDecision };
164
- //# sourceMappingURL=policy.mjs.map
165
- //# sourceMappingURL=policy.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/policy.ts"],"names":[],"mappings":"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","file":"policy.mjs","sourcesContent":["/**\n * Policy Integration for agentshield-nextjs\n *\n * This module provides policy evaluation support for the Next.js middleware.\n * It can use:\n * - Local policy configuration (static)\n * - Fetched policy from AgentShield API (dynamic with caching)\n * - Fallback/default policies\n *\n * @example\n * ```typescript\n * import { createPolicyMiddleware } from '@kya-os/agentshield-nextjs/policy';\n *\n * export default createPolicyMiddleware({\n * policy: {\n * enabled: true,\n * defaultAction: 'allow',\n * thresholds: { confidenceThreshold: 80, confidenceAction: 'block' },\n * allowList: [{ clientName: 'ChatGPT' }],\n * },\n * });\n * ```\n */\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport {\n evaluatePolicy,\n createEvaluationContext,\n createPolicyFetcher,\n matchPath,\n PolicyFetcher,\n PolicyConfigSchema,\n ENFORCEMENT_ACTIONS,\n DEFAULT_POLICY,\n type PolicyConfig,\n type PolicyEvaluationContext,\n type PolicyEvaluationResult,\n type PolicyFetcherConfig,\n type DetectionResult,\n} from '@kya-os/agentshield-shared';\n\n// Re-export shared policy types for convenience\nexport {\n evaluatePolicy,\n createEvaluationContext,\n type PolicyConfig,\n type PolicyEvaluationContext,\n type PolicyEvaluationResult,\n ENFORCEMENT_ACTIONS,\n DEFAULT_POLICY,\n} from '@kya-os/agentshield-shared';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Policy middleware configuration\n */\nexport interface PolicyMiddlewareConfig {\n /**\n * Local policy configuration (static)\n * If provided, this policy is used instead of fetching from API\n */\n policy?: Partial<PolicyConfig>;\n\n /**\n * Fetch policy from AgentShield API\n * Requires projectId and optionally an apiKey\n */\n fetchPolicy?: {\n /** Project ID to fetch policy for */\n projectId: string;\n /** API base URL (defaults to production) */\n apiUrl?: string;\n /** API key for authentication */\n apiKey?: string;\n /** Cache TTL in seconds (default: 300) */\n cacheTtlSeconds?: number;\n };\n\n /**\n * Fallback policy to use when fetch fails\n * Defaults to DEFAULT_POLICY (allow all)\n */\n fallbackPolicy?: Partial<PolicyConfig>;\n\n /**\n * Custom blocked response\n */\n blockedResponse?: {\n status?: number;\n message?: string;\n headers?: Record<string, string>;\n };\n\n /**\n * Default redirect URL for redirect actions\n */\n redirectUrl?: string;\n\n /**\n * Callback when policy decision is made\n */\n onPolicyDecision?: (\n request: NextRequest,\n decision: PolicyEvaluationResult,\n context: PolicyEvaluationContext\n ) => void | Promise<void>;\n\n /**\n * Custom response builder for blocked requests\n */\n customBlockedResponse?: (\n request: NextRequest,\n decision: PolicyEvaluationResult\n ) => NextResponse | Promise<NextResponse>;\n\n /**\n * Whether to fail open (allow) on policy evaluation errors\n * Default: true (recommended for production)\n */\n failOpen?: boolean;\n\n /**\n * Enable debug logging\n */\n debug?: boolean;\n}\n\n/**\n * Combined middleware configuration with policy support\n */\nexport interface NextJSPolicyMiddlewareConfig extends PolicyMiddlewareConfig {\n /**\n * Paths to skip (in addition to policy excludedPaths)\n */\n skipPaths?: string[];\n\n /**\n * Only enforce on these paths (overrides policy includedPaths)\n */\n includePaths?: string[];\n}\n\n// ============================================================================\n// Policy Evaluation Helper\n// ============================================================================\n\n/**\n * Create policy evaluation context from detection result and request\n */\nexport function createContextFromDetection(\n detection: DetectionResult,\n request: NextRequest\n): PolicyEvaluationContext {\n return createEvaluationContext({\n agentType: detection.detectedAgent?.type,\n agentName: detection.detectedAgent?.name,\n agentVendor: detection.detectedAgent?.vendor,\n confidence: detection.confidence,\n riskLevel: detection.riskLevel,\n path: request.nextUrl.pathname,\n method: request.method,\n signatureVerified: detection.verificationMethod === 'signature',\n isAuthenticated: false, // TODO: integrate with auth\n userAgent: request.headers.get('user-agent') || undefined,\n });\n}\n\n/**\n * Evaluate policy for a detection result\n */\nexport function evaluatePolicyForDetection(\n detection: DetectionResult,\n request: NextRequest,\n policy: PolicyConfig\n): PolicyEvaluationResult {\n const context = createContextFromDetection(detection, request);\n return evaluatePolicy(policy, context);\n}\n\n// ============================================================================\n// Response Builders\n// ============================================================================\n\n/**\n * Build blocked response based on policy decision\n */\nexport function buildBlockedResponse(\n decision: PolicyEvaluationResult,\n config: PolicyMiddlewareConfig\n): NextResponse {\n const status = config.blockedResponse?.status ?? 403;\n const message = config.blockedResponse?.message ?? decision.message ?? 'Access denied';\n\n const response = NextResponse.json(\n {\n error: message,\n code: 'POLICY_BLOCKED',\n reason: decision.reason,\n ruleId: decision.ruleId,\n matchType: decision.matchType,\n },\n { status }\n );\n\n // Add custom headers\n if (config.blockedResponse?.headers) {\n for (const [key, value] of Object.entries(config.blockedResponse.headers)) {\n response.headers.set(key, value);\n }\n }\n\n // Add AgentShield headers\n response.headers.set('KYA-Action', decision.action);\n response.headers.set('KYA-Reason', decision.reason);\n response.headers.set('KYA-Match-Type', decision.matchType);\n\n return response;\n}\n\n/**\n * Build redirect response based on policy decision\n */\nexport function buildRedirectResponse(\n request: NextRequest,\n decision: PolicyEvaluationResult,\n config: PolicyMiddlewareConfig,\n detection?: { detectedAgent?: { name?: string } }\n): NextResponse {\n const redirectUrl = decision.redirectUrl || config.redirectUrl || '/blocked';\n const url = new URL(redirectUrl, request.url);\n\n // Add query params with policy info\n url.searchParams.set('reason', decision.reason);\n if (decision.ruleId) {\n url.searchParams.set('ruleId', decision.ruleId);\n }\n const agentName = detection?.detectedAgent?.name;\n if (agentName && !url.searchParams.has('agent')) {\n url.searchParams.set('agent', agentName.toLowerCase());\n }\n\n return NextResponse.redirect(url);\n}\n\n/**\n * Build challenge response (placeholder - future implementation)\n */\nexport function buildChallengeResponse(\n request: NextRequest,\n decision: PolicyEvaluationResult,\n config: PolicyMiddlewareConfig,\n detection?: { detectedAgent?: { name?: string } }\n): NextResponse {\n // For now, treat challenge as redirect\n // Future: implement CAPTCHA, proof-of-work, etc.\n return buildRedirectResponse(request, decision, config, detection);\n}\n\n// ============================================================================\n// Policy Handler\n// ============================================================================\n\n/**\n * Handle policy decision and return appropriate response\n */\nexport async function handlePolicyDecision(\n request: NextRequest,\n decision: PolicyEvaluationResult,\n config: PolicyMiddlewareConfig,\n detection?: { detectedAgent?: { name?: string } }\n): Promise<NextResponse | null> {\n switch (decision.action) {\n case ENFORCEMENT_ACTIONS.BLOCK:\n if (config.customBlockedResponse) {\n return await config.customBlockedResponse(request, decision);\n }\n return buildBlockedResponse(decision, config);\n\n case ENFORCEMENT_ACTIONS.REDIRECT:\n return buildRedirectResponse(request, decision, config, detection);\n\n case ENFORCEMENT_ACTIONS.CHALLENGE:\n return buildChallengeResponse(request, decision, config, detection);\n\n case ENFORCEMENT_ACTIONS.LOG:\n // LOG action always logs - that's its purpose\n // (debug flag controls verbose debugging output, not LOG action behavior)\n console.log('[AgentShield] Policy decision (log):', {\n path: request.nextUrl.pathname,\n action: decision.action,\n reason: decision.reason,\n matchType: decision.matchType,\n ruleId: decision.ruleId,\n });\n return null; // Continue to allow\n\n case ENFORCEMENT_ACTIONS.ALLOW:\n default:\n return null; // Continue\n }\n}\n\n// ============================================================================\n// Policy Fetcher Integration\n// ============================================================================\n\n// Cache fetchers by config to avoid recreating them, but also support\n// different configurations (different apiUrl, apiKey, etc.)\nconst fetcherCache = new Map<string, PolicyFetcher>();\n\n/**\n * Generate a cache key for fetcher config.\n * Uses ?? to distinguish between explicit 0 and undefined values.\n */\nfunction getFetcherCacheKey(config: NonNullable<PolicyMiddlewareConfig['fetchPolicy']>): string {\n return `${config.apiUrl ?? 'default'}:${config.apiKey ?? ''}:${config.cacheTtlSeconds ?? 'default'}`;\n}\n\n/**\n * Get or create policy fetcher for the given config\n */\nfunction getPolicyFetcher(config: PolicyMiddlewareConfig['fetchPolicy']): PolicyFetcher {\n if (!config) {\n throw new Error('fetchPolicy config required');\n }\n\n const cacheKey = getFetcherCacheKey(config);\n let fetcher = fetcherCache.get(cacheKey);\n\n if (!fetcher) {\n const fetcherConfig: PolicyFetcherConfig = {\n apiBaseUrl: config.apiUrl || 'https://kya.vouched.id',\n apiKey: config.apiKey,\n cacheTtlSeconds: config.cacheTtlSeconds,\n };\n fetcher = createPolicyFetcher(fetcherConfig);\n fetcherCache.set(cacheKey, fetcher);\n }\n\n return fetcher;\n}\n\n/**\n * Get policy (local, fetched, or fallback)\n */\nexport async function getPolicy(config: PolicyMiddlewareConfig): Promise<PolicyConfig> {\n // Use local policy if provided\n if (config.policy) {\n return PolicyConfigSchema.parse({ ...DEFAULT_POLICY, ...config.policy });\n }\n\n // Fetch from API if configured\n if (config.fetchPolicy) {\n try {\n const fetcher = getPolicyFetcher(config.fetchPolicy);\n return await fetcher.getPolicy(config.fetchPolicy.projectId);\n } catch (error) {\n if (config.debug) {\n console.warn('[AgentShield] Policy fetch failed, using fallback:', error);\n }\n // Return fallback policy\n return PolicyConfigSchema.parse({\n ...DEFAULT_POLICY,\n ...(config.fallbackPolicy || {}),\n });\n }\n }\n\n // No policy configured - return default (allow all)\n return PolicyConfigSchema.parse(DEFAULT_POLICY);\n}\n\n// ============================================================================\n// Standalone Policy Middleware\n// ============================================================================\n\n/**\n * Apply policy to a detection result\n *\n * This function can be used standalone to evaluate policy after detection.\n * Supports extended config with skipPaths and includePaths for path-based filtering.\n *\n * @example\n * ```typescript\n * const result = await detector.analyze(context);\n * const response = await applyPolicy(request, result, {\n * policy: { thresholds: { confidenceThreshold: 80 } },\n * skipPaths: ['/health', '/api/public/*'],\n * includePaths: ['/api/*'],\n * });\n *\n * if (response) {\n * return response; // Policy blocked the request\n * }\n * ```\n */\nexport async function applyPolicy(\n request: NextRequest,\n detection: DetectionResult,\n config: NextJSPolicyMiddlewareConfig\n): Promise<NextResponse | null> {\n try {\n const path = request.nextUrl.pathname;\n\n // Check skipPaths - if path matches any skip pattern, allow through\n if (config.skipPaths?.some((pattern) => matchPath(path, pattern))) {\n return null; // Skip policy enforcement for this path\n }\n\n // Check includePaths - if defined, path must match at least one pattern\n if (config.includePaths && config.includePaths.length > 0) {\n if (!config.includePaths.some((pattern) => matchPath(path, pattern))) {\n return null; // Path not in included paths, skip policy enforcement\n }\n }\n\n // Get policy\n const policy = await getPolicy(config);\n\n // Create context and evaluate\n const context = createContextFromDetection(detection, request);\n const decision = evaluatePolicy(policy, context);\n\n // Call decision callback if provided\n if (config.onPolicyDecision) {\n await config.onPolicyDecision(request, decision, context);\n }\n\n // Handle decision — pass detection through so redirect can append ?agent=\n return await handlePolicyDecision(request, decision, config, detection);\n } catch (error) {\n if (config.debug) {\n console.error('[AgentShield] Policy evaluation error:', error);\n }\n\n if (config.failOpen !== false) {\n return null; // Allow on error\n }\n\n // Fail closed\n return NextResponse.json(\n { error: 'Security check failed', code: 'POLICY_ERROR' },\n { status: 503 }\n );\n }\n}\n"]}
@@ -1,55 +0,0 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
- import { DetectionResult } from '@kya-os/agentshield-shared';
3
-
4
- /**
5
- * Edge-compatible session tracking for AI agents
6
- * Uses cookie-based storage to work in Edge Runtime
7
- */
8
-
9
- interface SessionData {
10
- id: string;
11
- agent: string;
12
- confidence: number;
13
- detectedAt: number;
14
- expires: number;
15
- }
16
- interface SessionTrackingConfig {
17
- enabled: boolean;
18
- cookieName?: string;
19
- cookieMaxAge?: number;
20
- encryptionKey?: string;
21
- }
22
- declare class EdgeSessionTracker {
23
- private readonly config;
24
- constructor(config: SessionTrackingConfig);
25
- /**
26
- * Track a new AI agent session
27
- */
28
- track(_request: NextRequest, response: NextResponse, result: DetectionResult): Promise<NextResponse>;
29
- /**
30
- * Check for existing AI agent session
31
- */
32
- check(request: NextRequest): Promise<SessionData | null>;
33
- /**
34
- * Clear an existing session
35
- */
36
- clear(response: NextResponse): NextResponse;
37
- /**
38
- * Simple encryption using Web Crypto API (Edge-compatible)
39
- */
40
- private encrypt;
41
- /**
42
- * Simple decryption (Edge-compatible)
43
- */
44
- private decrypt;
45
- }
46
- /**
47
- * Stateless session checker for non-Next.js environments (Express, etc.)
48
- * Uses a combination of headers to identify continued sessions
49
- */
50
- declare class StatelessSessionChecker {
51
- static check(headers: Record<string, string>): SessionData | null;
52
- static setHeaders(response: any, session: SessionData): void;
53
- }
54
-
55
- export { EdgeSessionTracker, type SessionData, type SessionTrackingConfig, StatelessSessionChecker };
@@ -1,55 +0,0 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
- import { DetectionResult } from '@kya-os/agentshield-shared';
3
-
4
- /**
5
- * Edge-compatible session tracking for AI agents
6
- * Uses cookie-based storage to work in Edge Runtime
7
- */
8
-
9
- interface SessionData {
10
- id: string;
11
- agent: string;
12
- confidence: number;
13
- detectedAt: number;
14
- expires: number;
15
- }
16
- interface SessionTrackingConfig {
17
- enabled: boolean;
18
- cookieName?: string;
19
- cookieMaxAge?: number;
20
- encryptionKey?: string;
21
- }
22
- declare class EdgeSessionTracker {
23
- private readonly config;
24
- constructor(config: SessionTrackingConfig);
25
- /**
26
- * Track a new AI agent session
27
- */
28
- track(_request: NextRequest, response: NextResponse, result: DetectionResult): Promise<NextResponse>;
29
- /**
30
- * Check for existing AI agent session
31
- */
32
- check(request: NextRequest): Promise<SessionData | null>;
33
- /**
34
- * Clear an existing session
35
- */
36
- clear(response: NextResponse): NextResponse;
37
- /**
38
- * Simple encryption using Web Crypto API (Edge-compatible)
39
- */
40
- private encrypt;
41
- /**
42
- * Simple decryption (Edge-compatible)
43
- */
44
- private decrypt;
45
- }
46
- /**
47
- * Stateless session checker for non-Next.js environments (Express, etc.)
48
- * Uses a combination of headers to identify continued sessions
49
- */
50
- declare class StatelessSessionChecker {
51
- static check(headers: Record<string, string>): SessionData | null;
52
- static setHeaders(response: any, session: SessionData): void;
53
- }
54
-
55
- export { EdgeSessionTracker, type SessionData, type SessionTrackingConfig, StatelessSessionChecker };
@@ -1,170 +0,0 @@
1
- 'use strict';
2
-
3
- var agentshieldShared = require('@kya-os/agentshield-shared');
4
-
5
- // src/session-tracker.ts
6
- var EdgeSessionTracker = class {
7
- config;
8
- constructor(config) {
9
- this.config = {
10
- enabled: config.enabled,
11
- cookieName: config.cookieName || "__agentshield_session",
12
- cookieMaxAge: config.cookieMaxAge || 3600,
13
- // 1 hour default
14
- encryptionKey: config.encryptionKey || process.env.AGENTSHIELD_SECRET || "agentshield-default-key"
15
- };
16
- }
17
- /**
18
- * Track a new AI agent session
19
- */
20
- async track(_request, response, result) {
21
- try {
22
- if (!this.config.enabled || !agentshieldShared.shouldEnforce(result)) {
23
- return response;
24
- }
25
- const sessionData = {
26
- id: crypto.randomUUID(),
27
- agent: result.detectedAgent?.name || "unknown",
28
- confidence: result.confidence,
29
- detectedAt: Date.now(),
30
- expires: Date.now() + this.config.cookieMaxAge * 1e3
31
- };
32
- const encrypted = await this.encrypt(JSON.stringify(sessionData));
33
- response.cookies.set(this.config.cookieName, encrypted, {
34
- httpOnly: true,
35
- secure: process.env.NODE_ENV === "production",
36
- sameSite: "lax",
37
- maxAge: this.config.cookieMaxAge,
38
- path: "/"
39
- });
40
- return response;
41
- } catch (error) {
42
- if (process.env.DEBUG_AGENTSHIELD) {
43
- console.warn("AgentShield: Failed to track session:", error);
44
- }
45
- return response;
46
- }
47
- }
48
- /**
49
- * Check for existing AI agent session
50
- */
51
- async check(request) {
52
- try {
53
- if (!this.config.enabled) {
54
- return null;
55
- }
56
- const cookie = request.cookies.get(this.config.cookieName);
57
- if (!cookie?.value) {
58
- return null;
59
- }
60
- const decrypted = await this.decrypt(cookie.value);
61
- const session = JSON.parse(decrypted);
62
- if (session.expires < Date.now()) {
63
- return null;
64
- }
65
- return session;
66
- } catch (error) {
67
- if (process.env.DEBUG_AGENTSHIELD) {
68
- console.warn("AgentShield: Failed to check session:", error);
69
- }
70
- return null;
71
- }
72
- }
73
- /**
74
- * Clear an existing session
75
- */
76
- clear(response) {
77
- try {
78
- response.cookies.delete(this.config.cookieName);
79
- } catch (error) {
80
- if (process.env.DEBUG_AGENTSHIELD) {
81
- console.warn("AgentShield: Failed to clear session:", error);
82
- }
83
- }
84
- return response;
85
- }
86
- /**
87
- * Simple encryption using Web Crypto API (Edge-compatible)
88
- */
89
- async encrypt(data) {
90
- try {
91
- const key = this.config.encryptionKey;
92
- const encoded = new TextEncoder().encode(data);
93
- const obfuscated = new Uint8Array(encoded.length);
94
- for (let i = 0; i < encoded.length; i++) {
95
- obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);
96
- }
97
- return btoa(Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join(""));
98
- } catch (error) {
99
- return btoa(data);
100
- }
101
- }
102
- /**
103
- * Simple decryption (Edge-compatible)
104
- */
105
- async decrypt(data) {
106
- try {
107
- const key = this.config.encryptionKey;
108
- const decoded = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
109
- const deobfuscated = new Uint8Array(decoded.length);
110
- for (let i = 0; i < decoded.length; i++) {
111
- deobfuscated[i] = (decoded[i] || 0) ^ key.charCodeAt(i % key.length);
112
- }
113
- return new TextDecoder().decode(deobfuscated);
114
- } catch (error) {
115
- return atob(data);
116
- }
117
- }
118
- };
119
- var StatelessSessionChecker = class {
120
- static check(headers) {
121
- try {
122
- const agent = headers["kya-session-agent"];
123
- const confidence = headers["kya-session-confidence"];
124
- const sessionId = headers["kya-session-id"];
125
- if (agent && confidence && sessionId) {
126
- return {
127
- id: sessionId,
128
- agent,
129
- confidence: parseFloat(confidence),
130
- detectedAt: Date.now(),
131
- expires: Date.now() + 36e5
132
- // 1 hour
133
- };
134
- }
135
- const cookieHeader = headers["cookie"];
136
- if (cookieHeader && cookieHeader.includes("__agentshield_session=")) {
137
- const match = cookieHeader.match(/__agentshield_session=([^;]+)/);
138
- if (match && match[1]) {
139
- try {
140
- const decoded = atob(match[1]);
141
- return JSON.parse(decoded);
142
- } catch {
143
- }
144
- }
145
- }
146
- return null;
147
- } catch {
148
- return null;
149
- }
150
- }
151
- static setHeaders(response, session) {
152
- try {
153
- if (response.setHeader) {
154
- response.setHeader("KYA-Session-Agent", session.agent);
155
- response.setHeader("KYA-Session-Confidence", session.confidence.toString());
156
- response.setHeader("KYA-Session-Id", session.id);
157
- } else if (response.headers && response.headers.set) {
158
- response.headers.set("kya-session-agent", session.agent);
159
- response.headers.set("kya-session-confidence", session.confidence.toString());
160
- response.headers.set("kya-session-id", session.id);
161
- }
162
- } catch {
163
- }
164
- }
165
- };
166
-
167
- exports.EdgeSessionTracker = EdgeSessionTracker;
168
- exports.StatelessSessionChecker = StatelessSessionChecker;
169
- //# sourceMappingURL=session-tracker.js.map
170
- //# sourceMappingURL=session-tracker.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/session-tracker.ts"],"names":["shouldEnforce"],"mappings":"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","file":"session-tracker.js","sourcesContent":["/**\n * Edge-compatible session tracking for AI agents\n * Uses cookie-based storage to work in Edge Runtime\n */\n\nimport type { NextRequest, NextResponse } from 'next/server';\nimport type { DetectionResult } from '@kya-os/agentshield-shared';\nimport { shouldEnforce } from '@kya-os/agentshield-shared';\n\nexport interface SessionData {\n id: string;\n agent: string;\n confidence: number;\n detectedAt: number;\n expires: number;\n}\n\nexport interface SessionTrackingConfig {\n enabled: boolean;\n cookieName?: string;\n cookieMaxAge?: number; // in seconds\n encryptionKey?: string;\n}\n\nexport class EdgeSessionTracker {\n private readonly config: Required<SessionTrackingConfig>;\n\n constructor(config: SessionTrackingConfig) {\n this.config = {\n enabled: config.enabled,\n cookieName: config.cookieName || '__agentshield_session',\n cookieMaxAge: config.cookieMaxAge || 3600, // 1 hour default\n encryptionKey:\n config.encryptionKey || process.env.AGENTSHIELD_SECRET || 'agentshield-default-key',\n };\n }\n\n /**\n * Track a new AI agent session\n */\n async track(\n _request: NextRequest,\n response: NextResponse,\n result: DetectionResult\n ): Promise<NextResponse> {\n try {\n if (!this.config.enabled || !shouldEnforce(result)) {\n return response;\n }\n\n const sessionData: SessionData = {\n id: crypto.randomUUID(),\n agent: result.detectedAgent?.name || 'unknown',\n confidence: result.confidence,\n detectedAt: Date.now(),\n expires: Date.now() + this.config.cookieMaxAge * 1000,\n };\n\n // Encrypt session data for security\n const encrypted = await this.encrypt(JSON.stringify(sessionData));\n\n // Set secure httpOnly cookie\n response.cookies.set(this.config.cookieName, encrypted, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n maxAge: this.config.cookieMaxAge,\n path: '/',\n });\n\n return response;\n } catch (error) {\n // Fail gracefully - log error but don't break request\n if (process.env.DEBUG_AGENTSHIELD) {\n console.warn('AgentShield: Failed to track session:', error);\n }\n return response;\n }\n }\n\n /**\n * Check for existing AI agent session\n */\n async check(request: NextRequest): Promise<SessionData | null> {\n try {\n if (!this.config.enabled) {\n return null;\n }\n\n const cookie = request.cookies.get(this.config.cookieName);\n if (!cookie?.value) {\n return null;\n }\n\n // Decrypt and parse session data\n const decrypted = await this.decrypt(cookie.value);\n const session: SessionData = JSON.parse(decrypted);\n\n // Check if session is expired\n if (session.expires < Date.now()) {\n return null;\n }\n\n return session;\n } catch (error) {\n // Fail gracefully - invalid or corrupted session\n if (process.env.DEBUG_AGENTSHIELD) {\n console.warn('AgentShield: Failed to check session:', error);\n }\n return null;\n }\n }\n\n /**\n * Clear an existing session\n */\n clear(response: NextResponse): NextResponse {\n try {\n response.cookies.delete(this.config.cookieName);\n } catch (error) {\n // Fail gracefully\n if (process.env.DEBUG_AGENTSHIELD) {\n console.warn('AgentShield: Failed to clear session:', error);\n }\n }\n return response;\n }\n\n /**\n * Simple encryption using Web Crypto API (Edge-compatible)\n */\n private async encrypt(data: string): Promise<string> {\n try {\n // For Edge Runtime, use simple base64 encoding with obfuscation\n // In production, consider using Web Crypto API subtle.encrypt()\n const key = this.config.encryptionKey;\n const encoded = new TextEncoder().encode(data);\n\n // Simple XOR obfuscation\n const obfuscated = new Uint8Array(encoded.length);\n for (let i = 0; i < encoded.length; i++) {\n obfuscated[i] = (encoded[i] || 0) ^ key.charCodeAt(i % key.length);\n }\n\n // Convert to base64\n return btoa(Array.from(obfuscated, (byte) => String.fromCharCode(byte)).join(''));\n } catch (error) {\n // Fallback to simple base64 if encryption fails\n return btoa(data);\n }\n }\n\n /**\n * Simple decryption (Edge-compatible)\n */\n private async decrypt(data: string): Promise<string> {\n try {\n const key = this.config.encryptionKey;\n const decoded = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));\n\n // Reverse XOR obfuscation\n const deobfuscated = new Uint8Array(decoded.length);\n for (let i = 0; i < decoded.length; i++) {\n deobfuscated[i] = (decoded[i] || 0) ^ key.charCodeAt(i % key.length);\n }\n\n return new TextDecoder().decode(deobfuscated);\n } catch (error) {\n // Fallback to simple base64 if decryption fails\n return atob(data);\n }\n }\n}\n\n/**\n * Stateless session checker for non-Next.js environments (Express, etc.)\n * Uses a combination of headers to identify continued sessions\n */\nexport class StatelessSessionChecker {\n static check(headers: Record<string, string>): SessionData | null {\n try {\n // Check for session headers (set by previous response)\n const agent = headers['kya-session-agent'];\n const confidence = headers['kya-session-confidence'];\n const sessionId = headers['kya-session-id'];\n\n if (agent && confidence && sessionId) {\n return {\n id: sessionId,\n agent,\n confidence: parseFloat(confidence),\n detectedAt: Date.now(),\n expires: Date.now() + 3600000, // 1 hour\n };\n }\n\n // Check for cookie-based session (if cookies are parsed)\n const cookieHeader = headers['cookie'];\n if (cookieHeader && cookieHeader.includes('__agentshield_session=')) {\n // Simple cookie parsing\n const match = cookieHeader.match(/__agentshield_session=([^;]+)/);\n if (match && match[1]) {\n try {\n const decoded = atob(match[1]);\n return JSON.parse(decoded);\n } catch {\n // Invalid session data\n }\n }\n }\n\n return null;\n } catch {\n return null;\n }\n }\n\n static setHeaders(response: any, session: SessionData): void {\n try {\n // Set session headers for stateless tracking\n if (response.setHeader) {\n response.setHeader('KYA-Session-Agent', session.agent);\n response.setHeader('KYA-Session-Confidence', session.confidence.toString());\n response.setHeader('KYA-Session-Id', session.id);\n } else if (response.headers && response.headers.set) {\n response.headers.set('kya-session-agent', session.agent);\n response.headers.set('kya-session-confidence', session.confidence.toString());\n response.headers.set('kya-session-id', session.id);\n }\n } catch {\n // Fail gracefully\n }\n }\n}\n"]}