protect-mcp 0.6.2 → 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-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 +204 -53
- package/dist/cli.mjs +137 -12
- 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 +38 -5
- package/dist/index.d.ts +38 -5
- package/dist/index.js +82 -48
- package/dist/index.mjs +8 -4
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -35080,12 +35080,14 @@ __export(index_exports, {
|
|
|
35080
35080
|
parseLogFile: () => parseLogFile,
|
|
35081
35081
|
parseNotificationConfigFromEnv: () => parseNotificationConfigFromEnv,
|
|
35082
35082
|
parseRateLimit: () => parseRateLimit,
|
|
35083
|
+
policySetFromSource: () => policySetFromSource,
|
|
35083
35084
|
queryExternalPDP: () => queryExternalPDP,
|
|
35084
35085
|
receiptToVP: () => receiptToVP,
|
|
35085
35086
|
receiptsToHFRows: () => receiptsToHFRows,
|
|
35086
35087
|
redactFields: () => redactFields,
|
|
35087
35088
|
resolveCredential: () => resolveCredential,
|
|
35088
35089
|
revealField: () => revealField,
|
|
35090
|
+
runEvaluatorSelfTest: () => runEvaluatorSelfTest,
|
|
35089
35091
|
runInSandbox: () => runInSandbox,
|
|
35090
35092
|
sendApprovalNotification: () => sendApprovalNotification,
|
|
35091
35093
|
signCommittedDecision: () => signCommittedDecision,
|
|
@@ -35798,14 +35800,18 @@ function buildEntities(req) {
|
|
|
35798
35800
|
}
|
|
35799
35801
|
];
|
|
35800
35802
|
}
|
|
35801
|
-
|
|
35803
|
+
function onEvalError(reason, failClosed, extra) {
|
|
35804
|
+
return {
|
|
35805
|
+
allowed: !failClosed,
|
|
35806
|
+
reason: failClosed ? reason : `${reason} (observe mode; would DENY under enforcement)`,
|
|
35807
|
+
metadata: { error: true, fail_closed: failClosed, would_deny: true, ...extra || {} }
|
|
35808
|
+
};
|
|
35809
|
+
}
|
|
35810
|
+
async function evaluateCedar(policySet, req, schema, options) {
|
|
35811
|
+
const failClosed = options?.failClosed ?? true;
|
|
35802
35812
|
const available = await ensureCedarWasm();
|
|
35803
35813
|
if (!available) {
|
|
35804
|
-
return {
|
|
35805
|
-
allowed: true,
|
|
35806
|
-
reason: "cedar_wasm_not_available",
|
|
35807
|
-
metadata: { fallback: true }
|
|
35808
|
-
};
|
|
35814
|
+
return onEvalError("cedar_wasm_not_available", failClosed, { fallback: true });
|
|
35809
35815
|
}
|
|
35810
35816
|
try {
|
|
35811
35817
|
const agentId = req.agentId || req.tier;
|
|
@@ -35827,7 +35833,7 @@ async function evaluateCedar(policySet, req, schema) {
|
|
|
35827
35833
|
let result;
|
|
35828
35834
|
if (typeof cedarWasm.isAuthorized === "function") {
|
|
35829
35835
|
result = cedarWasm.isAuthorized({
|
|
35830
|
-
policies: policySet.source,
|
|
35836
|
+
policies: { staticPolicies: policySet.source },
|
|
35831
35837
|
entities,
|
|
35832
35838
|
principal: authRequest.principal,
|
|
35833
35839
|
action: authRequest.action,
|
|
@@ -35845,7 +35851,7 @@ async function evaluateCedar(policySet, req, schema) {
|
|
|
35845
35851
|
const cedarEngine = cedarWasm.default || cedarWasm;
|
|
35846
35852
|
if (typeof cedarEngine.isAuthorized === "function") {
|
|
35847
35853
|
result = cedarEngine.isAuthorized({
|
|
35848
|
-
policies: policySet.source,
|
|
35854
|
+
policies: { staticPolicies: policySet.source },
|
|
35849
35855
|
entities,
|
|
35850
35856
|
principal: authRequest.principal,
|
|
35851
35857
|
action: authRequest.action,
|
|
@@ -35854,61 +35860,87 @@ async function evaluateCedar(policySet, req, schema) {
|
|
|
35854
35860
|
schema: cedarSchema
|
|
35855
35861
|
});
|
|
35856
35862
|
} else {
|
|
35857
|
-
return {
|
|
35858
|
-
allowed: true,
|
|
35859
|
-
reason: "cedar_wasm_api_unsupported",
|
|
35860
|
-
metadata: { fallback: true, exports: Object.keys(cedarWasm) }
|
|
35861
|
-
};
|
|
35863
|
+
return onEvalError("cedar_wasm_api_unsupported", failClosed, { exports: Object.keys(cedarWasm) });
|
|
35862
35864
|
}
|
|
35863
35865
|
}
|
|
35864
|
-
const
|
|
35866
|
+
const parsed = parseWasmResult(result);
|
|
35867
|
+
const policyErrors = extractPolicyErrors(result);
|
|
35868
|
+
if (parsed.kind === "error") {
|
|
35869
|
+
return onEvalError(`cedar_unparseable_result: ${parsed.diagnostics}`, failClosed);
|
|
35870
|
+
}
|
|
35871
|
+
if (policyErrors.length > 0) {
|
|
35872
|
+
return onEvalError(
|
|
35873
|
+
`cedar_policy_errored: ${policyErrors.length} policy error(s); decision is unsound`,
|
|
35874
|
+
failClosed,
|
|
35875
|
+
{ policy_errors: policyErrors.slice(0, 5), policy_digest: policySet.digest }
|
|
35876
|
+
);
|
|
35877
|
+
}
|
|
35865
35878
|
return {
|
|
35866
|
-
allowed:
|
|
35867
|
-
reason:
|
|
35879
|
+
allowed: parsed.kind === "allow",
|
|
35880
|
+
reason: parsed.kind === "allow" ? void 0 : `cedar_deny${parsed.diagnostics ? ": " + parsed.diagnostics : ""}`,
|
|
35868
35881
|
metadata: {
|
|
35869
35882
|
policy_digest: policySet.digest,
|
|
35870
|
-
...
|
|
35883
|
+
...parsed.matchedPolicies ? { matched_policies: parsed.matchedPolicies } : {}
|
|
35871
35884
|
}
|
|
35872
35885
|
};
|
|
35873
35886
|
} catch (err) {
|
|
35874
|
-
return {
|
|
35875
|
-
allowed: true,
|
|
35876
|
-
reason: `cedar_eval_error: ${err instanceof Error ? err.message : "unknown"}`,
|
|
35877
|
-
metadata: { fallback: true, error: true }
|
|
35878
|
-
};
|
|
35887
|
+
return onEvalError(`cedar_eval_error: ${err instanceof Error ? err.message : "unknown"}`, failClosed);
|
|
35879
35888
|
}
|
|
35880
35889
|
}
|
|
35881
35890
|
function parseWasmResult(result) {
|
|
35882
|
-
if (!result) {
|
|
35883
|
-
|
|
35884
|
-
|
|
35885
|
-
|
|
35886
|
-
|
|
35887
|
-
|
|
35888
|
-
|
|
35889
|
-
return {
|
|
35890
|
-
|
|
35891
|
-
diagnostics: result.diagnostics ? JSON.stringify(result.diagnostics) : void 0,
|
|
35892
|
-
|
|
35893
|
-
|
|
35894
|
-
}
|
|
35895
|
-
if (result.decision === "
|
|
35896
|
-
|
|
35897
|
-
}
|
|
35898
|
-
|
|
35899
|
-
|
|
35900
|
-
|
|
35901
|
-
|
|
35902
|
-
|
|
35903
|
-
|
|
35904
|
-
if (typeof result === "boolean") {
|
|
35905
|
-
return { allowed: result };
|
|
35906
|
-
}
|
|
35907
|
-
return { allowed: true, diagnostics: `unknown result format: ${JSON.stringify(result)}` };
|
|
35891
|
+
if (!result) return { kind: "error", diagnostics: "null result from Cedar WASM" };
|
|
35892
|
+
if (result.type === "failure") {
|
|
35893
|
+
return { kind: "error", diagnostics: `cedar failure: ${JSON.stringify(result.errors ?? [])}` };
|
|
35894
|
+
}
|
|
35895
|
+
if (result.type === "success" && result.response) {
|
|
35896
|
+
const dec = result.response.decision;
|
|
35897
|
+
const reasons = result.response.diagnostics?.reason;
|
|
35898
|
+
if (dec === "allow" || dec === "Allow") return { kind: "allow", matchedPolicies: reasons };
|
|
35899
|
+
if (dec === "deny" || dec === "Deny") {
|
|
35900
|
+
return { kind: "deny", diagnostics: result.response.diagnostics ? JSON.stringify(result.response.diagnostics) : void 0, matchedPolicies: reasons };
|
|
35901
|
+
}
|
|
35902
|
+
}
|
|
35903
|
+
if (result.type === "allow" || result.decision === "Allow") return { kind: "allow" };
|
|
35904
|
+
if (result.type === "deny" || result.decision === "Deny") return { kind: "deny" };
|
|
35905
|
+
if (typeof result === "boolean") return result ? { kind: "allow" } : { kind: "deny" };
|
|
35906
|
+
return { kind: "error", diagnostics: `unknown result format: ${JSON.stringify(result)}` };
|
|
35907
|
+
}
|
|
35908
|
+
function extractPolicyErrors(result) {
|
|
35909
|
+
if (!result || typeof result !== "object") return [];
|
|
35910
|
+
const raw = result.errors ?? result.response?.diagnostics?.errors ?? result.diagnostics?.errors ?? [];
|
|
35911
|
+
if (!Array.isArray(raw)) return [];
|
|
35912
|
+
return raw.map((e) => typeof e === "string" ? e : e?.message ?? e?.error ?? JSON.stringify(e)).filter(Boolean);
|
|
35908
35913
|
}
|
|
35909
35914
|
async function isCedarAvailable() {
|
|
35910
35915
|
return ensureCedarWasm();
|
|
35911
35916
|
}
|
|
35917
|
+
function policySetFromSource(source, name = "inline") {
|
|
35918
|
+
const digest = (0, import_node_crypto2.createHash)("sha256").update(source).digest("hex").slice(0, 16);
|
|
35919
|
+
return { source, digest, fileCount: 1, files: [name] };
|
|
35920
|
+
}
|
|
35921
|
+
async function runEvaluatorSelfTest() {
|
|
35922
|
+
const wasmAvailable = await isCedarAvailable();
|
|
35923
|
+
const cases = [];
|
|
35924
|
+
const run = async (name, expected, policy, context) => {
|
|
35925
|
+
const d = await evaluateCedar(policy, { tool: "Bash", tier: "unknown", context }, void 0, { failClosed: true });
|
|
35926
|
+
const actual = d.allowed ? "ALLOW" : "DENY";
|
|
35927
|
+
cases.push({ name, expected, actual, pass: actual === expected, reason: d.reason });
|
|
35928
|
+
};
|
|
35929
|
+
if (!wasmAvailable) {
|
|
35930
|
+
await run("engine unavailable denies", "DENY", policySetFromSource("permit(principal, action, resource);"), {});
|
|
35931
|
+
return { wasmAvailable, passed: cases.every((c) => c.pass), cases };
|
|
35932
|
+
}
|
|
35933
|
+
const correct = policySetFromSource(
|
|
35934
|
+
'forbid(principal, action, resource) when { ["rm", "dd", "mkfs"].contains(context.command) };\npermit(principal, action, resource);'
|
|
35935
|
+
);
|
|
35936
|
+
await run("forbid denies rm", "DENY", correct, { command: "rm" });
|
|
35937
|
+
await run("permit allows ls", "ALLOW", correct, { command: "ls" });
|
|
35938
|
+
const broken = policySetFromSource(
|
|
35939
|
+
'forbid(principal, action, resource) when { context.command in ["rm", "dd"] };\npermit(principal, action, resource);'
|
|
35940
|
+
);
|
|
35941
|
+
await run("in-on-String forbid does not permit-all", "DENY", broken, { command: "rm" });
|
|
35942
|
+
return { wasmAvailable, passed: cases.every((c) => c.pass), cases };
|
|
35943
|
+
}
|
|
35912
35944
|
|
|
35913
35945
|
// src/notifications.ts
|
|
35914
35946
|
async function sendApprovalNotification(config, notification) {
|
|
@@ -42732,12 +42764,14 @@ function createSandboxServer() {
|
|
|
42732
42764
|
parseLogFile,
|
|
42733
42765
|
parseNotificationConfigFromEnv,
|
|
42734
42766
|
parseRateLimit,
|
|
42767
|
+
policySetFromSource,
|
|
42735
42768
|
queryExternalPDP,
|
|
42736
42769
|
receiptToVP,
|
|
42737
42770
|
receiptsToHFRows,
|
|
42738
42771
|
redactFields,
|
|
42739
42772
|
resolveCredential,
|
|
42740
42773
|
revealField,
|
|
42774
|
+
runEvaluatorSelfTest,
|
|
42741
42775
|
runInSandbox,
|
|
42742
42776
|
sendApprovalNotification,
|
|
42743
42777
|
signCommittedDecision,
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
formatSimulation,
|
|
7
7
|
parseLogFile,
|
|
8
8
|
simulate
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ZBKJANP7.mjs";
|
|
10
10
|
import {
|
|
11
11
|
ProtectGateway,
|
|
12
12
|
buildDecisionContext,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
resolveCredential,
|
|
19
19
|
sendApprovalNotification,
|
|
20
20
|
validateCredentials
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-OHUTUFTC.mjs";
|
|
22
22
|
import {
|
|
23
23
|
createSandboxServer
|
|
24
24
|
} from "./chunk-J6L4XCTE.mjs";
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
forwardReceipt,
|
|
34
34
|
getScopeBlindBridge,
|
|
35
35
|
startHookServer
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-X63ELMU4.mjs";
|
|
37
37
|
import {
|
|
38
38
|
checkRateLimit,
|
|
39
39
|
evaluateCedar,
|
|
@@ -45,8 +45,10 @@ import {
|
|
|
45
45
|
loadCedarPolicies,
|
|
46
46
|
loadPolicy,
|
|
47
47
|
parseRateLimit,
|
|
48
|
+
policySetFromSource,
|
|
49
|
+
runEvaluatorSelfTest,
|
|
48
50
|
signDecision
|
|
49
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-546U3A7R.mjs";
|
|
50
52
|
import {
|
|
51
53
|
ed25519,
|
|
52
54
|
sha256
|
|
@@ -1995,12 +1997,14 @@ export {
|
|
|
1995
1997
|
parseLogFile,
|
|
1996
1998
|
parseNotificationConfigFromEnv,
|
|
1997
1999
|
parseRateLimit,
|
|
2000
|
+
policySetFromSource,
|
|
1998
2001
|
queryExternalPDP,
|
|
1999
2002
|
receiptToVP,
|
|
2000
2003
|
receiptsToHFRows,
|
|
2001
2004
|
redactFields,
|
|
2002
2005
|
resolveCredential,
|
|
2003
2006
|
revealField,
|
|
2007
|
+
runEvaluatorSelfTest,
|
|
2004
2008
|
runInSandbox,
|
|
2005
2009
|
sendApprovalNotification,
|
|
2006
2010
|
signCommittedDecision,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "protect-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"mcpName": "com.scopeblind/protect-mcp",
|
|
5
|
-
"description": "Cedar policy + signed receipts for AI agent
|
|
5
|
+
"description": "Fail-closed Cedar policy gate + signed receipts for AI agent tool calls. Blocks what breaks the rules before it runs, denies on any policy error, and proves the gate is live with a startup self-test.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"module": "dist/index.mjs",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"dist",
|
|
26
26
|
"policies",
|
|
27
|
-
"README.md"
|
|
27
|
+
"README.md",
|
|
28
|
+
"CHANGELOG.md"
|
|
28
29
|
],
|
|
29
30
|
"keywords": [
|
|
30
31
|
"scopeblind",
|