tamperward 1.13.0 → 1.14.0

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 +49 -5
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -215,9 +215,13 @@ function enrich(c, beforeReader, afterReader) {
215
215
  const after = c.op !== "delete" ? afterReader(c.path) : null;
216
216
  return { ...c, before, after };
217
217
  }
218
+ function assertRev(rev) {
219
+ if (rev.startsWith("-")) throw new Error(`refusing a revision that git would read as an option: ${rev}`);
220
+ return rev;
221
+ }
218
222
  function mergeBaseOf(base, head, opts = {}) {
219
223
  try {
220
- return git(["merge-base", base, head], opts.cwd).trim() || base;
224
+ return git(["merge-base", assertRev(base), assertRev(head)], opts.cwd).trim() || base;
221
225
  } catch {
222
226
  return base;
223
227
  }
@@ -254,6 +258,8 @@ function diffSince(rev, opts = {}) {
254
258
  );
255
259
  }
256
260
  function diffRange(base, head, opts = {}) {
261
+ assertRev(base);
262
+ assertRev(head);
257
263
  const raw = git(["diff", "--no-color", "-M", `${base}...${head}`], opts.cwd);
258
264
  const mergeBase = mergeBaseOf(base, head, opts);
259
265
  return parseDiff(raw).map(
@@ -447,7 +453,36 @@ function defaultPolicy(version = 1) {
447
453
  // .test.tsx/.spec.tsx (hono, zustand) slipped the glob, so legit test-file casts blocked
448
454
  // instead of warning. Kept ADDITIVE (originals + new extensions) so the policy-diff sees a
449
455
  // strengthening, not a narrowing. picomatch expands the brace.
450
- tests: ["**/*.test.ts", "**/*.spec.ts", "**/*.{test,spec}.{tsx,cts,mts,js,jsx,cjs,mjs}", "**/__tests__/**"],
456
+ // test-skip, test-deletion and test-content-removal all gate on this
457
+ // class, so a repo it does not match gets rules that can NEVER fire —
458
+ // protection that is silently absent rather than visibly off. The JS/TS
459
+ // list shipped alone; these are the conventional layouts of the other
460
+ // ecosystems `init` is run in. (P2-13, external review.)
461
+ tests: [
462
+ // JavaScript / TypeScript
463
+ "**/*.test.ts",
464
+ "**/*.spec.ts",
465
+ "**/*.{test,spec}.{tsx,cts,mts,js,jsx,cjs,mjs}",
466
+ "**/__tests__/**",
467
+ // Python (pytest / unittest layouts)
468
+ "**/test_*.py",
469
+ "**/*_test.py",
470
+ "**/conftest.py",
471
+ // Go
472
+ "**/*_test.go",
473
+ // Rust (integration tests live in tests/, unit tests inline)
474
+ "**/tests/**/*.rs",
475
+ // Ruby (RSpec / minitest)
476
+ "**/*_spec.rb",
477
+ "**/*_test.rb",
478
+ // Java / Kotlin / Scala (Maven & Gradle convention)
479
+ "**/src/test/**",
480
+ // PHP (PHPUnit)
481
+ "**/*Test.php",
482
+ // C# (.NET)
483
+ "**/*Tests.cs",
484
+ "**/*Test.cs"
485
+ ],
451
486
  config: [
452
487
  "**/jest.config.*",
453
488
  "**/vitest.config.*",
@@ -1819,15 +1854,21 @@ function annotations(findings) {
1819
1854
  function cell(s) {
1820
1855
  return s.replace(/\s+/g, " ").replace(/\|/g, "\\|").trim();
1821
1856
  }
1857
+ function mdCode(s) {
1858
+ return s.replace(/`/g, "'").replace(/[\r\n]/g, " ");
1859
+ }
1860
+ function mdUrl(s) {
1861
+ return s.replace(/[()<> \r\n"'`\\]/g, (c) => "%" + c.charCodeAt(0).toString(16).padStart(2, "0").toUpperCase());
1862
+ }
1822
1863
  function locationCell(f, env) {
1823
1864
  if (!f.file) return "`(command)`";
1824
1865
  const label = `${f.file}${f.line ? `:${f.line}` : ""}`;
1825
1866
  const server = env.GITHUB_SERVER_URL;
1826
1867
  const repo = env.GITHUB_REPOSITORY;
1827
1868
  const sha2 = env.GITHUB_SHA;
1828
- if (!server || !repo || !sha2) return `\`${cell(label)}\``;
1869
+ if (!server || !repo || !sha2) return `\`${cell(mdCode(label))}\``;
1829
1870
  const frag = f.line ? `#L${f.line}` : "";
1830
- return `[\`${cell(label)}\`](${server}/${repo}/blob/${sha2}/${f.file}${frag})`;
1871
+ return `[\`${cell(mdCode(label))}\`](${server}/${repo}/blob/${sha2}/${mdUrl(f.file)}${frag})`;
1831
1872
  }
1832
1873
  function renderSummary(input, env = process.env) {
1833
1874
  const findings = orderFindings(input.findings);
@@ -2766,7 +2807,9 @@ function planClaudeHooks(cwd) {
2766
2807
  apply: () => {
2767
2808
  if (needPre) {
2768
2809
  (hooks.PreToolUse ??= []).push({
2769
- matcher: "Bash|Edit|Write|MultiEdit",
2810
+ // NotebookEdit is modelled by the adapter but was absent here, so
2811
+ // that branch was unreachable in the wiring we install. (P2-12.)
2812
+ matcher: "Bash|Edit|Write|MultiEdit|NotebookEdit",
2770
2813
  hooks: [{ type: "command", command: HOOK_CMD }]
2771
2814
  });
2772
2815
  }
@@ -2861,6 +2904,7 @@ function git2(args, cwd) {
2861
2904
  return execFileSync4("git", args, { cwd, encoding: "utf8", maxBuffer: 1 << 28 });
2862
2905
  }
2863
2906
  function resolveBase(base, cwd) {
2907
+ assertRev(base);
2864
2908
  const rev = git2(["rev-parse", "--verify", `${base}^{commit}`], cwd).trim();
2865
2909
  try {
2866
2910
  return git2(["merge-base", rev, "HEAD"], cwd).trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
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",