tamperward 1.10.2 → 1.10.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.
Files changed (2) hide show
  1. package/dist/cli/index.js +36 -4
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -329,7 +329,16 @@ var LITERAL = [
329
329
  { re: /(?:^|\s)--no-verify(?:\s|$)/, why: "--no-verify skips the pre-commit/pre-push hooks" },
330
330
  { re: /(?:^|\s)--no-hooks(?:\s|$)/, why: "--no-hooks disables hook execution" },
331
331
  { re: /(?:^|[\s;&|=])HUSKY=0(?:\s|$)/, why: "HUSKY=0 disables all Husky hooks" },
332
- { re: /\bHUSKY_SKIP_HOOKS=1\b/, why: "HUSKY_SKIP_HOOKS=1 skips Husky hooks" }
332
+ { re: /\bHUSKY_SKIP_HOOKS=1\b/, why: "HUSKY_SKIP_HOOKS=1 skips Husky hooks" },
333
+ // git-native hook disabling. core.hooksPath is strictly MORE general than the
334
+ // flags above: it turns off husky, lefthook, plain .git/hooks and pre-commit
335
+ // alike, and it was reachable in three spellings the literal list missed
336
+ // (-c, `git config`, and GIT_CONFIG_* env injection). (P1-9, external review.)
337
+ { re: /\bcore\.hooksPath\b/i, why: "core.hooksPath redirects git away from the installed hooks" },
338
+ { re: /\bGIT_CONFIG_KEY_\d+\s*=\s*["']?core\.hooksPath/i, why: "GIT_CONFIG_* injection sets core.hooksPath" },
339
+ { re: /(?:^|[\s;&|=])LEFTHOOK=0(?:\s|$)/, why: "LEFTHOOK=0 disables Lefthook hooks" },
340
+ { re: /\bSKIP_SIMPLE_GIT_HOOKS=1\b/, why: "SKIP_SIMPLE_GIT_HOOKS=1 skips simple-git-hooks" },
341
+ { re: /\bPRE_COMMIT_ALLOW_NO_CONFIG=1\b/, why: "PRE_COMMIT_ALLOW_NO_CONFIG=1 lets pre-commit no-op" }
333
342
  ];
334
343
  var SHORT_N = /^-[a-z]*n[a-z]*$/i;
335
344
  var noVerify = {
@@ -880,6 +889,9 @@ var CHECK = /\b(test|tests|lint|typecheck|type-check|tsc|eslint|jest|vitest|play
880
889
  var YAML_KEY = /^\s*-?\s*[A-Za-z_][\w-]*:\s*(?:$|\S)/;
881
890
  var INVOKES_TOOL = /(?:^\s*|[;&|`]\s*|\$\(\s*|\b(?:npx|yarn|pnpm|bunx?)\s+)(?:jest|vitest|eslint|tsc|playwright|pytest|tamperward)\b/;
882
891
  var INVOKES_SCRIPT = /\b(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:test|tests|lint|typecheck|type-check|coverage)\b|\b(?:make|cargo|go)\s+test\b|\bgradle\w*\s+(?:test|check)\b/;
892
+ function isActiveWorkflow(path) {
893
+ return /(^|\/)\.github\/workflows\/[^/]+\.ya?ml$/.test(path);
894
+ }
883
895
  var invokesCheck = (line) => INVOKES_TOOL.test(line) || INVOKES_SCRIPT.test(line);
884
896
  function commandCore(line) {
885
897
  return line.trim().replace(/^-\s*/, "").replace(/^(?:run|uses):\s*/, "").replace(/\s+/g, " ").trim();
@@ -910,6 +922,17 @@ var ciTampering = {
910
922
  const out2 = [];
911
923
  for (const c of changes) {
912
924
  if (c.kind !== "file") continue;
925
+ if (c.op === "rename" && c.oldPath && isActiveWorkflow(c.oldPath) && !isActiveWorkflow(c.path)) {
926
+ out2.push(
927
+ makeFinding(RULE5, policy, {
928
+ file: c.path,
929
+ message: `A CI workflow was renamed so it no longer runs (${c.oldPath} \u2192 ${c.path}).`,
930
+ evidence: `${c.oldPath} \u2192 ${c.path}`,
931
+ remediation: "Restore the path and extension. GitHub runs only *.yml/*.yaml directly under .github/workflows \u2014 renaming outside that disables the checks as surely as deleting them."
932
+ })
933
+ );
934
+ continue;
935
+ }
913
936
  if (!isProtected(c.path, policy, "ci")) continue;
914
937
  for (const l of addedLines(c)) {
915
938
  const coe = l.content.match(/^\s*-?\s*continue-on-error:\s*(.+?)\s*$/);
@@ -1260,7 +1283,7 @@ var testDeletion = {
1260
1283
  remediation: "Restoring the path. Renaming a spec out of the glob silently removes it from the suite."
1261
1284
  })
1262
1285
  );
1263
- } else if (c.op === "modify" && isTest && c.before != null && c.after != null) {
1286
+ } else if ((c.op === "modify" || c.op === "rename") && isTest && c.before != null && c.after != null) {
1264
1287
  const before = countTestBlocks(c.before);
1265
1288
  const after = countTestBlocks(c.after);
1266
1289
  if (after < before) {
@@ -1339,7 +1362,7 @@ var testContentRemoval = {
1339
1362
  }
1340
1363
  }
1341
1364
  for (const c of changes) {
1342
- if (c.kind !== "file" || c.op !== "modify" || c.before == null || c.after == null) continue;
1365
+ if (c.kind !== "file" || c.op !== "modify" && c.op !== "rename" || c.before == null || c.after == null) continue;
1343
1366
  if (!isProtected(c.path, policy, "tests")) continue;
1344
1367
  if (isProtected(c.path, policy, "snapshots")) continue;
1345
1368
  if (countTestBlocks(c.after) < countTestBlocks(c.before)) continue;
@@ -1665,8 +1688,17 @@ function wrap(text, width) {
1665
1688
  if (cur) out2.push(cur);
1666
1689
  return out2.length ? out2 : [""];
1667
1690
  }
1691
+ function stripControl(v) {
1692
+ let outStr = "";
1693
+ for (const ch of v) {
1694
+ const cp = ch.codePointAt(0) ?? 0;
1695
+ const control = cp < 32 && cp !== 9 && cp !== 10 && cp !== 13 || cp === 127 || cp >= 128 && cp <= 159;
1696
+ outStr += control ? "\uFFFD" : ch;
1697
+ }
1698
+ return outStr;
1699
+ }
1668
1700
  function clip(s, max) {
1669
- const one = s.replace(/\s+/g, " ").trim();
1701
+ const one = stripControl(s).replace(/\s+/g, " ").trim();
1670
1702
  return one.length > max ? one.slice(0, Math.max(1, max - 1)) + "\u2026" : one;
1671
1703
  }
1672
1704
  function orderFindings(findings) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "1.10.2",
3
+ "version": "1.10.3",
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",