protect-mcp 0.7.6 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -27339,11 +27339,11 @@ var require_util3 = __commonJS({
27339
27339
  return false;
27340
27340
  }
27341
27341
  exports2.schemaHasRules = schemaHasRules;
27342
- function schemaHasRulesButRef(schema, RULES) {
27342
+ function schemaHasRulesButRef(schema, RULES2) {
27343
27343
  if (typeof schema == "boolean")
27344
27344
  return !schema;
27345
27345
  for (const key in schema)
27346
- if (key !== "$ref" && RULES.all[key])
27346
+ if (key !== "$ref" && RULES2.all[key])
27347
27347
  return true;
27348
27348
  return false;
27349
27349
  }
@@ -28737,17 +28737,17 @@ var require_validate = __commonJS({
28737
28737
  }
28738
28738
  function schemaKeywords(it, types, typeErrors, errsCount) {
28739
28739
  const { gen, schema, data, allErrors, opts, self } = it;
28740
- const { RULES } = self;
28741
- if (schema.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema, RULES))) {
28742
- gen.block(() => keywordCode(it, "$ref", RULES.all.$ref.definition));
28740
+ const { RULES: RULES2 } = self;
28741
+ if (schema.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema, RULES2))) {
28742
+ gen.block(() => keywordCode(it, "$ref", RULES2.all.$ref.definition));
28743
28743
  return;
28744
28744
  }
28745
28745
  if (!opts.jtd)
28746
28746
  checkStrictTypes(it, types);
28747
28747
  gen.block(() => {
28748
- for (const group of RULES.rules)
28748
+ for (const group of RULES2.rules)
28749
28749
  groupKeywords(group);
28750
- groupKeywords(RULES.post);
28750
+ groupKeywords(RULES2.post);
28751
28751
  });
28752
28752
  function groupKeywords(group) {
28753
28753
  if (!(0, applicability_1.shouldUseGroup)(schema, group))
@@ -30449,10 +30449,10 @@ var require_core3 = __commonJS({
30449
30449
  }
30450
30450
  // Remove keyword
30451
30451
  removeKeyword(keyword) {
30452
- const { RULES } = this;
30453
- delete RULES.keywords[keyword];
30454
- delete RULES.all[keyword];
30455
- for (const group of RULES.rules) {
30452
+ const { RULES: RULES2 } = this;
30453
+ delete RULES2.keywords[keyword];
30454
+ delete RULES2.all[keyword];
30455
+ for (const group of RULES2.rules) {
30456
30456
  const i = group.rules.findIndex((rule) => rule.keyword === keyword);
30457
30457
  if (i >= 0)
30458
30458
  group.rules.splice(i, 1);
@@ -30620,9 +30620,9 @@ var require_core3 = __commonJS({
30620
30620
  }
30621
30621
  var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
30622
30622
  function checkKeyword(keyword, def) {
30623
- const { RULES } = this;
30623
+ const { RULES: RULES2 } = this;
30624
30624
  (0, util_1.eachItem)(keyword, (kwd) => {
30625
- if (RULES.keywords[kwd])
30625
+ if (RULES2.keywords[kwd])
30626
30626
  throw new Error(`Keyword ${kwd} is already defined`);
30627
30627
  if (!KEYWORD_NAME.test(kwd))
30628
30628
  throw new Error(`Keyword ${kwd} has invalid name`);
@@ -30638,13 +30638,13 @@ var require_core3 = __commonJS({
30638
30638
  const post = definition === null || definition === void 0 ? void 0 : definition.post;
30639
30639
  if (dataType && post)
30640
30640
  throw new Error('keyword with "post" flag cannot have "type"');
30641
- const { RULES } = this;
30642
- let ruleGroup = post ? RULES.post : RULES.rules.find(({ type: t }) => t === dataType);
30641
+ const { RULES: RULES2 } = this;
30642
+ let ruleGroup = post ? RULES2.post : RULES2.rules.find(({ type: t }) => t === dataType);
30643
30643
  if (!ruleGroup) {
30644
30644
  ruleGroup = { type: dataType, rules: [] };
30645
- RULES.rules.push(ruleGroup);
30645
+ RULES2.rules.push(ruleGroup);
30646
30646
  }
30647
- RULES.keywords[keyword] = true;
30647
+ RULES2.keywords[keyword] = true;
30648
30648
  if (!definition)
30649
30649
  return;
30650
30650
  const rule = {
@@ -30659,7 +30659,7 @@ var require_core3 = __commonJS({
30659
30659
  addBeforeRule.call(this, ruleGroup, rule, definition.before);
30660
30660
  else
30661
30661
  ruleGroup.rules.push(rule);
30662
- RULES.all[keyword] = rule;
30662
+ RULES2.all[keyword] = rule;
30663
30663
  (_a = definition.implements) === null || _a === void 0 ? void 0 : _a.forEach((kwd) => this.addKeyword(kwd));
30664
30664
  }
30665
30665
  function addBeforeRule(ruleGroup, rule, before) {
@@ -35553,6 +35553,7 @@ function signDecision(entry) {
35553
35553
  if (entry.timing) payload.timing = entry.timing;
35554
35554
  if (entry.swarm) payload.swarm = entry.swarm;
35555
35555
  if (entry.payload_digest) payload.payload_digest = entry.payload_digest;
35556
+ if (entry.enrichment) payload.enrichment = entry.enrichment;
35556
35557
  if (entry.action_readback) payload.action_readback = entry.action_readback;
35557
35558
  if (entry.deny_iteration) payload.deny_iteration = entry.deny_iteration;
35558
35559
  const result = artifactsModule.createSignedArtifact(
@@ -40360,6 +40361,89 @@ function forwardReceipt(signedReceipt) {
40360
40361
  getScopeBlindBridge().forward(signedReceipt);
40361
40362
  }
40362
40363
 
40364
+ // src/receipt-enrichment.ts
40365
+ var ENRICHMENT_VERSION = 1;
40366
+ function canonicalJson(value) {
40367
+ const seen = /* @__PURE__ */ new WeakSet();
40368
+ const enc = (v) => {
40369
+ if (v === null || v === void 0) return "null";
40370
+ const t = typeof v;
40371
+ if (t === "number") return Number.isFinite(v) ? JSON.stringify(v) : "null";
40372
+ if (t === "boolean" || t === "string") return JSON.stringify(v);
40373
+ if (t === "bigint") return JSON.stringify(v.toString());
40374
+ if (t === "function" || t === "symbol") return "null";
40375
+ if (Array.isArray(v)) return "[" + v.map(enc).join(",") + "]";
40376
+ if (t === "object") {
40377
+ const o = v;
40378
+ if (seen.has(o)) return '"[circular]"';
40379
+ seen.add(o);
40380
+ const body = Object.keys(o).sort().map((k) => JSON.stringify(k) + ":" + enc(o[k])).join(",");
40381
+ seen.delete(o);
40382
+ return "{" + body + "}";
40383
+ }
40384
+ return "null";
40385
+ };
40386
+ return enc(value);
40387
+ }
40388
+ function sha256Hex(s) {
40389
+ return bytesToHex(sha2562(new TextEncoder().encode(s)));
40390
+ }
40391
+ var RULES = [
40392
+ { cap: "exec.shell", tool: /bash|shell|exec|terminal|run_command|command/ },
40393
+ { cap: "fs.read", tool: /(^|[_.])(read|cat|glob|grep|search|ls|view|list_files|open)/ },
40394
+ { cap: "fs.write", tool: /write|create_file|save|append|edit|patch|replace|update_file|multiedit|notebook/ },
40395
+ { cap: "fs.delete", tool: /delete|remove|unlink|trash|(^|[_.])rm/ },
40396
+ { cap: "net.egress", tool: /fetch|http|curl|wget|request|download|browse|navigate|webfetch|web_search|scrape/ },
40397
+ { cap: "vcs", tool: /(^|[_.])git/, text: /\bgit\s+(commit|push|pull|clone|reset|checkout|branch|rebase|merge|tag)\b/ },
40398
+ { cap: "package.install", text: /\b(npm|pnpm|yarn)\s+(i|install|add)\b|\bpip3?\s+install\b|\bgo\s+get\b|\bcargo\s+add\b|\bbrew\s+install\b|\bapt(-get)?\s+install\b|\bgem\s+install\b/ },
40399
+ { cap: "secret.adjacent", text: /\.env\b|secret|credential|passwd|password|api[_-]?key|private[_-]?key|\.pem\b|\.key\b|id_rsa|bearer\s|aws_(access|secret)|authorization/ },
40400
+ { cap: "destructive", text: /rm\s+-[a-z]*[rf]|\brmdir\b|drop\s+table|truncate\s+table|delete\s+from|reset\s+--hard|--force\b|\bmkfs\b|\bdd\s+if=|shutdown|reboot|kill\s+-9|>\s*\/dev\/sd/ },
40401
+ { cap: "financial", text: /\b(order|trade|buy|sell|transfer|wire|payment|withdraw|deposit|swap|invoice|charge|refund|settle)\b/ },
40402
+ { cap: "data.query", text: /\bselect\s+[\s\S]+\bfrom\b|\binsert\s+into\b|\bupdate\s+[\s\S]+\bset\b|\bdelete\s+from\b/ }
40403
+ ];
40404
+ function deriveCapabilities(tool, input) {
40405
+ const t = String(tool || "").toLowerCase();
40406
+ let text = "";
40407
+ try {
40408
+ text = canonicalJson(input).toLowerCase();
40409
+ } catch {
40410
+ }
40411
+ const caps = /* @__PURE__ */ new Set();
40412
+ for (const r of RULES) {
40413
+ if (r.tool && r.tool.test(t)) caps.add(r.cap);
40414
+ if (r.text && r.text.test(text)) caps.add(r.cap);
40415
+ }
40416
+ return Array.from(caps).sort();
40417
+ }
40418
+ function deriveResource(input) {
40419
+ const o = input && typeof input === "object" ? input : {};
40420
+ const path = o.file_path ?? o.path ?? o.filePath ?? o.notebook_path ?? o.filename;
40421
+ if (typeof path === "string" && path.trim()) return { kind: "path", digest: sha256Hex(path.replace(/\\/g, "/")) };
40422
+ const url = o.url ?? o.uri ?? o.endpoint ?? o.href;
40423
+ if (typeof url === "string" && url.trim()) {
40424
+ try {
40425
+ return { kind: "host", digest: sha256Hex(new URL(url).host.toLowerCase()) };
40426
+ } catch {
40427
+ }
40428
+ }
40429
+ const cmd = o.command ?? o.cmd ?? o.script;
40430
+ if (typeof cmd === "string" && cmd.trim()) {
40431
+ const first = cmd.trim().split(/\s+/)[0];
40432
+ if (first) return { kind: "command", digest: sha256Hex(first) };
40433
+ }
40434
+ return void 0;
40435
+ }
40436
+ function buildEnrichment(tool, input) {
40437
+ const e = {
40438
+ v: ENRICHMENT_VERSION,
40439
+ input_digest: sha256Hex(canonicalJson(input ?? {})),
40440
+ capabilities: deriveCapabilities(tool, input)
40441
+ };
40442
+ const resource = deriveResource(input);
40443
+ if (resource) e.resource = resource;
40444
+ return e;
40445
+ }
40446
+
40363
40447
  // src/hook-server.ts
40364
40448
  var DEFAULT_PORT = 9377;
40365
40449
  var LOG_FILE3 = ".protect-mcp-log.jsonl";
@@ -40432,6 +40516,9 @@ async function handlePreToolUse(input, state) {
40432
40516
  });
40433
40517
  const payloadDigest = computePayloadDigest(input.toolInput);
40434
40518
  const actionReadback = buildActionReadback(toolName, input.toolInput || {});
40519
+ const enrichment = buildEnrichment(toolName, input.toolInput || {});
40520
+ const inflightRec = state.inflightTools.get(requestId);
40521
+ if (inflightRec) inflightRec.enrichment = enrichment;
40435
40522
  const swarm = {
40436
40523
  ...state.swarmContext,
40437
40524
  ...input.agentId && { agent_id: input.agentId },
@@ -40851,6 +40938,8 @@ function emitDecisionLog(state, entry) {
40851
40938
  ...entry.sandbox_state && { sandbox_state: entry.sandbox_state },
40852
40939
  ...entry.plan_receipt_id && { plan_receipt_id: entry.plan_receipt_id }
40853
40940
  };
40941
+ const enr = state.inflightTools.get(log.request_id)?.enrichment;
40942
+ if (enr) log.enrichment = enr;
40854
40943
  process.stderr.write(`[PROTECT_MCP] ${JSON.stringify(log)}
40855
40944
  `);
40856
40945
  try {
package/dist/index.mjs CHANGED
@@ -6,10 +6,9 @@ import {
6
6
  createSelectiveDisclosurePackage,
7
7
  discloseField,
8
8
  hmac,
9
- sha256 as sha2562,
10
9
  signCommittedDecision,
11
10
  verifySelectiveDisclosurePackage
12
- } from "./chunk-WV4DKYE4.mjs";
11
+ } from "./chunk-UWB5ALVO.mjs";
13
12
  import {
14
13
  formatReportMarkdown,
15
14
  generateReport
@@ -28,7 +27,7 @@ import {
28
27
  readInstalledConnectorPilots,
29
28
  simulate,
30
29
  writeConnectorPilots
31
- } from "./chunk-JCMDLN5I.mjs";
30
+ } from "./chunk-CXW2EIRM.mjs";
32
31
  import {
33
32
  ProtectGateway,
34
33
  buildDecisionContext,
@@ -40,7 +39,7 @@ import {
40
39
  resolveCredential,
41
40
  sendApprovalNotification,
42
41
  validateCredentials
43
- } from "./chunk-VTPZ4G5I.mjs";
42
+ } from "./chunk-GHR65WVD.mjs";
44
43
  import {
45
44
  createSandboxServer
46
45
  } from "./chunk-SETXVE2K.mjs";
@@ -55,7 +54,11 @@ import {
55
54
  forwardReceipt,
56
55
  getScopeBlindBridge,
57
56
  startHookServer
58
- } from "./chunk-6E2DHBAR.mjs";
57
+ } from "./chunk-744JMCY4.mjs";
58
+ import "./chunk-KMNXHGGT.mjs";
59
+ import {
60
+ sha256 as sha2562
61
+ } from "./chunk-AYNQIEN7.mjs";
59
62
  import {
60
63
  checkRateLimit,
61
64
  evaluateCedar,
@@ -70,7 +73,7 @@ import {
70
73
  policySetFromSource,
71
74
  runEvaluatorSelfTest,
72
75
  signDecision
73
- } from "./chunk-WIPWNWMJ.mjs";
76
+ } from "./chunk-IDUH2O4Q.mjs";
74
77
  import {
75
78
  Field,
76
79
  _abool2,
@@ -93,11 +96,13 @@ import {
93
96
  normalizeZ,
94
97
  numberToHexUnpadded,
95
98
  pippenger,
99
+ wNAF
100
+ } from "./chunk-FFVJL3KQ.mjs";
101
+ import {
96
102
  sha256,
97
103
  sha384,
98
- sha512,
99
- wNAF
100
- } from "./chunk-LJQOALYR.mjs";
104
+ sha512
105
+ } from "./chunk-JIDDQUSQ.mjs";
101
106
  import {
102
107
  ahash,
103
108
  bytesToHex,
@@ -3,8 +3,10 @@ import {
3
3
  discloseField,
4
4
  signCommittedDecision,
5
5
  verifySelectiveDisclosurePackage
6
- } from "./chunk-WV4DKYE4.mjs";
7
- import "./chunk-LJQOALYR.mjs";
6
+ } from "./chunk-UWB5ALVO.mjs";
7
+ import "./chunk-AYNQIEN7.mjs";
8
+ import "./chunk-FFVJL3KQ.mjs";
9
+ import "./chunk-JIDDQUSQ.mjs";
8
10
  import "./chunk-D733KAPG.mjs";
9
11
  import "./chunk-PQJP2ZCI.mjs";
10
12
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "protect-mcp",
3
- "version": "0.7.6",
3
+ "version": "0.9.2",
4
4
  "mcpName": "com.scopeblind/protect-mcp",
5
5
  "description": "Fail-closed Cedar policy gate + Ed25519 signed receipts for AI agent tool calls. Denies on any policy error, proves the gate is live with a startup self-test, and turns every decision into a local searchable record you own. The open gate behind Legate by ScopeBlind. scopeblind.com",
6
6
  "main": "dist/index.js",
@@ -1,50 +0,0 @@
1
- // Generated by protect-mcp init-hooks
2
- // Customize these policies to match your security requirements.
3
- // Cedar deny decisions are AUTHORITATIVE — they cannot be overridden.
4
-
5
- // Allow all read-only tools by default
6
- permit(
7
- principal,
8
- action == Action::"MCP::Tool::call",
9
- resource == Tool::"Read"
10
- );
11
-
12
- permit(
13
- principal,
14
- action == Action::"MCP::Tool::call",
15
- resource == Tool::"Glob"
16
- );
17
-
18
- permit(
19
- principal,
20
- action == Action::"MCP::Tool::call",
21
- resource == Tool::"Grep"
22
- );
23
-
24
- // Allow write/edit tools (remove these to require explicit approval)
25
- permit(
26
- principal,
27
- action == Action::"MCP::Tool::call",
28
- resource == Tool::"Write"
29
- );
30
-
31
- permit(
32
- principal,
33
- action == Action::"MCP::Tool::call",
34
- resource == Tool::"Edit"
35
- );
36
-
37
- // Allow Bash with caution (Cedar evaluates before hook patterns)
38
- permit(
39
- principal,
40
- action == Action::"MCP::Tool::call",
41
- resource == Tool::"Bash"
42
- );
43
-
44
- // Block dangerous tools entirely
45
- // Uncomment any of these to block specific tools:
46
- // forbid(
47
- // principal,
48
- // action == Action::"MCP::Tool::call",
49
- // resource == Tool::"delete_file"
50
- // );