@kya-os/agentshield-nextjs 0.1.40 → 0.1.42
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/.tsbuildinfo +1 -0
- package/dist/api-client.d.mts +145 -0
- package/dist/api-client.d.ts +145 -0
- package/dist/api-client.js +130 -0
- package/dist/api-client.js.map +1 -0
- package/dist/api-client.mjs +126 -0
- package/dist/api-client.mjs.map +1 -0
- package/dist/api-middleware.d.mts +118 -0
- package/dist/api-middleware.d.ts +118 -0
- package/dist/api-middleware.js +295 -0
- package/dist/api-middleware.js.map +1 -0
- package/dist/api-middleware.mjs +292 -0
- package/dist/api-middleware.mjs.map +1 -0
- package/dist/create-middleware.d.mts +2 -1
- package/dist/create-middleware.d.ts +2 -1
- package/dist/create-middleware.js +474 -200
- package/dist/create-middleware.js.map +1 -1
- package/dist/create-middleware.mjs +454 -200
- package/dist/create-middleware.mjs.map +1 -1
- package/dist/edge/index.d.mts +110 -0
- package/dist/edge/index.d.ts +110 -0
- package/dist/edge/index.js +253 -0
- package/dist/edge/index.js.map +1 -0
- package/dist/edge/index.mjs +251 -0
- package/dist/edge/index.mjs.map +1 -0
- package/dist/edge-detector-wrapper.d.mts +6 -15
- package/dist/edge-detector-wrapper.d.ts +6 -15
- package/dist/edge-detector-wrapper.js +314 -95
- package/dist/edge-detector-wrapper.js.map +1 -1
- package/dist/edge-detector-wrapper.mjs +294 -95
- package/dist/edge-detector-wrapper.mjs.map +1 -1
- package/dist/edge-runtime-loader.d.mts +1 -1
- package/dist/edge-runtime-loader.d.ts +1 -1
- package/dist/edge-runtime-loader.js +10 -25
- package/dist/edge-runtime-loader.js.map +1 -1
- package/dist/edge-runtime-loader.mjs +11 -23
- package/dist/edge-runtime-loader.mjs.map +1 -1
- package/dist/edge-wasm-middleware.js +2 -1
- package/dist/edge-wasm-middleware.js.map +1 -1
- package/dist/edge-wasm-middleware.mjs +2 -1
- package/dist/edge-wasm-middleware.mjs.map +1 -1
- package/dist/enhanced-middleware.d.mts +153 -0
- package/dist/enhanced-middleware.d.ts +153 -0
- package/dist/enhanced-middleware.js +1074 -0
- package/dist/enhanced-middleware.js.map +1 -0
- package/dist/enhanced-middleware.mjs +1072 -0
- package/dist/enhanced-middleware.mjs.map +1 -0
- package/dist/index.d.mts +8 -153
- package/dist/index.d.ts +8 -153
- package/dist/index.js +1390 -680
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1370 -685
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.d.mts +2 -1
- package/dist/middleware.d.ts +2 -1
- package/dist/middleware.js +474 -200
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +454 -200
- package/dist/middleware.mjs.map +1 -1
- package/dist/session-tracker.d.mts +1 -1
- package/dist/session-tracker.d.ts +1 -1
- package/dist/session-tracker.js.map +1 -1
- package/dist/session-tracker.mjs.map +1 -1
- package/dist/signature-verifier.d.mts +1 -0
- package/dist/signature-verifier.d.ts +1 -0
- package/dist/signature-verifier.js +204 -44
- package/dist/signature-verifier.js.map +1 -1
- package/dist/signature-verifier.mjs +184 -44
- package/dist/signature-verifier.mjs.map +1 -1
- package/dist/{types-BJTEUa4T.d.mts → types-DVmy9NE3.d.mts} +19 -2
- package/dist/{types-BJTEUa4T.d.ts → types-DVmy9NE3.d.ts} +19 -2
- package/dist/wasm-middleware.js +15 -6
- package/dist/wasm-middleware.js.map +1 -1
- package/dist/wasm-middleware.mjs +15 -6
- package/dist/wasm-middleware.mjs.map +1 -1
- package/package.json +43 -21
- package/wasm/agentshield_wasm.js +209 -152
- package/wasm/agentshield_wasm_bg.wasm +0 -0
- package/wasm/package.json +30 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/api-client.ts
|
|
4
|
+
var DEFAULT_BASE_URL = "https://api.agentshield.ai";
|
|
5
|
+
var DEFAULT_TIMEOUT = 5e3;
|
|
6
|
+
var AgentShieldClient = class {
|
|
7
|
+
apiKey;
|
|
8
|
+
baseUrl;
|
|
9
|
+
timeout;
|
|
10
|
+
debug;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
if (!config.apiKey) {
|
|
13
|
+
throw new Error("AgentShield API key is required");
|
|
14
|
+
}
|
|
15
|
+
this.apiKey = config.apiKey;
|
|
16
|
+
this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
17
|
+
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
18
|
+
this.debug = config.debug || false;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Call the enforce API to check if a request should be allowed
|
|
22
|
+
*/
|
|
23
|
+
async enforce(input) {
|
|
24
|
+
const startTime = Date.now();
|
|
25
|
+
try {
|
|
26
|
+
const controller = new AbortController();
|
|
27
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(`${this.baseUrl}/api/v1/enforce`, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: {
|
|
32
|
+
"Content-Type": "application/json",
|
|
33
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
34
|
+
"X-Request-ID": input.requestId || crypto.randomUUID()
|
|
35
|
+
},
|
|
36
|
+
body: JSON.stringify(input),
|
|
37
|
+
signal: controller.signal
|
|
38
|
+
});
|
|
39
|
+
clearTimeout(timeoutId);
|
|
40
|
+
const data = await response.json();
|
|
41
|
+
if (this.debug) {
|
|
42
|
+
console.log("[AgentShield] Enforce response:", {
|
|
43
|
+
status: response.status,
|
|
44
|
+
action: data.data?.decision.action,
|
|
45
|
+
processingTimeMs: Date.now() - startTime
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
error: {
|
|
52
|
+
code: `HTTP_${response.status}`,
|
|
53
|
+
message: data.error?.message || `HTTP error: ${response.status}`
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return data;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
clearTimeout(timeoutId);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
64
|
+
if (this.debug) {
|
|
65
|
+
console.warn("[AgentShield] Request timed out");
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
success: false,
|
|
69
|
+
error: {
|
|
70
|
+
code: "TIMEOUT",
|
|
71
|
+
message: `Request timed out after ${this.timeout}ms`
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (this.debug) {
|
|
76
|
+
console.error("[AgentShield] Request failed:", error);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: {
|
|
81
|
+
code: "NETWORK_ERROR",
|
|
82
|
+
message: error instanceof Error ? error.message : "Network request failed"
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Quick check - returns just the action without full response parsing
|
|
89
|
+
* Useful for very fast middleware that just needs allow/block
|
|
90
|
+
*/
|
|
91
|
+
async quickCheck(input) {
|
|
92
|
+
const result = await this.enforce(input);
|
|
93
|
+
if (!result.success || !result.data) {
|
|
94
|
+
return {
|
|
95
|
+
action: "allow",
|
|
96
|
+
error: result.error?.message
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
action: result.data.decision.action
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var clientInstance = null;
|
|
105
|
+
function getAgentShieldClient(config) {
|
|
106
|
+
if (!clientInstance) {
|
|
107
|
+
const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;
|
|
108
|
+
if (!apiKey) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config."
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
clientInstance = new AgentShieldClient({
|
|
114
|
+
apiKey,
|
|
115
|
+
baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,
|
|
116
|
+
timeout: config?.timeout,
|
|
117
|
+
debug: config?.debug || process.env.AGENTSHIELD_DEBUG === "true"
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return clientInstance;
|
|
121
|
+
}
|
|
122
|
+
function resetAgentShieldClient() {
|
|
123
|
+
clientInstance = null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
exports.AgentShieldClient = AgentShieldClient;
|
|
127
|
+
exports.getAgentShieldClient = getAgentShieldClient;
|
|
128
|
+
exports.resetAgentShieldClient = resetAgentShieldClient;
|
|
129
|
+
//# sourceMappingURL=api-client.js.map
|
|
130
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-client.ts"],"names":[],"mappings":"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","file":"api-client.js","sourcesContent":["/**\n * AgentShield API Client\n *\n * Lightweight client for calling the AgentShield enforce API from middleware.\n * Designed for Edge Runtime compatibility (no Node.js-specific APIs).\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * API client configuration\n */\nexport interface AgentShieldClientConfig {\n /** API key for authentication */\n apiKey: string;\n /** API base URL (defaults to production) */\n baseUrl?: string;\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n/**\n * Enforcement action\n */\nexport type EnforcementAction = 'allow' | 'block' | 'redirect' | 'challenge' | 'log';\n\n/**\n * Enforcement decision from the API\n */\nexport interface EnforcementDecision {\n action: EnforcementAction;\n reason: string;\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n redirectUrl?: string;\n message?: string;\n metadata?: {\n policyVersion?: string;\n signatureVerified?: boolean;\n denyListMatch?: {\n clientDid?: string;\n agentDid?: string;\n clientName?: string;\n reason?: string;\n };\n };\n}\n\n/**\n * Detection result (optional in response)\n */\nexport interface DetectionResult {\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n verificationMethod?: string;\n reasons?: string[];\n}\n\n/**\n * Enforce API response\n */\nexport interface EnforceResponse {\n success: boolean;\n data?: {\n decision: EnforcementDecision;\n processingTimeMs: number;\n requestId: string;\n detection?: DetectionResult;\n };\n error?: {\n code: string;\n message: string;\n };\n}\n\n/**\n * Request input for enforce API\n */\nexport interface EnforceInput {\n /** HTTP headers from the incoming request */\n headers?: Record<string, string>;\n /** User-Agent header */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Request path */\n path?: string;\n /** Request URL */\n url?: string;\n /** HTTP method */\n method?: string;\n /** Request ID for tracing */\n requestId?: string;\n /** Options */\n options?: {\n /** Include full detection result */\n includeDetectionResult?: boolean;\n /** Cache TTL override */\n cacheTTL?: number;\n };\n}\n\n// ============================================================================\n// Client Implementation\n// ============================================================================\n\nconst DEFAULT_BASE_URL = 'https://api.agentshield.ai';\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * AgentShield API Client\n *\n * @example\n * ```typescript\n * const client = new AgentShieldClient({\n * apiKey: process.env.AGENTSHIELD_API_KEY!,\n * });\n *\n * const result = await client.enforce({\n * headers: Object.fromEntries(request.headers),\n * path: request.nextUrl.pathname,\n * method: request.method,\n * });\n *\n * if (result.decision.action === 'block') {\n * return new Response('Access denied', { status: 403 });\n * }\n * ```\n */\nexport class AgentShieldClient {\n private apiKey: string;\n private baseUrl: string;\n private timeout: number;\n private debug: boolean;\n\n constructor(config: AgentShieldClientConfig) {\n if (!config.apiKey) {\n throw new Error('AgentShield API key is required');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n this.debug = config.debug || false;\n }\n\n /**\n * Call the enforce API to check if a request should be allowed\n */\n async enforce(input: EnforceInput): Promise<EnforceResponse> {\n const startTime = Date.now();\n\n try {\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(`${this.baseUrl}/api/v1/enforce`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'X-Request-ID': input.requestId || crypto.randomUUID(),\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = (await response.json()) as EnforceResponse;\n\n if (this.debug) {\n console.log('[AgentShield] Enforce response:', {\n status: response.status,\n action: data.data?.decision.action,\n processingTimeMs: Date.now() - startTime,\n });\n }\n\n // Handle non-2xx responses\n if (!response.ok) {\n return {\n success: false,\n error: {\n code: `HTTP_${response.status}`,\n message: data.error?.message || `HTTP error: ${response.status}`,\n },\n };\n }\n\n return data;\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Handle timeout\n if (error instanceof Error && error.name === 'AbortError') {\n if (this.debug) {\n console.warn('[AgentShield] Request timed out');\n }\n return {\n success: false,\n error: {\n code: 'TIMEOUT',\n message: `Request timed out after ${this.timeout}ms`,\n },\n };\n }\n\n // Handle network errors\n if (this.debug) {\n console.error('[AgentShield] Request failed:', error);\n }\n\n return {\n success: false,\n error: {\n code: 'NETWORK_ERROR',\n message: error instanceof Error ? error.message : 'Network request failed',\n },\n };\n }\n }\n\n /**\n * Quick check - returns just the action without full response parsing\n * Useful for very fast middleware that just needs allow/block\n */\n async quickCheck(input: EnforceInput): Promise<{\n action: EnforcementAction;\n error?: string;\n }> {\n const result = await this.enforce(input);\n\n if (!result.success || !result.data) {\n // On error, default to allow (fail-open)\n return {\n action: 'allow',\n error: result.error?.message,\n };\n }\n\n return {\n action: result.data.decision.action,\n };\n }\n}\n\n/**\n * Create a singleton client instance\n *\n * @example\n * ```typescript\n * // In middleware.ts\n * import { getAgentShieldClient } from '@kya-os/agentshield-nextjs';\n *\n * const client = getAgentShieldClient();\n * ```\n */\nlet clientInstance: AgentShieldClient | null = null;\n\nexport function getAgentShieldClient(config?: Partial<AgentShieldClientConfig>): AgentShieldClient {\n if (!clientInstance) {\n const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;\n\n if (!apiKey) {\n throw new Error(\n 'AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config.'\n );\n }\n\n clientInstance = new AgentShieldClient({\n apiKey,\n baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,\n timeout: config?.timeout,\n debug: config?.debug || process.env.AGENTSHIELD_DEBUG === 'true',\n });\n }\n\n return clientInstance;\n}\n\n/**\n * Reset the singleton client (useful for testing)\n */\nexport function resetAgentShieldClient(): void {\n clientInstance = null;\n}\n"]}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// src/api-client.ts
|
|
2
|
+
var DEFAULT_BASE_URL = "https://api.agentshield.ai";
|
|
3
|
+
var DEFAULT_TIMEOUT = 5e3;
|
|
4
|
+
var AgentShieldClient = class {
|
|
5
|
+
apiKey;
|
|
6
|
+
baseUrl;
|
|
7
|
+
timeout;
|
|
8
|
+
debug;
|
|
9
|
+
constructor(config) {
|
|
10
|
+
if (!config.apiKey) {
|
|
11
|
+
throw new Error("AgentShield API key is required");
|
|
12
|
+
}
|
|
13
|
+
this.apiKey = config.apiKey;
|
|
14
|
+
this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
15
|
+
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
16
|
+
this.debug = config.debug || false;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Call the enforce API to check if a request should be allowed
|
|
20
|
+
*/
|
|
21
|
+
async enforce(input) {
|
|
22
|
+
const startTime = Date.now();
|
|
23
|
+
try {
|
|
24
|
+
const controller = new AbortController();
|
|
25
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch(`${this.baseUrl}/api/v1/enforce`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
32
|
+
"X-Request-ID": input.requestId || crypto.randomUUID()
|
|
33
|
+
},
|
|
34
|
+
body: JSON.stringify(input),
|
|
35
|
+
signal: controller.signal
|
|
36
|
+
});
|
|
37
|
+
clearTimeout(timeoutId);
|
|
38
|
+
const data = await response.json();
|
|
39
|
+
if (this.debug) {
|
|
40
|
+
console.log("[AgentShield] Enforce response:", {
|
|
41
|
+
status: response.status,
|
|
42
|
+
action: data.data?.decision.action,
|
|
43
|
+
processingTimeMs: Date.now() - startTime
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: {
|
|
50
|
+
code: `HTTP_${response.status}`,
|
|
51
|
+
message: data.error?.message || `HTTP error: ${response.status}`
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return data;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
clearTimeout(timeoutId);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
62
|
+
if (this.debug) {
|
|
63
|
+
console.warn("[AgentShield] Request timed out");
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
error: {
|
|
68
|
+
code: "TIMEOUT",
|
|
69
|
+
message: `Request timed out after ${this.timeout}ms`
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (this.debug) {
|
|
74
|
+
console.error("[AgentShield] Request failed:", error);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
error: {
|
|
79
|
+
code: "NETWORK_ERROR",
|
|
80
|
+
message: error instanceof Error ? error.message : "Network request failed"
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Quick check - returns just the action without full response parsing
|
|
87
|
+
* Useful for very fast middleware that just needs allow/block
|
|
88
|
+
*/
|
|
89
|
+
async quickCheck(input) {
|
|
90
|
+
const result = await this.enforce(input);
|
|
91
|
+
if (!result.success || !result.data) {
|
|
92
|
+
return {
|
|
93
|
+
action: "allow",
|
|
94
|
+
error: result.error?.message
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
action: result.data.decision.action
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var clientInstance = null;
|
|
103
|
+
function getAgentShieldClient(config) {
|
|
104
|
+
if (!clientInstance) {
|
|
105
|
+
const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;
|
|
106
|
+
if (!apiKey) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
"AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config."
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
clientInstance = new AgentShieldClient({
|
|
112
|
+
apiKey,
|
|
113
|
+
baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,
|
|
114
|
+
timeout: config?.timeout,
|
|
115
|
+
debug: config?.debug || process.env.AGENTSHIELD_DEBUG === "true"
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return clientInstance;
|
|
119
|
+
}
|
|
120
|
+
function resetAgentShieldClient() {
|
|
121
|
+
clientInstance = null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export { AgentShieldClient, getAgentShieldClient, resetAgentShieldClient };
|
|
125
|
+
//# sourceMappingURL=api-client.mjs.map
|
|
126
|
+
//# sourceMappingURL=api-client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-client.ts"],"names":[],"mappings":"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","file":"api-client.mjs","sourcesContent":["/**\n * AgentShield API Client\n *\n * Lightweight client for calling the AgentShield enforce API from middleware.\n * Designed for Edge Runtime compatibility (no Node.js-specific APIs).\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * API client configuration\n */\nexport interface AgentShieldClientConfig {\n /** API key for authentication */\n apiKey: string;\n /** API base URL (defaults to production) */\n baseUrl?: string;\n /** Request timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n/**\n * Enforcement action\n */\nexport type EnforcementAction = 'allow' | 'block' | 'redirect' | 'challenge' | 'log';\n\n/**\n * Enforcement decision from the API\n */\nexport interface EnforcementDecision {\n action: EnforcementAction;\n reason: string;\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n redirectUrl?: string;\n message?: string;\n metadata?: {\n policyVersion?: string;\n signatureVerified?: boolean;\n denyListMatch?: {\n clientDid?: string;\n agentDid?: string;\n clientName?: string;\n reason?: string;\n };\n };\n}\n\n/**\n * Detection result (optional in response)\n */\nexport interface DetectionResult {\n isAgent: boolean;\n confidence: number;\n agentName?: string;\n agentType?: string;\n verificationMethod?: string;\n reasons?: string[];\n}\n\n/**\n * Enforce API response\n */\nexport interface EnforceResponse {\n success: boolean;\n data?: {\n decision: EnforcementDecision;\n processingTimeMs: number;\n requestId: string;\n detection?: DetectionResult;\n };\n error?: {\n code: string;\n message: string;\n };\n}\n\n/**\n * Request input for enforce API\n */\nexport interface EnforceInput {\n /** HTTP headers from the incoming request */\n headers?: Record<string, string>;\n /** User-Agent header */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Request path */\n path?: string;\n /** Request URL */\n url?: string;\n /** HTTP method */\n method?: string;\n /** Request ID for tracing */\n requestId?: string;\n /** Options */\n options?: {\n /** Include full detection result */\n includeDetectionResult?: boolean;\n /** Cache TTL override */\n cacheTTL?: number;\n };\n}\n\n// ============================================================================\n// Client Implementation\n// ============================================================================\n\nconst DEFAULT_BASE_URL = 'https://api.agentshield.ai';\nconst DEFAULT_TIMEOUT = 5000;\n\n/**\n * AgentShield API Client\n *\n * @example\n * ```typescript\n * const client = new AgentShieldClient({\n * apiKey: process.env.AGENTSHIELD_API_KEY!,\n * });\n *\n * const result = await client.enforce({\n * headers: Object.fromEntries(request.headers),\n * path: request.nextUrl.pathname,\n * method: request.method,\n * });\n *\n * if (result.decision.action === 'block') {\n * return new Response('Access denied', { status: 403 });\n * }\n * ```\n */\nexport class AgentShieldClient {\n private apiKey: string;\n private baseUrl: string;\n private timeout: number;\n private debug: boolean;\n\n constructor(config: AgentShieldClientConfig) {\n if (!config.apiKey) {\n throw new Error('AgentShield API key is required');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n this.debug = config.debug || false;\n }\n\n /**\n * Call the enforce API to check if a request should be allowed\n */\n async enforce(input: EnforceInput): Promise<EnforceResponse> {\n const startTime = Date.now();\n\n try {\n // Create abort controller for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(`${this.baseUrl}/api/v1/enforce`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'X-Request-ID': input.requestId || crypto.randomUUID(),\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = (await response.json()) as EnforceResponse;\n\n if (this.debug) {\n console.log('[AgentShield] Enforce response:', {\n status: response.status,\n action: data.data?.decision.action,\n processingTimeMs: Date.now() - startTime,\n });\n }\n\n // Handle non-2xx responses\n if (!response.ok) {\n return {\n success: false,\n error: {\n code: `HTTP_${response.status}`,\n message: data.error?.message || `HTTP error: ${response.status}`,\n },\n };\n }\n\n return data;\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n } catch (error) {\n // Handle timeout\n if (error instanceof Error && error.name === 'AbortError') {\n if (this.debug) {\n console.warn('[AgentShield] Request timed out');\n }\n return {\n success: false,\n error: {\n code: 'TIMEOUT',\n message: `Request timed out after ${this.timeout}ms`,\n },\n };\n }\n\n // Handle network errors\n if (this.debug) {\n console.error('[AgentShield] Request failed:', error);\n }\n\n return {\n success: false,\n error: {\n code: 'NETWORK_ERROR',\n message: error instanceof Error ? error.message : 'Network request failed',\n },\n };\n }\n }\n\n /**\n * Quick check - returns just the action without full response parsing\n * Useful for very fast middleware that just needs allow/block\n */\n async quickCheck(input: EnforceInput): Promise<{\n action: EnforcementAction;\n error?: string;\n }> {\n const result = await this.enforce(input);\n\n if (!result.success || !result.data) {\n // On error, default to allow (fail-open)\n return {\n action: 'allow',\n error: result.error?.message,\n };\n }\n\n return {\n action: result.data.decision.action,\n };\n }\n}\n\n/**\n * Create a singleton client instance\n *\n * @example\n * ```typescript\n * // In middleware.ts\n * import { getAgentShieldClient } from '@kya-os/agentshield-nextjs';\n *\n * const client = getAgentShieldClient();\n * ```\n */\nlet clientInstance: AgentShieldClient | null = null;\n\nexport function getAgentShieldClient(config?: Partial<AgentShieldClientConfig>): AgentShieldClient {\n if (!clientInstance) {\n const apiKey = config?.apiKey || process.env.AGENTSHIELD_API_KEY;\n\n if (!apiKey) {\n throw new Error(\n 'AgentShield API key is required. Set AGENTSHIELD_API_KEY environment variable or pass apiKey in config.'\n );\n }\n\n clientInstance = new AgentShieldClient({\n apiKey,\n baseUrl: config?.baseUrl || process.env.AGENTSHIELD_API_URL,\n timeout: config?.timeout,\n debug: config?.debug || process.env.AGENTSHIELD_DEBUG === 'true',\n });\n }\n\n return clientInstance;\n}\n\n/**\n * Reset the singleton client (useful for testing)\n */\nexport function resetAgentShieldClient(): void {\n clientInstance = null;\n}\n"]}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { EnforcementDecision } from './api-client.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* API-based AgentShield Middleware for Next.js
|
|
6
|
+
*
|
|
7
|
+
* This middleware uses the AgentShield API for detection and enforcement,
|
|
8
|
+
* instead of running detection locally. This approach:
|
|
9
|
+
*
|
|
10
|
+
* 1. Works reliably in Edge Runtime (no WASM loading issues)
|
|
11
|
+
* 2. Ensures consistent detection across all platforms
|
|
12
|
+
* 3. Applies centralized policies from the dashboard
|
|
13
|
+
* 4. Supports deny lists, thresholds, and path rules
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // middleware.ts
|
|
18
|
+
* import { withAgentShield } from '@kya-os/agentshield-nextjs/api-middleware';
|
|
19
|
+
*
|
|
20
|
+
* export default withAgentShield({
|
|
21
|
+
* apiKey: process.env.AGENTSHIELD_API_KEY!,
|
|
22
|
+
* // Optional overrides:
|
|
23
|
+
* onBlock: 'redirect', // 'block' | 'redirect' | 'challenge'
|
|
24
|
+
* redirectUrl: '/blocked',
|
|
25
|
+
* skipPaths: ['/api/health', '/_next/*'],
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* export const config = {
|
|
29
|
+
* matcher: ['/((?!_next/static|favicon.ico).*)'],
|
|
30
|
+
* };
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Middleware configuration
|
|
36
|
+
*/
|
|
37
|
+
interface AgentShieldMiddlewareConfig {
|
|
38
|
+
/** API key (or use AGENTSHIELD_API_KEY env var) */
|
|
39
|
+
apiKey?: string;
|
|
40
|
+
/** API base URL (defaults to production) */
|
|
41
|
+
apiUrl?: string;
|
|
42
|
+
/** Request timeout in ms (default: 5000) */
|
|
43
|
+
timeout?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Action to take when an agent should be blocked
|
|
46
|
+
* - 'block': Return 403 response
|
|
47
|
+
* - 'redirect': Redirect to redirectUrl
|
|
48
|
+
* - 'challenge': Show a challenge page (future)
|
|
49
|
+
* Default: uses policy from dashboard
|
|
50
|
+
*/
|
|
51
|
+
onBlock?: 'block' | 'redirect' | 'challenge';
|
|
52
|
+
/**
|
|
53
|
+
* URL to redirect to when blocking (if onBlock is 'redirect')
|
|
54
|
+
* Default: uses redirectUrl from dashboard policy
|
|
55
|
+
*/
|
|
56
|
+
redirectUrl?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Custom blocked response
|
|
59
|
+
*/
|
|
60
|
+
blockedResponse?: {
|
|
61
|
+
status?: number;
|
|
62
|
+
message?: string;
|
|
63
|
+
headers?: Record<string, string>;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Paths to skip (in addition to dashboard policy)
|
|
67
|
+
* Supports glob patterns: '/api/*', '/_next/*'
|
|
68
|
+
*/
|
|
69
|
+
skipPaths?: string[];
|
|
70
|
+
/**
|
|
71
|
+
* Only enforce on these paths (overrides dashboard policy)
|
|
72
|
+
*/
|
|
73
|
+
includePaths?: string[];
|
|
74
|
+
/**
|
|
75
|
+
* Callback when an agent is detected
|
|
76
|
+
*/
|
|
77
|
+
onAgentDetected?: (request: NextRequest, decision: EnforcementDecision) => void | Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Callback to customize the blocked response
|
|
80
|
+
*/
|
|
81
|
+
customBlockedResponse?: (request: NextRequest, decision: EnforcementDecision) => NextResponse | Promise<NextResponse>;
|
|
82
|
+
/**
|
|
83
|
+
* Whether to fail open (allow) on API errors
|
|
84
|
+
* Default: true (recommended for production)
|
|
85
|
+
*/
|
|
86
|
+
failOpen?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Enable debug logging
|
|
89
|
+
*/
|
|
90
|
+
debug?: boolean;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Create AgentShield middleware with API-based detection
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* // middleware.ts
|
|
98
|
+
* import { withAgentShield } from '@kya-os/agentshield-nextjs/api-middleware';
|
|
99
|
+
*
|
|
100
|
+
* export default withAgentShield({
|
|
101
|
+
* onBlock: 'block',
|
|
102
|
+
* skipPaths: ['/api/health'],
|
|
103
|
+
* });
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
declare function withAgentShield(config?: AgentShieldMiddlewareConfig): (request: NextRequest) => Promise<NextResponse>;
|
|
107
|
+
/**
|
|
108
|
+
* Convenience export for simple setup
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* // middleware.ts
|
|
113
|
+
* export { agentShieldMiddleware as default } from '@kya-os/agentshield-nextjs/api-middleware';
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
declare const agentShieldMiddleware: (request: NextRequest) => Promise<NextResponse>;
|
|
117
|
+
|
|
118
|
+
export { type AgentShieldMiddlewareConfig, agentShieldMiddleware, withAgentShield };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { EnforcementDecision } from './api-client.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* API-based AgentShield Middleware for Next.js
|
|
6
|
+
*
|
|
7
|
+
* This middleware uses the AgentShield API for detection and enforcement,
|
|
8
|
+
* instead of running detection locally. This approach:
|
|
9
|
+
*
|
|
10
|
+
* 1. Works reliably in Edge Runtime (no WASM loading issues)
|
|
11
|
+
* 2. Ensures consistent detection across all platforms
|
|
12
|
+
* 3. Applies centralized policies from the dashboard
|
|
13
|
+
* 4. Supports deny lists, thresholds, and path rules
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // middleware.ts
|
|
18
|
+
* import { withAgentShield } from '@kya-os/agentshield-nextjs/api-middleware';
|
|
19
|
+
*
|
|
20
|
+
* export default withAgentShield({
|
|
21
|
+
* apiKey: process.env.AGENTSHIELD_API_KEY!,
|
|
22
|
+
* // Optional overrides:
|
|
23
|
+
* onBlock: 'redirect', // 'block' | 'redirect' | 'challenge'
|
|
24
|
+
* redirectUrl: '/blocked',
|
|
25
|
+
* skipPaths: ['/api/health', '/_next/*'],
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* export const config = {
|
|
29
|
+
* matcher: ['/((?!_next/static|favicon.ico).*)'],
|
|
30
|
+
* };
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Middleware configuration
|
|
36
|
+
*/
|
|
37
|
+
interface AgentShieldMiddlewareConfig {
|
|
38
|
+
/** API key (or use AGENTSHIELD_API_KEY env var) */
|
|
39
|
+
apiKey?: string;
|
|
40
|
+
/** API base URL (defaults to production) */
|
|
41
|
+
apiUrl?: string;
|
|
42
|
+
/** Request timeout in ms (default: 5000) */
|
|
43
|
+
timeout?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Action to take when an agent should be blocked
|
|
46
|
+
* - 'block': Return 403 response
|
|
47
|
+
* - 'redirect': Redirect to redirectUrl
|
|
48
|
+
* - 'challenge': Show a challenge page (future)
|
|
49
|
+
* Default: uses policy from dashboard
|
|
50
|
+
*/
|
|
51
|
+
onBlock?: 'block' | 'redirect' | 'challenge';
|
|
52
|
+
/**
|
|
53
|
+
* URL to redirect to when blocking (if onBlock is 'redirect')
|
|
54
|
+
* Default: uses redirectUrl from dashboard policy
|
|
55
|
+
*/
|
|
56
|
+
redirectUrl?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Custom blocked response
|
|
59
|
+
*/
|
|
60
|
+
blockedResponse?: {
|
|
61
|
+
status?: number;
|
|
62
|
+
message?: string;
|
|
63
|
+
headers?: Record<string, string>;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Paths to skip (in addition to dashboard policy)
|
|
67
|
+
* Supports glob patterns: '/api/*', '/_next/*'
|
|
68
|
+
*/
|
|
69
|
+
skipPaths?: string[];
|
|
70
|
+
/**
|
|
71
|
+
* Only enforce on these paths (overrides dashboard policy)
|
|
72
|
+
*/
|
|
73
|
+
includePaths?: string[];
|
|
74
|
+
/**
|
|
75
|
+
* Callback when an agent is detected
|
|
76
|
+
*/
|
|
77
|
+
onAgentDetected?: (request: NextRequest, decision: EnforcementDecision) => void | Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Callback to customize the blocked response
|
|
80
|
+
*/
|
|
81
|
+
customBlockedResponse?: (request: NextRequest, decision: EnforcementDecision) => NextResponse | Promise<NextResponse>;
|
|
82
|
+
/**
|
|
83
|
+
* Whether to fail open (allow) on API errors
|
|
84
|
+
* Default: true (recommended for production)
|
|
85
|
+
*/
|
|
86
|
+
failOpen?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Enable debug logging
|
|
89
|
+
*/
|
|
90
|
+
debug?: boolean;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Create AgentShield middleware with API-based detection
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* // middleware.ts
|
|
98
|
+
* import { withAgentShield } from '@kya-os/agentshield-nextjs/api-middleware';
|
|
99
|
+
*
|
|
100
|
+
* export default withAgentShield({
|
|
101
|
+
* onBlock: 'block',
|
|
102
|
+
* skipPaths: ['/api/health'],
|
|
103
|
+
* });
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
declare function withAgentShield(config?: AgentShieldMiddlewareConfig): (request: NextRequest) => Promise<NextResponse>;
|
|
107
|
+
/**
|
|
108
|
+
* Convenience export for simple setup
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* // middleware.ts
|
|
113
|
+
* export { agentShieldMiddleware as default } from '@kya-os/agentshield-nextjs/api-middleware';
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
declare const agentShieldMiddleware: (request: NextRequest) => Promise<NextResponse>;
|
|
117
|
+
|
|
118
|
+
export { type AgentShieldMiddlewareConfig, agentShieldMiddleware, withAgentShield };
|