@metamynd/agentsafe-signer 0.15.1 → 0.15.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamynd/agentsafe-signer",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
4
4
  "description": "Local signer daemon for AgentSafe agent/service keys \u2014 the key never enters the calling guard's own process. See docs/design/agent-key-custody-local-signer-daemon-plan.md.",
5
5
  "type": "module",
6
6
  "main": "./daemon.mjs",
package/policy-core.mjs CHANGED
@@ -643,16 +643,24 @@ function evaluateMandate(mandate, req) {
643
643
  };
644
644
  }
645
645
  for (const p of perms) {
646
- const failing = (p.constraint ?? []).find((c) => !constraintSatisfied(c, req, true));
647
- if (!failing) return { decision: "allow", reasonCode: "AUTHORIZED" };
646
+ const failing2 = (p.constraint ?? []).find((c) => !constraintSatisfied(c, req, true));
647
+ if (!failing2) return { decision: "allow", reasonCode: "AUTHORIZED" };
648
648
  }
649
- const firstFail = (perms[0].constraint ?? []).find((c) => !constraintSatisfied(c, req, true));
649
+ const failing = (perms[0].constraint ?? []).filter((c) => !constraintSatisfied(c, req, true));
650
+ const firstFail = failing[0];
651
+ const reported = reportedFailure(failing) ?? firstFail;
650
652
  return {
651
653
  decision: firstFail?.onFail ?? "block",
652
- reasonCode: reasonFor(firstFail, req),
653
- matched: { kind: "permission", target: perms[0].target, constraint: firstFail }
654
+ reasonCode: reasonFor(reported, req),
655
+ matched: { kind: "permission", target: perms[0].target, constraint: reported }
654
656
  };
655
657
  }
658
+ function reportedFailure(failing) {
659
+ const first = failing[0];
660
+ if (!first || !AMOUNT_OPERANDS.has(first.leftOperand)) return void 0;
661
+ const decision = first.onFail ?? "block";
662
+ return failing.find((c) => !AMOUNT_OPERANDS.has(c.leftOperand) && (c.onFail ?? "block") === decision);
663
+ }
656
664
  function remainingBudget(b) {
657
665
  return Math.max(0, b.cap - b.spent - b.held);
658
666
  }
@@ -26,6 +26,13 @@ function defaultNodePath() {
26
26
  return process.execPath || '/usr/bin/node';
27
27
  }
28
28
 
29
+ /** One ExecStart= word. systemd splits the line on whitespace, so a path with a space in it (a node under
30
+ * `/opt/Node JS/`, or `C:\Program Files\nodejs\node.exe` when the unit is generated on Windows) became two words and
31
+ * the unit failed at exec. Double quotes are systemd's own quoting, with `\` and `"` escaped inside them. */
32
+ function systemdArg(value) {
33
+ return /\s/.test(value) ? `"${value.replace(/(["\\])/g, '\\$1')}"` : value;
34
+ }
35
+
29
36
  function assertTier(tier) {
30
37
  if (tier !== 1 && tier !== 2) throw new Error(`tier must be 1 or 2, got ${tier}`);
31
38
  }
@@ -74,7 +81,7 @@ export function generateSystemdUnits({ identity, tier, nodePath = defaultNodePat
74
81
  const cliInvocationPath = tier === 1 && !isAbsolutePath ? `%h/${resolvedCliPath.replace(/^\.?\//, '')}` : resolvedCliPath;
75
82
  // %S is systemd's own state-directory root, so this lines up with StateDirectory= below rather
76
83
  // than hard-coding a path that has to be kept in sync with it by hand.
77
- const invocation = `${nodePath} ${cliInvocationPath} start --state-dir %S/agentsafe/%i --role ${role} --identity %i`;
84
+ const invocation = `${systemdArg(nodePath)} ${systemdArg(cliInvocationPath)} start --state-dir %S/agentsafe/%i --role ${role} --identity %i`;
78
85
  // No setpriv wrapper. An earlier pass wrapped this in `setpriv --dumpable 0 --` to deny ptrace
79
86
  // via PR_SET_DUMPABLE=0. That option does not exist: util-linux's setpriv has no --dumpable flag
80
87
  // (checked on util-linux 2.39.3 / Ubuntu 24.04 LTS — the string is absent from the binary