Files
msd-core/tests/live-config-guard.test.cjs
0xdhx 104fc76f70 fix(#2665): watch the hook bundle and the install markers the census found
Self-found by re-deriving the guard-shape census against bin/install.js's own
write sites, not by a review finding. Three artifacts a global install writes
into a live config ROOT were watched by nothing:

  hooks/gsd-check-update.js, hooks/gsd-context-monitor.js,
  hooks/gsd-update-banner.js   -- `hooks` was absent from GSD_PREFIXED_PARENTS
  .gsd-source, .gsd-profile    -- absent from GSD_OWNED_ENTRIES, and an
                                  exact-name list does not match a dot-prefixed
                                  name via the `gsd-` prefix rule

This is the SAME shape as the leak that motivated the prefixed-parent scan in
round 1 -- a gsd-prefixed child under a parent nobody had listed -- one parent
over. That it recurred is the argument for re-deriving this list from the
installer each round instead of trusting it: the enumeration is the weak point
of an enumerate-and-block mechanism, and it does not announce when it falls
behind.

Ownership is unchanged, only coverage: `hooks/` is shared with the host agent,
so only `gsd-`-prefixed children are watched. A test asserts a host-owned
hook is still ignored, because widening the parent list must not widen
ownership -- a guard that flags the host's own files gets switched off, and
then catches nothing at all.

Reverting the widening fails the new test.
2026-08-08 05:50:49 -05:00

722 lines
29 KiB
JavaScript

'use strict';
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const fc = require('fast-check');
const {
GSD_OWNED_ENTRIES,
MAX_DEPTH,
resolveLiveConfigRoots,
resolveExtraWatchTargets,
snapshotLiveConfig,
diffLiveConfig,
formatViolations,
newestMtime,
} = require('../scripts/live-config-guard.cjs');
const { cleanup } = require('./helpers.cjs');
function tmpRoot() {
return fs.mkdtempSync(path.join(os.tmpdir(), 'live-config-guard-'));
}
/** Create `n` flat files under a fresh dir; returns [dir, entryCount-including-dir]. */
function treeWithEntries(n) {
const dir = tmpRoot();
for (let i = 0; i < n; i++) fs.writeFileSync(path.join(dir, `f${i}`), 'x');
return [dir, n + 1]; // +1: the directory itself is lstat'd and costs budget
}
describe('#2665: live-config hermeticity guard', () => {
test('resolves real runtime config roots via the product resolver', () => {
const roots = resolveLiveConfigRoots();
// Guards the guard: an empty set would make every downstream assertion
// vacuous, and run-tests.cjs would silently skip the check.
assert.ok(roots.length > 5, `expected many runtime config roots, got ${roots.length}`);
for (const root of roots) {
assert.ok(path.isAbsolute(root), `root must be absolute: ${root}`);
}
});
test('watches the HOME-derived fallback root, not only the ambient one', () => {
// #2665 round 5: the guard resolves env-first, so it sees the AMBIENT root.
// A child that blanks CLAUDE_CONFIG_DIR (which is exactly what TEST_ENV_BASE
// does) falls back to <HOME>/.claude instead. Watching only the ambient path
// leaves that fallback unwatched -- the escape route this PR closes, one
// process deeper.
const ambient = tmpRoot();
const fakeHome = tmpRoot();
const saved = process.env.CLAUDE_CONFIG_DIR;
try {
process.env.CLAUDE_CONFIG_DIR = ambient;
const roots = resolveLiveConfigRoots({ os: { homedir: () => fakeHome } });
assert.ok(
roots.includes(path.resolve(ambient)),
`ambient root missing from ${JSON.stringify(roots)}`,
);
assert.ok(
roots.includes(path.resolve(path.join(fakeHome, '.claude'))),
`HOME-derived fallback root missing from ${JSON.stringify(roots)}`,
);
} finally {
if (saved === undefined) delete process.env.CLAUDE_CONFIG_DIR;
else process.env.CLAUDE_CONFIG_DIR = saved;
cleanup(ambient);
cleanup(fakeHome);
}
});
test('a clean run produces no violations', () => {
const root = tmpRoot();
try {
fs.mkdirSync(path.join(root, 'gsd-core', 'bin'), { recursive: true });
fs.writeFileSync(path.join(root, 'gsd-core', 'bin', 'x.cjs'), 'x');
const before = snapshotLiveConfig([root]);
const after = snapshotLiveConfig([root]);
assert.deepStrictEqual(diffLiveConfig(before, after), []);
} finally {
cleanup(root);
}
});
test('detects a global install CREATED during the run', () => {
const root = tmpRoot();
try {
const before = snapshotLiveConfig([root]);
// Exactly the Blocker 1 shape: an in-process install(true, …) landing a
// full global install in a live config dir that was previously empty.
fs.mkdirSync(path.join(root, 'gsd-core'), { recursive: true });
fs.writeFileSync(path.join(root, 'gsd-file-manifest.json'), '{}');
const violations = diffLiveConfig(before, snapshotLiveConfig([root]));
const kinds = Object.fromEntries(violations.map((v) => [path.basename(v.path), v.kind]));
assert.strictEqual(kinds['gsd-core'], 'created');
assert.strictEqual(kinds['gsd-file-manifest.json'], 'created');
} finally {
cleanup(root);
}
});
test('detects an existing install MODIFIED during the run', () => {
const root = tmpRoot();
try {
const target = path.join(root, 'gsd-core', 'bin');
fs.mkdirSync(target, { recursive: true });
const file = path.join(target, 'gsd-tools.cjs');
fs.writeFileSync(file, 'original');
const before = snapshotLiveConfig([root]);
// mtime resolution is coarse on some filesystems; set it forward explicitly
// rather than racing the clock with a sleep.
const future = new Date(Date.now() + 10000);
fs.writeFileSync(file, 'clobbered');
fs.utimesSync(file, future, future);
const violations = diffLiveConfig(before, snapshotLiveConfig([root]));
assert.strictEqual(violations.length, 1);
assert.strictEqual(violations[0].kind, 'modified');
assert.strictEqual(path.basename(violations[0].path), 'gsd-core');
} finally {
cleanup(root);
}
});
test('ignores non-GSD writes in a shared config root', () => {
const root = tmpRoot();
try {
const before = snapshotLiveConfig([root]);
// A concurrent host-agent session writing its own state must NOT trip the
// guard — a guard that cries wolf gets disabled, and then catches nothing.
fs.writeFileSync(path.join(root, 'history.jsonl'), '{}');
fs.mkdirSync(path.join(root, 'todos'), { recursive: true });
fs.writeFileSync(path.join(root, 'settings.json'), '{}');
assert.deepStrictEqual(diffLiveConfig(before, snapshotLiveConfig([root])), []);
} finally {
cleanup(root);
}
});
test('watches exactly the GSD-owned entry set', () => {
const root = tmpRoot();
try {
const snap = snapshotLiveConfig([root]);
const watched = Object.keys(snap).map((p) => path.basename(p)).sort();
assert.deepStrictEqual(watched, [...GSD_OWNED_ENTRIES].sort());
} finally {
cleanup(root);
}
});
test('detects a gsd-prefixed artifact written into a SHARED dir', () => {
const root = tmpRoot();
try {
fs.mkdirSync(path.join(root, 'skills'), { recursive: true });
const before = snapshotLiveConfig([root]);
// The exact leak the first version of this guard MISSED: a writer that
// sandboxed HOME but inherited an ambient CLAUDE_CONFIG_DIR landed
// <live>/skills/gsd-dev-preferences/SKILL.md, outside the three
// top-level GSD entries.
fs.mkdirSync(path.join(root, 'skills', 'gsd-dev-preferences'), { recursive: true });
fs.writeFileSync(path.join(root, 'skills', 'gsd-dev-preferences', 'SKILL.md'), '# x');
const violations = diffLiveConfig(before, snapshotLiveConfig([root]));
assert.strictEqual(violations.length, 1);
assert.strictEqual(violations[0].kind, 'created');
assert.strictEqual(path.basename(violations[0].path), 'gsd-dev-preferences');
} finally {
cleanup(root);
}
});
test('ignores NON-gsd artifacts in a shared dir', () => {
const root = tmpRoot();
try {
fs.mkdirSync(path.join(root, 'skills'), { recursive: true });
const before = snapshotLiveConfig([root]);
// The host agent's own skills must not trip the guard.
fs.mkdirSync(path.join(root, 'skills', 'my-personal-skill'), { recursive: true });
fs.writeFileSync(path.join(root, 'skills', 'my-personal-skill', 'SKILL.md'), '# mine');
assert.deepStrictEqual(diffLiveConfig(before, snapshotLiveConfig([root])), []);
} finally {
cleanup(root);
}
});
test('detects a leaked hook script and the install marker files', () => {
// Self-found by re-deriving the census at round 5 rather than by a review
// finding. bin/install.js writes hooks/gsd-*.js, .gsd-source and .gsd-profile
// into the config ROOT; `hooks` was absent from GSD_PREFIXED_PARENTS and the
// two dot-prefixed markers from GSD_OWNED_ENTRIES, so all three leaked past
// the guard silently -- the same shape as the skills/gsd-dev-preferences miss
// that motivated the prefixed-parent scan in the first place.
const root = tmpRoot();
try {
fs.mkdirSync(path.join(root, 'hooks'), { recursive: true });
const before = snapshotLiveConfig([root]);
fs.writeFileSync(path.join(root, 'hooks', 'gsd-check-update.js'), '// x');
fs.writeFileSync(path.join(root, '.gsd-source'), 'npm');
fs.writeFileSync(path.join(root, '.gsd-profile'), 'default');
const created = diffLiveConfig(before, snapshotLiveConfig([root]))
.filter((v) => v.kind === 'created')
.map((v) => path.basename(v.path))
.sort();
assert.deepStrictEqual(created, ['.gsd-profile', '.gsd-source', 'gsd-check-update.js']);
} finally {
cleanup(root);
}
});
test('a NON-gsd hook belonging to the host agent is still ignored', () => {
// Widening GSD_PREFIXED_PARENTS must not widen ownership: `hooks/` is shared
// with the host agent, and a guard that flags its files gets switched off.
const root = tmpRoot();
try {
fs.mkdirSync(path.join(root, 'hooks'), { recursive: true });
const before = snapshotLiveConfig([root]);
fs.writeFileSync(path.join(root, 'hooks', 'my-own-hook.js'), '// mine');
assert.deepStrictEqual(diffLiveConfig(before, snapshotLiveConfig([root])), []);
} finally {
cleanup(root);
}
});
test('detects a DELETED top-level GSD entry', () => {
const root = tmpRoot();
try {
// A fixed owned entry is recorded at BOTH ends whether or not it exists,
// so a deletion reads {exists:true} -> {exists:false}. Before the union
// walk that pair matched no branch at all and the run passed silently.
fs.mkdirSync(path.join(root, 'gsd-core'), { recursive: true });
fs.writeFileSync(path.join(root, 'gsd-core', 'x'), 'x');
const before = snapshotLiveConfig([root]);
cleanup(path.join(root, 'gsd-core'));
const violations = diffLiveConfig(before, snapshotLiveConfig([root]));
const deleted = violations.filter((v) => v.kind === 'deleted');
assert.strictEqual(deleted.length, 1, JSON.stringify(violations));
assert.strictEqual(path.basename(deleted[0].path), 'gsd-core');
} finally {
cleanup(root);
}
});
test('detects a DELETED gsd-prefixed child of a shared dir', () => {
const root = tmpRoot();
try {
// The shape a `pre.exists && !post.exists` branch cannot reach on its own:
// prefixed children are DISCOVERED by readdir, so a deleted one is absent
// from the `after` snapshot entirely and never enters an after-keyed loop.
fs.mkdirSync(path.join(root, 'skills', 'gsd-dev-preferences'), { recursive: true });
fs.writeFileSync(path.join(root, 'skills', 'gsd-dev-preferences', 'SKILL.md'), '# x');
const before = snapshotLiveConfig([root]);
cleanup(path.join(root, 'skills', 'gsd-dev-preferences'));
const violations = diffLiveConfig(before, snapshotLiveConfig([root]));
const deleted = violations.filter((v) => v.kind === 'deleted');
assert.strictEqual(deleted.length, 1, JSON.stringify(violations));
assert.strictEqual(path.basename(deleted[0].path), 'gsd-dev-preferences');
} finally {
cleanup(root);
}
});
test('the scan budget is PER TARGET, so one big tree cannot cascade unverified', () => {
// #2665 round 5: with a single running budget, target A exhausting it made
// target B report `truncated` -> `unverified` -- a strict-mode FAILURE caused
// by an unrelated directory. Each target now gets its own allotment.
const [big] = treeWithEntries(8);
const [small] = treeWithEntries(2);
try {
const snap = snapshotLiveConfig([], [big, small], { perTarget: 9, total: 1000 });
assert.strictEqual(snap[path.resolve(big)].truncated, false, 'big target should fit its own budget');
assert.strictEqual(
snap[path.resolve(small)].truncated,
false,
'small target must NOT inherit exhaustion from a target scanned before it',
);
} finally {
cleanup(big);
cleanup(small);
}
});
test('the truncation verdict does not depend on target ORDER', () => {
const [big] = treeWithEntries(8);
const [small] = treeWithEntries(2);
try {
const limits = { perTarget: 9, total: 1000 };
const a = snapshotLiveConfig([], [big, small], limits);
const b = snapshotLiveConfig([], [small, big], limits);
assert.strictEqual(a[path.resolve(small)].truncated, b[path.resolve(small)].truncated);
assert.strictEqual(a[path.resolve(big)].truncated, b[path.resolve(big)].truncated);
} finally {
cleanup(big);
cleanup(small);
}
});
test('the GLOBAL ceiling still bounds the aggregate, and reports unverified', () => {
// The bound the single budget was really for is kept -- but when it engages,
// the curtailed target is reported rather than silently attested clean.
const [a] = treeWithEntries(5);
const [b] = treeWithEntries(5);
try {
const snap = snapshotLiveConfig([], [a, b], { perTarget: 6, total: 6 });
assert.strictEqual(snap[path.resolve(a)].truncated, false);
assert.strictEqual(snap[path.resolve(b)].truncated, true, 'ceiling-curtailed target must be truncated');
const violations = diffLiveConfig(snap, snap);
assert.ok(
violations.some((v) => v.kind === 'unverified' && v.path === path.resolve(b)),
`expected an unverified violation for the curtailed target: ${JSON.stringify(violations)}`,
);
} finally {
cleanup(a);
cleanup(b);
}
});
test('the report names the path and the remedy', () => {
const out = formatViolations([{ path: '/live/.claude/gsd-core', kind: 'created' }]);
assert.match(out, /HERMETICITY WARNING/);
assert.match(out, /\/live\/\.claude\/gsd-core/);
assert.match(out, /scrubConfigLocationEnv/);
assert.match(out, /GSD_SKIP_LIVE_CONFIG_GUARD/);
assert.match(out, /GSD_STRICT_LIVE_CONFIG_GUARD/);
});
});
// ── Round 3: the write surfaces that are not runtime config ROOTS ───────────
describe('#2665: guard watches non-root write surfaces', () => {
test('resolveExtraWatchTargets covers $GSD_HOME/.gsd and kimi config.toml', () => {
const home = tmpRoot();
const share = tmpRoot();
try {
const targets = resolveExtraWatchTargets({
env: { GSD_HOME: home, KIMI_SHARE_DIR: share },
os: { homedir: () => home },
});
assert.ok(
targets.includes(path.resolve(path.join(home, '.gsd'))),
`expected $GSD_HOME/.gsd in ${JSON.stringify(targets)}`,
);
assert.ok(
targets.some((t) => t === path.resolve(path.join(share, 'config.toml'))),
`expected kimi config.toml in ${JSON.stringify(targets)}`,
);
} finally {
cleanup(home);
cleanup(share);
}
});
test('extra targets are DERIVED from the descriptor array, not a named resolver', () => {
const {
NON_REGISTRY_CONFIG_HOME_DESCRIPTORS,
resolveConfigHomeFromDescriptor,
} = require('../gsd-core/bin/lib/runtime-homes.cjs');
const home = tmpRoot();
try {
const env = { GSD_HOME: home };
const targets = resolveExtraWatchTargets({ env, os: { homedir: () => home } });
// Every descriptor in the array must contribute a target. Calling one
// named resolver instead would cover one of today's two entries and silently
// miss tomorrow's — the same partial-enumeration defect that put
// KIMI_SHARE_DIR outside the scrub set, one layer over.
//
// SCOPE BOUNDARY (per round-2 Nit 7, and it bites here): this asserts one
// target PER DESCRIPTOR and nothing about whether one target per descriptor
// is ENOUGH. It is not — <root>/hooks/ is also GSD-written and unwatched
// (named residual in resolveExtraWatchTargets). A test whose expectation is
// derived from the same array it checks cannot see that class.
for (const d of NON_REGISTRY_CONFIG_HOME_DESCRIPTORS) {
const dir = resolveConfigHomeFromDescriptor(d, { env, home });
assert.ok(
targets.includes(path.resolve(path.join(dir, 'config.toml'))),
`descriptor ${JSON.stringify(d.env)} contributed no watch target`,
);
}
// The count is what actually catches a regression to a hardcoded call:
// it fails the moment the array grows and the guard does not follow.
assert.strictEqual(
targets.length,
1 + NON_REGISTRY_CONFIG_HOME_DESCRIPTORS.length,
'expected the GSD store root plus exactly one target per descriptor',
);
} finally {
cleanup(home);
}
});
test('#2755: kimi-code config.toml is watched — named, not enumeration-relative', () => {
// The test above derives its expectation FROM the descriptor array, so it
// passes for whatever that array happens to contain and cannot see a
// descriptor that was never added — the enumeration-relative scope boundary
// this suite already calls out one layer down. #2755 landed kimi-code's
// `~/.kimi-code` (KIMI_CODE_HOME) on `next` as an inline literal inside
// resolveKimiHooksTomlDir's body; until it was hoisted into
// NON_REGISTRY_CONFIG_HOME_DESCRIPTORS the guard watched Kimi CLI's
// config.toml and not Kimi Code's. Naming the path is what makes dropping
// the descriptor fail loudly instead of quietly shrinking the expectation.
const home = tmpRoot();
const codeHome = tmpRoot();
try {
const env = { GSD_HOME: home, KIMI_CODE_HOME: codeHome };
const targets = resolveExtraWatchTargets({ env, os: { homedir: () => home } });
assert.ok(
targets.includes(path.resolve(path.join(codeHome, 'config.toml'))),
`expected kimi-code config.toml in ${JSON.stringify(targets)}`,
);
} finally {
cleanup(home);
cleanup(codeHome);
}
});
test('GSD_HOME falls back to homedir when unset', () => {
const home = tmpRoot();
try {
const targets = resolveExtraWatchTargets({ env: {}, os: { homedir: () => home } });
assert.ok(targets.includes(path.resolve(path.join(home, '.gsd'))));
} finally {
cleanup(home);
}
});
test('detects a consent/defaults write into $GSD_HOME/.gsd', () => {
const home = tmpRoot();
try {
const target = path.join(home, '.gsd');
const before = snapshotLiveConfig([], [target]);
// The Blocker-1 shape one family over: an ambient GSD_HOME sends real
// consent records and defaults.json into the developer's own store.
fs.mkdirSync(target, { recursive: true });
fs.writeFileSync(path.join(target, 'consent.json'), '{}');
const violations = diffLiveConfig(before, snapshotLiveConfig([], [target]));
assert.strictEqual(violations.length, 1);
assert.strictEqual(violations[0].kind, 'created');
} finally {
cleanup(home);
}
});
test('detects a [[hooks]] write into kimi config.toml', () => {
const share = tmpRoot();
try {
const target = path.join(share, 'config.toml');
const before = snapshotLiveConfig([], [target]);
fs.writeFileSync(target, '[[hooks]]\n');
const violations = diffLiveConfig(before, snapshotLiveConfig([], [target]));
assert.strictEqual(violations.length, 1);
assert.strictEqual(violations[0].kind, 'created');
} finally {
cleanup(share);
}
});
test('NEGATIVE CONTROL: without the extras both leaks are silent', () => {
const home = tmpRoot();
try {
// This is the pre-round-3 guard shape — roots only. It is what let a leak
// on either variable pass through the PR's own safety net unreported.
const before = snapshotLiveConfig([]);
fs.mkdirSync(path.join(home, '.gsd'), { recursive: true });
fs.writeFileSync(path.join(home, '.gsd', 'consent.json'), '{}');
assert.deepStrictEqual(diffLiveConfig(before, snapshotLiveConfig([])), []);
} finally {
cleanup(home);
}
});
test('a whole-dir extra target does not watch unrelated siblings', () => {
const home = tmpRoot();
try {
const target = path.join(home, '.gsd');
fs.mkdirSync(target, { recursive: true });
const before = snapshotLiveConfig([], [target]);
// A sibling of .gsd is outside the watched target entirely.
fs.writeFileSync(path.join(home, 'unrelated.json'), '{}');
assert.deepStrictEqual(diffLiveConfig(before, snapshotLiveConfig([], [target])), []);
} finally {
cleanup(home);
}
});
});
// ── Round 3: the truncation budget — the module's own safety-critical case ───
describe('#2665: scan-budget truncation', () => {
test('boundary: limit-1 truncates, limit and limit+1 do not', () => {
const [dir, entries] = treeWithEntries(24);
try {
// RULESET.TESTS.boundary-coverage: N in {limit-1, limit, limit+1}. The
// budget is injected, so the boundary is exercised at a real threshold
// without materialising MAX_ENTRIES files.
assert.strictEqual(
newestMtime(dir, { remaining: entries - 1 }).truncated,
true,
'one entry short of the tree size MUST truncate',
);
assert.strictEqual(
newestMtime(dir, { remaining: entries }).truncated,
false,
'a budget exactly equal to the tree size must NOT truncate',
);
assert.strictEqual(
newestMtime(dir, { remaining: entries + 1 }).truncated,
false,
'a budget above the tree size must NOT truncate',
);
} finally {
cleanup(dir);
}
});
// RULESET.TESTS.boundary-coverage asks for {limit-1, limit, limit+1}. This was
// exercised only at limit+2, which pins neither side of the edge: an off-by-one
// that truncated a legal depth would have passed. `nestedDepth(n)` builds a tree
// whose deepest entry sits at walk-depth n below the scanned root, and
// newestMtime truncates iff that depth EXCEEDS MAX_DEPTH.
const nestedDepth = (n) => {
const dir = tmpRoot();
let deep = dir;
for (let i = 0; i < n; i++) deep = path.join(deep, `d${i}`);
fs.mkdirSync(deep, { recursive: true });
return dir;
};
test(`MAX_DEPTH boundary: depth ${MAX_DEPTH - 1} (limit-1) does NOT truncate`, () => {
const dir = nestedDepth(MAX_DEPTH - 1);
try {
assert.strictEqual(newestMtime(dir, { remaining: 1e6 }).truncated, false);
} finally {
cleanup(dir);
}
});
test(`MAX_DEPTH boundary: depth ${MAX_DEPTH} (limit) does NOT truncate`, () => {
const dir = nestedDepth(MAX_DEPTH);
try {
assert.strictEqual(
newestMtime(dir, { remaining: 1e6 }).truncated,
false,
'a tree exactly at MAX_DEPTH is within bounds and must be attested',
);
} finally {
cleanup(dir);
}
});
test(`MAX_DEPTH boundary: depth ${MAX_DEPTH + 1} (limit+1) truncates`, () => {
const dir = nestedDepth(MAX_DEPTH + 1);
try {
assert.strictEqual(
newestMtime(dir, { remaining: 1e6 }).truncated,
true,
'a tree deeper than MAX_DEPTH must truncate',
);
} finally {
cleanup(dir);
}
});
test('a truncated scan reports UNVERIFIED, never clean', () => {
const [dir, entries] = treeWithEntries(10);
try {
// The safety-critical branch named in this module's own docstring: a scan
// that hit a bound must not read as an attestation of cleanliness.
const snap = { [dir]: { exists: true, newest: 1, truncated: true } };
const violations = diffLiveConfig(snap, {
[dir]: { exists: true, newest: 1, truncated: true },
});
assert.strictEqual(violations.length, 1);
assert.strictEqual(violations[0].kind, 'unverified');
assert.match(formatViolations(violations), /UNVERIFIED \(scan bound hit/);
assert.ok(entries > 0);
} finally {
cleanup(dir);
}
});
test('a modified path outranks unverified (a real leak is never downgraded)', () => {
const p = '/live/.claude/gsd-core';
const violations = diffLiveConfig(
{ [p]: { exists: true, newest: 1, truncated: true } },
{ [p]: { exists: true, newest: 2, truncated: true } },
);
assert.strictEqual(violations[0].kind, 'modified');
});
test('property: truncation is monotone in the budget (boundary containment)', () => {
const [dir, entries] = treeWithEntries(12);
try {
fc.assert(
fc.property(fc.integer({ min: 1, max: entries * 3 }), (budget) => {
const { truncated } = newestMtime(dir, { remaining: budget });
// The invariant: a budget at or above the tree size never truncates,
// and one below it always does. A regression flipping `truncated` to
// false on an exhausted budget — the exact silent-clean failure the
// module warns about — breaks this for every budget < entries.
return budget >= entries ? truncated === false : truncated === true;
}),
{ numRuns: 100 },
);
} finally {
cleanup(dir);
}
});
test('property: newest mtime never exceeds the true maximum', () => {
const [dir, entries] = treeWithEntries(8);
try {
const trueMax = Math.max(
...fs.readdirSync(dir).map((f) => fs.lstatSync(path.join(dir, f)).mtimeMs),
fs.lstatSync(dir).mtimeMs,
);
fc.assert(
fc.property(fc.integer({ min: 1, max: entries * 2 }), (budget) => {
const { newest } = newestMtime(dir, { remaining: budget });
return newest <= trueMax;
}),
{ numRuns: 50 },
);
} finally {
cleanup(dir);
}
});
});
describe('#2665 round 4: CI wires the guard to strict mode', () => {
// The reversion this guards: dropping GSD_STRICT_LIVE_CONFIG_GUARD from
// test.yml silently demotes the guard back to report-only, and a future
// leak of exactly the class #2665 closes prints a warning and CI stays
// green. Windows lanes are deliberately report-only until the documented
// pre-existing USERPROFILE leak class is swept (SEVERITY note in
// scripts/live-config-guard.cjs) — so the assertion is per-OS, not global.
test('both guard env vars are documented for humans, not just in the source', () => {
// A skip-switch on a safety guard has to be discoverable: an undocumented
// bypass is one people eventually set without knowing what they turned off.
// The Docs Required gate gets satisfied by ANY docs/ file in the diff --
// including a generated index -- so it cannot stand in for this.
const doc = fs.readFileSync(path.join(__dirname, '..', 'docs', 'TESTING-SUITES.md'), 'utf8');
for (const v of ['GSD_STRICT_LIVE_CONFIG_GUARD', 'GSD_SKIP_LIVE_CONFIG_GUARD']) {
assert.ok(doc.includes(v), `${v} must be documented in docs/TESTING-SUITES.md`);
}
});
// DERIVED, not hand-listed. The previous version named three jobs as literals,
// so it could not see a FOURTH lane that runs the suite — and there was one:
// qa-loop-walk reaches run-tests.cjs through `npm run test:qa` and escaped
// strict mode entirely while this test stayed green. A hand-list that certifies
// its own completeness is the exact defect this PR exists to fix, reproduced in
// the test that guards the fix.
test('EVERY job that runs the suite wires GSD_STRICT_LIVE_CONFIG_GUARD', () => {
const yaml = require('js-yaml');
const root = path.join(__dirname, '..');
const wf = yaml.load(fs.readFileSync(path.join(root, '.github', 'workflows', 'test.yml'), 'utf8'));
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
// A step reaches the runner directly OR through an npm script, transitively.
// Grepping the filename alone misses the indirection that hid qa-loop-walk.
const scriptRunsSuite = (name, seen = new Set()) => {
if (seen.has(name)) return false;
seen.add(name);
const body = pkg.scripts?.[name];
if (!body) return false;
if (/run-tests\.cjs/.test(body)) return true;
return [...body.matchAll(/npm run ([\w:.-]+)/g)].some((m) => scriptRunsSuite(m[1], seen));
};
const runsSuite = (run) =>
/run-tests\.cjs/.test(run)
|| [...run.matchAll(/npm run ([\w:.-]+)/g)].some((m) => scriptRunsSuite(m[1]));
const suiteJobs = Object.entries(wf.jobs ?? {})
.filter(([, job]) => (job?.steps ?? []).some((s) => typeof s?.run === 'string' && runsSuite(s.run)))
.map(([name]) => name)
.sort();
// Guards the guard: an empty derivation would make every assertion below
// vacuously true, which is the failure mode of the literal list it replaces.
assert.ok(
suiteJobs.length >= 4,
`expected at least 4 suite-running jobs, derived ${JSON.stringify(suiteJobs)}`,
);
const windowsMatrix = (job) => JSON.stringify(job?.strategy?.matrix ?? {}).includes('windows');
const problems = [];
for (const name of suiteJobs) {
const job = wf.jobs[name];
const v = String(job?.env?.GSD_STRICT_LIVE_CONFIG_GUARD ?? '');
// Windows lanes stay report-only until the pre-existing USERPROFILE leak
// class is swept (SEVERITY note in scripts/live-config-guard.cjs), so a
// job whose matrix includes Windows carries the conditional; an
// ubuntu/macOS-only lane must be strict outright.
const ok = windowsMatrix(job)
// The WHOLE expression, anchored — a prefix match accepted both
// `&& '1' || '1'` (Windows silently strict) and a malformed tail.
? /^\$\{\{\s*matrix\.os\s*!=\s*'windows-latest'\s*&&\s*'1'\s*\|\|\s*''\s*\}\}$/.test(v)
: v === '1';
if (!ok) problems.push(`jobs.${name}: ${JSON.stringify(v)}`);
}
assert.deepStrictEqual(
problems,
[],
`every job running run-tests.cjs must wire the guard to strict mode `
+ `(Windows matrices carved out). Derived jobs: ${JSON.stringify(suiteJobs)}`,
);
});
});