tamperward 2.24.0 → 2.24.1

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 +55 -3
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -7365,6 +7365,50 @@ function syntheticBase(over) {
7365
7365
  }
7366
7366
  return base;
7367
7367
  }
7368
+ function freshLefthookBase(after) {
7369
+ const base = {};
7370
+ for (const [section, v] of Object.entries(after)) {
7371
+ if (!isObj(v)) continue;
7372
+ for (const group of ["commands", "scripts"]) {
7373
+ const g = v[group];
7374
+ if (!isObj(g)) continue;
7375
+ for (const [name, cfg] of Object.entries(g)) {
7376
+ if (!isObj(cfg)) continue;
7377
+ const run = cfg.run ?? cfg.runner;
7378
+ if (!namesGate(name) && !namesGate(run)) continue;
7379
+ const sec = base[section] ??= {};
7380
+ const grp = sec[group] ??= {};
7381
+ grp[name] = { run: typeof run === "string" ? liveOrCanonical(run) : CANONICAL_GATE };
7382
+ }
7383
+ }
7384
+ }
7385
+ return base;
7386
+ }
7387
+ function freshPreCommitBase(after) {
7388
+ const stagesOf = (v) => Array.isArray(v) && v.map(String).some((s) => COMMIT_STAGES.has(s)) ? v.map(String) : null;
7389
+ const defaults = stagesOf(after.default_stages);
7390
+ const repos = [];
7391
+ for (const repo of Array.isArray(after.repos) ? after.repos : []) {
7392
+ if (!isObj(repo)) continue;
7393
+ const hooks = [];
7394
+ for (const h of Array.isArray(repo.hooks) ? repo.hooks : []) {
7395
+ if (!isObj(h)) continue;
7396
+ if (![repo.repo, h.id, h.entry, h.name].some(namesGate)) continue;
7397
+ const fresh = { id: h.id, name: h.name };
7398
+ if (h.entry !== void 0) {
7399
+ const args = Array.isArray(h.args) ? h.args.map(String).join(" ") : "";
7400
+ fresh.entry = liveOrCanonical(`${typeof h.entry === "string" ? h.entry : ""} ${args}`.trim());
7401
+ }
7402
+ const stages = stagesOf(h.stages);
7403
+ if (stages) fresh.stages = stages;
7404
+ hooks.push(fresh);
7405
+ }
7406
+ repos.push({ repo: repo.repo, hooks });
7407
+ }
7408
+ const base = { repos };
7409
+ if (defaults) base.default_stages = defaults;
7410
+ return base;
7411
+ }
7368
7412
  function configFindings(c, changes, policy, ctx) {
7369
7413
  if (c.after == null) return null;
7370
7414
  let reasons;
@@ -7374,8 +7418,11 @@ function configFindings(c, changes, policy, ctx) {
7374
7418
  if (overBefore === null) return null;
7375
7419
  const base = lefthookBase(c, changes, ctx) ?? syntheticBase(overAfter ?? {});
7376
7420
  reasons = overAfter === null ? ["the file no longer parses as YAML \u2014 the tool that reads it runs nothing"] : lefthookWeakening(mergeDocs(base, overBefore), mergeDocs(base, overAfter));
7421
+ } else if (c.before == null) {
7422
+ const after = parseDoc(c.after);
7423
+ if (after === null) return [];
7424
+ reasons = isLefthook(c.path) ? lefthookWeakening(freshLefthookBase(after), after) : preCommitWeakening(freshPreCommitBase(after), after);
7377
7425
  } else {
7378
- if (c.before == null) return null;
7379
7426
  const before = parseDoc(c.before);
7380
7427
  const after = parseDoc(c.after);
7381
7428
  if (before === null) return null;
@@ -7433,7 +7480,7 @@ function huskyRuntimeWrite(path, content, ctx) {
7433
7480
  (i) => i.identity === "tamperward check --staged" && i.state === "live" && i.pin === TW_VERSION
7434
7481
  );
7435
7482
  }
7436
- var RULE8, ownerExec2, EVENTS, isObj, declares, GATE_KEYS, HOOK_TYPES, SIBLING_KEYS, MENTIONS_GATE, FORGERY, ENV_MECHANISM, envMechanism, mentionsGate, carriesGate, HUSKY_SHIM, HUSKY_SHIM_NAMES, HUSKY_H_KNOWN, HUSKY_DEPRECATION, stripTrailingNl, isHuskyScript, isHookNote, hookTampering;
7483
+ var RULE8, ownerExec2, EVENTS, isObj, declares, GATE_KEYS, HOOK_TYPES, SIBLING_KEYS, MENTIONS_GATE, FORGERY, ENV_MECHANISM, envMechanism, mentionsGate, carriesGate, CANONICAL_GATE, COMMIT_STAGES, namesGate, liveOrCanonical, HUSKY_SHIM, HUSKY_SHIM_NAMES, HUSKY_H_KNOWN, HUSKY_DEPRECATION, stripTrailingNl, isHuskyScript, isHookNote, hookTampering;
7437
7484
  var init_hook_tampering = __esm({
7438
7485
  "src/detectors/hook-tampering.ts"() {
7439
7486
  "use strict";
@@ -7469,6 +7516,10 @@ var init_hook_tampering = __esm({
7469
7516
  envMechanism = (k) => ENV_MECHANISM.find(([re]) => re.test(k))?.[1] ?? null;
7470
7517
  mentionsGate = (v) => /\btamperward\b/.test(JSON.stringify(v) ?? "");
7471
7518
  carriesGate = (s) => EVENTS.some((e) => gateCandidates(s, e).length > 0);
7519
+ CANONICAL_GATE = "npx tamperward check --staged";
7520
+ COMMIT_STAGES = /* @__PURE__ */ new Set(["pre-commit", "pre-push", "commit", "push"]);
7521
+ namesGate = (v) => typeof v === "string" && /\btamperward\b/.test(v);
7522
+ liveOrCanonical = (cmd) => gateOf(cmd) !== null ? cmd : CANONICAL_GATE;
7472
7523
  HUSKY_SHIM = '#!/usr/bin/env sh\n. "$(dirname "$0")/h"';
7473
7524
  HUSKY_SHIM_NAMES = /* @__PURE__ */ new Set(["applypatch-msg", "commit-msg", "post-applypatch", "post-checkout", "post-commit", "post-merge", "post-rewrite", "pre-applypatch", "pre-auto-gc", "pre-commit", "pre-merge-commit", "pre-push", "pre-rebase", "prepare-commit-msg"]);
7474
7525
  HUSKY_H_KNOWN = [
@@ -7508,7 +7559,7 @@ They WILL FAIL in v10.0.0
7508
7559
  "`;
7509
7560
  stripTrailingNl = (s) => s.endsWith("\n") ? s.slice(0, -1) : s;
7510
7561
  isHuskyScript = (path) => /(?:^|\/)\.husky\/[^/]+$/.test(path);
7511
- isHookNote = (path) => /\.(?:md|markdown|txt)$|(?:^|\/)\.git(?:ignore|attributes)$/i.test(path);
7562
+ isHookNote = (path) => !/(?:^|\/)\.husky\/_\//.test(path) && /\.(?:md|markdown|txt)$|(?:^|\/)\.git(?:ignore|attributes)$/i.test(path);
7512
7563
  hookTampering = {
7513
7564
  id: RULE8,
7514
7565
  surface: ["file", "command"],
@@ -7582,6 +7633,7 @@ They WILL FAIL in v10.0.0
7582
7633
  out3.push(...semantic);
7583
7634
  } else if (!c.path.endsWith(POLICY_FILE)) {
7584
7635
  if ((c.op === "add" || c.op === "modify") && huskyRuntimeWrite(c.path, c.after, ctx)) continue;
7636
+ if (isHookNote(c.path)) continue;
7585
7637
  const husky = isHuskyScript(c.path);
7586
7638
  const opts = husky ? { errexit: true } : {};
7587
7639
  const beforePin = c.before != null && c.after != null ? initScriptPin(c.before) : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.24.0",
3
+ "version": "2.24.1",
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",