protect-mcp 0.6.3 → 0.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 +47 -0
- package/README.md +11 -0
- package/dist/{chunk-UV53U6D4.mjs → chunk-546U3A7R.mjs} +79 -47
- package/dist/chunk-D733KAPG.mjs +252 -0
- package/dist/chunk-LYKNULYU.mjs +2446 -0
- package/dist/{chunk-PLKRTBDR.mjs → chunk-OHUTUFTC.mjs} +1 -1
- package/dist/{chunk-3YCKR72H.mjs → chunk-X63ELMU4.mjs} +1 -1
- package/dist/{chunk-S4ICHNSP.mjs → chunk-ZBKJANP7.mjs} +2 -2
- package/dist/cli.js +2850 -78
- package/dist/cli.mjs +143 -18
- package/dist/ed25519-DZMMNNVE.mjs +38 -0
- package/dist/hook-server.js +50 -47
- package/dist/hook-server.mjs +2 -2
- package/dist/{http-transport-MO32ESHZ.mjs → http-transport-R5AO7X6D.mjs} +2 -2
- package/dist/index.d.mts +47 -42
- package/dist/index.d.ts +47 -42
- package/dist/index.js +2634 -523
- package/dist/index.mjs +70 -113
- package/dist/utils-6AYZFE5A.mjs +77 -0
- package/package.json +5 -5
package/dist/cli.mjs
CHANGED
|
@@ -3,20 +3,26 @@ import {
|
|
|
3
3
|
formatSimulation,
|
|
4
4
|
parseLogFile,
|
|
5
5
|
simulate
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZBKJANP7.mjs";
|
|
7
7
|
import {
|
|
8
8
|
ProtectGateway,
|
|
9
9
|
validateCredentials
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-OHUTUFTC.mjs";
|
|
11
11
|
import {
|
|
12
|
+
evaluateCedar,
|
|
12
13
|
initSigning,
|
|
13
14
|
isCedarAvailable,
|
|
14
15
|
loadCedarPolicies,
|
|
15
|
-
loadPolicy
|
|
16
|
-
|
|
16
|
+
loadPolicy,
|
|
17
|
+
policySetFromSource,
|
|
18
|
+
runEvaluatorSelfTest,
|
|
19
|
+
signDecision
|
|
20
|
+
} from "./chunk-546U3A7R.mjs";
|
|
17
21
|
import "./chunk-PQJP2ZCI.mjs";
|
|
18
22
|
|
|
19
23
|
// src/cli.ts
|
|
24
|
+
import { readFileSync as readFileSyncCli, existsSync as existsSyncCli, appendFileSync as appendFileSyncCli, mkdirSync as mkdirSyncCli } from "fs";
|
|
25
|
+
import { basename as basenameCli, join as joinCli } from "path";
|
|
20
26
|
function printHelp() {
|
|
21
27
|
process.stderr.write(`
|
|
22
28
|
protect-mcp \u2014 Enterprise security gateway for MCP servers & Claude Code hooks
|
|
@@ -49,6 +55,8 @@ Options:
|
|
|
49
55
|
|
|
50
56
|
Commands:
|
|
51
57
|
serve Start HTTP hook server for Claude Code integration (port 9377)
|
|
58
|
+
evaluate Evaluate one tool call against a Cedar policy (PreToolUse gate; exit 2 = deny, fail-closed)
|
|
59
|
+
sign Sign one tool call into a receipt (PostToolUse)
|
|
52
60
|
init-hooks Generate Claude Code hook config + skill + sample Cedar policy
|
|
53
61
|
quickstart Zero-config onboarding: init + demo + show receipts in one command
|
|
54
62
|
connect Create a ScopeBlind sandbox dashboard and configure receipt upload
|
|
@@ -145,8 +153,8 @@ async function handleInit(argv) {
|
|
|
145
153
|
let keypair;
|
|
146
154
|
{
|
|
147
155
|
const { randomBytes } = await import("crypto");
|
|
148
|
-
const { ed25519 } = await import("
|
|
149
|
-
const { bytesToHex } = await import("
|
|
156
|
+
const { ed25519 } = await import("./ed25519-DZMMNNVE.mjs");
|
|
157
|
+
const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
|
|
150
158
|
const privateKey = randomBytes(32);
|
|
151
159
|
const publicKey = ed25519.getPublicKey(privateKey);
|
|
152
160
|
keypair = {
|
|
@@ -765,8 +773,8 @@ ${bold("protect-mcp quickstart")}
|
|
|
765
773
|
const { randomBytes } = await import("crypto");
|
|
766
774
|
let keypair;
|
|
767
775
|
try {
|
|
768
|
-
const { ed25519 } = await import("
|
|
769
|
-
const { bytesToHex } = await import("
|
|
776
|
+
const { ed25519 } = await import("./ed25519-DZMMNNVE.mjs");
|
|
777
|
+
const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
|
|
770
778
|
const privateKey = randomBytes(32);
|
|
771
779
|
const publicKey = ed25519.getPublicKey(privateKey);
|
|
772
780
|
keypair = {
|
|
@@ -1111,8 +1119,8 @@ ${bold("protect-mcp init-hooks")}
|
|
|
1111
1119
|
if (!existsSync(keysDir)) mkdirSync(keysDir, { recursive: true });
|
|
1112
1120
|
const { randomBytes: rb } = await import("crypto");
|
|
1113
1121
|
try {
|
|
1114
|
-
const { ed25519 } = await import("
|
|
1115
|
-
const { bytesToHex } = await import("
|
|
1122
|
+
const { ed25519 } = await import("./ed25519-DZMMNNVE.mjs");
|
|
1123
|
+
const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
|
|
1116
1124
|
const privateKey = rb(32);
|
|
1117
1125
|
const publicKey = ed25519.getPublicKey(privateKey);
|
|
1118
1126
|
writeFileSync(keyPath, JSON.stringify({
|
|
@@ -1273,6 +1281,88 @@ async function sendInstallTelemetry() {
|
|
|
1273
1281
|
} catch {
|
|
1274
1282
|
}
|
|
1275
1283
|
}
|
|
1284
|
+
function flagValue(argv, name) {
|
|
1285
|
+
const i = argv.indexOf(name);
|
|
1286
|
+
return i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
|
|
1287
|
+
}
|
|
1288
|
+
function loadPolicyArg(argv) {
|
|
1289
|
+
const cedarDir = flagValue(argv, "--cedar");
|
|
1290
|
+
const policyFile = flagValue(argv, "--policy");
|
|
1291
|
+
try {
|
|
1292
|
+
if (cedarDir) return loadCedarPolicies(cedarDir);
|
|
1293
|
+
if (policyFile && existsSyncCli(policyFile)) {
|
|
1294
|
+
return policySetFromSource(readFileSyncCli(policyFile, "utf-8"), basenameCli(policyFile));
|
|
1295
|
+
}
|
|
1296
|
+
} catch {
|
|
1297
|
+
}
|
|
1298
|
+
return null;
|
|
1299
|
+
}
|
|
1300
|
+
async function handleEvaluate(argv) {
|
|
1301
|
+
const tool = flagValue(argv, "--tool") || "";
|
|
1302
|
+
const inputRaw = flagValue(argv, "--input") || "{}";
|
|
1303
|
+
const contextRaw = flagValue(argv, "--context");
|
|
1304
|
+
const failOnMissing = flagValue(argv, "--fail-on-missing-policy") !== "false";
|
|
1305
|
+
const policySet = loadPolicyArg(argv);
|
|
1306
|
+
if (!policySet) {
|
|
1307
|
+
if (failOnMissing) {
|
|
1308
|
+
process.stderr.write("protect-mcp evaluate: policy not found; denying (fail-closed). Pass --fail-on-missing-policy false to allow.\n");
|
|
1309
|
+
process.exit(2);
|
|
1310
|
+
}
|
|
1311
|
+
process.stdout.write(JSON.stringify({ allowed: true, reason: "no_policy_configured" }) + "\n");
|
|
1312
|
+
process.exit(0);
|
|
1313
|
+
}
|
|
1314
|
+
let input = {};
|
|
1315
|
+
try {
|
|
1316
|
+
input = JSON.parse(inputRaw);
|
|
1317
|
+
} catch {
|
|
1318
|
+
}
|
|
1319
|
+
let extra = {};
|
|
1320
|
+
if (contextRaw) {
|
|
1321
|
+
try {
|
|
1322
|
+
extra = JSON.parse(contextRaw);
|
|
1323
|
+
} catch {
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
const context = { ...input, ...extra };
|
|
1327
|
+
if (typeof input.command === "string" && context.command_pattern === void 0) {
|
|
1328
|
+
context.command_pattern = input.command;
|
|
1329
|
+
}
|
|
1330
|
+
const decision = await evaluateCedar(policySet, { tool, tier: "unknown", context }, void 0, { failClosed: true });
|
|
1331
|
+
process.stdout.write(JSON.stringify({ allowed: decision.allowed, reason: decision.reason, policy_digest: policySet.digest }) + "\n");
|
|
1332
|
+
process.exit(decision.allowed ? 0 : 2);
|
|
1333
|
+
}
|
|
1334
|
+
async function handleSign(argv) {
|
|
1335
|
+
const tool = flagValue(argv, "--tool") || "";
|
|
1336
|
+
const receiptsDir = flagValue(argv, "--receipts") || "./receipts/";
|
|
1337
|
+
const keyPath = flagValue(argv, "--key");
|
|
1338
|
+
if (keyPath && existsSyncCli(keyPath)) {
|
|
1339
|
+
try {
|
|
1340
|
+
await initSigning({ enabled: true, key_path: keyPath });
|
|
1341
|
+
} catch {
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
const requestId = `tu-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
1345
|
+
const signed = signDecision({
|
|
1346
|
+
tool,
|
|
1347
|
+
decision: "allow",
|
|
1348
|
+
reason_code: "post_execution_receipt",
|
|
1349
|
+
policy_digest: "none",
|
|
1350
|
+
request_id: requestId,
|
|
1351
|
+
mode: "enforce",
|
|
1352
|
+
timestamp: Date.now()
|
|
1353
|
+
});
|
|
1354
|
+
try {
|
|
1355
|
+
mkdirSyncCli(receiptsDir, { recursive: true });
|
|
1356
|
+
} catch {
|
|
1357
|
+
}
|
|
1358
|
+
const line = signed.signed ?? JSON.stringify({ tool, request_id: requestId, signed: false, note: signed.warning || "no signer configured" });
|
|
1359
|
+
try {
|
|
1360
|
+
appendFileSyncCli(joinCli(receiptsDir, "receipts.jsonl"), line + "\n");
|
|
1361
|
+
} catch {
|
|
1362
|
+
}
|
|
1363
|
+
process.stdout.write(JSON.stringify({ signed: Boolean(signed.signed), artifact_type: signed.artifact_type, request_id: requestId }) + "\n");
|
|
1364
|
+
process.exit(0);
|
|
1365
|
+
}
|
|
1276
1366
|
async function main() {
|
|
1277
1367
|
sendInstallTelemetry().catch(() => {
|
|
1278
1368
|
});
|
|
@@ -1281,6 +1371,14 @@ async function main() {
|
|
|
1281
1371
|
printHelp();
|
|
1282
1372
|
process.exit(0);
|
|
1283
1373
|
}
|
|
1374
|
+
if (args[0] === "evaluate") {
|
|
1375
|
+
await handleEvaluate(args.slice(1));
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
if (args[0] === "sign") {
|
|
1379
|
+
await handleSign(args.slice(1));
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1284
1382
|
if (args[0] === "serve") {
|
|
1285
1383
|
const { startHookServer } = await import("./hook-server.mjs");
|
|
1286
1384
|
const portIdx = args.indexOf("--port");
|
|
@@ -1289,13 +1387,22 @@ async function main() {
|
|
|
1289
1387
|
const policyPath2 = policyIdx >= 0 && args[policyIdx + 1] ? args[policyIdx + 1] : void 0;
|
|
1290
1388
|
const cedarIdx = args.indexOf("--cedar");
|
|
1291
1389
|
const cedarDir2 = cedarIdx >= 0 && args[cedarIdx + 1] ? args[cedarIdx + 1] : void 0;
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1390
|
+
const enforce2 = args.includes("--enforce");
|
|
1391
|
+
const verbose2 = args.includes("--verbose") || args.includes("-v");
|
|
1392
|
+
if (enforce2) {
|
|
1393
|
+
const selfTest = await runEvaluatorSelfTest();
|
|
1394
|
+
if (!selfTest.passed) {
|
|
1395
|
+
process.stderr.write("protect-mcp serve --enforce: the policy-engine restraint self-test FAILED. Refusing to arm the gate.\n");
|
|
1396
|
+
for (const c of selfTest.cases.filter((c2) => !c2.pass)) {
|
|
1397
|
+
process.stderr.write(` [FAIL] ${c.name}: expected ${c.expected}, got ${c.actual}
|
|
1398
|
+
`);
|
|
1399
|
+
}
|
|
1400
|
+
process.exit(1);
|
|
1401
|
+
}
|
|
1402
|
+
if (verbose2) process.stderr.write(`protect-mcp: restraint self-test passed (${selfTest.cases.length} vectors). Arming gate.
|
|
1403
|
+
`);
|
|
1404
|
+
}
|
|
1405
|
+
await startHookServer({ port, policyPath: policyPath2, cedarDir: cedarDir2, enforce: enforce2, verbose: verbose2 });
|
|
1299
1406
|
return;
|
|
1300
1407
|
}
|
|
1301
1408
|
if (args[0] === "init-hooks") {
|
|
@@ -1442,7 +1549,7 @@ async function main() {
|
|
|
1442
1549
|
if (useHttp) {
|
|
1443
1550
|
const portIdx = args.indexOf("--port");
|
|
1444
1551
|
const httpPort = portIdx >= 0 && args[portIdx + 1] ? parseInt(args[portIdx + 1]) : 3e3;
|
|
1445
|
-
const { startHttpTransport } = await import("./http-transport-
|
|
1552
|
+
const { startHttpTransport } = await import("./http-transport-R5AO7X6D.mjs");
|
|
1446
1553
|
startHttpTransport({ port: httpPort, config, serverCommand: childCommand });
|
|
1447
1554
|
return;
|
|
1448
1555
|
}
|
|
@@ -1605,6 +1712,24 @@ async function handleDoctor() {
|
|
|
1605
1712
|
} catch {
|
|
1606
1713
|
process.stdout.write(dim2(" ScopeBlind API not reachable \u2014 offline mode (receipts stored locally)\n"));
|
|
1607
1714
|
}
|
|
1715
|
+
process.stdout.write("\nRestraint self-test:\n");
|
|
1716
|
+
try {
|
|
1717
|
+
const st = await runEvaluatorSelfTest();
|
|
1718
|
+
if (!st.wasmAvailable) {
|
|
1719
|
+
process.stdout.write(dim2(" Cedar WASM not installed; the gate fails closed (denies) until it is.\n"));
|
|
1720
|
+
}
|
|
1721
|
+
for (const c of st.cases) {
|
|
1722
|
+
process.stdout.write(c.pass ? green2(` ${c.name}
|
|
1723
|
+
`) : `\x1B[31m FAIL: ${c.name} (expected ${c.expected}, got ${c.actual})
|
|
1724
|
+
\x1B[0m`);
|
|
1725
|
+
}
|
|
1726
|
+
if (!st.passed) issues++;
|
|
1727
|
+
else process.stdout.write(green2(" the gate denies what it should and allows what it should\n"));
|
|
1728
|
+
} catch (err) {
|
|
1729
|
+
process.stdout.write(yellow2(` self-test could not run: ${err instanceof Error ? err.message : "unknown"}
|
|
1730
|
+
`));
|
|
1731
|
+
issues++;
|
|
1732
|
+
}
|
|
1608
1733
|
process.stdout.write("\n");
|
|
1609
1734
|
if (issues === 0) {
|
|
1610
1735
|
process.stdout.write("\x1B[32m\x1B[1mAll checks passed.\x1B[0m Ready to wrap MCP servers.\n");
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ED25519_TORSION_SUBGROUP,
|
|
3
|
+
RistrettoPoint,
|
|
4
|
+
ed25519,
|
|
5
|
+
ed25519_hasher,
|
|
6
|
+
ed25519ctx,
|
|
7
|
+
ed25519ph,
|
|
8
|
+
edwardsToMontgomery,
|
|
9
|
+
edwardsToMontgomeryPriv,
|
|
10
|
+
edwardsToMontgomeryPub,
|
|
11
|
+
encodeToCurve,
|
|
12
|
+
hashToCurve,
|
|
13
|
+
hashToRistretto255,
|
|
14
|
+
hash_to_ristretto255,
|
|
15
|
+
ristretto255,
|
|
16
|
+
ristretto255_hasher,
|
|
17
|
+
x25519
|
|
18
|
+
} from "./chunk-LYKNULYU.mjs";
|
|
19
|
+
import "./chunk-D733KAPG.mjs";
|
|
20
|
+
import "./chunk-PQJP2ZCI.mjs";
|
|
21
|
+
export {
|
|
22
|
+
ED25519_TORSION_SUBGROUP,
|
|
23
|
+
RistrettoPoint,
|
|
24
|
+
ed25519,
|
|
25
|
+
ed25519_hasher,
|
|
26
|
+
ed25519ctx,
|
|
27
|
+
ed25519ph,
|
|
28
|
+
edwardsToMontgomery,
|
|
29
|
+
edwardsToMontgomeryPriv,
|
|
30
|
+
edwardsToMontgomeryPub,
|
|
31
|
+
encodeToCurve,
|
|
32
|
+
hashToCurve,
|
|
33
|
+
hashToRistretto255,
|
|
34
|
+
hash_to_ristretto255,
|
|
35
|
+
ristretto255,
|
|
36
|
+
ristretto255_hasher,
|
|
37
|
+
x25519
|
|
38
|
+
};
|
package/dist/hook-server.js
CHANGED
|
@@ -89,14 +89,18 @@ function buildEntities(req) {
|
|
|
89
89
|
}
|
|
90
90
|
];
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
function onEvalError(reason, failClosed, extra) {
|
|
93
|
+
return {
|
|
94
|
+
allowed: !failClosed,
|
|
95
|
+
reason: failClosed ? reason : `${reason} (observe mode; would DENY under enforcement)`,
|
|
96
|
+
metadata: { error: true, fail_closed: failClosed, would_deny: true, ...extra || {} }
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async function evaluateCedar(policySet, req, schema, options) {
|
|
100
|
+
const failClosed = options?.failClosed ?? true;
|
|
93
101
|
const available = await ensureCedarWasm();
|
|
94
102
|
if (!available) {
|
|
95
|
-
return {
|
|
96
|
-
allowed: true,
|
|
97
|
-
reason: "cedar_wasm_not_available",
|
|
98
|
-
metadata: { fallback: true }
|
|
99
|
-
};
|
|
103
|
+
return onEvalError("cedar_wasm_not_available", failClosed, { fallback: true });
|
|
100
104
|
}
|
|
101
105
|
try {
|
|
102
106
|
const agentId = req.agentId || req.tier;
|
|
@@ -118,7 +122,7 @@ async function evaluateCedar(policySet, req, schema) {
|
|
|
118
122
|
let result;
|
|
119
123
|
if (typeof cedarWasm.isAuthorized === "function") {
|
|
120
124
|
result = cedarWasm.isAuthorized({
|
|
121
|
-
policies: policySet.source,
|
|
125
|
+
policies: { staticPolicies: policySet.source },
|
|
122
126
|
entities,
|
|
123
127
|
principal: authRequest.principal,
|
|
124
128
|
action: authRequest.action,
|
|
@@ -136,7 +140,7 @@ async function evaluateCedar(policySet, req, schema) {
|
|
|
136
140
|
const cedarEngine = cedarWasm.default || cedarWasm;
|
|
137
141
|
if (typeof cedarEngine.isAuthorized === "function") {
|
|
138
142
|
result = cedarEngine.isAuthorized({
|
|
139
|
-
policies: policySet.source,
|
|
143
|
+
policies: { staticPolicies: policySet.source },
|
|
140
144
|
entities,
|
|
141
145
|
principal: authRequest.principal,
|
|
142
146
|
action: authRequest.action,
|
|
@@ -145,57 +149,56 @@ async function evaluateCedar(policySet, req, schema) {
|
|
|
145
149
|
schema: cedarSchema
|
|
146
150
|
});
|
|
147
151
|
} else {
|
|
148
|
-
return {
|
|
149
|
-
allowed: true,
|
|
150
|
-
reason: "cedar_wasm_api_unsupported",
|
|
151
|
-
metadata: { fallback: true, exports: Object.keys(cedarWasm) }
|
|
152
|
-
};
|
|
152
|
+
return onEvalError("cedar_wasm_api_unsupported", failClosed, { exports: Object.keys(cedarWasm) });
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
-
const
|
|
155
|
+
const parsed = parseWasmResult(result);
|
|
156
|
+
const policyErrors = extractPolicyErrors(result);
|
|
157
|
+
if (parsed.kind === "error") {
|
|
158
|
+
return onEvalError(`cedar_unparseable_result: ${parsed.diagnostics}`, failClosed);
|
|
159
|
+
}
|
|
160
|
+
if (policyErrors.length > 0) {
|
|
161
|
+
return onEvalError(
|
|
162
|
+
`cedar_policy_errored: ${policyErrors.length} policy error(s); decision is unsound`,
|
|
163
|
+
failClosed,
|
|
164
|
+
{ policy_errors: policyErrors.slice(0, 5), policy_digest: policySet.digest }
|
|
165
|
+
);
|
|
166
|
+
}
|
|
156
167
|
return {
|
|
157
|
-
allowed:
|
|
158
|
-
reason:
|
|
168
|
+
allowed: parsed.kind === "allow",
|
|
169
|
+
reason: parsed.kind === "allow" ? void 0 : `cedar_deny${parsed.diagnostics ? ": " + parsed.diagnostics : ""}`,
|
|
159
170
|
metadata: {
|
|
160
171
|
policy_digest: policySet.digest,
|
|
161
|
-
...
|
|
172
|
+
...parsed.matchedPolicies ? { matched_policies: parsed.matchedPolicies } : {}
|
|
162
173
|
}
|
|
163
174
|
};
|
|
164
175
|
} catch (err) {
|
|
165
|
-
return {
|
|
166
|
-
allowed: true,
|
|
167
|
-
reason: `cedar_eval_error: ${err instanceof Error ? err.message : "unknown"}`,
|
|
168
|
-
metadata: { fallback: true, error: true }
|
|
169
|
-
};
|
|
176
|
+
return onEvalError(`cedar_eval_error: ${err instanceof Error ? err.message : "unknown"}`, failClosed);
|
|
170
177
|
}
|
|
171
178
|
}
|
|
172
179
|
function parseWasmResult(result) {
|
|
173
|
-
if (!result) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
|
|
182
|
-
diagnostics: result.diagnostics ? JSON.stringify(result.diagnostics) : void 0,
|
|
183
|
-
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
if (result.decision === "Allow") {
|
|
187
|
-
return { allowed: true };
|
|
188
|
-
}
|
|
189
|
-
if (result.decision === "Deny") {
|
|
190
|
-
return {
|
|
191
|
-
allowed: false,
|
|
192
|
-
diagnostics: result.diagnostics ? JSON.stringify(result.diagnostics) : void 0
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
if (typeof result === "boolean") {
|
|
196
|
-
return { allowed: result };
|
|
180
|
+
if (!result) return { kind: "error", diagnostics: "null result from Cedar WASM" };
|
|
181
|
+
if (result.type === "failure") {
|
|
182
|
+
return { kind: "error", diagnostics: `cedar failure: ${JSON.stringify(result.errors ?? [])}` };
|
|
183
|
+
}
|
|
184
|
+
if (result.type === "success" && result.response) {
|
|
185
|
+
const dec = result.response.decision;
|
|
186
|
+
const reasons = result.response.diagnostics?.reason;
|
|
187
|
+
if (dec === "allow" || dec === "Allow") return { kind: "allow", matchedPolicies: reasons };
|
|
188
|
+
if (dec === "deny" || dec === "Deny") {
|
|
189
|
+
return { kind: "deny", diagnostics: result.response.diagnostics ? JSON.stringify(result.response.diagnostics) : void 0, matchedPolicies: reasons };
|
|
190
|
+
}
|
|
197
191
|
}
|
|
198
|
-
|
|
192
|
+
if (result.type === "allow" || result.decision === "Allow") return { kind: "allow" };
|
|
193
|
+
if (result.type === "deny" || result.decision === "Deny") return { kind: "deny" };
|
|
194
|
+
if (typeof result === "boolean") return result ? { kind: "allow" } : { kind: "deny" };
|
|
195
|
+
return { kind: "error", diagnostics: `unknown result format: ${JSON.stringify(result)}` };
|
|
196
|
+
}
|
|
197
|
+
function extractPolicyErrors(result) {
|
|
198
|
+
if (!result || typeof result !== "object") return [];
|
|
199
|
+
const raw = result.errors ?? result.response?.diagnostics?.errors ?? result.diagnostics?.errors ?? [];
|
|
200
|
+
if (!Array.isArray(raw)) return [];
|
|
201
|
+
return raw.map((e) => typeof e === "string" ? e : e?.message ?? e?.error ?? JSON.stringify(e)).filter(Boolean);
|
|
199
202
|
}
|
|
200
203
|
async function isCedarAvailable() {
|
|
201
204
|
return ensureCedarWasm();
|
package/dist/hook-server.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -595,19 +595,52 @@ interface CedarSchema {
|
|
|
595
595
|
* Throws if the directory doesn't exist or contains no .cedar files.
|
|
596
596
|
*/
|
|
597
597
|
declare function loadCedarPolicies(dirPath: string): CedarPolicySet;
|
|
598
|
+
interface CedarEvalOptions {
|
|
599
|
+
/**
|
|
600
|
+
* Default true (0.7.0+). When true, ANY evaluation error, engine
|
|
601
|
+
* unavailability, malformed result, or per-policy error DENIES. When false
|
|
602
|
+
* (explicit observe/shadow use only), those paths ALLOW but are flagged
|
|
603
|
+
* would_deny:true in the decision metadata so the failure is never silent.
|
|
604
|
+
*/
|
|
605
|
+
failClosed?: boolean;
|
|
606
|
+
}
|
|
598
607
|
/**
|
|
599
608
|
* Evaluate a Cedar policy set against a tool call.
|
|
600
609
|
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
610
|
+
* FAIL-CLOSED by default (0.7.0+): if Cedar is unavailable, the engine API is
|
|
611
|
+
* unsupported, evaluation throws, the result is malformed, or ANY policy errored
|
|
612
|
+
* during evaluation (which Cedar otherwise silently discards, leaving a residual
|
|
613
|
+
* permit standing), this returns DENY. The allow-on-error behavior is reachable
|
|
614
|
+
* only by explicitly passing { failClosed: false }, and even then it is flagged.
|
|
604
615
|
*/
|
|
605
|
-
declare function evaluateCedar(policySet: CedarPolicySet, req: CedarEvalRequest, schema?: CedarSchema): Promise<ExternalDecision>;
|
|
616
|
+
declare function evaluateCedar(policySet: CedarPolicySet, req: CedarEvalRequest, schema?: CedarSchema, options?: CedarEvalOptions): Promise<ExternalDecision>;
|
|
606
617
|
/**
|
|
607
618
|
* Validate that Cedar WASM is available.
|
|
608
619
|
* Useful for CLI startup diagnostics.
|
|
609
620
|
*/
|
|
610
621
|
declare function isCedarAvailable(): Promise<boolean>;
|
|
622
|
+
/** Build a CedarPolicySet from inline source (for the self-test). */
|
|
623
|
+
declare function policySetFromSource(source: string, name?: string): CedarPolicySet;
|
|
624
|
+
interface SelfTestCase {
|
|
625
|
+
name: string;
|
|
626
|
+
expected: 'ALLOW' | 'DENY';
|
|
627
|
+
actual: 'ALLOW' | 'DENY';
|
|
628
|
+
pass: boolean;
|
|
629
|
+
reason?: string;
|
|
630
|
+
}
|
|
631
|
+
interface SelfTestReport {
|
|
632
|
+
wasmAvailable: boolean;
|
|
633
|
+
passed: boolean;
|
|
634
|
+
cases: SelfTestCase[];
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Run known deny/allow vectors through the LIVE evaluator before the gate is
|
|
638
|
+
* trusted. Always proves the fail-closed invariant (the engine being unable to
|
|
639
|
+
* decide must DENY). When Cedar WASM is present it also proves a real forbid
|
|
640
|
+
* denies, a permit allows, and a policy using the silently-discarded
|
|
641
|
+
* `in`-on-String pattern DENIES rather than permit-all (the 0.6.x regression).
|
|
642
|
+
*/
|
|
643
|
+
declare function runEvaluatorSelfTest(): Promise<SelfTestReport>;
|
|
611
644
|
|
|
612
645
|
/**
|
|
613
646
|
* MCP tool-calling gateway that intercepts JSON-RPC requests,
|
|
@@ -2019,29 +2052,6 @@ interface ApprovalResult {
|
|
|
2019
2052
|
contextHash: string;
|
|
2020
2053
|
/** Timestamp of approval */
|
|
2021
2054
|
approvedAt: string;
|
|
2022
|
-
/** On failure, a machine-readable reason (e.g. 'invalid_signature'). */
|
|
2023
|
-
reason?: string;
|
|
2024
|
-
}
|
|
2025
|
-
/**
|
|
2026
|
-
* The registered credential public key, extracted from the COSE_Key at
|
|
2027
|
-
* registration. ES256 keys are an uncompressed P-256 point; EdDSA keys are a
|
|
2028
|
-
* 32-byte Ed25519 public key.
|
|
2029
|
-
*/
|
|
2030
|
-
interface CredentialPublicKey {
|
|
2031
|
-
/** COSE algorithm: -7 = ES256 (P-256 / ECDSA), -8 = EdDSA (Ed25519). */
|
|
2032
|
-
alg: -7 | -8;
|
|
2033
|
-
/** Public key, hex. ES256: 65-byte uncompressed point (0x04 || x || y). EdDSA: 32-byte key. */
|
|
2034
|
-
publicKeyHex: string;
|
|
2035
|
-
}
|
|
2036
|
-
interface VerifyAssertionOptions {
|
|
2037
|
-
/** Allowed origin(s) the assertion must come from, e.g. 'https://app.scopeblind.com'. Defaults to https://<rpId>. */
|
|
2038
|
-
expectedOrigin?: string | string[];
|
|
2039
|
-
/** Require the UV (user-verified / biometric or PIN) flag. Default true. */
|
|
2040
|
-
requireUserVerification?: boolean;
|
|
2041
|
-
/** The signCount stored from the previous assertion; a non-increasing counter signals a cloned authenticator. */
|
|
2042
|
-
prevSignCount?: number;
|
|
2043
|
-
/** Override 'now' (ms) for testing. */
|
|
2044
|
-
now?: number;
|
|
2045
2055
|
}
|
|
2046
2056
|
/**
|
|
2047
2057
|
* Create a WebAuthn challenge for approving a tool call.
|
|
@@ -2078,22 +2088,17 @@ declare function toCredentialRequestOptions(challenge: ApprovalChallenge, allowC
|
|
|
2078
2088
|
};
|
|
2079
2089
|
};
|
|
2080
2090
|
/**
|
|
2081
|
-
* Verify a WebAuthn assertion
|
|
2082
|
-
*
|
|
2083
|
-
*
|
|
2084
|
-
*
|
|
2085
|
-
*
|
|
2086
|
-
* signature over authenticatorData || SHA-256(clientDataJSON) using the
|
|
2087
|
-
* registered credential public key (ES256 or EdDSA); and signCount monotonicity
|
|
2088
|
-
* (clone detection) when a previous count is supplied. Any failure returns
|
|
2089
|
-
* valid:false with a reason; nothing is trusted on a partial check.
|
|
2091
|
+
* Verify a WebAuthn assertion from the client.
|
|
2092
|
+
*
|
|
2093
|
+
* This is a simplified verification that checks the structure
|
|
2094
|
+
* and extracts the authenticator data. For production use with
|
|
2095
|
+
* full signature verification, use the @simplewebauthn/server package.
|
|
2090
2096
|
*
|
|
2091
|
-
* @param challenge -
|
|
2092
|
-
* @param assertion -
|
|
2093
|
-
* @
|
|
2094
|
-
* @param opts - origin / UV / signCount / clock options
|
|
2097
|
+
* @param challenge - The original challenge
|
|
2098
|
+
* @param assertion - The assertion from navigator.credentials.get()
|
|
2099
|
+
* @returns ApprovalResult with verification details
|
|
2095
2100
|
*/
|
|
2096
|
-
declare function verifyApprovalAssertion(challenge: ApprovalChallenge, assertion: ApprovalAssertion
|
|
2101
|
+
declare function verifyApprovalAssertion(challenge: ApprovalChallenge, assertion: ApprovalAssertion): ApprovalResult;
|
|
2097
2102
|
/**
|
|
2098
2103
|
* Create the approval receipt payload for embedding in an Acta receipt.
|
|
2099
2104
|
*
|
|
@@ -3010,4 +3015,4 @@ declare function getScopeBlindBridge(): ScopeBlindBridge;
|
|
|
3010
3015
|
/** Convenience: forward a signed receipt without instantiating yourself. */
|
|
3011
3016
|
declare function forwardReceipt(signedReceipt: any): void;
|
|
3012
3017
|
|
|
3013
|
-
export { type ActionReceipt, type AdmissionResult, type AgentId, type AgentManifest, type ApprovalAssertion, type ApprovalChallenge, type ApprovalNotification, type ApprovalResult, type ArenaPayload, type ArenaReceipt, type AttestationDocument, type AttestationPayload, type AttestationProvider, type AttestationReceipt, type AttestationResult, type AuditBundle, type AuditBundleOptions, type BenchmarkPayload, type BenchmarkReceipt, type BuilderId, type C2PAAssertion, type C2PAIngredient, type C2PAManifest, type C2PAOptions, type CCRConnectorConfig, type CCRSessionContext, type CalibrationScore, type CedarEvalRequest, type CedarPolicySet, type CedarSchema, type CedarSchemaResult, type CommittedFieldOpening, type CommittedSignResult, type ComplianceReport, ConfidentialGate, type ConfidentialGateConfig, type ConfidentialInferenceConfig, type CredentialConfig, type DecisionContext, type DecisionLog, type DelegationReceipt, type DisclosureMode, type Ed25519PublicKey, type EvidenceAttestation, type EvidenceAttestationInput, type EvidenceIssuer, type EvidenceReceipt, type EvidenceReceiptBase, type EvidenceSummary, type EvidenceSummaryEntry, type EvidenceType, type ExternalDecision, type ExternalPDPConfig, type HFDatasetMetadata, type HFReceiptRow, type HookEventName, type HookInput, type HookResponse, type IssuerType, type JsonRpcRequest, type JsonRpcResponse, type LeaseCompatibility, type ManifestBuilder, type ManifestCapabilities, type ManifestConfig, type ManifestIdentity, type ManifestPresentation, type ManifestSignature, type ManifestStatus, type McpToolDescription, type MinimalDisclosure, type NotificationConfig, type PassportTokenClaims, type PayloadDigest, type PlanReceipt, type PolicyEngineMode, type PredictionReceipt, type PredictionResolution, type PropagatorConfig, type ProtectConfig, ProtectGateway, type ProtectPolicy, type RateLimit, ReceiptPropagator, type RedactedResult, type RedactionSalt, type RekorAnchor, type RekorVerification, type RestraintPayload, type RestraintReceipt, type SHA256Hash, type SafetyTranscript, type Sandbox, type SandboxConfig, type SandboxReceipt, type SandboxResult, type SandboxToolCall, type SchemaGeneratorConfig, ScopeBlindBridge, type SigningConfig, type SimulationResult, type SimulationSummary, type SwarmContext, type TierOverrides, type TimingMetrics, type ToolPolicy, type TrustTier, type WorkPayload, type WorkReceipt, anchorToRekor, buildDecisionContext, checkRateLimit, collectSignedReceipts, computeCalibration, confidentialInference, createApprovalChallenge, createApprovalReceiptPayload, createAttestationField, createAuditBundle, createC2PAManifest, createDisclosurePackage, createEvidenceAttestation, createLogAnchorField, createReceiptChannel, createSandbox, destroySandbox, discloseField, ed25519ToDIDKey, evaluateCedar, evaluateTier, exportC2PAManifestJSON, exportJSONL, formatReportMarkdown, formatSimulation, forwardReceipt, generateC2PACommand, generateCedarSchema, generateDatasetCard, generateHFMetadata, generateReport, generateSafetyTranscript, generateSchemaStub, getScopeBlindBridge, getSignerInfo, getToolPolicy, hashReceipt, hashResponseBody, initSigning, isAgentId, isCedarAvailable, isDisclosureMode, isEvidenceType, isManifestStatus, isSigningEnabled, listCredentialLabels, loadCedarPolicies, loadPolicy, manifestToVC, meetsMinTier, parseLogFile, parseNotificationConfigFromEnv, parseRateLimit, queryExternalPDP, receiptToVP, receiptsToHFRows, redactFields, resolveCredential, revealField, runInSandbox, sendApprovalNotification, signCommittedDecision, signDecision, simulate, toCredentialRequestOptions, toManifoldFormat, toMetaculusFormat, validateCredentials, validateEvidenceReceipt, validateManifest, verifyActaC2PAAssertions, verifyAllCommitments, verifyApprovalAssertion, verifyCommitment, verifyEvidenceAttestation, verifyRekorAnchor };
|
|
3018
|
+
export { type ActionReceipt, type AdmissionResult, type AgentId, type AgentManifest, type ApprovalAssertion, type ApprovalChallenge, type ApprovalNotification, type ApprovalResult, type ArenaPayload, type ArenaReceipt, type AttestationDocument, type AttestationPayload, type AttestationProvider, type AttestationReceipt, type AttestationResult, type AuditBundle, type AuditBundleOptions, type BenchmarkPayload, type BenchmarkReceipt, type BuilderId, type C2PAAssertion, type C2PAIngredient, type C2PAManifest, type C2PAOptions, type CCRConnectorConfig, type CCRSessionContext, type CalibrationScore, type CedarEvalOptions, type CedarEvalRequest, type CedarPolicySet, type CedarSchema, type CedarSchemaResult, type CommittedFieldOpening, type CommittedSignResult, type ComplianceReport, ConfidentialGate, type ConfidentialGateConfig, type ConfidentialInferenceConfig, type CredentialConfig, type DecisionContext, type DecisionLog, type DelegationReceipt, type DisclosureMode, type Ed25519PublicKey, type EvidenceAttestation, type EvidenceAttestationInput, type EvidenceIssuer, type EvidenceReceipt, type EvidenceReceiptBase, type EvidenceSummary, type EvidenceSummaryEntry, type EvidenceType, type ExternalDecision, type ExternalPDPConfig, type HFDatasetMetadata, type HFReceiptRow, type HookEventName, type HookInput, type HookResponse, type IssuerType, type JsonRpcRequest, type JsonRpcResponse, type LeaseCompatibility, type ManifestBuilder, type ManifestCapabilities, type ManifestConfig, type ManifestIdentity, type ManifestPresentation, type ManifestSignature, type ManifestStatus, type McpToolDescription, type MinimalDisclosure, type NotificationConfig, type PassportTokenClaims, type PayloadDigest, type PlanReceipt, type PolicyEngineMode, type PredictionReceipt, type PredictionResolution, type PropagatorConfig, type ProtectConfig, ProtectGateway, type ProtectPolicy, type RateLimit, ReceiptPropagator, type RedactedResult, type RedactionSalt, type RekorAnchor, type RekorVerification, type RestraintPayload, type RestraintReceipt, type SHA256Hash, type SafetyTranscript, type Sandbox, type SandboxConfig, type SandboxReceipt, type SandboxResult, type SandboxToolCall, type SchemaGeneratorConfig, ScopeBlindBridge, type SelfTestCase, type SelfTestReport, type SigningConfig, type SimulationResult, type SimulationSummary, type SwarmContext, type TierOverrides, type TimingMetrics, type ToolPolicy, type TrustTier, type WorkPayload, type WorkReceipt, anchorToRekor, buildDecisionContext, checkRateLimit, collectSignedReceipts, computeCalibration, confidentialInference, createApprovalChallenge, createApprovalReceiptPayload, createAttestationField, createAuditBundle, createC2PAManifest, createDisclosurePackage, createEvidenceAttestation, createLogAnchorField, createReceiptChannel, createSandbox, destroySandbox, discloseField, ed25519ToDIDKey, evaluateCedar, evaluateTier, exportC2PAManifestJSON, exportJSONL, formatReportMarkdown, formatSimulation, forwardReceipt, generateC2PACommand, generateCedarSchema, generateDatasetCard, generateHFMetadata, generateReport, generateSafetyTranscript, generateSchemaStub, getScopeBlindBridge, getSignerInfo, getToolPolicy, hashReceipt, hashResponseBody, initSigning, isAgentId, isCedarAvailable, isDisclosureMode, isEvidenceType, isManifestStatus, isSigningEnabled, listCredentialLabels, loadCedarPolicies, loadPolicy, manifestToVC, meetsMinTier, parseLogFile, parseNotificationConfigFromEnv, parseRateLimit, policySetFromSource, queryExternalPDP, receiptToVP, receiptsToHFRows, redactFields, resolveCredential, revealField, runEvaluatorSelfTest, runInSandbox, sendApprovalNotification, signCommittedDecision, signDecision, simulate, toCredentialRequestOptions, toManifoldFormat, toMetaculusFormat, validateCredentials, validateEvidenceReceipt, validateManifest, verifyActaC2PAAssertions, verifyAllCommitments, verifyApprovalAssertion, verifyCommitment, verifyEvidenceAttestation, verifyRekorAnchor };
|