tamperward 2.23.14 → 2.23.16

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 +115 -82
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -10885,10 +10885,55 @@ var init_verifier_limits = __esm({
10885
10885
  }
10886
10886
  });
10887
10887
 
10888
+ // src/safe-write.ts
10889
+ import { randomBytes } from "node:crypto";
10890
+ import { lstatSync as lstatSync7, renameSync as renameSync3, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "node:fs";
10891
+ function writeTargetKind(path) {
10892
+ let st;
10893
+ try {
10894
+ st = lstatSync7(path, { throwIfNoEntry: false });
10895
+ } catch (e) {
10896
+ if (errnoCode(e) === "ENOTDIR") return "absent";
10897
+ throw e;
10898
+ }
10899
+ if (!st) return "absent";
10900
+ if (st.isSymbolicLink()) return "symlink";
10901
+ return st.isFile() ? "file" : "irregular";
10902
+ }
10903
+ function refuseNonRegular(path) {
10904
+ switch (writeTargetKind(path)) {
10905
+ case "symlink":
10906
+ return "refusing: symlink \u2014 the target would be written instead of this path; replace the link with a regular file, then re-run";
10907
+ case "irregular":
10908
+ return "refusing: not a regular file \u2014 a directory or special file stands where a file is expected; remove it, then re-run";
10909
+ default:
10910
+ return null;
10911
+ }
10912
+ }
10913
+ function existingMode(path, fallback) {
10914
+ if (writeTargetKind(path) !== "file") return fallback;
10915
+ return lstatSync7(path).mode & 511;
10916
+ }
10917
+ function atomicReplaceFile(path, content, mode) {
10918
+ const tmp = `${path}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`;
10919
+ try {
10920
+ writeFileSync7(tmp, content, { encoding: "utf8", flag: "wx", mode });
10921
+ renameSync3(tmp, path);
10922
+ } finally {
10923
+ rmSync5(tmp, { force: true });
10924
+ }
10925
+ }
10926
+ var init_safe_write = __esm({
10927
+ "src/safe-write.ts"() {
10928
+ "use strict";
10929
+ init_narrow();
10930
+ }
10931
+ });
10932
+
10888
10933
  // src/cli/init.ts
10889
10934
  import { execFileSync as execFileSync6 } from "node:child_process";
10890
10935
  import { createHash as createHash6 } from "node:crypto";
10891
- import { existsSync as existsSync11, lstatSync as lstatSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync12, realpathSync as realpathSync4, writeFileSync as writeFileSync7, chmodSync as chmodSync2, statSync as statSync3 } from "node:fs";
10936
+ import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync12, realpathSync as realpathSync4 } from "node:fs";
10892
10937
  import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute8, join as join14, relative as relative3, resolve as resolve4 } from "node:path";
