@kya-os/checkpoint-nextjs 1.0.1 → 1.1.1
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/CHANGELOG.md +105 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/edge-runtime-loader.d.mts +44 -8
- package/dist/edge-runtime-loader.d.ts +44 -8
- package/dist/edge-runtime-loader.js +67 -46
- package/dist/edge-runtime-loader.mjs +64 -47
- package/dist/index.js +24 -9
- package/dist/index.mjs +24 -9
- package/dist/middleware-edge.js +17 -8
- package/dist/middleware-edge.mjs +17 -8
- package/dist/middleware-node.d.mts +36 -0
- package/dist/middleware-node.d.ts +36 -0
- package/dist/middleware-node.js +17 -8
- package/dist/middleware-node.mjs +17 -8
- package/dist/policy.js +7 -1
- package/dist/policy.mjs +7 -1
- package/dist/translate.d.mts +36 -9
- package/dist/translate.d.ts +36 -9
- package/dist/translate.js +13 -6
- package/dist/translate.mjs +13 -6
- package/dist/wasm-middleware.d.mts +29 -10
- package/dist/wasm-middleware.d.ts +29 -10
- package/package.json +3 -3
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
import { NextRequest } from 'next/server';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Edge Runtime Compatible WASM Loader for
|
|
4
|
+
* Edge Runtime Compatible WASM Loader for Checkpoint
|
|
5
5
|
*
|
|
6
6
|
* This module provides a pre-built solution for loading WASM in Edge Runtime.
|
|
7
7
|
* It requires the WASM file to be manually placed in the project.
|
|
8
|
+
*
|
|
9
|
+
* ## SSOT for pattern detection
|
|
10
|
+
*
|
|
11
|
+
* The fallback `patternDetection` path imports BOTH pattern arrays
|
|
12
|
+
* from `@kya-os/checkpoint-shared/constants/agents`:
|
|
13
|
+
*
|
|
14
|
+
* - `KNOWN_AGENT_PATTERNS` — strict SSOT carved for the server-side
|
|
15
|
+
* classifier (per-agent rows with category + isLegitimate fields).
|
|
16
|
+
* - `INTERACTIVE_AGENT_PATTERNS` — supplement holding interactive-
|
|
17
|
+
* session tokens, generic vendor fallbacks, and the GPT-Crawler
|
|
18
|
+
* entry that lives in seed-agent-types-data.ts (NOT in the strict
|
|
19
|
+
* SSOT).
|
|
20
|
+
*
|
|
21
|
+
* Pre-#2599 this file had a 15-pattern inline subset that diverged
|
|
22
|
+
* from the shared SSOT — the you.com regex was tightened in agents.ts
|
|
23
|
+
* but reverted here. SSOT-Fallback-Drift-1 (this PR) collapsed the
|
|
24
|
+
* inline duplication and moved the supplement into checkpoint-shared
|
|
25
|
+
* so both layers reference the same source. EPIC #2573 (PDM-2) is the
|
|
26
|
+
* future unified-SSOT consolidation that folds the two arrays into
|
|
27
|
+
* one canonical table.
|
|
28
|
+
*
|
|
29
|
+
* ## Naming
|
|
30
|
+
*
|
|
31
|
+
* Old `AgentShield`-prefixed exports are kept as `@deprecated` aliases
|
|
32
|
+
* for one release (`createEdgeAgentShield`, `EdgeRuntimeAgentShield`,
|
|
33
|
+
* `AgentShieldConfig`, `getDefaultAgentShield`). New code should import
|
|
34
|
+
* the `Checkpoint`-prefixed names directly.
|
|
8
35
|
*/
|
|
9
36
|
|
|
10
37
|
interface WasmModule {
|
|
@@ -19,7 +46,7 @@ interface DetectionResult {
|
|
|
19
46
|
riskLevel?: 'low' | 'medium' | 'high' | 'critical';
|
|
20
47
|
timestamp: string;
|
|
21
48
|
}
|
|
22
|
-
interface
|
|
49
|
+
interface EdgeCheckpointConfig {
|
|
23
50
|
wasmModule?: WebAssembly.Module;
|
|
24
51
|
enableWasm?: boolean;
|
|
25
52
|
onAgentDetected?: (result: DetectionResult) => void;
|
|
@@ -27,12 +54,12 @@ interface AgentShieldConfig {
|
|
|
27
54
|
allowedAgents?: string[];
|
|
28
55
|
debug?: boolean;
|
|
29
56
|
}
|
|
30
|
-
declare class
|
|
57
|
+
declare class EdgeRuntimeCheckpoint {
|
|
31
58
|
private wasmInstance;
|
|
32
59
|
private wasmMemory;
|
|
33
60
|
private config;
|
|
34
61
|
private initialized;
|
|
35
|
-
constructor(config?:
|
|
62
|
+
constructor(config?: EdgeCheckpointConfig);
|
|
36
63
|
init(wasmModule?: WebAssembly.Module): Promise<void>;
|
|
37
64
|
private readString;
|
|
38
65
|
private writeString;
|
|
@@ -42,9 +69,18 @@ declare class EdgeRuntimeAgentShield {
|
|
|
42
69
|
getVerificationMethod(): 'cryptographic' | 'pattern';
|
|
43
70
|
}
|
|
44
71
|
/**
|
|
45
|
-
* Factory function to create
|
|
72
|
+
* Factory function to create a Checkpoint instance for Edge Runtime.
|
|
46
73
|
*/
|
|
47
|
-
declare function
|
|
48
|
-
declare function
|
|
74
|
+
declare function createEdgeCheckpoint(config?: EdgeCheckpointConfig): EdgeRuntimeCheckpoint;
|
|
75
|
+
declare function getDefaultEdgeCheckpoint(config?: EdgeCheckpointConfig): EdgeRuntimeCheckpoint;
|
|
76
|
+
|
|
77
|
+
/** @deprecated Renamed to {@link EdgeCheckpointConfig}. */
|
|
78
|
+
type AgentShieldConfig = EdgeCheckpointConfig;
|
|
79
|
+
/** @deprecated Renamed to {@link EdgeRuntimeCheckpoint}. */
|
|
80
|
+
declare const EdgeRuntimeAgentShield: typeof EdgeRuntimeCheckpoint;
|
|
81
|
+
/** @deprecated Renamed to {@link createEdgeCheckpoint}. */
|
|
82
|
+
declare const createEdgeAgentShield: typeof createEdgeCheckpoint;
|
|
83
|
+
/** @deprecated Renamed to {@link getDefaultEdgeCheckpoint}. */
|
|
84
|
+
declare const getDefaultAgentShield: typeof getDefaultEdgeCheckpoint;
|
|
49
85
|
|
|
50
|
-
export { type AgentShieldConfig, type DetectionResult, type WasmModule, createEdgeAgentShield, getDefaultAgentShield };
|
|
86
|
+
export { type AgentShieldConfig, type DetectionResult, type EdgeCheckpointConfig, EdgeRuntimeAgentShield, EdgeRuntimeCheckpoint, type WasmModule, createEdgeAgentShield, createEdgeCheckpoint, getDefaultAgentShield, getDefaultEdgeCheckpoint };
|
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
import { NextRequest } from 'next/server';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Edge Runtime Compatible WASM Loader for
|
|
4
|
+
* Edge Runtime Compatible WASM Loader for Checkpoint
|
|
5
5
|
*
|
|
6
6
|
* This module provides a pre-built solution for loading WASM in Edge Runtime.
|
|
7
7
|
* It requires the WASM file to be manually placed in the project.
|
|
8
|
+
*
|
|
9
|
+
* ## SSOT for pattern detection
|
|
10
|
+
*
|
|
11
|
+
* The fallback `patternDetection` path imports BOTH pattern arrays
|
|
12
|
+
* from `@kya-os/checkpoint-shared/constants/agents`:
|
|
13
|
+
*
|
|
14
|
+
* - `KNOWN_AGENT_PATTERNS` — strict SSOT carved for the server-side
|
|
15
|
+
* classifier (per-agent rows with category + isLegitimate fields).
|
|
16
|
+
* - `INTERACTIVE_AGENT_PATTERNS` — supplement holding interactive-
|
|
17
|
+
* session tokens, generic vendor fallbacks, and the GPT-Crawler
|
|
18
|
+
* entry that lives in seed-agent-types-data.ts (NOT in the strict
|
|
19
|
+
* SSOT).
|
|
20
|
+
*
|
|
21
|
+
* Pre-#2599 this file had a 15-pattern inline subset that diverged
|
|
22
|
+
* from the shared SSOT — the you.com regex was tightened in agents.ts
|
|
23
|
+
* but reverted here. SSOT-Fallback-Drift-1 (this PR) collapsed the
|
|
24
|
+
* inline duplication and moved the supplement into checkpoint-shared
|
|
25
|
+
* so both layers reference the same source. EPIC #2573 (PDM-2) is the
|
|
26
|
+
* future unified-SSOT consolidation that folds the two arrays into
|
|
27
|
+
* one canonical table.
|
|
28
|
+
*
|
|
29
|
+
* ## Naming
|
|
30
|
+
*
|
|
31
|
+
* Old `AgentShield`-prefixed exports are kept as `@deprecated` aliases
|
|
32
|
+
* for one release (`createEdgeAgentShield`, `EdgeRuntimeAgentShield`,
|
|
33
|
+
* `AgentShieldConfig`, `getDefaultAgentShield`). New code should import
|
|
34
|
+
* the `Checkpoint`-prefixed names directly.
|
|
8
35
|
*/
|
|
9
36
|
|
|
10
37
|
interface WasmModule {
|
|
@@ -19,7 +46,7 @@ interface DetectionResult {
|
|
|
19
46
|
riskLevel?: 'low' | 'medium' | 'high' | 'critical';
|
|
20
47
|
timestamp: string;
|
|
21
48
|
}
|
|
22
|
-
interface
|
|
49
|
+
interface EdgeCheckpointConfig {
|
|
23
50
|
wasmModule?: WebAssembly.Module;
|
|
24
51
|
enableWasm?: boolean;
|
|
25
52
|
onAgentDetected?: (result: DetectionResult) => void;
|
|
@@ -27,12 +54,12 @@ interface AgentShieldConfig {
|
|
|
27
54
|
allowedAgents?: string[];
|
|
28
55
|
debug?: boolean;
|
|
29
56
|
}
|
|
30
|
-
declare class
|
|
57
|
+
declare class EdgeRuntimeCheckpoint {
|
|
31
58
|
private wasmInstance;
|
|
32
59
|
private wasmMemory;
|
|
33
60
|
private config;
|
|
34
61
|
private initialized;
|
|
35
|
-
constructor(config?:
|
|
62
|
+
constructor(config?: EdgeCheckpointConfig);
|
|
36
63
|
init(wasmModule?: WebAssembly.Module): Promise<void>;
|
|
37
64
|
private readString;
|
|
38
65
|
private writeString;
|
|
@@ -42,9 +69,18 @@ declare class EdgeRuntimeAgentShield {
|
|
|
42
69
|
getVerificationMethod(): 'cryptographic' | 'pattern';
|
|
43
70
|
}
|
|
44
71
|
/**
|
|
45
|
-
* Factory function to create
|
|
72
|
+
* Factory function to create a Checkpoint instance for Edge Runtime.
|
|
46
73
|
*/
|
|
47
|
-
declare function
|
|
48
|
-
declare function
|
|
74
|
+
declare function createEdgeCheckpoint(config?: EdgeCheckpointConfig): EdgeRuntimeCheckpoint;
|
|
75
|
+
declare function getDefaultEdgeCheckpoint(config?: EdgeCheckpointConfig): EdgeRuntimeCheckpoint;
|
|
76
|
+
|
|
77
|
+
/** @deprecated Renamed to {@link EdgeCheckpointConfig}. */
|
|
78
|
+
type AgentShieldConfig = EdgeCheckpointConfig;
|
|
79
|
+
/** @deprecated Renamed to {@link EdgeRuntimeCheckpoint}. */
|
|
80
|
+
declare const EdgeRuntimeAgentShield: typeof EdgeRuntimeCheckpoint;
|
|
81
|
+
/** @deprecated Renamed to {@link createEdgeCheckpoint}. */
|
|
82
|
+
declare const createEdgeAgentShield: typeof createEdgeCheckpoint;
|
|
83
|
+
/** @deprecated Renamed to {@link getDefaultEdgeCheckpoint}. */
|
|
84
|
+
declare const getDefaultAgentShield: typeof getDefaultEdgeCheckpoint;
|
|
49
85
|
|
|
50
|
-
export { type AgentShieldConfig, type DetectionResult, type WasmModule, createEdgeAgentShield, getDefaultAgentShield };
|
|
86
|
+
export { type AgentShieldConfig, type DetectionResult, type EdgeCheckpointConfig, EdgeRuntimeAgentShield, EdgeRuntimeCheckpoint, type WasmModule, createEdgeAgentShield, createEdgeCheckpoint, getDefaultAgentShield, getDefaultEdgeCheckpoint };
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var checkpointShared = require('@kya-os/checkpoint-shared');
|
|
4
|
+
|
|
5
|
+
// src/edge-runtime-loader.ts
|
|
6
|
+
|
|
3
7
|
// src/utils.ts
|
|
4
8
|
function getClientIp(request) {
|
|
5
9
|
const forwardedFor = request.headers.get("x-forwarded-for");
|
|
@@ -17,7 +21,40 @@ function getClientIp(request) {
|
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
// src/edge-runtime-loader.ts
|
|
20
|
-
var
|
|
24
|
+
var SUSPICIOUS_HEADER_PREFIXES = ["x-openai-", "x-anthropic-", "x-ai-", "x-llm-", "x-gpt-"];
|
|
25
|
+
function escapeRegex(s) {
|
|
26
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
27
|
+
}
|
|
28
|
+
var TOKEN_REGEX_CACHE = /* @__PURE__ */ new Map();
|
|
29
|
+
function tokenRegex(token) {
|
|
30
|
+
const cached = TOKEN_REGEX_CACHE.get(token);
|
|
31
|
+
if (cached) return cached;
|
|
32
|
+
const regex = new RegExp(`\\b${escapeRegex(token)}\\b`, "i");
|
|
33
|
+
TOKEN_REGEX_CACHE.set(token, regex);
|
|
34
|
+
return regex;
|
|
35
|
+
}
|
|
36
|
+
function matchKnownAgent(lowercasedUa) {
|
|
37
|
+
let best = null;
|
|
38
|
+
for (const entry of checkpointShared.KNOWN_AGENT_PATTERNS) {
|
|
39
|
+
for (const pattern of entry.patterns) {
|
|
40
|
+
if (tokenRegex(pattern).test(lowercasedUa)) {
|
|
41
|
+
if (!best || entry.confidence > best.confidence) {
|
|
42
|
+
best = { name: entry.name, confidence: entry.confidence };
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (const { pattern, name, confidence } of checkpointShared.INTERACTIVE_AGENT_PATTERNS) {
|
|
49
|
+
if (pattern.test(lowercasedUa)) {
|
|
50
|
+
if (!best || confidence > best.confidence) {
|
|
51
|
+
best = { name, confidence };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return best;
|
|
56
|
+
}
|
|
57
|
+
var EdgeRuntimeCheckpoint = class {
|
|
21
58
|
wasmInstance = null;
|
|
22
59
|
wasmMemory = null;
|
|
23
60
|
config;
|
|
@@ -49,7 +86,7 @@ var EdgeRuntimeAgentShield = class {
|
|
|
49
86
|
this.wasmInstance = await WebAssembly.instantiate(module, imports);
|
|
50
87
|
this.initialized = true;
|
|
51
88
|
if (this.config.debug) {
|
|
52
|
-
console.log("\u2705
|
|
89
|
+
console.log("\u2705 Checkpoint WASM initialized in Edge Runtime");
|
|
53
90
|
}
|
|
54
91
|
} catch (error) {
|
|
55
92
|
console.warn("\u26A0\uFE0F WASM initialization failed, using pattern detection:", error);
|
|
@@ -126,52 +163,29 @@ var EdgeRuntimeAgentShield = class {
|
|
|
126
163
|
return this.patternDetection(metadata);
|
|
127
164
|
}
|
|
128
165
|
patternDetection(metadata) {
|
|
129
|
-
const
|
|
130
|
-
const patterns = [
|
|
131
|
-
// High confidence - explicit AI identifiers
|
|
132
|
-
{ pattern: /chatgpt-user/i, name: "ChatGPT", confidence: 0.95 },
|
|
133
|
-
{ pattern: /claude-web/i, name: "Claude", confidence: 0.95 },
|
|
134
|
-
{ pattern: /claude-user/i, name: "Claude", confidence: 0.95 },
|
|
135
|
-
{ pattern: /gpt-crawler/i, name: "GPT Crawler", confidence: 0.95 },
|
|
136
|
-
{ pattern: /perplexitybot/i, name: "Perplexity", confidence: 0.95 },
|
|
137
|
-
{ pattern: /perplexity-user/i, name: "Perplexity", confidence: 0.95 },
|
|
138
|
-
{ pattern: /perplexity-ai/i, name: "Perplexity", confidence: 0.95 },
|
|
139
|
-
// Medium-high confidence - company identifiers
|
|
140
|
-
{ pattern: /anthropic/i, name: "Anthropic", confidence: 0.9 },
|
|
141
|
-
{ pattern: /openai/i, name: "OpenAI", confidence: 0.9 },
|
|
142
|
-
// Medium confidence - product names
|
|
143
|
-
{ pattern: /copilot/i, name: "GitHub Copilot", confidence: 0.85 },
|
|
144
|
-
{ pattern: /bard/i, name: "Google Bard", confidence: 0.85 },
|
|
145
|
-
{ pattern: /gemini/i, name: "Google Gemini", confidence: 0.85 },
|
|
146
|
-
{ pattern: /perplexity/i, name: "Perplexity", confidence: 0.85 },
|
|
147
|
-
// Fallback
|
|
148
|
-
{ pattern: /\byou\.com\b/i, name: "You.com", confidence: 0.8 },
|
|
149
|
-
{ pattern: /\bphind\b/i, name: "Phind", confidence: 0.8 }
|
|
150
|
-
];
|
|
151
|
-
const suspiciousHeaders = ["x-openai-", "x-anthropic-", "x-ai-", "x-llm-", "x-gpt-"];
|
|
166
|
+
const lowercasedUa = metadata.userAgent.toLowerCase();
|
|
152
167
|
let headerBoost = 0;
|
|
153
|
-
for (const
|
|
154
|
-
if (
|
|
168
|
+
for (const key of Object.keys(metadata.headers)) {
|
|
169
|
+
if (SUSPICIOUS_HEADER_PREFIXES.some((prefix) => key.toLowerCase().startsWith(prefix))) {
|
|
155
170
|
headerBoost = 0.1;
|
|
156
171
|
break;
|
|
157
172
|
}
|
|
158
173
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
return result;
|
|
174
|
+
const match = matchKnownAgent(lowercasedUa);
|
|
175
|
+
if (match) {
|
|
176
|
+
const finalConfidence = Math.min(match.confidence + headerBoost, 1);
|
|
177
|
+
const result = {
|
|
178
|
+
isAgent: true,
|
|
179
|
+
confidence: finalConfidence,
|
|
180
|
+
agent: match.name,
|
|
181
|
+
verificationMethod: "pattern",
|
|
182
|
+
riskLevel: finalConfidence > 0.9 ? "high" : "medium",
|
|
183
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
184
|
+
};
|
|
185
|
+
if (this.config.onAgentDetected) {
|
|
186
|
+
this.config.onAgentDetected(result);
|
|
174
187
|
}
|
|
188
|
+
return result;
|
|
175
189
|
}
|
|
176
190
|
return {
|
|
177
191
|
isAgent: false,
|
|
@@ -187,16 +201,23 @@ var EdgeRuntimeAgentShield = class {
|
|
|
187
201
|
return this.wasmInstance ? "cryptographic" : "pattern";
|
|
188
202
|
}
|
|
189
203
|
};
|
|
190
|
-
function
|
|
191
|
-
return new
|
|
204
|
+
function createEdgeCheckpoint(config) {
|
|
205
|
+
return new EdgeRuntimeCheckpoint(config);
|
|
192
206
|
}
|
|
193
207
|
var defaultInstance = null;
|
|
194
|
-
function
|
|
208
|
+
function getDefaultEdgeCheckpoint(config) {
|
|
195
209
|
if (!defaultInstance) {
|
|
196
|
-
defaultInstance = new
|
|
210
|
+
defaultInstance = new EdgeRuntimeCheckpoint(config);
|
|
197
211
|
}
|
|
198
212
|
return defaultInstance;
|
|
199
213
|
}
|
|
214
|
+
var EdgeRuntimeAgentShield = EdgeRuntimeCheckpoint;
|
|
215
|
+
var createEdgeAgentShield = createEdgeCheckpoint;
|
|
216
|
+
var getDefaultAgentShield = getDefaultEdgeCheckpoint;
|
|
200
217
|
|
|
218
|
+
exports.EdgeRuntimeAgentShield = EdgeRuntimeAgentShield;
|
|
219
|
+
exports.EdgeRuntimeCheckpoint = EdgeRuntimeCheckpoint;
|
|
201
220
|
exports.createEdgeAgentShield = createEdgeAgentShield;
|
|
221
|
+
exports.createEdgeCheckpoint = createEdgeCheckpoint;
|
|
202
222
|
exports.getDefaultAgentShield = getDefaultAgentShield;
|
|
223
|
+
exports.getDefaultEdgeCheckpoint = getDefaultEdgeCheckpoint;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { KNOWN_AGENT_PATTERNS, INTERACTIVE_AGENT_PATTERNS } from '@kya-os/checkpoint-shared';
|
|
2
|
+
|
|
3
|
+
// src/edge-runtime-loader.ts
|
|
4
|
+
|
|
1
5
|
// src/utils.ts
|
|
2
6
|
function getClientIp(request) {
|
|
3
7
|
const forwardedFor = request.headers.get("x-forwarded-for");
|
|
@@ -15,7 +19,40 @@ function getClientIp(request) {
|
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
// src/edge-runtime-loader.ts
|
|
18
|
-
var
|
|
22
|
+
var SUSPICIOUS_HEADER_PREFIXES = ["x-openai-", "x-anthropic-", "x-ai-", "x-llm-", "x-gpt-"];
|
|
23
|
+
function escapeRegex(s) {
|
|
24
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25
|
+
}
|
|
26
|
+
var TOKEN_REGEX_CACHE = /* @__PURE__ */ new Map();
|
|
27
|
+
function tokenRegex(token) {
|
|
28
|
+
const cached = TOKEN_REGEX_CACHE.get(token);
|
|
29
|
+
if (cached) return cached;
|
|
30
|
+
const regex = new RegExp(`\\b${escapeRegex(token)}\\b`, "i");
|
|
31
|
+
TOKEN_REGEX_CACHE.set(token, regex);
|
|
32
|
+
return regex;
|
|
33
|
+
}
|
|
34
|
+
function matchKnownAgent(lowercasedUa) {
|
|
35
|
+
let best = null;
|
|
36
|
+
for (const entry of KNOWN_AGENT_PATTERNS) {
|
|
37
|
+
for (const pattern of entry.patterns) {
|
|
38
|
+
if (tokenRegex(pattern).test(lowercasedUa)) {
|
|
39
|
+
if (!best || entry.confidence > best.confidence) {
|
|
40
|
+
best = { name: entry.name, confidence: entry.confidence };
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (const { pattern, name, confidence } of INTERACTIVE_AGENT_PATTERNS) {
|
|
47
|
+
if (pattern.test(lowercasedUa)) {
|
|
48
|
+
if (!best || confidence > best.confidence) {
|
|
49
|
+
best = { name, confidence };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return best;
|
|
54
|
+
}
|
|
55
|
+
var EdgeRuntimeCheckpoint = class {
|
|
19
56
|
wasmInstance = null;
|
|
20
57
|
wasmMemory = null;
|
|
21
58
|
config;
|
|
@@ -47,7 +84,7 @@ var EdgeRuntimeAgentShield = class {
|
|
|
47
84
|
this.wasmInstance = await WebAssembly.instantiate(module, imports);
|
|
48
85
|
this.initialized = true;
|
|
49
86
|
if (this.config.debug) {
|
|
50
|
-
console.log("\u2705
|
|
87
|
+
console.log("\u2705 Checkpoint WASM initialized in Edge Runtime");
|
|
51
88
|
}
|
|
52
89
|
} catch (error) {
|
|
53
90
|
console.warn("\u26A0\uFE0F WASM initialization failed, using pattern detection:", error);
|
|
@@ -124,52 +161,29 @@ var EdgeRuntimeAgentShield = class {
|
|
|
124
161
|
return this.patternDetection(metadata);
|
|
125
162
|
}
|
|
126
163
|
patternDetection(metadata) {
|
|
127
|
-
const
|
|
128
|
-
const patterns = [
|
|
129
|
-
// High confidence - explicit AI identifiers
|
|
130
|
-
{ pattern: /chatgpt-user/i, name: "ChatGPT", confidence: 0.95 },
|
|
131
|
-
{ pattern: /claude-web/i, name: "Claude", confidence: 0.95 },
|
|
132
|
-
{ pattern: /claude-user/i, name: "Claude", confidence: 0.95 },
|
|
133
|
-
{ pattern: /gpt-crawler/i, name: "GPT Crawler", confidence: 0.95 },
|
|
134
|
-
{ pattern: /perplexitybot/i, name: "Perplexity", confidence: 0.95 },
|
|
135
|
-
{ pattern: /perplexity-user/i, name: "Perplexity", confidence: 0.95 },
|
|
136
|
-
{ pattern: /perplexity-ai/i, name: "Perplexity", confidence: 0.95 },
|
|
137
|
-
// Medium-high confidence - company identifiers
|
|
138
|
-
{ pattern: /anthropic/i, name: "Anthropic", confidence: 0.9 },
|
|
139
|
-
{ pattern: /openai/i, name: "OpenAI", confidence: 0.9 },
|
|
140
|
-
// Medium confidence - product names
|
|
141
|
-
{ pattern: /copilot/i, name: "GitHub Copilot", confidence: 0.85 },
|
|
142
|
-
{ pattern: /bard/i, name: "Google Bard", confidence: 0.85 },
|
|
143
|
-
{ pattern: /gemini/i, name: "Google Gemini", confidence: 0.85 },
|
|
144
|
-
{ pattern: /perplexity/i, name: "Perplexity", confidence: 0.85 },
|
|
145
|
-
// Fallback
|
|
146
|
-
{ pattern: /\byou\.com\b/i, name: "You.com", confidence: 0.8 },
|
|
147
|
-
{ pattern: /\bphind\b/i, name: "Phind", confidence: 0.8 }
|
|
148
|
-
];
|
|
149
|
-
const suspiciousHeaders = ["x-openai-", "x-anthropic-", "x-ai-", "x-llm-", "x-gpt-"];
|
|
164
|
+
const lowercasedUa = metadata.userAgent.toLowerCase();
|
|
150
165
|
let headerBoost = 0;
|
|
151
|
-
for (const
|
|
152
|
-
if (
|
|
166
|
+
for (const key of Object.keys(metadata.headers)) {
|
|
167
|
+
if (SUSPICIOUS_HEADER_PREFIXES.some((prefix) => key.toLowerCase().startsWith(prefix))) {
|
|
153
168
|
headerBoost = 0.1;
|
|
154
169
|
break;
|
|
155
170
|
}
|
|
156
171
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
return result;
|
|
172
|
+
const match = matchKnownAgent(lowercasedUa);
|
|
173
|
+
if (match) {
|
|
174
|
+
const finalConfidence = Math.min(match.confidence + headerBoost, 1);
|
|
175
|
+
const result = {
|
|
176
|
+
isAgent: true,
|
|
177
|
+
confidence: finalConfidence,
|
|
178
|
+
agent: match.name,
|
|
179
|
+
verificationMethod: "pattern",
|
|
180
|
+
riskLevel: finalConfidence > 0.9 ? "high" : "medium",
|
|
181
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
182
|
+
};
|
|
183
|
+
if (this.config.onAgentDetected) {
|
|
184
|
+
this.config.onAgentDetected(result);
|
|
172
185
|
}
|
|
186
|
+
return result;
|
|
173
187
|
}
|
|
174
188
|
return {
|
|
175
189
|
isAgent: false,
|
|
@@ -185,15 +199,18 @@ var EdgeRuntimeAgentShield = class {
|
|
|
185
199
|
return this.wasmInstance ? "cryptographic" : "pattern";
|
|
186
200
|
}
|
|
187
201
|
};
|
|
188
|
-
function
|
|
189
|
-
return new
|
|
202
|
+
function createEdgeCheckpoint(config) {
|
|
203
|
+
return new EdgeRuntimeCheckpoint(config);
|
|
190
204
|
}
|
|
191
205
|
var defaultInstance = null;
|
|
192
|
-
function
|
|
206
|
+
function getDefaultEdgeCheckpoint(config) {
|
|
193
207
|
if (!defaultInstance) {
|
|
194
|
-
defaultInstance = new
|
|
208
|
+
defaultInstance = new EdgeRuntimeCheckpoint(config);
|
|
195
209
|
}
|
|
196
210
|
return defaultInstance;
|
|
197
211
|
}
|
|
212
|
+
var EdgeRuntimeAgentShield = EdgeRuntimeCheckpoint;
|
|
213
|
+
var createEdgeAgentShield = createEdgeCheckpoint;
|
|
214
|
+
var getDefaultAgentShield = getDefaultEdgeCheckpoint;
|
|
198
215
|
|
|
199
|
-
export { createEdgeAgentShield, getDefaultAgentShield };
|
|
216
|
+
export { EdgeRuntimeAgentShield, EdgeRuntimeCheckpoint, createEdgeAgentShield, createEdgeCheckpoint, getDefaultAgentShield, getDefaultEdgeCheckpoint };
|
package/dist/index.js
CHANGED
|
@@ -56,21 +56,28 @@ function applyHeaders(res, headers) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// src/translate.ts
|
|
59
|
-
function nextRequestToHttpLike(req) {
|
|
59
|
+
async function nextRequestToHttpLike(req, opts = {}) {
|
|
60
60
|
const url = new URL(req.url);
|
|
61
|
+
const body = await tryDrainJsonBody(req, opts);
|
|
61
62
|
return {
|
|
62
63
|
method: req.method,
|
|
63
64
|
// Path + query only — orchestrator's URL parsing expects no scheme/host.
|
|
64
65
|
url: url.pathname + url.search,
|
|
65
66
|
headers: headersToRecord(req.headers),
|
|
66
|
-
|
|
67
|
-
// The orchestrator routes to PlainHttp when body is falsy, which
|
|
68
|
-
// is the right call for streaming middlewares that don't want to
|
|
69
|
-
// buffer the request body just to detect agents.
|
|
70
|
-
body: null,
|
|
67
|
+
body,
|
|
71
68
|
remoteAddress: extractRemoteAddress(req)
|
|
72
69
|
};
|
|
73
70
|
}
|
|
71
|
+
async function tryDrainJsonBody(req, opts) {
|
|
72
|
+
if (opts.drainJsonBody === false) return null;
|
|
73
|
+
const contentType = req.headers.get("content-type") ?? "";
|
|
74
|
+
if (!contentType.toLowerCase().includes("application/json")) return null;
|
|
75
|
+
try {
|
|
76
|
+
return await req.clone().text();
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
74
81
|
function headersToRecord(headers) {
|
|
75
82
|
const out = {};
|
|
76
83
|
headers.forEach((value, key) => {
|
|
@@ -91,8 +98,9 @@ function extractRemoteAddress(req) {
|
|
|
91
98
|
// src/middleware-node.ts
|
|
92
99
|
function withCheckpoint(config) {
|
|
93
100
|
const opts = buildVerifyOpts(config);
|
|
101
|
+
const translateOpts = { drainJsonBody: config.drainJsonBody };
|
|
94
102
|
return async function checkpointMiddleware(req) {
|
|
95
|
-
const httpLike = nextRequestToHttpLike(req);
|
|
103
|
+
const httpLike = await nextRequestToHttpLike(req, translateOpts);
|
|
96
104
|
const result = await orchestrator.verifyRequest(httpLike, opts);
|
|
97
105
|
await dispatchOnResult(config, result, req);
|
|
98
106
|
const rendered = orchestrator.renderDecisionAsResponse(result);
|
|
@@ -110,7 +118,8 @@ function buildVerifyOpts(config) {
|
|
|
110
118
|
tenantHost: config.tenantHost,
|
|
111
119
|
enforcementMode: config.enforcementMode ?? "enforce",
|
|
112
120
|
reputationBaseline: config.reputationBaseline,
|
|
113
|
-
argusUrl: config.argusUrl
|
|
121
|
+
argusUrl: config.argusUrl,
|
|
122
|
+
legacyEnvelopeFallback: config.legacyEnvelopeFallback ?? false
|
|
114
123
|
};
|
|
115
124
|
}
|
|
116
125
|
async function dispatchOnResult(config, result, req) {
|
|
@@ -973,7 +982,13 @@ async function applyPolicy(request, detection, config) {
|
|
|
973
982
|
const context = createContextFromDetection(detection, request);
|
|
974
983
|
const decision = checkpointShared.evaluatePolicy(policy, context);
|
|
975
984
|
if (config.onPolicyDecision) {
|
|
976
|
-
|
|
985
|
+
try {
|
|
986
|
+
await config.onPolicyDecision(request, decision, context);
|
|
987
|
+
} catch (error) {
|
|
988
|
+
if (config.debug) {
|
|
989
|
+
console.error("[Checkpoint] onPolicyDecision callback failed:", error);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
977
992
|
}
|
|
978
993
|
return await handlePolicyDecision(request, decision, config, detection);
|
|
979
994
|
} catch (error) {
|
package/dist/index.mjs
CHANGED
|
@@ -55,21 +55,28 @@ function applyHeaders(res, headers) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// src/translate.ts
|
|
58
|
-
function nextRequestToHttpLike(req) {
|
|
58
|
+
async function nextRequestToHttpLike(req, opts = {}) {
|
|
59
59
|
const url = new URL(req.url);
|
|
60
|
+
const body = await tryDrainJsonBody(req, opts);
|
|
60
61
|
return {
|
|
61
62
|
method: req.method,
|
|
62
63
|
// Path + query only — orchestrator's URL parsing expects no scheme/host.
|
|
63
64
|
url: url.pathname + url.search,
|
|
64
65
|
headers: headersToRecord(req.headers),
|
|
65
|
-
|
|
66
|
-
// The orchestrator routes to PlainHttp when body is falsy, which
|
|
67
|
-
// is the right call for streaming middlewares that don't want to
|
|
68
|
-
// buffer the request body just to detect agents.
|
|
69
|
-
body: null,
|
|
66
|
+
body,
|
|
70
67
|
remoteAddress: extractRemoteAddress(req)
|
|
71
68
|
};
|
|
72
69
|
}
|
|
70
|
+
async function tryDrainJsonBody(req, opts) {
|
|
71
|
+
if (opts.drainJsonBody === false) return null;
|
|
72
|
+
const contentType = req.headers.get("content-type") ?? "";
|
|
73
|
+
if (!contentType.toLowerCase().includes("application/json")) return null;
|
|
74
|
+
try {
|
|
75
|
+
return await req.clone().text();
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
73
80
|
function headersToRecord(headers) {
|
|
74
81
|
const out = {};
|
|
75
82
|
headers.forEach((value, key) => {
|
|
@@ -90,8 +97,9 @@ function extractRemoteAddress(req) {
|
|
|
90
97
|
// src/middleware-node.ts
|
|
91
98
|
function withCheckpoint(config) {
|
|
92
99
|
const opts = buildVerifyOpts(config);
|
|
100
|
+
const translateOpts = { drainJsonBody: config.drainJsonBody };
|
|
93
101
|
return async function checkpointMiddleware(req) {
|
|
94
|
-
const httpLike = nextRequestToHttpLike(req);
|
|
102
|
+
const httpLike = await nextRequestToHttpLike(req, translateOpts);
|
|
95
103
|
const result = await verifyRequest(httpLike, opts);
|
|
96
104
|
await dispatchOnResult(config, result, req);
|
|
97
105
|
const rendered = renderDecisionAsResponse(result);
|
|
@@ -109,7 +117,8 @@ function buildVerifyOpts(config) {
|
|
|
109
117
|
tenantHost: config.tenantHost,
|
|
110
118
|
enforcementMode: config.enforcementMode ?? "enforce",
|
|
111
119
|
reputationBaseline: config.reputationBaseline,
|
|
112
|
-
argusUrl: config.argusUrl
|
|
120
|
+
argusUrl: config.argusUrl,
|
|
121
|
+
legacyEnvelopeFallback: config.legacyEnvelopeFallback ?? false
|
|
113
122
|
};
|
|
114
123
|
}
|
|
115
124
|
async function dispatchOnResult(config, result, req) {
|
|
@@ -972,7 +981,13 @@ async function applyPolicy(request, detection, config) {
|
|
|
972
981
|
const context = createContextFromDetection(detection, request);
|
|
973
982
|
const decision = evaluatePolicy(policy, context);
|
|
974
983
|
if (config.onPolicyDecision) {
|
|
975
|
-
|
|
984
|
+
try {
|
|
985
|
+
await config.onPolicyDecision(request, decision, context);
|
|
986
|
+
} catch (error) {
|
|
987
|
+
if (config.debug) {
|
|
988
|
+
console.error("[Checkpoint] onPolicyDecision callback failed:", error);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
976
991
|
}
|
|
977
992
|
return await handlePolicyDecision(request, decision, config, detection);
|
|
978
993
|
} catch (error) {
|
package/dist/middleware-edge.js
CHANGED
|
@@ -57,21 +57,28 @@ function applyHeaders(res, headers) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// src/translate.ts
|
|
60
|
-
function nextRequestToHttpLike(req) {
|
|
60
|
+
async function nextRequestToHttpLike(req, opts = {}) {
|
|
61
61
|
const url = new URL(req.url);
|
|
62
|
+
const body = await tryDrainJsonBody(req, opts);
|
|
62
63
|
return {
|
|
63
64
|
method: req.method,
|
|
64
65
|
// Path + query only — orchestrator's URL parsing expects no scheme/host.
|
|
65
66
|
url: url.pathname + url.search,
|
|
66
67
|
headers: headersToRecord(req.headers),
|
|
67
|
-
|
|
68
|
-
// The orchestrator routes to PlainHttp when body is falsy, which
|
|
69
|
-
// is the right call for streaming middlewares that don't want to
|
|
70
|
-
// buffer the request body just to detect agents.
|
|
71
|
-
body: null,
|
|
68
|
+
body,
|
|
72
69
|
remoteAddress: extractRemoteAddress(req)
|
|
73
70
|
};
|
|
74
71
|
}
|
|
72
|
+
async function tryDrainJsonBody(req, opts) {
|
|
73
|
+
if (opts.drainJsonBody === false) return null;
|
|
74
|
+
const contentType = req.headers.get("content-type") ?? "";
|
|
75
|
+
if (!contentType.toLowerCase().includes("application/json")) return null;
|
|
76
|
+
try {
|
|
77
|
+
return await req.clone().text();
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
75
82
|
function headersToRecord(headers) {
|
|
76
83
|
const out = {};
|
|
77
84
|
headers.forEach((value, key) => {
|
|
@@ -101,7 +108,8 @@ function buildVerifyOpts(config) {
|
|
|
101
108
|
tenantHost: config.tenantHost,
|
|
102
109
|
enforcementMode: config.enforcementMode ?? "enforce",
|
|
103
110
|
reputationBaseline: config.reputationBaseline,
|
|
104
|
-
argusUrl: config.argusUrl
|
|
111
|
+
argusUrl: config.argusUrl,
|
|
112
|
+
legacyEnvelopeFallback: config.legacyEnvelopeFallback ?? false
|
|
105
113
|
};
|
|
106
114
|
}
|
|
107
115
|
|
|
@@ -109,8 +117,9 @@ function buildVerifyOpts(config) {
|
|
|
109
117
|
function withCheckpoint(config) {
|
|
110
118
|
void edge.initEngineEdge();
|
|
111
119
|
const opts = buildVerifyOpts(config);
|
|
120
|
+
const translateOpts = { drainJsonBody: config.drainJsonBody };
|
|
112
121
|
return async function checkpointMiddlewareEdge(req) {
|
|
113
|
-
const httpLike = nextRequestToHttpLike(req);
|
|
122
|
+
const httpLike = await nextRequestToHttpLike(req, translateOpts);
|
|
114
123
|
const result = await edge.verifyRequestEdge(httpLike, opts);
|
|
115
124
|
await dispatchOnResult(config, result, req);
|
|
116
125
|
const rendered = edge.renderDecisionAsResponse(result);
|