tamperward 2.39.12 → 2.39.14
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.
- package/dist/cli/index.js +105 -73
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -791,6 +791,37 @@ function readStateFile(abs) {
|
|
|
791
791
|
`${abs} ${notARegularFile(e)}; the gate reads its state files only as regular files \u2014 remove what stands there so the state can be re-established`
|
|
792
792
|
);
|
|
793
793
|
}
|
|
794
|
+
function openRegular(abs, refused) {
|
|
795
|
+
let st;
|
|
796
|
+
try {
|
|
797
|
+
st = lstatSync2(abs);
|
|
798
|
+
} catch (e) {
|
|
799
|
+
const code2 = errCode(e);
|
|
800
|
+
if (code2 === "ENOENT" || code2 === "ENOTDIR") return null;
|
|
801
|
+
throw new StateFileError(`${abs} cannot be read (${errText(e)})${refused}`);
|
|
802
|
+
}
|
|
803
|
+
if (!st.isFile()) throw new StateFileError(`${abs} ${notARegularFile(classify(abs, st))}${refused}`);
|
|
804
|
+
let fd;
|
|
805
|
+
try {
|
|
806
|
+
fd = openSync(abs, O_READ);
|
|
807
|
+
} catch (e) {
|
|
808
|
+
const code2 = errCode(e);
|
|
809
|
+
if (code2 === "ENOENT") return null;
|
|
810
|
+
throw new StateFileError(
|
|
811
|
+
`${abs} ${code2 === "ELOOP" || code2 === "ENXIO" ? `changed shape while it was being opened (${errText(e)})` : `cannot be read (${errText(e)})`}${refused}`
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
let now;
|
|
815
|
+
try {
|
|
816
|
+
now = fstatSync(fd);
|
|
817
|
+
} catch (e) {
|
|
818
|
+
closeSync(fd);
|
|
819
|
+
throw new StateFileError(`${abs} cannot be read (${errText(e)})${refused}`);
|
|
820
|
+
}
|
|
821
|
+
if (now.isFile()) return fd;
|
|
822
|
+
closeSync(fd);
|
|
823
|
+
throw new StateFileError(`${abs} is a ${now.isDirectory() ? "directory" : irregularName(now)}${refused}`);
|
|
824
|
+
}
|
|
794
825
|
function stateDirectory(gitDir3) {
|
|
795
826
|
const dir = join3(gitDir3, "tamperward");
|
|
796
827
|
const st = lstatSync2(dir, { throwIfNoEntry: false });
|
|
@@ -12432,7 +12463,7 @@ var init_fs_events = __esm({
|
|
|
12432
12463
|
|
|
12433
12464
|
// src/cli/audit.ts
|
|
12434
12465
|
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
12435
|
-
import { closeSync as closeSync4,
|
|
12466
|
+
import { closeSync as closeSync4, mkdirSync as mkdirSync5, readSync as readSync3 } from "node:fs";
|
|
12436
12467
|
import { dirname as dirname4, isAbsolute as isAbsolute8, join as join13, resolve as resolve4 } from "node:path";
|
|
12437
12468
|
function sessionHash(sessionId) {
|
|
12438
12469
|
if (!sessionId) return void 0;
|
|
@@ -12552,7 +12583,8 @@ function parseAuditLine(line, lineNumber) {
|
|
|
12552
12583
|
return parseAuditEvent(value, lineNumber);
|
|
12553
12584
|
}
|
|
12554
12585
|
function forEachAuditLine(file, onLine, maxLineBytes = MAX_AUDIT_LINE_BYTES) {
|
|
12555
|
-
const fd =
|
|
12586
|
+
const fd = openRegular(file, STATS_READ_REFUSED);
|
|
12587
|
+
if (fd === null) return false;
|
|
12556
12588
|
try {
|
|
12557
12589
|
const chunk = Buffer.allocUnsafe(64 * 1024);
|
|
12558
12590
|
let pending = [];
|
|
@@ -12592,6 +12624,7 @@ function forEachAuditLine(file, onLine, maxLineBytes = MAX_AUDIT_LINE_BYTES) {
|
|
|
12592
12624
|
} finally {
|
|
12593
12625
|
closeSync4(fd);
|
|
12594
12626
|
}
|
|
12627
|
+
return true;
|
|
12595
12628
|
}
|
|
12596
12629
|
function parseSince(value, nowMs = Date.now()) {
|
|
12597
12630
|
const relative13 = /^(\d+)(m|h|d)$/.exec(value);
|
|
@@ -12654,11 +12687,6 @@ function createAuditAggregator() {
|
|
|
12654
12687
|
}
|
|
12655
12688
|
};
|
|
12656
12689
|
}
|
|
12657
|
-
function summarizeAudit(events) {
|
|
12658
|
-
const aggregator = createAuditAggregator();
|
|
12659
|
-
for (const event of events) aggregator.add(event);
|
|
12660
|
-
return aggregator.finish();
|
|
12661
|
-
}
|
|
12662
12690
|
function renderAuditStats(summary) {
|
|
12663
12691
|
const out3 = [
|
|
12664
12692
|
"TamperWard audit stats",
|
|
@@ -12691,23 +12719,18 @@ function runStats(opts = {}) {
|
|
|
12691
12719
|
const file = opts.file ? isAbsolute8(opts.file) ? opts.file : resolve4(cwd, opts.file) : configured && configured !== "auto" ? isAbsolute8(configured) ? configured : resolve4(cwd, configured) : defaultAuditPath(cwd);
|
|
12692
12720
|
const cutoff = opts.since ? parseSince(opts.since) : null;
|
|
12693
12721
|
if (!file) throw new Error("stats needs --file outside a Git repository");
|
|
12694
|
-
if (!existsSync5(file)) {
|
|
12695
|
-
if (explicitFile) throw new Error(`audit file not found: ${file}`);
|
|
12696
|
-
const empty2 = summarizeAudit([]);
|
|
12697
|
-
process.stdout.write(opts.json ? JSON.stringify(empty2) + "\n" : renderAuditStats(empty2));
|
|
12698
|
-
return 0;
|
|
12699
|
-
}
|
|
12700
12722
|
const aggregator = createAuditAggregator();
|
|
12701
|
-
forEachAuditLine(file, (line, lineNumber) => {
|
|
12723
|
+
const read = forEachAuditLine(file, (line, lineNumber) => {
|
|
12702
12724
|
const event = parseAuditLine(line, lineNumber);
|
|
12703
12725
|
if (cutoff !== null && Date.parse(event.timestamp) < cutoff) return;
|
|
12704
12726
|
aggregator.add(event);
|
|
12705
12727
|
});
|
|
12728
|
+
if (!read && explicitFile) throw new Error(`audit file not found: ${file}`);
|
|
12706
12729
|
const summary = aggregator.finish();
|
|
12707
12730
|
process.stdout.write(opts.json ? JSON.stringify(summary) + "\n" : renderAuditStats(summary));
|
|
12708
12731
|
return 0;
|
|
12709
12732
|
}
|
|
12710
|
-
var AUDIT_SCHEMA_VERSION, AUDIT_LOG_ENV, MAX_AUDIT_LINE_BYTES, ALLOWED_KEYS;
|
|
12733
|
+
var AUDIT_SCHEMA_VERSION, AUDIT_LOG_ENV, MAX_AUDIT_LINE_BYTES, ALLOWED_KEYS, STATS_READ_REFUSED;
|
|
12711
12734
|
var init_audit = __esm({
|
|
12712
12735
|
"src/cli/audit.ts"() {
|
|
12713
12736
|
"use strict";
|
|
@@ -12727,11 +12750,12 @@ var init_audit = __esm({
|
|
|
12727
12750
|
"decision",
|
|
12728
12751
|
"session"
|
|
12729
12752
|
]);
|
|
12753
|
+
STATS_READ_REFUSED = "; stats reads the audit log only as a regular file \u2014 remove what stands there so the log can be read";
|
|
12730
12754
|
}
|
|
12731
12755
|
});
|
|
12732
12756
|
|
|
12733
12757
|
// src/cli/hook.ts
|
|
12734
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as
|
|
12758
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync5 } from "node:fs";
|
|
12735
12759
|
import { createHash as createHash7 } from "node:crypto";
|
|
12736
12760
|
function setSnapshotCache(cache3) {
|
|
12737
12761
|
snapshotCache = cache3;
|
|
@@ -13028,7 +13052,7 @@ function turnTransientBlocks(cwd, sessionId, policy, changes) {
|
|
|
13028
13052
|
recordObserverHealth(cwd, sessionId, telemetry.state, telemetry.reason);
|
|
13029
13053
|
}
|
|
13030
13054
|
const cp = cursorPath(cwd, sessionId);
|
|
13031
|
-
if (!
|
|
13055
|
+
if (!existsSync5(log)) return none;
|
|
13032
13056
|
let offset = 0;
|
|
13033
13057
|
const cursorText = cp ? readStateFile(cp) : null;
|
|
13034
13058
|
if (cursorText !== null) {
|
|
@@ -13306,7 +13330,7 @@ var init_ptree_cache = __esm({
|
|
|
13306
13330
|
});
|
|
13307
13331
|
|
|
13308
13332
|
// src/cli/hook-service.ts
|
|
13309
|
-
import { chmodSync, existsSync as
|
|
13333
|
+
import { chmodSync, existsSync as existsSync6, lstatSync as lstatSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync7, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
13310
13334
|
import { createConnection as createConnection2, createServer } from "node:net";
|
|
13311
13335
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
13312
13336
|
import { sep } from "node:path";
|
|
@@ -13394,7 +13418,7 @@ function removeQuietly(p) {
|
|
|
13394
13418
|
}
|
|
13395
13419
|
}
|
|
13396
13420
|
function ensurePrivateDir(dir, uid) {
|
|
13397
|
-
if (!
|
|
13421
|
+
if (!existsSync6(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
13398
13422
|
const st = lstatSync8(dir);
|
|
13399
13423
|
if (!st.isDirectory()) throw new Error(`${dir} is not a directory`);
|
|
13400
13424
|
if (st.uid !== uid) throw new Error(`${dir} is owned by uid ${st.uid}, not ${uid}; refusing to serve from it`);
|
|
@@ -13591,7 +13615,7 @@ async function stopHookService(paths, expectedRoot) {
|
|
|
13591
13615
|
process.kill(st.pid, "SIGTERM");
|
|
13592
13616
|
} catch {
|
|
13593
13617
|
}
|
|
13594
|
-
const gone = () => !pidAlive2(st.pid) || !
|
|
13618
|
+
const gone = () => !pidAlive2(st.pid) || !existsSync6(paths.socket);
|
|
13595
13619
|
const until = Date.now() + STOP_WAIT_MS;
|
|
13596
13620
|
while (!gone() && Date.now() < until) sleepSync(50);
|
|
13597
13621
|
if (!gone()) throw new Error(`hook service pid ${st.pid} did not exit within ${STOP_WAIT_MS / 1e3}s`);
|
|
@@ -13828,7 +13852,7 @@ var init_verifier_limits = __esm({
|
|
|
13828
13852
|
// src/cli/init.ts
|
|
13829
13853
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
13830
13854
|
import { createHash as createHash8 } from "node:crypto";
|
|
13831
|
-
import { existsSync as
|
|
13855
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync8, realpathSync as realpathSync5 } from "node:fs";
|
|
13832
13856
|
import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute9, join as join15, relative as relative4, resolve as resolve6 } from "node:path";
|
|
13833
13857
|
function gitConfig(cwd, key2) {
|
|
13834
13858
|
try {
|
|
@@ -13932,7 +13956,7 @@ function verifierCandidates(cwd) {
|
|
|
13932
13956
|
if (/^\s*\[tool\.pytest(?:\.ini_options)?\]\s*$/m.test(pyproject)) add("pytest");
|
|
13933
13957
|
} catch {
|
|
13934
13958
|
}
|
|
13935
|
-
if (
|
|
13959
|
+
if (existsSync7(join15(cwd, "pytest.ini"))) add("pytest");
|
|
13936
13960
|
try {
|
|
13937
13961
|
const setupCfg = readFileSync8(join15(cwd, "setup.cfg"), "utf8");
|
|
13938
13962
|
if (/^\s*\[tool:pytest\]\s*$/m.test(setupCfg)) add("pytest");
|
|
@@ -13992,7 +14016,7 @@ function planPolicy(cwd) {
|
|
|
13992
14016
|
const path = join15(cwd, POLICY_FILE);
|
|
13993
14017
|
const refused = refuseNonRegular(path);
|
|
13994
14018
|
if (refused) return { item: "policy", path: POLICY_FILE, status: "error", detail: refused };
|
|
13995
|
-
if (
|
|
14019
|
+
if (existsSync7(path)) {
|
|
13996
14020
|
try {
|
|
13997
14021
|
loadPolicy(cwd);
|
|
13998
14022
|
} catch (e) {
|
|
@@ -14011,7 +14035,7 @@ function planPolicy(cwd) {
|
|
|
14011
14035
|
function planGitignore(cwd) {
|
|
14012
14036
|
const rel = ".gitignore";
|
|
14013
14037
|
const path = join15(cwd, rel);
|
|
14014
|
-
if (!
|
|
14038
|
+
if (!existsSync7(join15(cwd, "node_modules"))) return null;
|
|
14015
14039
|
let existing = "";
|
|
14016
14040
|
let kind;
|
|
14017
14041
|
try {
|
|
@@ -14076,7 +14100,7 @@ function planClaudeHooks(cwd) {
|
|
|
14076
14100
|
const refused = refuseNonRegular(path);
|
|
14077
14101
|
if (refused) return { item: "agent", path: rel, status: "error", detail: refused };
|
|
14078
14102
|
let settings = {};
|
|
14079
|
-
if (
|
|
14103
|
+
if (existsSync7(path)) {
|
|
14080
14104
|
let parsed;
|
|
14081
14105
|
try {
|
|
14082
14106
|
parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
@@ -14128,7 +14152,7 @@ function planClaudeHooks(cwd) {
|
|
|
14128
14152
|
return {
|
|
14129
14153
|
item: "agent",
|
|
14130
14154
|
path: rel,
|
|
14131
|
-
status:
|
|
14155
|
+
status: existsSync7(path) ? "update" : "create",
|
|
14132
14156
|
detail: [
|
|
14133
14157
|
needPre && "wire PreToolUse deny",
|
|
14134
14158
|
needStop && "wire Stop sweep",
|
|
@@ -14172,7 +14196,7 @@ function hooksDir(cwd) {
|
|
|
14172
14196
|
if (out3) return resolve6(cwd, out3);
|
|
14173
14197
|
} catch {
|
|
14174
14198
|
}
|
|
14175
|
-
return
|
|
14199
|
+
return existsSync7(join15(cwd, ".git")) ? join15(cwd, ".git", "hooks") : null;
|
|
14176
14200
|
}
|
|
14177
14201
|
function display(cwd, path) {
|
|
14178
14202
|
const rel = relative4(cwd, path);
|
|
@@ -14199,11 +14223,11 @@ function planPreCommit(cwd) {
|
|
|
14199
14223
|
const skip = (detail) => ({ item: "pre-commit", path: "(none)", status: "skip", detail });
|
|
14200
14224
|
let path;
|
|
14201
14225
|
let note;
|
|
14202
|
-
if (
|
|
14226
|
+
if (existsSync7(join15(cwd, ".husky"))) {
|
|
14203
14227
|
path = join15(cwd, ".husky", "pre-commit");
|
|
14204
14228
|
note = "husky";
|
|
14205
14229
|
} else {
|
|
14206
|
-
const managed = MANAGED_HOOKS.find((m) => m.configs.some((c) =>
|
|
14230
|
+
const managed = MANAGED_HOOKS.find((m) => m.configs.some((c) => existsSync7(join15(cwd, c))));
|
|
14207
14231
|
if (managed) {
|
|
14208
14232
|
return skip(`${managed.name} manages this repository's hooks and regenerates them on install, so a line in the hooks directory would not last \u2014 ${managed.hint}`);
|
|
14209
14233
|
}
|
|
@@ -14221,7 +14245,7 @@ function planPreCommit(cwd) {
|
|
|
14221
14245
|
const rel = display(cwd, path);
|
|
14222
14246
|
const refused = refuseNonRegular(path);
|
|
14223
14247
|
if (refused) return { item: "pre-commit", path: rel, status: "error", detail: refused };
|
|
14224
|
-
const existing =
|
|
14248
|
+
const existing = existsSync7(path) ? readFileSync8(path, "utf8") : null;
|
|
14225
14249
|
const hookMode = () => existingMode(path, 493) | 73;
|
|
14226
14250
|
if (existing === null) {
|
|
14227
14251
|
return {
|
|
@@ -14352,7 +14376,7 @@ function planWorkflow(cwd, force) {
|
|
|
14352
14376
|
};
|
|
14353
14377
|
const refused = refuseNonRegular(path);
|
|
14354
14378
|
if (refused) return { item: "ci", path: rel, status: "error", detail: refused };
|
|
14355
|
-
if (!
|
|
14379
|
+
if (!existsSync7(path)) {
|
|
14356
14380
|
return {
|
|
14357
14381
|
item: "ci",
|
|
14358
14382
|
path: rel,
|
|
@@ -14710,7 +14734,7 @@ var init_fingerprint = __esm({
|
|
|
14710
14734
|
// src/dependency-env.ts
|
|
14711
14735
|
import { createHash as createHash10 } from "node:crypto";
|
|
14712
14736
|
import {
|
|
14713
|
-
existsSync as
|
|
14737
|
+
existsSync as existsSync8,
|
|
14714
14738
|
lstatSync as lstatSync9,
|
|
14715
14739
|
readdirSync as readdirSync3,
|
|
14716
14740
|
readFileSync as readFileSync9,
|
|
@@ -14864,7 +14888,7 @@ function snapshot(roots, probes, cwd) {
|
|
|
14864
14888
|
const h = createHash10("sha256");
|
|
14865
14889
|
for (const probe of probes) {
|
|
14866
14890
|
h.update(
|
|
14867
|
-
"probe\0" + probe.kind + "\0" + probe.path + "\0" + (
|
|
14891
|
+
"probe\0" + probe.kind + "\0" + probe.path + "\0" + (existsSync8(probe.path) ? "present" : "absent") + "\0"
|
|
14868
14892
|
);
|
|
14869
14893
|
}
|
|
14870
14894
|
for (const root of roots) fingerprintRoot(root, cwd, h);
|
|
@@ -14923,7 +14947,7 @@ function resolveExecutable(token, cwd, env) {
|
|
|
14923
14947
|
if (token.includes("/") || token.includes("\\")) {
|
|
14924
14948
|
const path = isAbsolute10(token) ? token : resolve7(cwd, token);
|
|
14925
14949
|
try {
|
|
14926
|
-
return
|
|
14950
|
+
return existsSync8(path) ? realpathSync6(path) : null;
|
|
14927
14951
|
} catch {
|
|
14928
14952
|
return null;
|
|
14929
14953
|
}
|
|
@@ -14931,7 +14955,7 @@ function resolveExecutable(token, cwd, env) {
|
|
|
14931
14955
|
for (const entry of (env.PATH ?? "").split(delimiter).filter(Boolean)) {
|
|
14932
14956
|
const candidate = resolve7(entry, token);
|
|
14933
14957
|
try {
|
|
14934
|
-
if (
|
|
14958
|
+
if (existsSync8(candidate) && statSync3(candidate).isFile()) return realpathSync6(candidate);
|
|
14935
14959
|
} catch {
|
|
14936
14960
|
}
|
|
14937
14961
|
}
|
|
@@ -14954,7 +14978,7 @@ function inferredVenvFromToken(token, cwd) {
|
|
|
14954
14978
|
const parentName = basename4(parent).toLowerCase();
|
|
14955
14979
|
if (parentName !== "bin" && parentName !== "scripts") return null;
|
|
14956
14980
|
const root = dirname6(parent);
|
|
14957
|
-
return
|
|
14981
|
+
return existsSync8(join16(root, "pyvenv.cfg")) ? root : null;
|
|
14958
14982
|
}
|
|
14959
14983
|
function unsupportedEcosystem(command, token) {
|
|
14960
14984
|
const name = token ? basename4(token).toLowerCase() : "";
|
|
@@ -14970,11 +14994,11 @@ function inferredVenv(executable) {
|
|
|
14970
14994
|
const parentName = basename4(parent).toLowerCase();
|
|
14971
14995
|
if (parentName !== "bin" && parentName !== "scripts") return null;
|
|
14972
14996
|
const root = dirname6(parent);
|
|
14973
|
-
return
|
|
14997
|
+
return existsSync8(join16(root, "pyvenv.cfg")) ? root : null;
|
|
14974
14998
|
}
|
|
14975
14999
|
function venvProblem(root) {
|
|
14976
15000
|
const cfg = join16(root, "pyvenv.cfg");
|
|
14977
|
-
if (!
|
|
15001
|
+
if (!existsSync8(cfg)) return "VIRTUAL_ENV " + JSON.stringify(root) + " has no pyvenv.cfg";
|
|
14978
15002
|
try {
|
|
14979
15003
|
const body = readFileSync9(cfg, "utf8");
|
|
14980
15004
|
if (/^\s*include-system-site-packages\s*=\s*true\s*$/im.test(body)) {
|
|
@@ -15001,7 +15025,7 @@ function discoverDependencyEnvironment(cwdInput, command, env = process.env) {
|
|
|
15001
15025
|
};
|
|
15002
15026
|
try {
|
|
15003
15027
|
const nodeModules = join16(cwd, "node_modules");
|
|
15004
|
-
if (
|
|
15028
|
+
if (existsSync8(nodeModules)) addRoot("node_modules", nodeModules);
|
|
15005
15029
|
else probes.push({ kind: "node_modules", path: nodeModules });
|
|
15006
15030
|
const token = firstExecutable(command);
|
|
15007
15031
|
const executable = resolveExecutable(token, cwd, env);
|
|
@@ -15013,7 +15037,7 @@ function discoverDependencyEnvironment(cwdInput, command, env = process.env) {
|
|
|
15013
15037
|
venv = resolve7(env.VIRTUAL_ENV);
|
|
15014
15038
|
} else {
|
|
15015
15039
|
venv = inferredVenv(executable) ?? inferredVenvFromToken(token, cwd);
|
|
15016
|
-
if (!venv && /(?:^|\s)uv\s+run(?:\s|$)/.test(command) &&
|
|
15040
|
+
if (!venv && /(?:^|\s)uv\s+run(?:\s|$)/.test(command) && existsSync8(join16(cwd, ".venv", "pyvenv.cfg"))) {
|
|
15017
15041
|
venv = join16(cwd, ".venv");
|
|
15018
15042
|
}
|
|
15019
15043
|
}
|
|
@@ -16616,7 +16640,7 @@ var init_verification_receipt = __esm({
|
|
|
16616
16640
|
// src/cli/verify.ts
|
|
16617
16641
|
import { execFileSync as execFileSync9, spawnSync as spawnSync3 } from "node:child_process";
|
|
16618
16642
|
import { createHash as createHash14 } from "node:crypto";
|
|
16619
|
-
import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync10, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync3, readFileSync as readFileSync12, readlinkSync as readlinkSync3, rmSync as rmSync9, symlinkSync, writeFileSync as writeFileSync8, existsSync as
|
|
16643
|
+
import { chmodSync as chmodSync2, cpSync, lstatSync as lstatSync10, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync3, readFileSync as readFileSync12, readlinkSync as readlinkSync3, rmSync as rmSync9, symlinkSync, writeFileSync as writeFileSync8, existsSync as existsSync9 } from "node:fs";
|
|
16620
16644
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
16621
16645
|
import { dirname as dirname10, isAbsolute as isAbsolute11, join as join21, relative as relative6, resolve as resolve9, sep as sep4 } from "node:path";
|
|
16622
16646
|
function oracleAssuranceReport() {
|
|
@@ -16812,7 +16836,7 @@ function probeFilesystemCaseSensitivity(dir) {
|
|
|
16812
16836
|
const lower = join21(dir, ".tw-case-probe-a");
|
|
16813
16837
|
try {
|
|
16814
16838
|
writeFileSync8(upper, "", { flag: "wx" });
|
|
16815
|
-
return !
|
|
16839
|
+
return !existsSync9(lower);
|
|
16816
16840
|
} finally {
|
|
16817
16841
|
rmSync9(upper, { force: true });
|
|
16818
16842
|
}
|
|
@@ -18605,7 +18629,7 @@ raise SystemExit(0)
|
|
|
18605
18629
|
|
|
18606
18630
|
// src/cli/doctor.ts
|
|
18607
18631
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
18608
|
-
import { existsSync as
|
|
18632
|
+
import { existsSync as existsSync10, readFileSync as readFileSync14, readdirSync as readdirSync5 } from "node:fs";
|
|
18609
18633
|
import { join as join23, resolve as resolve11 } from "node:path";
|
|
18610
18634
|
function lifecyclePlatformCheck(platform = process.platform, linuxPython = platform === "linux" ? trustedLinuxPython() : null) {
|
|
18611
18635
|
if (platform === "linux") {
|
|
@@ -18661,7 +18685,7 @@ function wiringState(status, authority) {
|
|
|
18661
18685
|
return authority ? "BROKEN" : "WARN";
|
|
18662
18686
|
}
|
|
18663
18687
|
function pinsInFile(path) {
|
|
18664
|
-
if (!
|
|
18688
|
+
if (!existsSync10(path)) return [];
|
|
18665
18689
|
let src = "";
|
|
18666
18690
|
try {
|
|
18667
18691
|
src = readFileSync14(path, "utf8");
|
|
@@ -18685,7 +18709,7 @@ function workflowPermissionCheck(cwd, workflowRels = [".github/workflows/tamperw
|
|
|
18685
18709
|
const warnings = [];
|
|
18686
18710
|
for (const rel of workflowRels) {
|
|
18687
18711
|
const path = resolve11(cwd, rel);
|
|
18688
|
-
if (!
|
|
18712
|
+
if (!existsSync10(path)) {
|
|
18689
18713
|
broken.push(`${rel} is missing`);
|
|
18690
18714
|
continue;
|
|
18691
18715
|
}
|
|
@@ -19090,7 +19114,7 @@ function diagnose(opts = {}) {
|
|
|
19090
19114
|
} else {
|
|
19091
19115
|
const workflowDirRel = ".github/workflows";
|
|
19092
19116
|
const workflowDir = resolve11(cwd, workflowDirRel);
|
|
19093
|
-
if (!
|
|
19117
|
+
if (!existsSync10(workflowDir)) {
|
|
19094
19118
|
return fail("ci-verifier", `${workflowDirRel}: workflow directory does not exist`);
|
|
19095
19119
|
}
|
|
19096
19120
|
let entries;
|
|
@@ -19110,7 +19134,7 @@ function diagnose(opts = {}) {
|
|
|
19110
19134
|
const authorityWorkflows = [];
|
|
19111
19135
|
for (const workflowRel of workflowRels) {
|
|
19112
19136
|
const workflowPath = resolve11(cwd, workflowRel);
|
|
19113
|
-
if (!
|
|
19137
|
+
if (!existsSync10(workflowPath)) {
|
|
19114
19138
|
return fail("ci-verifier", `${workflowRel}: workflow does not exist`);
|
|
19115
19139
|
}
|
|
19116
19140
|
let doc;
|
|
@@ -19429,7 +19453,7 @@ var init_status2 = __esm({
|
|
|
19429
19453
|
// src/cli/trace-verify.ts
|
|
19430
19454
|
import { execFileSync as execFileSync12, spawnSync as spawnSync5 } from "node:child_process";
|
|
19431
19455
|
import {
|
|
19432
|
-
existsSync as
|
|
19456
|
+
existsSync as existsSync11,
|
|
19433
19457
|
mkdtempSync as mkdtempSync5,
|
|
19434
19458
|
readFileSync as readFileSync15,
|
|
19435
19459
|
readdirSync as readdirSync6,
|
|
@@ -19688,7 +19712,7 @@ function materializeBase(base, cwd, dest) {
|
|
|
19688
19712
|
}
|
|
19689
19713
|
const hostModules = join24(cwd, "node_modules");
|
|
19690
19714
|
const tracedModules = join24(dest, "node_modules");
|
|
19691
|
-
if (
|
|
19715
|
+
if (existsSync11(hostModules) && !existsSync11(tracedModules)) {
|
|
19692
19716
|
symlinkSync2(resolve12(hostModules), tracedModules, "dir");
|
|
19693
19717
|
}
|
|
19694
19718
|
}
|
|
@@ -20000,7 +20024,7 @@ var init_immutable = __esm({
|
|
|
20000
20024
|
});
|
|
20001
20025
|
|
|
20002
20026
|
// src/runtimes.ts
|
|
20003
|
-
import { existsSync as
|
|
20027
|
+
import { existsSync as existsSync12, readFileSync as readFileSync16 } from "node:fs";
|
|
20004
20028
|
import { join as join25 } from "node:path";
|
|
20005
20029
|
function isRecord6(x) {
|
|
20006
20030
|
return typeof x === "object" && x !== null && !Array.isArray(x);
|
|
@@ -20030,7 +20054,7 @@ function claudeSettingsBeyondTamperward(cwd) {
|
|
|
20030
20054
|
const path = join25(cwd, ".claude", "settings.json");
|
|
20031
20055
|
let raw;
|
|
20032
20056
|
try {
|
|
20033
|
-
if (!
|
|
20057
|
+
if (!existsSync12(path)) return false;
|
|
20034
20058
|
raw = readFileSync16(path, "utf8");
|
|
20035
20059
|
} catch {
|
|
20036
20060
|
return false;
|
|
@@ -20057,7 +20081,7 @@ function detectRuntimes(cwd) {
|
|
|
20057
20081
|
for (const rt of KNOWN_RUNTIMES) {
|
|
20058
20082
|
const marker = rt.markers.find((m) => {
|
|
20059
20083
|
try {
|
|
20060
|
-
return
|
|
20084
|
+
return existsSync12(join25(cwd, m));
|
|
20061
20085
|
} catch {
|
|
20062
20086
|
return false;
|
|
20063
20087
|
}
|
|
@@ -20149,15 +20173,15 @@ function immutableRuntimeCapabilities(caps) {
|
|
|
20149
20173
|
function immutableRuntimeAdapter(adapter) {
|
|
20150
20174
|
return Object.freeze(adapter);
|
|
20151
20175
|
}
|
|
20152
|
-
function unknownToolFinding(toolName) {
|
|
20176
|
+
function unknownToolFinding(runtime, toolName) {
|
|
20153
20177
|
const name = toolName.length > 80 ? toolName.slice(0, 77) + "..." : toolName;
|
|
20154
|
-
const detail = '
|
|
20178
|
+
const detail = runtime + ' tool "' + name + `" is not in TamperWard's explicit operation vocabulary`;
|
|
20155
20179
|
return {
|
|
20156
20180
|
rule: "unknown-tool",
|
|
20157
20181
|
severity: "block",
|
|
20158
20182
|
message: "TamperWard cannot safely classify " + detail + "; it is denied rather than treated as a no-op.",
|
|
20159
20183
|
evidence: detail,
|
|
20160
|
-
remediation: "Confirm the tool is non-mutating and add it to the
|
|
20184
|
+
remediation: "Confirm the tool is non-mutating and add it to the " + runtime + " adapter allowlist, or obtain an explicit sign-off before retrying.",
|
|
20161
20185
|
signoff: { required: true, command: 'tamperward allow --reason "..."' }
|
|
20162
20186
|
};
|
|
20163
20187
|
}
|
|
@@ -20568,7 +20592,8 @@ function codexOperationKind(toolName) {
|
|
|
20568
20592
|
if (FILE_EDIT_TOOLS.has(toolName)) return "file-edit";
|
|
20569
20593
|
if (toolName.startsWith(MCP_PREFIX)) return "mcp";
|
|
20570
20594
|
if (FILE_READ_TOOLS.has(toolName)) return "file-read";
|
|
20571
|
-
return "other";
|
|
20595
|
+
if (NON_MUTATING_TOOLS.has(toolName)) return "other";
|
|
20596
|
+
return "unknown";
|
|
20572
20597
|
}
|
|
20573
20598
|
function inputFrom(parsed) {
|
|
20574
20599
|
return {
|
|
@@ -20601,7 +20626,7 @@ function normalizeCodexEvent(raw, phase) {
|
|
|
20601
20626
|
}
|
|
20602
20627
|
return eventFrom2(inputFrom(parsed), phase);
|
|
20603
20628
|
}
|
|
20604
|
-
var SHELL_TOOLS, FILE_EDIT_TOOLS, FILE_READ_TOOLS, MCP_PREFIX;
|
|
20629
|
+
var SHELL_TOOLS, FILE_EDIT_TOOLS, FILE_READ_TOOLS, NON_MUTATING_TOOLS, MCP_PREFIX;
|
|
20605
20630
|
var init_schema = __esm({
|
|
20606
20631
|
"src/adapters/codex/schema.ts"() {
|
|
20607
20632
|
"use strict";
|
|
@@ -20609,6 +20634,7 @@ var init_schema = __esm({
|
|
|
20609
20634
|
SHELL_TOOLS = /* @__PURE__ */ new Set(["Bash"]);
|
|
20610
20635
|
FILE_EDIT_TOOLS = /* @__PURE__ */ new Set(["apply_patch", "Write", "Edit"]);
|
|
20611
20636
|
FILE_READ_TOOLS = /* @__PURE__ */ new Set(["view_image"]);
|
|
20637
|
+
NON_MUTATING_TOOLS = /* @__PURE__ */ new Set(["update_plan", "web_search"]);
|
|
20612
20638
|
MCP_PREFIX = "mcp__";
|
|
20613
20639
|
}
|
|
20614
20640
|
});
|
|
@@ -20704,6 +20730,12 @@ var init_adapter2 = __esm({
|
|
|
20704
20730
|
const findings = swept.findings ? [...swept.findings] : [];
|
|
20705
20731
|
return { outcome: "ok", wire: swept.stdout, decision: { verdict: swept.stdout ? "deny" : "allow", findings, reason: swept.stdout || void 0 } };
|
|
20706
20732
|
}
|
|
20733
|
+
if (parsed.operation.kind === "unknown") {
|
|
20734
|
+
const detail = `unrecognized Codex tool ${parsed.operation.name}; refusing to evaluate it as a no-op`;
|
|
20735
|
+
const findings = [unknownToolFinding("Codex", parsed.operation.name)];
|
|
20736
|
+
const wire = this.denyPayload(findings, "pre-action");
|
|
20737
|
+
return { outcome: "ok", wire, detail, decision: { verdict: "deny", findings, reason: wire } };
|
|
20738
|
+
}
|
|
20707
20739
|
try {
|
|
20708
20740
|
const root = idv.trustedRoot ?? repoRoot(defaultCwd ?? process.cwd());
|
|
20709
20741
|
const sessionCwd = parsed.identity.claimedCwd ?? defaultCwd ?? process.cwd();
|
|
@@ -20841,7 +20873,7 @@ function copilotOperationKind(toolName) {
|
|
|
20841
20873
|
if (FILE_EDIT_TOOLS2.has(name)) return "file-edit";
|
|
20842
20874
|
if (name.startsWith(MCP_PREFIX2)) return "mcp";
|
|
20843
20875
|
if (FILE_READ_TOOLS2.has(name)) return "file-read";
|
|
20844
|
-
if (
|
|
20876
|
+
if (NON_MUTATING_TOOLS2.has(name)) return "other";
|
|
20845
20877
|
return "unknown";
|
|
20846
20878
|
}
|
|
20847
20879
|
function argsFrom(parsed) {
|
|
@@ -20905,7 +20937,7 @@ function copilotStopInput(raw) {
|
|
|
20905
20937
|
...input.stop_hook_active !== void 0 ? { stop_hook_active: input.stop_hook_active } : {}
|
|
20906
20938
|
});
|
|
20907
20939
|
}
|
|
20908
|
-
var SHELL_TOOLS2, FILE_EDIT_TOOLS2, FILE_READ_TOOLS2,
|
|
20940
|
+
var SHELL_TOOLS2, FILE_EDIT_TOOLS2, FILE_READ_TOOLS2, NON_MUTATING_TOOLS2, MCP_PREFIX2;
|
|
20909
20941
|
var init_schema2 = __esm({
|
|
20910
20942
|
"src/adapters/copilot/schema.ts"() {
|
|
20911
20943
|
"use strict";
|
|
@@ -20913,7 +20945,7 @@ var init_schema2 = __esm({
|
|
|
20913
20945
|
SHELL_TOOLS2 = /* @__PURE__ */ new Set(["bash", "powershell", "write_bash", "write_powershell"]);
|
|
20914
20946
|
FILE_EDIT_TOOLS2 = /* @__PURE__ */ new Set(["create", "edit", "write", "multiedit", "apply_patch", "str_replace_editor"]);
|
|
20915
20947
|
FILE_READ_TOOLS2 = /* @__PURE__ */ new Set(["view", "read", "grep", "rg", "glob"]);
|
|
20916
|
-
|
|
20948
|
+
NON_MUTATING_TOOLS2 = /* @__PURE__ */ new Set([
|
|
20917
20949
|
"read_bash",
|
|
20918
20950
|
"read_powershell",
|
|
20919
20951
|
"stop_bash",
|
|
@@ -21050,7 +21082,7 @@ var init_adapter3 = __esm({
|
|
|
21050
21082
|
}
|
|
21051
21083
|
if (parsed.operation.kind === "unknown") {
|
|
21052
21084
|
const detail = `unrecognized Copilot tool ${parsed.operation.name}; refusing to evaluate it as a no-op`;
|
|
21053
|
-
const findings = [unknownToolFinding(parsed.operation.name)];
|
|
21085
|
+
const findings = [unknownToolFinding("Copilot", parsed.operation.name)];
|
|
21054
21086
|
const wire = this.denyPayload(findings, "pre-action");
|
|
21055
21087
|
return { outcome: "ok", wire, detail, decision: { verdict: "deny", findings, reason: wire } };
|
|
21056
21088
|
}
|
|
@@ -21082,7 +21114,7 @@ var init_adapter3 = __esm({
|
|
|
21082
21114
|
|
|
21083
21115
|
// src/adapters/copilot-sdk/changes.ts
|
|
21084
21116
|
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
21085
|
-
import { mkdtempSync as mkdtempSync6, writeFileSync as writeFileSync10, readFileSync as readFileSync17, rmSync as rmSync12, existsSync as
|
|
21117
|
+
import { mkdtempSync as mkdtempSync6, writeFileSync as writeFileSync10, readFileSync as readFileSync17, rmSync as rmSync12, existsSync as existsSync13, realpathSync as realpathSync10 } from "node:fs";
|
|
21086
21118
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
21087
21119
|
import { basename as basename5, dirname as dirname12, isAbsolute as isAbsolute16, relative as relative11, resolve as resolve15, join as join26 } from "node:path";
|
|
21088
21120
|
function asStr4(v) {
|
|
@@ -21101,7 +21133,7 @@ function canonicalContainedTarget(abs, root) {
|
|
|
21101
21133
|
}
|
|
21102
21134
|
let dir = abs;
|
|
21103
21135
|
const tail = [];
|
|
21104
|
-
while (!
|
|
21136
|
+
while (!existsSync13(dir)) {
|
|
21105
21137
|
const parent = dirname12(dir);
|
|
21106
21138
|
if (parent === dir) break;
|
|
21107
21139
|
tail.unshift(basename5(dir));
|
|
@@ -21929,7 +21961,7 @@ var init_runtime_qualification = __esm({
|
|
|
21929
21961
|
|
|
21930
21962
|
// src/cli/runtime.ts
|
|
21931
21963
|
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
21932
|
-
import { existsSync as
|
|
21964
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync18 } from "node:fs";
|
|
21933
21965
|
import { join as join27, dirname as dirname13 } from "node:path";
|
|
21934
21966
|
function storePath(cwd) {
|
|
21935
21967
|
const dir = gitDir(cwd);
|
|
@@ -22110,7 +22142,7 @@ function resolveHookConfigHash(canonicalId, cwd) {
|
|
|
22110
22142
|
"github-copilot-cli": join27(cwd, ".github", "hooks", "tamperward.json")
|
|
22111
22143
|
};
|
|
22112
22144
|
const file = files[canonicalId];
|
|
22113
|
-
if (!file || !
|
|
22145
|
+
if (!file || !existsSync14(file)) return null;
|
|
22114
22146
|
try {
|
|
22115
22147
|
const raw = readFileSync18(file, "utf8");
|
|
22116
22148
|
let hooks = raw;
|
|
@@ -23588,7 +23620,7 @@ var init_record = __esm({
|
|
|
23588
23620
|
// src/research/run.ts
|
|
23589
23621
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
23590
23622
|
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
23591
|
-
import { closeSync as closeSync5, existsSync as
|
|
23623
|
+
import { closeSync as closeSync5, existsSync as existsSync15, linkSync, mkdirSync as mkdirSync12, openSync as openSync4, readFileSync as readFileSync21, renameSync as renameSync4, rmSync as rmSync14, unlinkSync, writeFileSync as writeFileSync11, writeSync as writeSync2 } from "node:fs";
|
|
23592
23624
|
import { hostname } from "node:os";
|
|
23593
23625
|
import { join as join29, resolve as resolve19 } from "node:path";
|
|
23594
23626
|
function git7(args, cwd) {
|
|
@@ -23625,7 +23657,7 @@ function lockHolder(path) {
|
|
|
23625
23657
|
function acquireResearchLock(ledger, breakStaleLock = false) {
|
|
23626
23658
|
mkdirSync12(ledger, { recursive: true });
|
|
23627
23659
|
const path = researchLockPath(ledger);
|
|
23628
|
-
if (breakStaleLock &&
|
|
23660
|
+
if (breakStaleLock && existsSync15(path)) {
|
|
23629
23661
|
const owner2 = readLockOwner(path);
|
|
23630
23662
|
if (!owner2) {
|
|
23631
23663
|
throw new ResearchError(`research output ${ledger} has an unreadable lock at ${path}; verify no run is active, then remove it deliberately`);
|
|
@@ -23668,7 +23700,7 @@ function acquireResearchLock(ledger, breakStaleLock = false) {
|
|
|
23668
23700
|
const text = JSON.stringify(owner) + "\n";
|
|
23669
23701
|
let fd = -1;
|
|
23670
23702
|
try {
|
|
23671
|
-
fd =
|
|
23703
|
+
fd = openSync4(path, "wx", 384);
|
|
23672
23704
|
writeSync2(fd, text);
|
|
23673
23705
|
closeSync5(fd);
|
|
23674
23706
|
fd = -1;
|
|
@@ -23998,7 +24030,7 @@ function runResearch(opts) {
|
|
|
23998
24030
|
try {
|
|
23999
24031
|
for (let pair = 1; pair <= pairs; pair++) {
|
|
24000
24032
|
const path = pairRecordPath(ledger, task.id, pair);
|
|
24001
|
-
if (!
|
|
24033
|
+
if (!existsSync15(path)) continue;
|
|
24002
24034
|
const existing = resumableRecord(path, {
|
|
24003
24035
|
task: task.id,
|
|
24004
24036
|
pair,
|
|
@@ -24118,7 +24150,7 @@ var init_run2 = __esm({
|
|
|
24118
24150
|
});
|
|
24119
24151
|
|
|
24120
24152
|
// src/research/ledger-files.ts
|
|
24121
|
-
import { closeSync as closeSync6, constants as constants5, fstatSync as fstatSync3, lstatSync as lstatSync12, openSync as
|
|
24153
|
+
import { closeSync as closeSync6, constants as constants5, fstatSync as fstatSync3, lstatSync as lstatSync12, openSync as openSync5, readFileSync as readFileSync22, readdirSync as readdirSync7, realpathSync as realpathSync12 } from "node:fs";
|
|
24122
24154
|
import { join as join30, resolve as resolve20 } from "node:path";
|
|
24123
24155
|
function lstatOrNull(path) {
|
|
24124
24156
|
try {
|
|
@@ -24151,7 +24183,7 @@ function readRegularFile(path, realPairsDir, name) {
|
|
|
24151
24183
|
const flags = constants5.O_RDONLY | (constants5.O_NOFOLLOW ?? 0);
|
|
24152
24184
|
let fd;
|
|
24153
24185
|
try {
|
|
24154
|
-
fd =
|
|
24186
|
+
fd = openSync5(path, flags);
|
|
24155
24187
|
} catch (e) {
|
|
24156
24188
|
throw new ResearchError(`cannot open ledger record ${path}: ${errorMessage(e)}`);
|
|
24157
24189
|
}
|
|
@@ -24334,7 +24366,7 @@ var init_summarize = __esm({
|
|
|
24334
24366
|
|
|
24335
24367
|
// src/research/init.ts
|
|
24336
24368
|
import { createHash as createHash17 } from "node:crypto";
|
|
24337
|
-
import { existsSync as
|
|
24369
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "node:fs";
|
|
24338
24370
|
import { basename as basename6, dirname as dirname15, resolve as resolve21 } from "node:path";
|
|
24339
24371
|
function idFrom(repo) {
|
|
24340
24372
|
const clean = repo.replace(/[\\/]$/, "").split(/[\\/]/).pop() || "task";
|
|
@@ -24346,7 +24378,7 @@ function createResearchManifest(opts) {
|
|
|
24346
24378
|
if (!opts.prompt) throw new ResearchError("research init requires --prompt");
|
|
24347
24379
|
if (!opts.verifyCommand) throw new ResearchError("research init requires --verify-command");
|
|
24348
24380
|
const out3 = resolve21(opts.out);
|
|
24349
|
-
if (
|
|
24381
|
+
if (existsSync16(out3)) throw new ResearchError(`research init refuses to overwrite ${out3}; choose a new path`);
|
|
24350
24382
|
const id = opts.id ?? idFrom(opts.repo);
|
|
24351
24383
|
if (!/^[A-Za-z0-9._-]+$/.test(id)) throw new ResearchError(`research init id must match [A-Za-z0-9._-] (got "${id}")`);
|
|
24352
24384
|
if (opts.verifyBudget !== void 0 && (!Number.isFinite(opts.verifyBudget) || opts.verifyBudget <= 0)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tamperward",
|
|
3
|
-
"version": "2.39.
|
|
3
|
+
"version": "2.39.14",
|
|
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",
|