tamperward 2.24.0 → 2.24.1
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 +55 -3
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -7365,6 +7365,50 @@ function syntheticBase(over) {
|
|
|
7365
7365
|
}
|
|
7366
7366
|
return base;
|
|
7367
7367
|
}
|
|
7368
|
+
function freshLefthookBase(after) {
|
|
7369
|
+
const base = {};
|
|
7370
|
+
for (const [section, v] of Object.entries(after)) {
|
|
7371
|
+
if (!isObj(v)) continue;
|
|
7372
|
+
for (const group of ["commands", "scripts"]) {
|
|
7373
|
+
const g = v[group];
|
|
7374
|
+
if (!isObj(g)) continue;
|
|
7375
|
+
for (const [name, cfg] of Object.entries(g)) {
|
|
7376
|
+
if (!isObj(cfg)) continue;
|
|
7377
|
+
const run = cfg.run ?? cfg.runner;
|
|
7378
|
+
if (!namesGate(name) && !namesGate(run)) continue;
|
|
7379
|
+
const sec = base[section] ??= {};
|
|
7380
|
+
const grp = sec[group] ??= {};
|
|
7381
|
+
grp[name] = { run: typeof run === "string" ? liveOrCanonical(run) : CANONICAL_GATE };
|
|
7382
|
+
}
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7385
|
+
return base;
|
|
7386
|
+
}
|
|
7387
|
+
function freshPreCommitBase(after) {
|
|
7388
|
+
const stagesOf = (v) => Array.isArray(v) && v.map(String).some((s) => COMMIT_STAGES.has(s)) ? v.map(String) : null;
|
|
7389
|
+
const defaults = stagesOf(after.default_stages);
|
|
7390
|
+
const repos = [];
|
|
7391
|
+
for (const repo of Array.isArray(after.repos) ? after.repos : []) {
|
|
7392
|
+
if (!isObj(repo)) continue;
|
|
7393
|
+
const hooks = [];
|
|
7394
|
+
for (const h of Array.isArray(repo.hooks) ? repo.hooks : []) {
|
|
7395
|
+
if (!isObj(h)) continue;
|
|
7396
|
+
if (![repo.repo, h.id, h.entry, h.name].some(namesGate)) continue;
|
|
7397
|
+
const fresh = { id: h.id, name: h.name };
|
|
7398
|
+
if (h.entry !== void 0) {
|
|
7399
|
+
const args = Array.isArray(h.args) ? h.args.map(String).join(" ") : "";
|
|
7400
|
+
fresh.entry = liveOrCanonical(`${typeof h.entry === "string" ? h.entry : ""} ${args}`.trim());
|
|
7401
|
+
}
|
|
7402
|
+
const stages = stagesOf(h.stages);
|
|
7403
|
+
if (stages) fresh.stages = stages;
|
|
7404
|
+
hooks.push(fresh);
|
|
7405
|
+
}
|
|
7406
|
+
repos.push({ repo: repo.repo, hooks });
|
|
7407
|
+
}
|
|
7408
|
+
const base = { repos };
|
|
7409
|
+
if (defaults) base.default_stages = defaults;
|
|
7410
|
+
return base;
|
|
7411
|
+
}
|
|
7368
7412
|
function configFindings(c, changes, policy, ctx) {
|
|
7369
7413
|
if (c.after == null) return null;
|
|
7370
7414
|
let reasons;
|
|
@@ -7374,8 +7418,11 @@ function configFindings(c, changes, policy, ctx) {
|
|
|
7374
7418
|
if (overBefore === null) return null;
|
|
7375
7419
|
const base = lefthookBase(c, changes, ctx) ?? syntheticBase(overAfter ?? {});
|
|
7376
7420
|
reasons = overAfter === null ? ["the file no longer parses as YAML \u2014 the tool that reads it runs nothing"] : lefthookWeakening(mergeDocs(base, overBefore), mergeDocs(base, overAfter));
|
|
7421
|
+
} else if (c.before == null) {
|
|
7422
|
+
const after = parseDoc(c.after);
|
|
7423
|
+
if (after === null) return [];
|
|
7424
|
+
reasons = isLefthook(c.path) ? lefthookWeakening(freshLefthookBase(after), after) : preCommitWeakening(freshPreCommitBase(after), after);
|
|
7377
7425
|
} else {
|
|
7378
|
-
if (c.before == null) return null;
|
|
7379
7426
|
const before = parseDoc(c.before);
|
|
7380
7427
|
const after = parseDoc(c.after);
|
|
7381
7428
|
if (before === null) return null;
|
|
@@ -7433,7 +7480,7 @@ function huskyRuntimeWrite(path, content, ctx) {
|
|
|
7433
7480
|
(i) => i.identity === "tamperward check --staged" && i.state === "live" && i.pin === TW_VERSION
|
|
7434
7481
|
);
|
|
7435
7482
|
}
|
|
7436
|
-
var RULE8, ownerExec2, EVENTS, isObj, declares, GATE_KEYS, HOOK_TYPES, SIBLING_KEYS, MENTIONS_GATE, FORGERY, ENV_MECHANISM, envMechanism, mentionsGate, carriesGate, HUSKY_SHIM, HUSKY_SHIM_NAMES, HUSKY_H_KNOWN, HUSKY_DEPRECATION, stripTrailingNl, isHuskyScript, isHookNote, hookTampering;
|
|
7483
|
+
var RULE8, ownerExec2, EVENTS, isObj, declares, GATE_KEYS, HOOK_TYPES, SIBLING_KEYS, MENTIONS_GATE, FORGERY, ENV_MECHANISM, envMechanism, mentionsGate, carriesGate, CANONICAL_GATE, COMMIT_STAGES, namesGate, liveOrCanonical, HUSKY_SHIM, HUSKY_SHIM_NAMES, HUSKY_H_KNOWN, HUSKY_DEPRECATION, stripTrailingNl, isHuskyScript, isHookNote, hookTampering;
|
|
7437
7484
|
var init_hook_tampering = __esm({
|
|
7438
7485
|
"src/detectors/hook-tampering.ts"() {
|
|
7439
7486
|
"use strict";
|
|
@@ -7469,6 +7516,10 @@ var init_hook_tampering = __esm({
|
|
|
7469
7516
|
envMechanism = (k) => ENV_MECHANISM.find(([re]) => re.test(k))?.[1] ?? null;
|
|
7470
7517
|
mentionsGate = (v) => /\btamperward\b/.test(JSON.stringify(v) ?? "");
|
|
7471
7518
|
carriesGate = (s) => EVENTS.some((e) => gateCandidates(s, e).length > 0);
|
|
7519
|
+
CANONICAL_GATE = "npx tamperward check --staged";
|
|
7520
|
+
COMMIT_STAGES = /* @__PURE__ */ new Set(["pre-commit", "pre-push", "commit", "push"]);
|
|
7521
|
+
namesGate = (v) => typeof v === "string" && /\btamperward\b/.test(v);
|
|
7522
|
+
liveOrCanonical = (cmd) => gateOf(cmd) !== null ? cmd : CANONICAL_GATE;
|
|
7472
7523
|
HUSKY_SHIM = '#!/usr/bin/env sh\n. "$(dirname "$0")/h"';
|
|
7473
7524
|
HUSKY_SHIM_NAMES = /* @__PURE__ */ new Set(["applypatch-msg", "commit-msg", "post-applypatch", "post-checkout", "post-commit", "post-merge", "post-rewrite", "pre-applypatch", "pre-auto-gc", "pre-commit", "pre-merge-commit", "pre-push", "pre-rebase", "prepare-commit-msg"]);
|
|
7474
7525
|
HUSKY_H_KNOWN = [
|
|
@@ -7508,7 +7559,7 @@ They WILL FAIL in v10.0.0
|
|
|
7508
7559
|
"`;
|
|
7509
7560
|
stripTrailingNl = (s) => s.endsWith("\n") ? s.slice(0, -1) : s;
|
|
7510
7561
|
isHuskyScript = (path) => /(?:^|\/)\.husky\/[^/]+$/.test(path);
|
|
7511
|
-
isHookNote = (path) => /\.(?:md|markdown|txt)$|(?:^|\/)\.git(?:ignore|attributes)$/i.test(path);
|
|
7562
|
+
isHookNote = (path) => !/(?:^|\/)\.husky\/_\//.test(path) && /\.(?:md|markdown|txt)$|(?:^|\/)\.git(?:ignore|attributes)$/i.test(path);
|
|
7512
7563
|
hookTampering = {
|
|
7513
7564
|
id: RULE8,
|
|
7514
7565
|
surface: ["file", "command"],
|
|
@@ -7582,6 +7633,7 @@ They WILL FAIL in v10.0.0
|
|
|
7582
7633
|
out3.push(...semantic);
|
|
7583
7634
|
} else if (!c.path.endsWith(POLICY_FILE)) {
|
|
7584
7635
|
if ((c.op === "add" || c.op === "modify") && huskyRuntimeWrite(c.path, c.after, ctx)) continue;
|
|
7636
|
+
if (isHookNote(c.path)) continue;
|
|
7585
7637
|
const husky = isHuskyScript(c.path);
|
|
7586
7638
|
const opts = husky ? { errexit: true } : {};
|
|
7587
7639
|
const beforePin = c.before != null && c.after != null ? initScriptPin(c.before) : null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tamperward",
|
|
3
|
-
"version": "2.24.
|
|
3
|
+
"version": "2.24.1",
|
|
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",
|