10893
10938
  function gitConfig(cwd, key2) {
10894
10939
  try {
@@ -11050,6 +11095,8 @@ function verifierSetupMessage(cwd) {
11050
11095
  }
11051
11096
  function planPolicy(cwd) {
11052
11097
  const path = join14(cwd, POLICY_FILE);
11098
+ const refused = refuseNonRegular(path);
11099
+ if (refused) return { item: "policy", path: POLICY_FILE, status: "error", detail: refused };
11053
11100
  if (existsSync11(path)) {
11054
11101
  try {
11055
11102
  loadPolicy(cwd);
@@ -11063,7 +11110,7 @@ function planPolicy(cwd) {
11063
11110
  path: POLICY_FILE,
11064
11111
  status: "create",
11065
11112
  detail: "baseline policy with commented overrides",
11066
- apply: () => writeFileSync7(path, POLICY_CONTENT)
11113
+ apply: () => atomicReplaceFile(path, POLICY_CONTENT, 420)
11067
11114
  };
11068
11115
  }
11069
11116
  function planGitignore(cwd) {
@@ -11071,23 +11118,15 @@ function planGitignore(cwd) {
11071
11118
  const path = join14(cwd, rel);
11072
11119
  if (!existsSync11(join14(cwd, "node_modules"))) return null;
11073
11120
  let existing = "";
11074
- if (existsSync11(path)) {
11075
- let st;
11076
- try {
11077
- st = lstatSync7(path);
11078
- } catch (e) {
11079
- return { item: "gitignore", path: rel, status: "error", detail: "cannot inspect \u2014 " + errText3(e) };
11080
- }
11081
- if (!st.isFile()) {
11082
- return {
11083
- item: "gitignore",
11084
- path: rel,
11085
- status: "error",
11086
- detail: st.isSymbolicLink() ? "is a symbolic link \u2014 refusing to follow it" : "exists but is not a regular file \u2014 refusing to write"
11087
- };
11088
- }
11089
- existing = readFileSync12(path, "utf8");
11121
+ let kind;
11122
+ try {
11123
+ kind = writeTargetKind(path);
11124
+ } catch (e) {
11125
+ return { item: "gitignore", path: rel, status: "error", detail: "cannot inspect \u2014 " + errText3(e) };
11090
11126
  }
11127
+ const refused = refuseNonRegular(path);
11128
+ if (refused) return { item: "gitignore", path: rel, status: "error", detail: refused };
11129
+ if (kind === "file") existing = readFileSync12(path, "utf8");
11091
11130
  try {
11092
11131
  const tracked = execFileSync6("git", ["ls-files", "--cached", "--", "node_modules"], {
11093
11132
  cwd,
@@ -11120,14 +11159,16 @@ function planGitignore(cwd) {
11120
11159
  return {
11121
11160
  item: "gitignore",
11122
11161
  path: rel,
11123
- status: existsSync11(path) ? "update" : "create",
11162
+ status: kind === "file" ? "update" : "create",
11124
11163
  detail: "exclude installed Node dependencies from repository diffs",
11125
- apply: () => writeFileSync7(path, next)
11164
+ apply: () => atomicReplaceFile(path, next, existingMode(path, 420))
11126
11165
  };
11127
11166
  }
11128
11167
  function planClaudeHooks(cwd) {
11129
11168
  const rel = ".claude/settings.json";
11130
11169
  const path = join14(cwd, rel);
11170
+ const refused = refuseNonRegular(path);
11171
+ if (refused) return { item: "agent", path: rel, status: "error", detail: refused };
11131
11172
  let settings = {};
11132
11173
  if (existsSync11(path)) {
11133
11174
  let parsed;
@@ -11194,7 +11235,7 @@ function planClaudeHooks(cwd) {
11194
11235
  }
11195
11236
  if (needDisableFalse) settings.disableAllHooks = false;
11196
11237
  mkdirSync6(dirname4(path), { recursive: true });
11197
- writeFileSync7(path, JSON.stringify(settings, null, 2) + "\n");
11238
+ atomicReplaceFile(path, JSON.stringify(settings, null, 2) + "\n", existingMode(path, 420));
11198
11239
  }
11199
11240
  };
11200
11241
  }
@@ -11251,10 +11292,10 @@ function planPreCommit(cwd) {
11251
11292
  }
11252
11293
  }
11253
11294
  const rel = display(cwd, path);
11254
- if (existsSync11(path) && !statSync3(path).isFile()) {
11255
- return { item: "pre-commit", path: rel, status: "error", detail: "exists but is not a regular file \u2014 refusing to write" };
11256
- }
11295
+ const refused = refuseNonRegular(path);
11296
+ if (refused) return { item: "pre-commit", path: rel, status: "error", detail: refused };
11257
11297
  const existing = existsSync11(path) ? readFileSync12(path, "utf8") : null;
11298
+ const hookMode = () => existingMode(path, 493) | 73;
11258
11299
  if (existing === null) {
11259
11300
  return {
11260
11301
  item: "pre-commit",
@@ -11263,10 +11304,9 @@ function planPreCommit(cwd) {
11263
11304
  detail: `create via ${note}`,
11264
11305
  apply: () => {
11265
11306
  mkdirSync6(dirname4(path), { recursive: true });
11266
- writeFileSync7(path, `#!/bin/sh
11307
+ atomicReplaceFile(path, `#!/bin/sh
11267
11308
  ${block.join("\n")}
11268
- `);
11269
- chmodSync2(path, 493);
11309
+ `, hookMode());
11270
11310
  }
11271
11311
  };
11272
11312
  }
@@ -11277,10 +11317,7 @@ ${block.join("\n")}
11277
11317
  const lines = existing.split("\n");
11278
11318
  const dead = unconditionalExitAt(lines);
11279
11319
  const at = lines.findIndex((l) => PRECOMMIT_RE.test(l));
11280
- const write = (out3) => {
11281
- writeFileSync7(path, out3.join("\n"));
11282
- chmodSync2(path, 493);
11283
- };
11320
+ const write = (out3) => atomicReplaceFile(path, out3.join("\n"), hookMode());
11284
11321
  if (at !== -1 && (dead === -1 || at < dead)) {
11285
11322
  if (!stalePin(lines[at])) {
11286
11323
  return { item: "pre-commit", path: rel, status: "ok", detail: "already runs the staged check" };
@@ -11343,9 +11380,11 @@ ${block.join("\n")}
11343
11380
  function planCodeowners(cwd) {
11344
11381
  const rel = ".github/CODEOWNERS";
11345
11382
  const existingRel = [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"].find(
11346
- (r) => existsSync11(join14(cwd, r))
11383
+ (r) => writeTargetKind(join14(cwd, r)) !== "absent"
11347
11384
  );
11348
11385
  const path = join14(cwd, existingRel ?? rel);
11386
+ const refused = refuseNonRegular(path);
11387
+ if (refused) return { item: "codeowners", path: existingRel ?? rel, status: "error", detail: refused };
11349
11388
  const existing = existingRel ? readFileSync12(path, "utf8") : null;
11350
11389
  const missing = CODEOWNERS_PATHS.filter((p) => existing === null || !coveredBy(existing, p));
11351
11390
  if (existingRel !== void 0 && missing.length === 0) {
@@ -11373,7 +11412,7 @@ ${CODEOWNERS_MARK}
11373
11412
  } : {},
11374
11413
  apply: () => {
11375
11414
  mkdirSync6(dirname4(path), { recursive: true });
11376
- writeFileSync7(path, existing === null ? block.replace(/^\n/, "") : existing.replace(/\n?$/, "\n") + block);
11415
+ atomicReplaceFile(path, existing === null ? block.replace(/^\n/, "") : existing.replace(/\n?$/, "\n") + block, existingMode(path, 420));
11377
11416
  }
11378
11417
  };
11379
11418
  }
@@ -11382,8 +11421,10 @@ function planWorkflow(cwd, force) {
11382
11421
  const path = join14(cwd, rel);
11383
11422
  const write = () => {
11384
11423
  mkdirSync6(dirname4(path), { recursive: true });
11385
- writeFileSync7(path, workflowFile(WORKFLOW_CONTENT));
11424
+ atomicReplaceFile(path, workflowFile(WORKFLOW_CONTENT), existingMode(path, 420));
11386
11425
  };
11426
+ const refused = refuseNonRegular(path);
11427
+ if (refused) return { item: "ci", path: rel, status: "error", detail: refused };
11387
11428
  if (!existsSync11(path)) {
11388
11429
  return {
11389
11430
  item: "ci",
@@ -11519,6 +11560,7 @@ var init_init = __esm({
11519
11560
  init_wiring();
11520
11561
  init_narrow();
11521
11562
  init_repo_context();
11563
+ init_safe_write();
11522
11564
  errText3 = (e) => (e instanceof Error ? e.message : String(e)).replace(/\s+/g, " ").trim();
11523
11565
  isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
11524
11566
  HOOK_RE = /\btamperward(?:@\S+)?\s+hook\s+claude\b/;
@@ -11693,7 +11735,7 @@ import {
11693
11735
  readFileSync as readFileSync13,
11694
11736
  readlinkSync as readlinkSync2,
11695
11737
  realpathSync as realpathSync5,
11696
- statSync as statSync4
11738
+ statSync as statSync3
11697
11739
  } from "node:fs";
11698
11740
  import {
11699
11741
  basename as basename3,
@@ -11735,7 +11777,7 @@ function fingerprintRoot(root, cwd, h) {
11735
11777
  root.kind + " root moved from " + JSON.stringify(root.realPath) + " to " + JSON.stringify(currentReal)
11736
11778
  );
11737
11779
  }
11738
- if (!statSync4(root.path).isDirectory()) {
11780
+ if (!statSync3(root.path).isDirectory()) {
11739
11781
  throw new Unattestable(root.kind + " root " + JSON.stringify(root.path) + " is not a directory");
11740
11782
  }
11741
11783
  h.update("root\0" + root.kind + "\0" + root.path + "\0" + root.realPath + "\0" + String(rootStat.mode) + "\0");
@@ -11745,7 +11787,7 @@ function fingerprintRoot(root, cwd, h) {
11745
11787
  const hashFile = (path, label) => {
11746
11788
  let st;
11747
11789
  try {
11748
- st = statSync4(path);
11790
+ st = statSync3(path);
11749
11791
  if (!st.isFile()) throw new Error("not a regular file");
11750
11792
  h.update(label + "\0external-file\0" + path + "\0" + String(st.mode) + "\0");
11751
11793
  h.update(readFileSync13(path));
@@ -11811,7 +11853,7 @@ function fingerprintRoot(root, cwd, h) {
11811
11853
  } catch {
11812
11854
  continue;
11813
11855
  }
11814
- const followed = statSync4(path);
11856
+ const followed = statSync3(path);
11815
11857
  if (followed.isFile()) {
11816
11858
  hashFile(resolved, "@target:" + rel);
11817
11859
  continue;
@@ -11859,7 +11901,7 @@ function measuredSnapshot(roots, probes, cwd, metrics) {
11859
11901
  }
11860
11902
  function freezeRoot(kind, path) {
11861
11903
  try {
11862
- if (!statSync4(path).isDirectory()) {
11904
+ if (!statSync3(path).isDirectory()) {
11863
11905
  throw new Error("not a directory");
11864
11906
  }
11865
11907
  return { kind, path: resolve5(path), realPath: realpathSync5(path) };
@@ -11908,7 +11950,7 @@ function resolveExecutable(token, cwd, env) {
11908
11950
  for (const entry of (env.PATH ?? "").split(delimiter).filter(Boolean)) {
11909
11951
  const candidate = resolve5(entry, token);
11910
11952
  try {
11911
- if (existsSync12(candidate) && statSync4(candidate).isFile()) return realpathSync5(candidate);
11953
+ if (existsSync12(candidate) && statSync3(candidate).isFile()) return realpathSync5(candidate);
11912
11954
  } catch {
11913
11955
  }
11914
11956
  }
@@ -12095,7 +12137,7 @@ var init_dependency_env = __esm({
12095
12137
 
12096
12138
  // src/suite-diagnostics.ts
12097
12139
  import { spawnSync } from "node:child_process";
12098
- import { mkdtempSync as mkdtempSync2, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "node:fs";
12140
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "node:fs";
12099
12141
  import { tmpdir as tmpdir3 } from "node:os";
12100
12142
  import { join as join16 } from "node:path";
12101
12143
  function emptySuiteDiagnostics() {
@@ -12224,7 +12266,7 @@ function runCapturedProcessSync(executable, args, opts) {
12224
12266
  }
12225
12267
  return parsed;
12226
12268
  } finally {
12227
- rmSync5(stateDir, { recursive: true, force: true });
12269
+ rmSync6(stateDir, { recursive: true, force: true });
12228
12270
  }
12229
12271
  }
12230
12272
  var DIAGNOSTIC_TAIL_BYTES, EMPTY_STREAM, CAPTURE_SUPERVISOR;
@@ -12414,7 +12456,7 @@ if (child) {
12414
12456
  // src/verifier-backend.ts
12415
12457
  import { createHash as createHash9, randomUUID } from "node:crypto";
12416
12458
  import { spawnSync as spawnSync2 } from "node:child_process";
12417
- import { accessSync as accessSync2, constants as constants3, readFileSync as readFileSync15, realpathSync as realpathSync6, statSync as statSync5 } from "node:fs";
12459
+ import { accessSync as accessSync2, constants as constants3, readFileSync as readFileSync15, realpathSync as realpathSync6, statSync as statSync4 } from "node:fs";
12418
12460
  import { delimiter as delimiter2, dirname as dirname6, join as join17, resolve as resolve6 } from "node:path";
12419
12461
  function identity() {
12420
12462
  const uid = typeof process.getuid === "function" ? process.getuid() : null;
@@ -12423,7 +12465,7 @@ function identity() {
12423
12465
  }
12424
12466
  function writableByCaller(path) {
12425
12467
  try {
12426
- const st = statSync5(path);
12468
+ const st = statSync4(path);
12427
12469
  const { uid, groups } = identity();
12428
12470
  if (uid === 0) return true;
12429
12471
  if ((st.mode & 2) !== 0) return true;
@@ -12893,7 +12935,7 @@ var init_verifier_backend = __esm({
12893
12935
  // src/cli/verify.ts
12894
12936
  import { execFileSync as execFileSync8, spawnSync as spawnSync3 } from "node:child_process";
12895
12937
  import { createHash as createHash10 } from "node:crypto";
12896
- import { chmodSync as chmodSync3, cpSync, lstatSync as lstatSync9, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, readlinkSync as readlinkSync3, rmSync as rmSync6, symlinkSync, writeFileSync as writeFileSync9, existsSync as existsSync13 } from "node:fs";
12938
+ import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync9, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, readlinkSync as readlinkSync3, rmSync as rmSync7, symlinkSync, writeFileSync as writeFileSync9, existsSync as existsSync13 } from "node:fs";
12897
12939
  import { tmpdir as tmpdir4 } from "node:os";
12898
12940
  import { dirname as dirname7, isAbsolute as isAbsolute10, join as join18, relative as relative5, resolve as resolve7, sep as sep3 } from "node:path";
12899
12941
  function oracleAssuranceReport() {
@@ -12941,7 +12983,7 @@ function safeSymlink(target, out3, root, label) {
12941
12983
  if (rootedLinkTarget(target) || !inside2(root, resolve7(dirname7(out3), target))) {
12942
12984
  throw linkEscape(label, target);
12943
12985
  }
12944
- rmSync6(out3, { force: true });
12986
+ rmSync7(out3, { force: true });
12945
12987
  symlinkSync(target, out3);
12946
12988
  }
12947
12989
  function validateSymlinkGraph(target, out3, root, dependencyRoot, label) {
@@ -13032,7 +13074,7 @@ function materialize(cwd, dest, dependencyRoot) {
13032
13074
  links.push({ rel, out: out3, target });
13033
13075
  } else if (st.isFile()) {
13034
13076
  cpSync(src, out3, { dereference: true });
13035
- chmodSync3(out3, st.mode);
13077
+ chmodSync2(out3, st.mode);
13036
13078
  } else {
13037
13079
  throw new Error(`${rel} is ${st.isDirectory() ? "a directory" : "a special file"} where git expects a file`);
13038
13080
  }
@@ -13052,7 +13094,7 @@ function probeFilesystemCaseSensitivity(dir) {
13052
13094
  writeFileSync9(upper, "", { flag: "wx" });
13053
13095
  return !existsSync13(lower);
13054
13096
  } finally {
13055
- rmSync6(upper, { force: true });
13097
+ rmSync7(upper, { force: true });
13056
13098
  }
13057
13099
  }
13058
13100
  function foldCase(p) {
@@ -13070,7 +13112,7 @@ function caseFoldCollision(paths) {
13070
13112
  }
13071
13113
  function dropSymlink(p) {
13072
13114
  try {
13073
- if (lstatSync9(p).isSymbolicLink()) rmSync6(p, { force: true });
13115
+ if (lstatSync9(p).isSymbolicLink()) rmSync7(p, { force: true });
13074
13116
  } catch {
13075
13117
  }
13076
13118
  }
@@ -13146,14 +13188,14 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot, caseSensi
13146
13188
  const content = execFileSync8("git", ["show", `${base}:${e.path}`], { cwd, maxBuffer: 1 << 28, env: trustedGitEnv() });
13147
13189
  const out3 = join18(dest, e.path);
13148
13190
  mkdirSync7(dirname7(out3), { recursive: true });
13149
- rmSync6(out3, { force: true });
13191
+ rmSync7(out3, { force: true });
13150
13192
  if (e.mode === "120000") {
13151
13193
  const target = content.toString("utf8");
13152
13194
  safeSymlink(target, out3, dest, `the base's ${e.path}`);
13153
13195
  restoredLinks.push({ path: e.path, out: out3, target });
13154
13196
  } else {
13155
13197
  writeFileSync9(out3, content);
13156
- chmodSync3(out3, parseInt(e.mode.slice(-4), 8) & 511);
13198
+ chmodSync2(out3, parseInt(e.mode.slice(-4), 8) & 511);
13157
13199
  }
13158
13200
  restored.push(e.path);
13159
13201
  }
@@ -13161,7 +13203,7 @@ function overlayPristine(cwd, base, dest, policy, cmd, dependencyRoot, caseSensi
13161
13203
  const inCopy = workingTreePaths(cwd);
13162
13204
  for (const rel of inCopy) {
13163
13205
  if (!isOverlay(rel) || baseProtected.has(fold(rel))) continue;
13164
- rmSync6(join18(dest, rel), { force: true });
13206
+ rmSync7(join18(dest, rel), { force: true });
13165
13207
  removed++;
13166
13208
  }
13167
13209
  for (const link of restoredLinks) {
@@ -13248,7 +13290,7 @@ function runLocalSuite(dir, cmd, budgetSecs) {
13248
13290
  }
13249
13291
  return { exit: r.exit, secs, diagnostics: r.diagnostics };
13250
13292
  } finally {
13251
- rmSync6(scratch, { recursive: true, force: true });
13293
+ rmSync7(scratch, { recursive: true, force: true });
13252
13294
  }
13253
13295
  }
13254
13296
  function diagnosticsJson(diagnostics, includeTail) {
@@ -13401,7 +13443,7 @@ function runVerify(opts) {
13401
13443
  const visDir = join18(visRoot, "t");
13402
13444
  const cleanup = (dirs) => {
13403
13445
  if (opts.keep) return;
13404
- for (const d of dirs) rmSync6(d, { recursive: true, force: true });
13446
+ for (const d of dirs) rmSync7(d, { recursive: true, force: true });
13405
13447
  };
13406
13448
  filesystemCaseSensitive = (opts.probeCaseSensitivity ?? probeFilesystemCaseSensitivity)(visRoot);
13407
13449
  if (!filesystemCaseSensitive) {
@@ -13767,8 +13809,8 @@ var init_verify = __esm({
13767
13809
 
13768
13810
  // src/cli/run.ts
13769
13811
  import { execFileSync as execFileSync9, spawn, spawnSync as spawnSync4 } from "node:child_process";
13770
- import { randomBytes } from "node:crypto";
13771
- import { accessSync as accessSync3, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync17, readlinkSync as readlinkSync4, realpathSync as realpathSync8, rmSync as rmSync7, statSync as statSync6, writeFileSync as writeFileSync10 } from "node:fs";
13812
+ import { randomBytes as randomBytes2 } from "node:crypto";
13813
+ import { accessSync as accessSync3, constants as fsConstants, mkdtempSync as mkdtempSync4, readdirSync as readdirSync4, readFileSync as readFileSync17, readlinkSync as readlinkSync4, realpathSync as realpathSync8, rmSync as rmSync8, statSync as statSync5, writeFileSync as writeFileSync10 } from "node:fs";
13772
13814
  import { tmpdir as tmpdir5 } from "node:os";
13773
13815
  import { dirname as dirname8, join as join19, resolve as resolve8 } from "node:path";
13774
13816
  function authoritativeRunLifecyclePlatform(platform = process.platform) {
@@ -13824,7 +13866,7 @@ function trustedLinuxPython(candidates = DEFAULT_TRUSTED_PYTHON_CANDIDATES) {
13824
13866
  for (const candidate of candidates) {
13825
13867
  try {
13826
13868
  const real = realpathSync8(candidate);
13827
- const st = statSync6(real);
13869
+ const st = statSync5(real);
13828
13870
  if (!st.isFile() || writableByCaller2(real)) continue;
13829
13871
  const probe = spawnSync4(
13830
13872
  real,
@@ -13947,7 +13989,7 @@ function runAgentSupervised(argv, cwd, budgetSecs, linuxPythonCandidates, lifecy
13947
13989
  ...state.failure ? { failure: state.failure } : {}
13948
13990
  };
13949
13991
  } finally {
13950
- rmSync7(stateDir, { recursive: true, force: true });
13992
+ rmSync8(stateDir, { recursive: true, force: true });
13951
13993
  }
13952
13994
  }
13953
13995
  function waitMs(ms) {
@@ -13978,7 +14020,7 @@ function supervisedObserverLog(cwd) {
13978
14020
  return join19(
13979
14021
  gd,
13980
14022
  "tamperward",
13981
- `run-observer-${process.pid}-${Date.now()}-${randomBytes(4).toString("hex")}.jsonl`
14023
+ `run-observer-${process.pid}-${Date.now()}-${randomBytes2(4).toString("hex")}.jsonl`
13982
14024
  );
13983
14025
  }
13984
14026
  function startSupervisedObserver(cwd, base, entry) {
@@ -15320,7 +15362,7 @@ import {
15320
15362
  readFileSync as readFileSync19,
15321
15363
  readdirSync as readdirSync6,
15322
15364
  realpathSync as realpathSync9,
15323
- rmSync as rmSync8,
15365
+ rmSync as rmSync9,
15324
15366
  symlinkSync as symlinkSync2
15325
15367
  } from "node:fs";
15326
15368
  import { tmpdir as tmpdir6 } from "node:os";
@@ -15518,8 +15560,8 @@ function runOneTrace(cwd, base, command, budget) {
15518
15560
  accesses: rewriteTraceRoot(accesses, root)
15519
15561
  };
15520
15562
  } finally {
15521
- rmSync8(root, { recursive: true, force: true });
15522
- rmSync8(traceDir, { recursive: true, force: true });
15563
+ rmSync9(root, { recursive: true, force: true });
15564
+ rmSync9(traceDir, { recursive: true, force: true });
15523
15565
  }
15524
15566
  }
15525
15567
  function renderText2(report2) {
@@ -15703,8 +15745,7 @@ var init_trace_verify = __esm({
15703
15745
 
15704
15746
  // src/cli/onboard.ts
15705
15747
  import { execFileSync as execFileSync12 } from "node:child_process";
15706
- import { randomBytes as randomBytes2 } from "node:crypto";
15707
- import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as readFileSync20, realpathSync as realpathSync10, renameSync as renameSync3, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "node:fs";
15748
+ import { lstatSync as lstatSync10, mkdtempSync as mkdtempSync6, readFileSync as readFileSync20, realpathSync as realpathSync10, rmSync as rmSync10 } from "node:fs";
15708
15749
  import { tmpdir as tmpdir7 } from "node:os";
15709
15750
  import { join as join22, relative as relative7, resolve as resolve11, sep as sep5 } from "node:path";
15710
15751
  import { createInterface } from "node:readline";
@@ -15773,23 +15814,14 @@ function explainVerdict(v, code2) {
15773
15814
  return `Could not verify${v?.reason ? ` (${v.reason})` : ""} \u2014 TamperWard failed closed${v?.detail ? `: ${v.detail}` : "."}`;
15774
15815
  }
15775
15816
  }
15776
- function atomicReplaceFile(path, content, mode) {
15777
- const tmp = `${path}.${process.pid}.${randomBytes2(8).toString("hex")}.tmp`;
15778
- try {
15779
- writeFileSync11(tmp, content, { encoding: "utf8", flag: "wx", mode });
15780
- renameSync3(tmp, path);
15781
- } finally {
15782
- rmSync9(tmp, { force: true });
15783
- }
15784
- }
15785
15817
  function writeVerifyCommand(cwd, command) {
15786
15818
  const path = join22(cwd, POLICY_FILE);
15787
- const st = lstatSync10(path, { throwIfNoEntry: false });
15788
- if (st && (!st.isFile() || st.isSymbolicLink())) {
15819
+ const kind = writeTargetKind(path);
15820
+ if (kind === "symlink" || kind === "irregular") {
15789
15821
  return `${POLICY_FILE} is not a regular file; refusing to follow or replace a symlink or special file`;
15790
15822
  }
15791
- const mode = st ? st.mode & 511 : 420;
15792
- const original = st ? readFileSync20(path, "utf8") : null;
15823
+ const mode = existingMode(path, 420);
15824
+ const original = kind === "file" ? readFileSync20(path, "utf8") : null;
15793
15825
  let next;
15794
15826
  try {
15795
15827
  const doc = parseDocument(original && original.trim() ? original : "version: 1\n");
@@ -15806,7 +15838,7 @@ function writeVerifyCommand(cwd, command) {
15806
15838
  if (loaded2.verify?.command !== command) throw new Error("verify.command did not round-trip");
15807
15839
  return null;
15808
15840
  } catch (e) {
15809
- if (original === null) rmSync9(path, { force: true });
15841
+ if (original === null) rmSync10(path, { force: true });
15810
15842
  else atomicReplaceFile(path, original, mode);
15811
15843
  return `the updated ${POLICY_FILE} does not load (${errorMessage(e)}); restored the previous file`;
15812
15844
  }
@@ -16206,7 +16238,7 @@ function runDemo(cwd, head, runners, out3) {
16206
16238
  } finally {
16207
16239
  git5(cwd, ["worktree", "remove", "--force", wt]);
16208
16240
  git5(cwd, ["worktree", "prune"]);
16209
- rmSync9(wt, { recursive: true, force: true });
16241
+ rmSync10(wt, { recursive: true, force: true });
16210
16242
  }
16211
16243
  const after = treeFingerprint(cwd);
16212
16244
  out3(`working tree fingerprint after: ${after.slice(0, 16)}`);
@@ -16231,6 +16263,7 @@ var init_onboard = __esm({
16231
16263
  init_policy_load();
16232
16264
  init_narrow();
16233
16265
  init_wiring();
16266
+ init_safe_write();
16234
16267
  Aborted = class extends Error {
16235
16268
  };
16236
16269
  SECTIONS = [
@@ -16621,7 +16654,7 @@ var init_record = __esm({
16621
16654
 
16622
16655
  // src/research/run.ts
16623
16656
  import { execFileSync as execFileSync13 } from "node:child_process";
16624
- import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync22, renameSync as renameSync4, rmSync as rmSync10, writeFileSync as writeFileSync12 } from "node:fs";
16657
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync22, renameSync as renameSync4, rmSync as rmSync11, writeFileSync as writeFileSync11 } from "node:fs";
16625
16658
  import { join as join23, resolve as resolve14 } from "node:path";
16626
16659
  function git6(args, cwd) {
16627
16660
  return execFileSync13("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
@@ -16669,12 +16702,12 @@ function resumableRecord(path, expected) {
16669
16702
  }
16670
16703
  function writeRecordAtomically(path, text) {
16671
16704
  const tmp = `${path}.${process.pid}.tmp`;
16672
- writeFileSync12(tmp, text);
16705
+ writeFileSync11(tmp, text);
16673
16706
  renameSync4(tmp, path);
16674
16707
  }
16675
16708
  function freshWorkspace(ledger, task, pair, arm, sourceBase) {
16676
16709
  const ws2 = join23(ledger, "workspaces", `${task.id}--${pair}--${arm}`);
16677
- rmSync10(ws2, { recursive: true, force: true });
16710
+ rmSync11(ws2, { recursive: true, force: true });
16678
16711
  mkdirSync8(join23(ledger, "workspaces"), { recursive: true });
16679
16712
  try {
16680
16713
  execFileSync13("git", ["clone", "-q", "--no-hardlinks", task.repo, ws2], { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tamperward",
3
- "version": "2.23.14",
3
+ "version": "2.23.16",
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",