tamperward 2.39.7 → 2.39.8

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.
Files changed (2) hide show
  1. package/dist/cli/index.js +30 -10
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -11030,25 +11030,25 @@ function applyLocalSignoffs(findings, cwd, policy, now = Date.now()) {
11030
11030
  return { findings: remaining, cleared };
11031
11031
  }
11032
11032
  function oobToken(want, oob, head) {
11033
+ const supplied = head !== void 0 && head.trim() !== "";
11034
+ const bound = supplied && FULL_OBJECT_ID.test(head.trim()) ? head.trim().toLowerCase() : null;
11033
11035
  for (const raw of oob) {
11034
11036
  const t = raw.trim();
11035
11037
  if (!t) continue;
11036
11038
  if (t.startsWith(COMPACT_OOB_PREFIX)) {
11037
- const expected = head === void 0 ? null : compactOobToken(want, head);
11039
+ const expected = bound === null ? null : compactOobToken(want, bound);
11038
11040
  if (expected !== null && t === expected) return t;
11039
11041
  continue;
11040
11042
  }
11041
11043
  const at = t.lastIndexOf("@");
11042
11044
  if (at === -1) {
11043
- if (!head && t === want) return t;
11045
+ if (!supplied && t === want) return t;
11044
11046
  continue;
11045
11047
  }
11046
11048
  const [rule, sha3] = [t.slice(0, at), t.slice(at + 1)];
11047
11049
  if (rule !== want) continue;
11048
- if (!head) return t;
11049
- const normalizedHead = head.trim().toLowerCase();
11050
- const normalizedSha = sha3.trim().toLowerCase();
11051
- if (/^[0-9a-f]+$/.test(normalizedHead) && normalizedSha === normalizedHead) return t;
11050
+ if (!supplied) return t;
11051
+ if (bound !== null && sha3.trim().toLowerCase() === bound) return t;
11052
11052
  }
11053
11053
  return null;
11054
11054
  }
@@ -11065,7 +11065,12 @@ function applyOobSignoffs(findings, oob, head) {
11065
11065
  }
11066
11066
  function oobHeadFromEnv(env = process.env) {
11067
11067
  const v = (env.TAMPERWARD_OOB_HEAD ?? "").trim().toLowerCase();
11068
- return /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(v) ? v : void 0;
11068
+ return v === "" ? void 0 : v;
11069
+ }
11070
+ function oobHeadProblem(head) {
11071
+ if (head === void 0 || head.trim() === "" || FULL_OBJECT_ID.test(head.trim())) return null;
11072
+ const shown = head.length > 72 ? `${head.slice(0, 69)}\u2026` : head;
11073
+ return `TAMPERWARD_OOB_HEAD is set to ${JSON.stringify(shown)}, which is not the full 40- or 64-character object id of the head under adjudication; no out-of-band approval is honored against a head the gate cannot identify`;
11069
11074
  }
11070
11075
  function oobFromEnv(env = process.env) {
11071
11076
  return (env.TAMPERWARD_OOB_SIGNOFF ?? "").split(",").map((s) => s.trim().replace(/^tamperward:allow:/, "")).filter(Boolean);
@@ -11459,8 +11464,13 @@ function check(opts) {
11459
11464
  const ignoredFiles = changes.filter((c) => isSuppressed(c, policy)).length;
11460
11465
  let findings = evaluate(changes, policy, void 0, view, { cwd });
11461
11466
  if (view === "worktree") findings = findings.concat(unjudgeableProtected(cwd, policy, changes));
11462
- const { findings: remaining, cleared } = layer === "local" ? applyLocalSignoffs(findings, cwd, policy) : applyOobSignoffs(findings, oobFromEnv(), oobHeadFromEnv());
11467
+ const oob = layer === "ci" ? oobFromEnv() : [];
11468
+ const oobHead = layer === "ci" ? oobHeadFromEnv() : void 0;
11469
+ const { findings: remaining, cleared } = layer === "local" ? applyLocalSignoffs(findings, cwd, policy) : applyOobSignoffs(findings, oob, oobHead);
11463
11470
  findings = remaining;
11471
+ const headProblem = oob.length > 0 ? oobHeadProblem(oobHead) : null;
11472
+ if (headProblem) process.stderr.write(`tamperward: ${headProblem}
11473
+ `);
11464
11474
  if (cleared.length) {
11465
11475
  const how = layer === "local" ? "local human sign-off (ledger)" : "out-of-band approval";
11466
11476
  process.stderr.write(`tamperward: ${cleared.length} blocking finding(s) cleared by ${how}: ${cleared.map((f) => f.rule + (f.file ? `(${f.file})` : "")).join(", ")}
@@ -17350,8 +17360,13 @@ function runVerifyImpl(opts) {
17350
17360
  } catch {
17351
17361
  }
17352
17362
  }
17353
- const signedOff = verdict2 === "MASKED_FAILURE" ? oobToken("verify", oobFromEnv(), oobHeadFromEnv()) : null;
17363
+ const oob = oobFromEnv();
17364
+ const oobHead = oobHeadFromEnv();
17365
+ const signedOff = verdict2 === "MASKED_FAILURE" ? oobToken("verify", oob, oobHead) : null;
17354
17366
  if (signedOff) code2 = 0;
17367
+ const headProblem = verdict2 === "MASKED_FAILURE" && oob.length > 0 ? oobHeadProblem(oobHead) : null;
17368
+ if (headProblem) process.stderr.write(`tamperward: ${headProblem}
17369
+ `);
17355
17370
  if (opts.json) {
17356
17371
  out3(
17357
17372
  JSON.stringify({
@@ -24793,7 +24808,12 @@ function ciAdjudicate(cwd, opts) {
24793
24808
  if (check2.reason !== void 0) {
24794
24809
  return { ci: { verdict: "CANNOT_VERIFY", binding, binding_error: check2.reason }, exit: 2 };
24795
24810
  }
24796
- const signedOff = check2.verdict === "MASKED_FAILURE" && oobToken("verify", oobFromEnv(), oobHeadFromEnv()) !== null;
24811
+ const oob = oobFromEnv();
24812
+ const oobHead = oobHeadFromEnv();
24813
+ const signedOff = check2.verdict === "MASKED_FAILURE" && oobToken("verify", oob, oobHead) !== null;
24814
+ const headProblem = check2.verdict === "MASKED_FAILURE" && oob.length > 0 ? oobHeadProblem(oobHead) : null;
24815
+ if (headProblem) process.stderr.write(`tamperward: ${headProblem}
24816
+ `);
24797
24817
  return {
24798
24818
  ci: {
24799
24819
  verdict: check2.verdict,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.39.7",
3
+ "version": "2.39.8",
4
4
  "description": "The deterministic agent-integrity gate. One ruleset, evaluated on the actual diff/commands as a verdict, enforced everywhere a change can be made.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "hexrift",