tamperward 2.23.16 → 2.23.17
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 +56 -19
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -6756,6 +6756,25 @@ function entryShape(h) {
|
|
|
6756
6756
|
}
|
|
6757
6757
|
return { problems, runs };
|
|
6758
6758
|
}
|
|
6759
|
+
function judgeGateEntry(event, matcher, h) {
|
|
6760
|
+
const cmd = typeof h.command === "string" ? h.command : "";
|
|
6761
|
+
const a = analyseGateCommand(cmd, event);
|
|
6762
|
+
const shape = entryShape(h);
|
|
6763
|
+
const problems = [...a.problems, ...shape.problems];
|
|
6764
|
+
let runs = a.runs && shape.runs;
|
|
6765
|
+
let tools = null;
|
|
6766
|
+
if (event === "PreToolUse") {
|
|
6767
|
+
const sel = matcherSelection(matcher);
|
|
6768
|
+
tools = sel.tools;
|
|
6769
|
+
if (sel.problem) {
|
|
6770
|
+
problems.push(sel.problem);
|
|
6771
|
+
}
|
|
6772
|
+
} else if (matcher !== void 0 && !(typeof matcher === "string" && (matcher === "" || matcher === "*"))) {
|
|
6773
|
+
problems.push(`a \`matcher\` (${JSON.stringify(matcher)}) on the Stop entry`);
|
|
6774
|
+
runs = false;
|
|
6775
|
+
}
|
|
6776
|
+
return { runs, problems, pin: a.pin, tools };
|
|
6777
|
+
}
|
|
6759
6778
|
function gateCandidates(s, event) {
|
|
6760
6779
|
const out3 = [];
|
|
6761
6780
|
for (const group of eventEntries(s, event) ?? []) {
|
|
@@ -6778,22 +6797,7 @@ function gateCandidates(s, event) {
|
|
|
6778
6797
|
continue;
|
|
6779
6798
|
}
|
|
6780
6799
|
if (!MENTIONS_GATE.test(cmd)) continue;
|
|
6781
|
-
|
|
6782
|
-
const shape = entryShape(h);
|
|
6783
|
-
const problems = [...a.problems, ...shape.problems];
|
|
6784
|
-
let runs = a.runs && shape.runs;
|
|
6785
|
-
let tools = null;
|
|
6786
|
-
if (event === "PreToolUse") {
|
|
6787
|
-
const sel = matcherSelection(group.matcher);
|
|
6788
|
-
tools = sel.tools;
|
|
6789
|
-
if (sel.problem) {
|
|
6790
|
-
problems.push(sel.problem);
|
|
6791
|
-
}
|
|
6792
|
-
} else if (group.matcher !== void 0 && !(typeof group.matcher === "string" && (group.matcher === "" || group.matcher === "*"))) {
|
|
6793
|
-
problems.push(`a \`matcher\` (${JSON.stringify(group.matcher)}) on the Stop entry`);
|
|
6794
|
-
runs = false;
|
|
6795
|
-
}
|
|
6796
|
-
out3.push({ key: JSON.stringify({ m: group.matcher, h }), runs, problems, pin: a.pin, tools });
|
|
6800
|
+
out3.push({ key: JSON.stringify({ m: group.matcher, h }), ...judgeGateEntry(event, group.matcher, h) });
|
|
6797
6801
|
}
|
|
6798
6802
|
}
|
|
6799
6803
|
return out3;
|
|
@@ -11164,6 +11168,17 @@ function planGitignore(cwd) {
|
|
|
11164
11168
|
apply: () => atomicReplaceFile(path, next, existingMode(path, 420))
|
|
11165
11169
|
};
|
|
11166
11170
|
}
|
|
11171
|
+
function gateVerdicts(event, groups, needle) {
|
|
11172
|
+
const out3 = [];
|
|
11173
|
+
for (const group of groups) {
|
|
11174
|
+
for (const h of group.hooks ?? []) {
|
|
11175
|
+
if (!needle.test(String(h.command ?? ""))) continue;
|
|
11176
|
+
const { runs, problems } = judgeGateEntry(event, group.matcher, h);
|
|
11177
|
+
out3.push({ event, group, h, runs, problems });
|
|
11178
|
+
}
|
|
11179
|
+
}
|
|
11180
|
+
return out3;
|
|
11181
|
+
}
|
|
11167
11182
|
function planClaudeHooks(cwd) {
|
|
11168
11183
|
const rel = ".claude/settings.json";
|
|
11169
11184
|
const path = join14(cwd, rel);
|
|
@@ -11197,10 +11212,24 @@ function planClaudeHooks(cwd) {
|
|
|
11197
11212
|
const needPre = preEntries.length === 0;
|
|
11198
11213
|
const stopEntries = entriesWith(hooks.Stop, SWEEP_RE);
|
|
11199
11214
|
const needStop = stopEntries.length === 0;
|
|
11215
|
+
const neutralised = [
|
|
11216
|
+
...gateVerdicts("PreToolUse", preEntries, HOOK_RE),
|
|
11217
|
+
...gateVerdicts("Stop", stopEntries, SWEEP_RE)
|
|
11218
|
+
].filter((v) => !v.runs);
|
|
11219
|
+
const foreign = neutralised.filter((v) => ours(String(v.h.command ?? "")) === null);
|
|
11220
|
+
if (foreign.length > 0) {
|
|
11221
|
+
return {
|
|
11222
|
+
item: "agent",
|
|
11223
|
+
path: rel,
|
|
11224
|
+
status: "error",
|
|
11225
|
+
detail: `${foreign.map((v) => `the ${v.event} gate entry is neutralised (${v.problems.join("; ")})`).join("; ")} \u2014 the runtime would not run the gate through it; remove the entry, then re-run init (refusing to rewrite a hand-written entry)`
|
|
11226
|
+
};
|
|
11227
|
+
}
|
|
11200
11228
|
const stale = preEntries.filter((m) => missingTools(m.matcher).length > 0);
|
|
11201
|
-
const
|
|
11229
|
+
const restoring = new Set(neutralised.map((v) => v.h));
|
|
11230
|
+
const repin = [...preEntries, ...stopEntries].flatMap((m) => m.hooks ?? []).filter((h) => !restoring.has(h) && stalePin(String(h.command ?? "")));
|
|
11202
11231
|
const needDisableFalse = settings.disableAllHooks !== false;
|
|
11203
|
-
if (!needPre && !needStop && stale.length === 0 && repin.length === 0 && !needDisableFalse) {
|
|
11232
|
+
if (!needPre && !needStop && stale.length === 0 && repin.length === 0 && neutralised.length === 0 && !needDisableFalse) {
|
|
11204
11233
|
return { item: "agent", path: rel, status: "ok", detail: "PreToolUse + Stop hooks already wired" };
|
|
11205
11234
|
}
|
|
11206
11235
|
const repairing = stale.length > 0 ? [...new Set(stale.flatMap((m) => missingTools(m.matcher)))] : [];
|
|
@@ -11214,9 +11243,16 @@ function planClaudeHooks(cwd) {
|
|
|
11214
11243
|
needStop && "wire Stop sweep",
|
|
11215
11244
|
repairing.length > 0 && `widen the PreToolUse matcher to cover ${repairing.join(", ")}`,
|
|
11216
11245
|
repin.length > 0 && `re-pin the hook commands to tamperward@${TW_VERSION} (was ${pins.join(", ")})`,
|
|
11246
|
+
...neutralised.map((v) => `restore the ${v.event} gate entry to the shape init writes \u2014 it is neutralised (${v.problems.join("; ")})`),
|
|
11217
11247
|
needDisableFalse && (settings.disableAllHooks === void 0 ? "declare disableAllHooks: false so the user settings file cannot switch the hooks off" : `set disableAllHooks: false (was ${JSON.stringify(settings.disableAllHooks)})`)
|
|
11218
11248
|
].filter(Boolean).join(" + "),
|
|
11219
11249
|
apply: () => {
|
|
11250
|
+
for (const v of neutralised) {
|
|
11251
|
+
for (const k of Object.keys(v.h)) delete v.h[k];
|
|
11252
|
+
v.h.type = "command";
|
|
11253
|
+
v.h.command = v.event === "PreToolUse" ? HOOK_CMD : SWEEP_CMD;
|
|
11254
|
+
if (v.event === "Stop") delete v.group.matcher;
|
|
11255
|
+
}
|
|
11220
11256
|
for (const h of repin) {
|
|
11221
11257
|
const kind = ours(String(h.command ?? ""))?.[2];
|
|
11222
11258
|
h.command = kind === "hook claude" ? HOOK_CMD : kind === "sweep claude" ? SWEEP_CMD : h.command;
|
|
@@ -11316,7 +11352,7 @@ ${block.join("\n")}
|
|
|
11316
11352
|
}
|
|
11317
11353
|
const lines = existing.split("\n");
|
|
11318
11354
|
const dead = unconditionalExitAt(lines);
|
|
11319
|
-
const at = lines.findIndex((l) => PRECOMMIT_RE.test(l));
|
|
11355
|
+
const at = lines.findIndex((l) => !/^\s*#/.test(l) && PRECOMMIT_RE.test(l));
|
|
11320
11356
|
const write = (out3) => atomicReplaceFile(path, out3.join("\n"), hookMode());
|
|
11321
11357
|
if (at !== -1 && (dead === -1 || at < dead)) {
|
|
11322
11358
|
if (!stalePin(lines[at])) {
|
|
@@ -11559,6 +11595,7 @@ var init_init = __esm({
|
|
|
11559
11595
|
init_verifier_limits();
|
|
11560
11596
|
init_wiring();
|
|
11561
11597
|
init_narrow();
|
|
11598
|
+
init_hook_tampering();
|
|
11562
11599
|
init_repo_context();
|
|
11563
11600
|
init_safe_write();
|
|
11564
11601
|
errText3 = (e) => (e instanceof Error ? e.message : String(e)).replace(/\s+/g, " ").trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tamperward",
|
|
3
|
-
"version": "2.23.
|
|
3
|
+
"version": "2.23.17",
|
|
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",
|