@kya-os/checkpoint-nextjs 1.2.0 → 1.7.0
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 +159 -0
- package/dist/composed-policy.d.mts +108 -0
- package/dist/composed-policy.d.ts +108 -0
- package/dist/composed-policy.js +91 -0
- package/dist/composed-policy.mjs +85 -0
- package/dist/config-_nfPN3E3.d.mts +205 -0
- package/dist/config-kxFihzR_.d.ts +205 -0
- package/dist/create-middleware.js +0 -2
- package/dist/create-middleware.mjs +0 -2
- package/dist/edge-runtime-loader.js +3 -1
- package/dist/edge-runtime-loader.mjs +3 -1
- package/dist/edge-wasm-middleware.d.mts +6 -6
- package/dist/edge-wasm-middleware.d.ts +6 -6
- package/dist/index.d.mts +6 -14
- package/dist/index.d.ts +6 -14
- package/dist/index.js +160 -8
- package/dist/index.mjs +161 -9
- package/dist/middleware-edge.d.mts +7 -3
- package/dist/middleware-edge.d.ts +7 -3
- package/dist/middleware-edge.js +157 -3
- package/dist/middleware-edge.mjs +154 -3
- package/dist/middleware-node.d.mts +39 -116
- package/dist/middleware-node.d.ts +39 -116
- package/dist/middleware-node.js +164 -3
- package/dist/middleware-node.mjs +161 -4
- package/dist/middleware.d.mts +10 -1
- package/dist/middleware.d.ts +10 -1
- package/dist/middleware.js +6 -0
- package/dist/middleware.mjs +6 -1
- package/dist/nodejs-wasm-loader.d.mts +3 -4
- package/dist/nodejs-wasm-loader.d.ts +3 -4
- package/dist/nodejs-wasm-loader.js +1 -1
- package/dist/nodejs-wasm-loader.mjs +1 -1
- package/dist/signature-verifier.js +2 -2
- package/dist/signature-verifier.mjs +2 -2
- package/dist/wasm-setup.js +1 -1
- package/dist/wasm-setup.mjs +1 -1
- package/package.json +4 -9
- package/dist/.tsbuildinfo +0 -1
- package/dist/wasm-middleware.d.mts +0 -98
- package/dist/wasm-middleware.d.ts +0 -98
- package/dist/wasm-middleware.js +0 -125
- package/dist/wasm-middleware.mjs +0 -121
- package/templates/middleware-wasm-100.ts +0 -161
package/dist/index.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
var orchestrator = require('@kya-os/checkpoint-wasm-runtime/orchestrator');
|
|
4
4
|
var adapters = require('@kya-os/checkpoint-wasm-runtime/adapters');
|
|
5
|
-
var
|
|
5
|
+
var reporter = require('@kya-os/checkpoint-wasm-runtime/reporter');
|
|
6
6
|
var checkpointShared = require('@kya-os/checkpoint-shared');
|
|
7
|
+
var server = require('next/server');
|
|
8
|
+
var composedPolicy = require('@kya-os/checkpoint-wasm-runtime/composed-policy');
|
|
7
9
|
|
|
8
10
|
// src/middleware-node.ts
|
|
9
11
|
function adaptToNextResponse(rendered, req) {
|
|
@@ -54,6 +56,85 @@ function applyHeaders(res, headers) {
|
|
|
54
56
|
res.headers.set(key, value);
|
|
55
57
|
}
|
|
56
58
|
}
|
|
59
|
+
var DEFAULT_DASHBOARD_URL = "https://kya.vouched.id";
|
|
60
|
+
var NOOP_LOGGER = {
|
|
61
|
+
shadowDivergence: () => {
|
|
62
|
+
},
|
|
63
|
+
evaluationError: () => {
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
function makeComposedPolicyContext(opts) {
|
|
67
|
+
const { projectId, fetcher } = opts;
|
|
68
|
+
const cache = composedPolicy.makeComposedPolicyCache({ compile: opts.compile, cacheMax: opts.cacheMax });
|
|
69
|
+
const logger = opts.logger ?? NOOP_LOGGER;
|
|
70
|
+
return {
|
|
71
|
+
async apply(result, path) {
|
|
72
|
+
const structured = { decision: result.decision, acted: false };
|
|
73
|
+
const policy = await fetcher.getPolicy(projectId);
|
|
74
|
+
const outcome = await composedPolicy.evaluateComposedPolicy({
|
|
75
|
+
cache,
|
|
76
|
+
projectId,
|
|
77
|
+
flags: {
|
|
78
|
+
policyLanguage: policy.policyLanguage,
|
|
79
|
+
policySourceText: policy.policySourceText,
|
|
80
|
+
engineEnforcementEnabled: policy.engineEnforcementEnabled,
|
|
81
|
+
enabled: policy.enabled
|
|
82
|
+
},
|
|
83
|
+
authorizeInput: composedPolicy.verifyResultToAuthorizeInput(result, { tenantId: projectId, path }),
|
|
84
|
+
baselineDecisionKind: result.decision.kind
|
|
85
|
+
});
|
|
86
|
+
if ((outcome.status === "acting" || outcome.status === "shadow") && outcome.diverged) {
|
|
87
|
+
logger.shadowDivergence({
|
|
88
|
+
projectId,
|
|
89
|
+
path,
|
|
90
|
+
engineDecision: outcome.engineDecision.kind,
|
|
91
|
+
structuredDecision: result.decision.kind,
|
|
92
|
+
detectionClass: result.detectionDetail.detectionClass.type,
|
|
93
|
+
verificationMethod: result.detectionDetail.verificationMethod,
|
|
94
|
+
confidence: result.detectionDetail.confidence,
|
|
95
|
+
agentName: result.detectionDetail.detectedAgent?.name
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (outcome.status === "error") {
|
|
99
|
+
logger.evaluationError(projectId, outcome.error);
|
|
100
|
+
return structured;
|
|
101
|
+
}
|
|
102
|
+
if (outcome.status === "acting") {
|
|
103
|
+
return { decision: outcome.engineDecision, acted: true };
|
|
104
|
+
}
|
|
105
|
+
return structured;
|
|
106
|
+
},
|
|
107
|
+
async trustedDelegationRoots() {
|
|
108
|
+
const policy = await fetcher.getPolicy(projectId);
|
|
109
|
+
return policy.trustedDelegationRoots ?? [];
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
async function resolveTrustedDelegationRootsForRequest(resolver, headers) {
|
|
114
|
+
if (!resolver) return void 0;
|
|
115
|
+
if (!checkpointShared.requestCarriesDelegationProof(headers)) return void 0;
|
|
116
|
+
const roots = await resolver();
|
|
117
|
+
return roots.length > 0 ? roots : void 0;
|
|
118
|
+
}
|
|
119
|
+
async function applyComposedPolicy(context, result, path) {
|
|
120
|
+
if (!context) return;
|
|
121
|
+
try {
|
|
122
|
+
const outcome = await context.apply(result, path);
|
|
123
|
+
if (outcome.acted) result.decision = outcome.decision;
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
var consoleComposedPolicyLogger = {
|
|
128
|
+
shadowDivergence(info) {
|
|
129
|
+
console.warn("[checkpoint/composed-policy] shadow-divergence", info);
|
|
130
|
+
},
|
|
131
|
+
evaluationError(projectId, error) {
|
|
132
|
+
console.error(
|
|
133
|
+
`[checkpoint/composed-policy] evaluation failed for ${projectId}; using structured decision:`,
|
|
134
|
+
error
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
57
138
|
|
|
58
139
|
// src/translate.ts
|
|
59
140
|
async function nextRequestToHttpLike(req, opts = {}) {
|
|
@@ -98,15 +179,91 @@ function extractRemoteAddress(req) {
|
|
|
98
179
|
// src/middleware-node.ts
|
|
99
180
|
function withCheckpoint(config) {
|
|
100
181
|
const opts = buildVerifyOpts(config);
|
|
182
|
+
const reporter = buildReporter(config);
|
|
183
|
+
const composed = buildComposedContext(config);
|
|
184
|
+
const trustedRootsResolver = buildTrustedRootsResolver(config, composed);
|
|
101
185
|
const translateOpts = { drainJsonBody: config.drainJsonBody };
|
|
102
|
-
return async function checkpointMiddleware(req) {
|
|
186
|
+
return async function checkpointMiddleware(req, event) {
|
|
103
187
|
const httpLike = await nextRequestToHttpLike(req, translateOpts);
|
|
104
|
-
const
|
|
188
|
+
const trustedDelegationRoots = await resolveTrustedDelegationRootsForRequest(
|
|
189
|
+
trustedRootsResolver,
|
|
190
|
+
req.headers
|
|
191
|
+
);
|
|
192
|
+
const result = await orchestrator.verifyRequest(
|
|
193
|
+
httpLike,
|
|
194
|
+
trustedDelegationRoots ? { ...opts, trustedDelegationRoots } : opts
|
|
195
|
+
);
|
|
196
|
+
await applyComposedPolicy(composed, result, req.nextUrl.pathname);
|
|
197
|
+
if (reporter) {
|
|
198
|
+
const reportPromise = reporter(result, extractReporterContext(req));
|
|
199
|
+
if (event) {
|
|
200
|
+
event.waitUntil(reportPromise);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
105
203
|
await dispatchOnResult(config, result, req);
|
|
106
204
|
const rendered = orchestrator.renderDecisionAsResponse(result);
|
|
107
205
|
return adaptToNextResponse(rendered, req);
|
|
108
206
|
};
|
|
109
207
|
}
|
|
208
|
+
var SDK_NAME = "@kya-os/checkpoint-nextjs";
|
|
209
|
+
var VERSION = "1.7.0";
|
|
210
|
+
function buildReporter(config, runtime = "node") {
|
|
211
|
+
if (!config.apiKey) return null;
|
|
212
|
+
return reporter.makeDetectionReporter({
|
|
213
|
+
apiKey: config.apiKey,
|
|
214
|
+
baseUrl: config.baseUrl,
|
|
215
|
+
debug: config.debug,
|
|
216
|
+
// Self-identify (incl. node-vs-edge) so the dashboard can version-gate
|
|
217
|
+
// enforcement. Next.js EDGE composed enforcement is opt-in (needs
|
|
218
|
+
// `cedarWasmModule`), so the dashboard shows it as opt-in, never "Enforcing".
|
|
219
|
+
sdk: { name: SDK_NAME, version: VERSION, runtime }
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function buildTrustedRootsResolver(config, composed) {
|
|
223
|
+
if (composed?.trustedDelegationRoots) {
|
|
224
|
+
return () => composed.trustedDelegationRoots();
|
|
225
|
+
}
|
|
226
|
+
if (!config.projectId) return null;
|
|
227
|
+
const fetcher = new checkpointShared.PolicyFetcher({
|
|
228
|
+
apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
|
|
229
|
+
apiKey: config.apiKey,
|
|
230
|
+
cacheTtlSeconds: config.policyCacheTtlSeconds
|
|
231
|
+
});
|
|
232
|
+
const projectId = config.projectId;
|
|
233
|
+
return async () => (await fetcher.getPolicy(projectId)).trustedDelegationRoots ?? [];
|
|
234
|
+
}
|
|
235
|
+
function buildComposedContext(config) {
|
|
236
|
+
if (config.composedPolicyEnforcer) return config.composedPolicyEnforcer;
|
|
237
|
+
if (!config.projectId) return null;
|
|
238
|
+
return makeComposedPolicyContext({
|
|
239
|
+
projectId: config.projectId,
|
|
240
|
+
fetcher: new checkpointShared.PolicyFetcher({
|
|
241
|
+
apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
|
|
242
|
+
apiKey: config.apiKey,
|
|
243
|
+
cacheTtlSeconds: config.policyCacheTtlSeconds
|
|
244
|
+
}),
|
|
245
|
+
// LAZY dynamic import — NOT a top-level `import` — so the node-only
|
|
246
|
+
// `./policy` glue (`createRequire`/`fs` at module load) is never pulled into
|
|
247
|
+
// the Edge bundle. `middleware-edge.ts` imports helpers from this file, so a
|
|
248
|
+
// top-level `./policy` import would surface as a side-effect import in the
|
|
249
|
+
// edge bundle and boot-fail on Vercel edge. The import is cached after first
|
|
250
|
+
// call; the core's single-flight cache wraps the (now async) compile.
|
|
251
|
+
compile: async (_language, source) => {
|
|
252
|
+
const { createPolicyEvaluator } = await import('@kya-os/checkpoint-wasm-runtime/policy');
|
|
253
|
+
return createPolicyEvaluator(source);
|
|
254
|
+
},
|
|
255
|
+
logger: config.debug ? consoleComposedPolicyLogger : void 0
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
function extractReporterContext(req) {
|
|
259
|
+
return {
|
|
260
|
+
userAgent: req.headers.get("user-agent") ?? void 0,
|
|
261
|
+
ipAddress: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? req.headers.get("x-real-ip") ?? void 0,
|
|
262
|
+
path: req.nextUrl.pathname,
|
|
263
|
+
url: req.nextUrl.href,
|
|
264
|
+
method: req.method
|
|
265
|
+
};
|
|
266
|
+
}
|
|
110
267
|
function buildVerifyOpts(config) {
|
|
111
268
|
const overrides = config.adapters ?? {};
|
|
112
269
|
return {
|
|
@@ -130,8 +287,6 @@ async function dispatchOnResult(config, result, req) {
|
|
|
130
287
|
} catch {
|
|
131
288
|
}
|
|
132
289
|
}
|
|
133
|
-
|
|
134
|
-
// src/middleware.ts
|
|
135
290
|
var MIGRATION_ERROR = "@kya-os/checkpoint-nextjs's `createAgentShieldMiddleware` / `agentShield` were deleted in Phase D (engine consolidation). The 600-line TS pattern matcher that backed them is gone. Migrate to `withCheckpoint` from `@kya-os/checkpoint-nextjs` (Node runtime) or `@kya-os/checkpoint-nextjs/edge` (Edge runtime). See packages/checkpoint-nextjs/CHANGELOG.md (1.0.0) for the recipe.";
|
|
136
291
|
function createAgentShieldMiddleware(_config = {}) {
|
|
137
292
|
throw new Error(MIGRATION_ERROR);
|
|
@@ -1007,9 +1162,6 @@ async function applyPolicy(request, detection, config) {
|
|
|
1007
1162
|
);
|
|
1008
1163
|
}
|
|
1009
1164
|
}
|
|
1010
|
-
|
|
1011
|
-
// src/index.ts
|
|
1012
|
-
var VERSION = "0.1.0";
|
|
1013
1165
|
/**
|
|
1014
1166
|
* @fileoverview Checkpoint Next.js Integration
|
|
1015
1167
|
* @license MIT OR Apache-2.0
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { verifyRequest, renderDecisionAsResponse } from '@kya-os/checkpoint-wasm-runtime/orchestrator';
|
|
2
2
|
import { makeSystemClock, makePolicyEvaluator, makeReputationOracle, makeStatusListCache, makeDidResolver } from '@kya-os/checkpoint-wasm-runtime/adapters';
|
|
3
|
-
import {
|
|
4
|
-
import { isKnownAiCrawler, createEvaluationContext, evaluatePolicy, ENFORCEMENT_ACTIONS, PolicyConfigSchema, DEFAULT_POLICY, matchPath, acceptsHtml, encodeVerdictCookie, classifyResponseShape, BLOCKED_PATH, createPolicyFetcher, VERDICT_COOKIE_NAME } from '@kya-os/checkpoint-shared';
|
|
3
|
+
import { makeDetectionReporter } from '@kya-os/checkpoint-wasm-runtime/reporter';
|
|
4
|
+
import { PolicyFetcher, isKnownAiCrawler, createEvaluationContext, evaluatePolicy, ENFORCEMENT_ACTIONS, PolicyConfigSchema, DEFAULT_POLICY, matchPath, requestCarriesDelegationProof, acceptsHtml, encodeVerdictCookie, classifyResponseShape, BLOCKED_PATH, createPolicyFetcher, VERDICT_COOKIE_NAME } from '@kya-os/checkpoint-shared';
|
|
5
5
|
export { DEFAULT_POLICY, ENFORCEMENT_ACTIONS, createEvaluationContext, evaluatePolicy } from '@kya-os/checkpoint-shared';
|
|
6
|
+
import { NextResponse } from 'next/server';
|
|
7
|
+
import { makeComposedPolicyCache, evaluateComposedPolicy, verifyResultToAuthorizeInput } from '@kya-os/checkpoint-wasm-runtime/composed-policy';
|
|
6
8
|
|
|
7
9
|
// src/middleware-node.ts
|
|
8
10
|
function adaptToNextResponse(rendered, req) {
|
|
@@ -53,6 +55,85 @@ function applyHeaders(res, headers) {
|
|
|
53
55
|
res.headers.set(key, value);
|
|
54
56
|
}
|
|
55
57
|
}
|
|
58
|
+
var DEFAULT_DASHBOARD_URL = "https://kya.vouched.id";
|
|
59
|
+
var NOOP_LOGGER = {
|
|
60
|
+
shadowDivergence: () => {
|
|
61
|
+
},
|
|
62
|
+
evaluationError: () => {
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function makeComposedPolicyContext(opts) {
|
|
66
|
+
const { projectId, fetcher } = opts;
|
|
67
|
+
const cache = makeComposedPolicyCache({ compile: opts.compile, cacheMax: opts.cacheMax });
|
|
68
|
+
const logger = opts.logger ?? NOOP_LOGGER;
|
|
69
|
+
return {
|
|
70
|
+
async apply(result, path) {
|
|
71
|
+
const structured = { decision: result.decision, acted: false };
|
|
72
|
+
const policy = await fetcher.getPolicy(projectId);
|
|
73
|
+
const outcome = await evaluateComposedPolicy({
|
|
74
|
+
cache,
|
|
75
|
+
projectId,
|
|
76
|
+
flags: {
|
|
77
|
+
policyLanguage: policy.policyLanguage,
|
|
78
|
+
policySourceText: policy.policySourceText,
|
|
79
|
+
engineEnforcementEnabled: policy.engineEnforcementEnabled,
|
|
80
|
+
enabled: policy.enabled
|
|
81
|
+
},
|
|
82
|
+
authorizeInput: verifyResultToAuthorizeInput(result, { tenantId: projectId, path }),
|
|
83
|
+
baselineDecisionKind: result.decision.kind
|
|
84
|
+
});
|
|
85
|
+
if ((outcome.status === "acting" || outcome.status === "shadow") && outcome.diverged) {
|
|
86
|
+
logger.shadowDivergence({
|
|
87
|
+
projectId,
|
|
88
|
+
path,
|
|
89
|
+
engineDecision: outcome.engineDecision.kind,
|
|
90
|
+
structuredDecision: result.decision.kind,
|
|
91
|
+
detectionClass: result.detectionDetail.detectionClass.type,
|
|
92
|
+
verificationMethod: result.detectionDetail.verificationMethod,
|
|
93
|
+
confidence: result.detectionDetail.confidence,
|
|
94
|
+
agentName: result.detectionDetail.detectedAgent?.name
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (outcome.status === "error") {
|
|
98
|
+
logger.evaluationError(projectId, outcome.error);
|
|
99
|
+
return structured;
|
|
100
|
+
}
|
|
101
|
+
if (outcome.status === "acting") {
|
|
102
|
+
return { decision: outcome.engineDecision, acted: true };
|
|
103
|
+
}
|
|
104
|
+
return structured;
|
|
105
|
+
},
|
|
106
|
+
async trustedDelegationRoots() {
|
|
107
|
+
const policy = await fetcher.getPolicy(projectId);
|
|
108
|
+
return policy.trustedDelegationRoots ?? [];
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async function resolveTrustedDelegationRootsForRequest(resolver, headers) {
|
|
113
|
+
if (!resolver) return void 0;
|
|
114
|
+
if (!requestCarriesDelegationProof(headers)) return void 0;
|
|
115
|
+
const roots = await resolver();
|
|
116
|
+
return roots.length > 0 ? roots : void 0;
|
|
117
|
+
}
|
|
118
|
+
async function applyComposedPolicy(context, result, path) {
|
|
119
|
+
if (!context) return;
|
|
120
|
+
try {
|
|
121
|
+
const outcome = await context.apply(result, path);
|
|
122
|
+
if (outcome.acted) result.decision = outcome.decision;
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
var consoleComposedPolicyLogger = {
|
|
127
|
+
shadowDivergence(info) {
|
|
128
|
+
console.warn("[checkpoint/composed-policy] shadow-divergence", info);
|
|
129
|
+
},
|
|
130
|
+
evaluationError(projectId, error) {
|
|
131
|
+
console.error(
|
|
132
|
+
`[checkpoint/composed-policy] evaluation failed for ${projectId}; using structured decision:`,
|
|
133
|
+
error
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
56
137
|
|
|
57
138
|
// src/translate.ts
|
|
58
139
|
async function nextRequestToHttpLike(req, opts = {}) {
|
|
@@ -97,15 +178,91 @@ function extractRemoteAddress(req) {
|
|
|
97
178
|
// src/middleware-node.ts
|
|
98
179
|
function withCheckpoint(config) {
|
|
99
180
|
const opts = buildVerifyOpts(config);
|
|
181
|
+
const reporter = buildReporter(config);
|
|
182
|
+
const composed = buildComposedContext(config);
|
|
183
|
+
const trustedRootsResolver = buildTrustedRootsResolver(config, composed);
|
|
100
184
|
const translateOpts = { drainJsonBody: config.drainJsonBody };
|
|
101
|
-
return async function checkpointMiddleware(req) {
|
|
185
|
+
return async function checkpointMiddleware(req, event) {
|
|
102
186
|
const httpLike = await nextRequestToHttpLike(req, translateOpts);
|
|
103
|
-
const
|
|
187
|
+
const trustedDelegationRoots = await resolveTrustedDelegationRootsForRequest(
|
|
188
|
+
trustedRootsResolver,
|
|
189
|
+
req.headers
|
|
190
|
+
);
|
|
191
|
+
const result = await verifyRequest(
|
|
192
|
+
httpLike,
|
|
193
|
+
trustedDelegationRoots ? { ...opts, trustedDelegationRoots } : opts
|
|
194
|
+
);
|
|
195
|
+
await applyComposedPolicy(composed, result, req.nextUrl.pathname);
|
|
196
|
+
if (reporter) {
|
|
197
|
+
const reportPromise = reporter(result, extractReporterContext(req));
|
|
198
|
+
if (event) {
|
|
199
|
+
event.waitUntil(reportPromise);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
104
202
|
await dispatchOnResult(config, result, req);
|
|
105
203
|
const rendered = renderDecisionAsResponse(result);
|
|
106
204
|
return adaptToNextResponse(rendered, req);
|
|
107
205
|
};
|
|
108
206
|
}
|
|
207
|
+
var SDK_NAME = "@kya-os/checkpoint-nextjs";
|
|
208
|
+
var VERSION = "1.7.0";
|
|
209
|
+
function buildReporter(config, runtime = "node") {
|
|
210
|
+
if (!config.apiKey) return null;
|
|
211
|
+
return makeDetectionReporter({
|
|
212
|
+
apiKey: config.apiKey,
|
|
213
|
+
baseUrl: config.baseUrl,
|
|
214
|
+
debug: config.debug,
|
|
215
|
+
// Self-identify (incl. node-vs-edge) so the dashboard can version-gate
|
|
216
|
+
// enforcement. Next.js EDGE composed enforcement is opt-in (needs
|
|
217
|
+
// `cedarWasmModule`), so the dashboard shows it as opt-in, never "Enforcing".
|
|
218
|
+
sdk: { name: SDK_NAME, version: VERSION, runtime }
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
function buildTrustedRootsResolver(config, composed) {
|
|
222
|
+
if (composed?.trustedDelegationRoots) {
|
|
223
|
+
return () => composed.trustedDelegationRoots();
|
|
224
|
+
}
|
|
225
|
+
if (!config.projectId) return null;
|
|
226
|
+
const fetcher = new PolicyFetcher({
|
|
227
|
+
apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
|
|
228
|
+
apiKey: config.apiKey,
|
|
229
|
+
cacheTtlSeconds: config.policyCacheTtlSeconds
|
|
230
|
+
});
|
|
231
|
+
const projectId = config.projectId;
|
|
232
|
+
return async () => (await fetcher.getPolicy(projectId)).trustedDelegationRoots ?? [];
|
|
233
|
+
}
|
|
234
|
+
function buildComposedContext(config) {
|
|
235
|
+
if (config.composedPolicyEnforcer) return config.composedPolicyEnforcer;
|
|
236
|
+
if (!config.projectId) return null;
|
|
237
|
+
return makeComposedPolicyContext({
|
|
238
|
+
projectId: config.projectId,
|
|
239
|
+
fetcher: new PolicyFetcher({
|
|
240
|
+
apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
|
|
241
|
+
apiKey: config.apiKey,
|
|
242
|
+
cacheTtlSeconds: config.policyCacheTtlSeconds
|
|
243
|
+
}),
|
|
244
|
+
// LAZY dynamic import — NOT a top-level `import` — so the node-only
|
|
245
|
+
// `./policy` glue (`createRequire`/`fs` at module load) is never pulled into
|
|
246
|
+
// the Edge bundle. `middleware-edge.ts` imports helpers from this file, so a
|
|
247
|
+
// top-level `./policy` import would surface as a side-effect import in the
|
|
248
|
+
// edge bundle and boot-fail on Vercel edge. The import is cached after first
|
|
249
|
+
// call; the core's single-flight cache wraps the (now async) compile.
|
|
250
|
+
compile: async (_language, source) => {
|
|
251
|
+
const { createPolicyEvaluator } = await import('@kya-os/checkpoint-wasm-runtime/policy');
|
|
252
|
+
return createPolicyEvaluator(source);
|
|
253
|
+
},
|
|
254
|
+
logger: config.debug ? consoleComposedPolicyLogger : void 0
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function extractReporterContext(req) {
|
|
258
|
+
return {
|
|
259
|
+
userAgent: req.headers.get("user-agent") ?? void 0,
|
|
260
|
+
ipAddress: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? req.headers.get("x-real-ip") ?? void 0,
|
|
261
|
+
path: req.nextUrl.pathname,
|
|
262
|
+
url: req.nextUrl.href,
|
|
263
|
+
method: req.method
|
|
264
|
+
};
|
|
265
|
+
}
|
|
109
266
|
function buildVerifyOpts(config) {
|
|
110
267
|
const overrides = config.adapters ?? {};
|
|
111
268
|
return {
|
|
@@ -129,8 +286,6 @@ async function dispatchOnResult(config, result, req) {
|
|
|
129
286
|
} catch {
|
|
130
287
|
}
|
|
131
288
|
}
|
|
132
|
-
|
|
133
|
-
// src/middleware.ts
|
|
134
289
|
var MIGRATION_ERROR = "@kya-os/checkpoint-nextjs's `createAgentShieldMiddleware` / `agentShield` were deleted in Phase D (engine consolidation). The 600-line TS pattern matcher that backed them is gone. Migrate to `withCheckpoint` from `@kya-os/checkpoint-nextjs` (Node runtime) or `@kya-os/checkpoint-nextjs/edge` (Edge runtime). See packages/checkpoint-nextjs/CHANGELOG.md (1.0.0) for the recipe.";
|
|
135
290
|
function createAgentShieldMiddleware(_config = {}) {
|
|
136
291
|
throw new Error(MIGRATION_ERROR);
|
|
@@ -1006,9 +1161,6 @@ async function applyPolicy(request, detection, config) {
|
|
|
1006
1161
|
);
|
|
1007
1162
|
}
|
|
1008
1163
|
}
|
|
1009
|
-
|
|
1010
|
-
// src/index.ts
|
|
1011
|
-
var VERSION = "0.1.0";
|
|
1012
1164
|
/**
|
|
1013
1165
|
* @fileoverview Checkpoint Next.js Integration
|
|
1014
1166
|
* @license MIT OR Apache-2.0
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
1
|
+
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
2
2
|
export { initEngineEdge } from '@kya-os/checkpoint-wasm-runtime/orchestrator/edge';
|
|
3
|
-
|
|
3
|
+
export { initPolicyEvaluatorEdge } from '@kya-os/checkpoint-wasm-runtime/policy-edge';
|
|
4
|
+
import { C as CheckpointConfig } from './config-_nfPN3E3.mjs';
|
|
4
5
|
import '@kya-os/checkpoint-wasm-runtime/adapters';
|
|
5
6
|
import '@kya-os/checkpoint-wasm-runtime/engine';
|
|
7
|
+
import './composed-policy.mjs';
|
|
8
|
+
import '@kya-os/checkpoint-wasm-runtime/composed-policy';
|
|
9
|
+
import '@kya-os/checkpoint-shared';
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* D.3 — Edge-runtime Next.js middleware entry.
|
|
@@ -41,6 +45,6 @@ import '@kya-os/checkpoint-wasm-runtime/engine';
|
|
|
41
45
|
* factory closure is being built. The first request awaits the same
|
|
42
46
|
* promise; subsequent requests resolve sync.
|
|
43
47
|
*/
|
|
44
|
-
declare function withCheckpoint(config: CheckpointConfig): (req: NextRequest) => Promise<NextResponse>;
|
|
48
|
+
declare function withCheckpoint(config: CheckpointConfig): (req: NextRequest, event?: NextFetchEvent) => Promise<NextResponse>;
|
|
45
49
|
|
|
46
50
|
export { CheckpointConfig, withCheckpoint };
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
1
|
+
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
2
2
|
export { initEngineEdge } from '@kya-os/checkpoint-wasm-runtime/orchestrator/edge';
|
|
3
|
-
|
|
3
|
+
export { initPolicyEvaluatorEdge } from '@kya-os/checkpoint-wasm-runtime/policy-edge';
|
|
4
|
+
import { C as CheckpointConfig } from './config-kxFihzR_.js';
|
|
4
5
|
import '@kya-os/checkpoint-wasm-runtime/adapters';
|
|
5
6
|
import '@kya-os/checkpoint-wasm-runtime/engine';
|
|
7
|
+
import './composed-policy.js';
|
|
8
|
+
import '@kya-os/checkpoint-wasm-runtime/composed-policy';
|
|
9
|
+
import '@kya-os/checkpoint-shared';
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* D.3 — Edge-runtime Next.js middleware entry.
|
|
@@ -41,6 +45,6 @@ import '@kya-os/checkpoint-wasm-runtime/engine';
|
|
|
41
45
|
* factory closure is being built. The first request awaits the same
|
|
42
46
|
* promise; subsequent requests resolve sync.
|
|
43
47
|
*/
|
|
44
|
-
declare function withCheckpoint(config: CheckpointConfig): (req: NextRequest) => Promise<NextResponse>;
|
|
48
|
+
declare function withCheckpoint(config: CheckpointConfig): (req: NextRequest, event?: NextFetchEvent) => Promise<NextResponse>;
|
|
45
49
|
|
|
46
50
|
export { CheckpointConfig, withCheckpoint };
|
package/dist/middleware-edge.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var edge = require('@kya-os/checkpoint-wasm-runtime/orchestrator/edge');
|
|
4
|
-
var
|
|
4
|
+
var policyEdge = require('@kya-os/checkpoint-wasm-runtime/policy-edge');
|
|
5
5
|
var checkpointShared = require('@kya-os/checkpoint-shared');
|
|
6
|
+
var server = require('next/server');
|
|
7
|
+
var composedPolicy = require('@kya-os/checkpoint-wasm-runtime/composed-policy');
|
|
6
8
|
require('@kya-os/checkpoint-wasm-runtime/orchestrator');
|
|
7
9
|
var adapters = require('@kya-os/checkpoint-wasm-runtime/adapters');
|
|
10
|
+
var reporter = require('@kya-os/checkpoint-wasm-runtime/reporter');
|
|
8
11
|
|
|
9
12
|
// src/middleware-edge.ts
|
|
10
13
|
function adaptToNextResponse(rendered, req) {
|
|
@@ -55,6 +58,85 @@ function applyHeaders(res, headers) {
|
|
|
55
58
|
res.headers.set(key, value);
|
|
56
59
|
}
|
|
57
60
|
}
|
|
61
|
+
var DEFAULT_DASHBOARD_URL = "https://kya.vouched.id";
|
|
62
|
+
var NOOP_LOGGER = {
|
|
63
|
+
shadowDivergence: () => {
|
|
64
|
+
},
|
|
65
|
+
evaluationError: () => {
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
function makeComposedPolicyContext(opts) {
|
|
69
|
+
const { projectId, fetcher } = opts;
|
|
70
|
+
const cache = composedPolicy.makeComposedPolicyCache({ compile: opts.compile, cacheMax: opts.cacheMax });
|
|
71
|
+
const logger = opts.logger ?? NOOP_LOGGER;
|
|
72
|
+
return {
|
|
73
|
+
async apply(result, path) {
|
|
74
|
+
const structured = { decision: result.decision, acted: false };
|
|
75
|
+
const policy = await fetcher.getPolicy(projectId);
|
|
76
|
+
const outcome = await composedPolicy.evaluateComposedPolicy({
|
|
77
|
+
cache,
|
|
78
|
+
projectId,
|
|
79
|
+
flags: {
|
|
80
|
+
policyLanguage: policy.policyLanguage,
|
|
81
|
+
policySourceText: policy.policySourceText,
|
|
82
|
+
engineEnforcementEnabled: policy.engineEnforcementEnabled,
|
|
83
|
+
enabled: policy.enabled
|
|
84
|
+
},
|
|
85
|
+
authorizeInput: composedPolicy.verifyResultToAuthorizeInput(result, { tenantId: projectId, path }),
|
|
86
|
+
baselineDecisionKind: result.decision.kind
|
|
87
|
+
});
|
|
88
|
+
if ((outcome.status === "acting" || outcome.status === "shadow") && outcome.diverged) {
|
|
89
|
+
logger.shadowDivergence({
|
|
90
|
+
projectId,
|
|
91
|
+
path,
|
|
92
|
+
engineDecision: outcome.engineDecision.kind,
|
|
93
|
+
structuredDecision: result.decision.kind,
|
|
94
|
+
detectionClass: result.detectionDetail.detectionClass.type,
|
|
95
|
+
verificationMethod: result.detectionDetail.verificationMethod,
|
|
96
|
+
confidence: result.detectionDetail.confidence,
|
|
97
|
+
agentName: result.detectionDetail.detectedAgent?.name
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (outcome.status === "error") {
|
|
101
|
+
logger.evaluationError(projectId, outcome.error);
|
|
102
|
+
return structured;
|
|
103
|
+
}
|
|
104
|
+
if (outcome.status === "acting") {
|
|
105
|
+
return { decision: outcome.engineDecision, acted: true };
|
|
106
|
+
}
|
|
107
|
+
return structured;
|
|
108
|
+
},
|
|
109
|
+
async trustedDelegationRoots() {
|
|
110
|
+
const policy = await fetcher.getPolicy(projectId);
|
|
111
|
+
return policy.trustedDelegationRoots ?? [];
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
async function resolveTrustedDelegationRootsForRequest(resolver, headers) {
|
|
116
|
+
if (!resolver) return void 0;
|
|
117
|
+
if (!checkpointShared.requestCarriesDelegationProof(headers)) return void 0;
|
|
118
|
+
const roots = await resolver();
|
|
119
|
+
return roots.length > 0 ? roots : void 0;
|
|
120
|
+
}
|
|
121
|
+
async function applyComposedPolicy(context, result, path) {
|
|
122
|
+
if (!context) return;
|
|
123
|
+
try {
|
|
124
|
+
const outcome = await context.apply(result, path);
|
|
125
|
+
if (outcome.acted) result.decision = outcome.decision;
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
var consoleComposedPolicyLogger = {
|
|
130
|
+
shadowDivergence(info) {
|
|
131
|
+
console.warn("[checkpoint/composed-policy] shadow-divergence", info);
|
|
132
|
+
},
|
|
133
|
+
evaluationError(projectId, error) {
|
|
134
|
+
console.error(
|
|
135
|
+
`[checkpoint/composed-policy] evaluation failed for ${projectId}; using structured decision:`,
|
|
136
|
+
error
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
58
140
|
|
|
59
141
|
// src/translate.ts
|
|
60
142
|
async function nextRequestToHttpLike(req, opts = {}) {
|
|
@@ -97,6 +179,42 @@ function extractRemoteAddress(req) {
|
|
|
97
179
|
}
|
|
98
180
|
|
|
99
181
|
// src/middleware-node.ts
|
|
182
|
+
var SDK_NAME = "@kya-os/checkpoint-nextjs";
|
|
183
|
+
var VERSION = "1.7.0";
|
|
184
|
+
function buildReporter(config, runtime = "node") {
|
|
185
|
+
if (!config.apiKey) return null;
|
|
186
|
+
return reporter.makeDetectionReporter({
|
|
187
|
+
apiKey: config.apiKey,
|
|
188
|
+
baseUrl: config.baseUrl,
|
|
189
|
+
debug: config.debug,
|
|
190
|
+
// Self-identify (incl. node-vs-edge) so the dashboard can version-gate
|
|
191
|
+
// enforcement. Next.js EDGE composed enforcement is opt-in (needs
|
|
192
|
+
// `cedarWasmModule`), so the dashboard shows it as opt-in, never "Enforcing".
|
|
193
|
+
sdk: { name: SDK_NAME, version: VERSION, runtime }
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function buildTrustedRootsResolver(config, composed) {
|
|
197
|
+
if (composed?.trustedDelegationRoots) {
|
|
198
|
+
return () => composed.trustedDelegationRoots();
|
|
199
|
+
}
|
|
200
|
+
if (!config.projectId) return null;
|
|
201
|
+
const fetcher = new checkpointShared.PolicyFetcher({
|
|
202
|
+
apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
|
|
203
|
+
apiKey: config.apiKey,
|
|
204
|
+
cacheTtlSeconds: config.policyCacheTtlSeconds
|
|
205
|
+
});
|
|
206
|
+
const projectId = config.projectId;
|
|
207
|
+
return async () => (await fetcher.getPolicy(projectId)).trustedDelegationRoots ?? [];
|
|
208
|
+
}
|
|
209
|
+
function extractReporterContext(req) {
|
|
210
|
+
return {
|
|
211
|
+
userAgent: req.headers.get("user-agent") ?? void 0,
|
|
212
|
+
ipAddress: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? req.headers.get("x-real-ip") ?? void 0,
|
|
213
|
+
path: req.nextUrl.pathname,
|
|
214
|
+
url: req.nextUrl.href,
|
|
215
|
+
method: req.method
|
|
216
|
+
};
|
|
217
|
+
}
|
|
100
218
|
function buildVerifyOpts(config) {
|
|
101
219
|
const overrides = config.adapters ?? {};
|
|
102
220
|
return {
|
|
@@ -118,10 +236,27 @@ function buildVerifyOpts(config) {
|
|
|
118
236
|
function withCheckpoint(config) {
|
|
119
237
|
void edge.initEngineEdge();
|
|
120
238
|
const opts = buildVerifyOpts(config);
|
|
239
|
+
const reporter = buildReporter(config, "edge");
|
|
240
|
+
const composed = buildComposedContext(config);
|
|
241
|
+
const trustedRootsResolver = buildTrustedRootsResolver(config, composed);
|
|
121
242
|
const translateOpts = { drainJsonBody: config.drainJsonBody };
|
|
122
|
-
return async function checkpointMiddlewareEdge(req) {
|
|
243
|
+
return async function checkpointMiddlewareEdge(req, event) {
|
|
123
244
|
const httpLike = await nextRequestToHttpLike(req, translateOpts);
|
|
124
|
-
const
|
|
245
|
+
const trustedDelegationRoots = await resolveTrustedDelegationRootsForRequest(
|
|
246
|
+
trustedRootsResolver,
|
|
247
|
+
req.headers
|
|
248
|
+
);
|
|
249
|
+
const result = await edge.verifyRequestEdge(
|
|
250
|
+
httpLike,
|
|
251
|
+
trustedDelegationRoots ? { ...opts, trustedDelegationRoots } : opts
|
|
252
|
+
);
|
|
253
|
+
await applyComposedPolicy(composed, result, req.nextUrl.pathname);
|
|
254
|
+
if (reporter) {
|
|
255
|
+
const reportPromise = reporter(result, extractReporterContext(req));
|
|
256
|
+
if (event) {
|
|
257
|
+
event.waitUntil(reportPromise);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
125
260
|
await dispatchOnResult(config, result, req);
|
|
126
261
|
const rendered = edge.renderDecisionAsResponse(result);
|
|
127
262
|
return adaptToNextResponse(rendered, req);
|
|
@@ -134,9 +269,28 @@ async function dispatchOnResult(config, result, req) {
|
|
|
134
269
|
} catch {
|
|
135
270
|
}
|
|
136
271
|
}
|
|
272
|
+
function buildComposedContext(config) {
|
|
273
|
+
if (config.composedPolicyEnforcer) return config.composedPolicyEnforcer;
|
|
274
|
+
if (!config.projectId || !config.cedarWasmModule) return null;
|
|
275
|
+
policyEdge.initPolicyEvaluatorEdge(config.cedarWasmModule);
|
|
276
|
+
return makeComposedPolicyContext({
|
|
277
|
+
projectId: config.projectId,
|
|
278
|
+
fetcher: new checkpointShared.PolicyFetcher({
|
|
279
|
+
apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
|
|
280
|
+
apiKey: config.apiKey,
|
|
281
|
+
cacheTtlSeconds: config.policyCacheTtlSeconds
|
|
282
|
+
}),
|
|
283
|
+
compile: (language, source) => policyEdge.evaluatePolicy(language, source),
|
|
284
|
+
logger: config.debug ? consoleComposedPolicyLogger : void 0
|
|
285
|
+
});
|
|
286
|
+
}
|
|
137
287
|
|
|
138
288
|
Object.defineProperty(exports, "initEngineEdge", {
|
|
139
289
|
enumerable: true,
|
|
140
290
|
get: function () { return edge.initEngineEdge; }
|
|
141
291
|
});
|
|
292
|
+
Object.defineProperty(exports, "initPolicyEvaluatorEdge", {
|
|
293
|
+
enumerable: true,
|
|
294
|
+
get: function () { return policyEdge.initPolicyEvaluatorEdge; }
|
|
295
|
+
});
|
|
142
296
|
exports.withCheckpoint = withCheckpoint;
|