fix(#2665): scrub config-location env on the parent for in-process install()

The blocker the review said decides this PR. These tests call the real installer
IN-PROCESS with only HOME/USERPROFILE sandboxed. getGlobalConfigDir is env-first,
so an ambient CLAUDE_CONFIG_DIR beats the sandbox and a complete global install --
agents/, commands/, skills/, gsd-core/, manifest, settings -- lands in the
developer's live config dir. No child-env scrub can reach it; only clearing the
parent's env can.

The review named two describe blocks in install.test.cjs. A sweep for the shape
found four there (bug #3571 and bug #3288 each appear twice in the file), and the
post-suite guard added later in this series found a fifth in
runtime-artifact-layout.test.cjs, which the review did not name. All five now
save/clear/restore via scrubConfigLocationEnv().

Verified: codebuddy-install, cline-install and codex-config already guard their
own config-location var around in-process install(), so the class is closed.

Addresses review finding: Blocker 1.
This commit is contained in:
0xdhx
2026-07-28 06:05:16 -05:00
parent bc5c362610
commit 466db2f1a3
2 changed files with 45 additions and 5 deletions

View File

@@ -3825,7 +3825,7 @@ const SHARED_DIR = path.join(REPO_ROOT, 'gsd-core', 'bin', 'shared');
const { install } = require('../bin/install.js');
const { createTempDir, cleanup } = require('./helpers.cjs');
const { createTempDir, cleanup, scrubConfigLocationEnv } = require('./helpers.cjs');
const makeTmpDir = () => createTempDir('gsd-3571-');
function silenceConsole(fn) {
@@ -3851,6 +3851,7 @@ describe('bug #3571: configuration generated manifests resolve in install layout
let savedHome;
let savedUserProfile;
let savedExplicitConfigDir;
let restoreConfigLocationEnv;
beforeEach(() => {
tmpRoot = makeTmpDir();
@@ -3859,6 +3860,12 @@ describe('bug #3571: configuration generated manifests resolve in install layout
savedUserProfile = process.env.USERPROFILE;
savedExplicitConfigDir = process.env.GSD_EXPLICIT_CONFIG_DIR;
delete process.env.GSD_EXPLICIT_CONFIG_DIR;
// #2665: this block calls the real installer IN-PROCESS with only HOME
// sandboxed. getGlobalConfigDir is env-FIRST, so an ambient CLAUDE_CONFIG_DIR
// (or CODEX_HOME, or any other runtime's config-location var) overrides that
// sandbox and a complete global install lands in the developer's live config
// dir. TEST_ENV_BASE cannot reach this — it only scrubs CHILD process env.
restoreConfigLocationEnv = scrubConfigLocationEnv();
});
afterEach(() => {
@@ -3870,6 +3877,7 @@ describe('bug #3571: configuration generated manifests resolve in install layout
} else {
process.env.GSD_EXPLICIT_CONFIG_DIR = savedExplicitConfigDir;
}
restoreConfigLocationEnv();
cleanup(tmpRoot);
});
@@ -3982,7 +3990,7 @@ const { install } = require('../bin/install.js');
// ─── helpers ─────────────────────────────────────────────────────────────────
const { createTempDir, cleanup } = require('./helpers.cjs');
const { createTempDir, cleanup, scrubConfigLocationEnv } = require('./helpers.cjs');
const makeTmpDir = createTempDir;
const rmTmpDir = cleanup;
@@ -4024,6 +4032,7 @@ describe('bug #3288: model-catalog.cjs install-layout resolution', () => {
let savedHome;
let savedUserProfile;
let savedExplicitConfigDir;
let restoreConfigLocationEnv;
beforeEach(() => {
tmpRoot = makeTmpDir('gsd-3288-');
@@ -4038,6 +4047,12 @@ describe('bug #3288: model-catalog.cjs install-layout resolution', () => {
// and target a different directory than tmpRoot (CR finding, PR #3293).
savedExplicitConfigDir = process.env.GSD_EXPLICIT_CONFIG_DIR;
delete process.env.GSD_EXPLICIT_CONFIG_DIR;
// #2665: this block calls the real installer IN-PROCESS with only HOME
// sandboxed. getGlobalConfigDir is env-FIRST, so an ambient CLAUDE_CONFIG_DIR
// (or CODEX_HOME, or any other runtime's config-location var) overrides that
// sandbox and a complete global install lands in the developer's live config
// dir. TEST_ENV_BASE cannot reach this — it only scrubs CHILD process env.
restoreConfigLocationEnv = scrubConfigLocationEnv();
});
afterEach(() => {
@@ -4049,6 +4064,7 @@ describe('bug #3288: model-catalog.cjs install-layout resolution', () => {
} else {
process.env.GSD_EXPLICIT_CONFIG_DIR = savedExplicitConfigDir;
}
restoreConfigLocationEnv();
rmTmpDir(tmpRoot);
});
@@ -7871,7 +7887,7 @@ const SHARED_DIR = path.join(REPO_ROOT, 'gsd-core', 'bin', 'shared');
const { install } = require('../bin/install.js');
const { createTempDir, cleanup } = require('./helpers.cjs');
const { createTempDir, cleanup, scrubConfigLocationEnv } = require('./helpers.cjs');
const makeTmpDir = () => createTempDir('gsd-3571-');
function silenceConsole(fn) {
@@ -7897,6 +7913,7 @@ describe('bug #3571: configuration generated manifests resolve in install layout
let savedHome;
let savedUserProfile;
let savedExplicitConfigDir;
let restoreConfigLocationEnv;
beforeEach(() => {
tmpRoot = makeTmpDir();
@@ -7905,6 +7922,12 @@ describe('bug #3571: configuration generated manifests resolve in install layout
savedUserProfile = process.env.USERPROFILE;
savedExplicitConfigDir = process.env.GSD_EXPLICIT_CONFIG_DIR;
delete process.env.GSD_EXPLICIT_CONFIG_DIR;
// #2665: this block calls the real installer IN-PROCESS with only HOME
// sandboxed. getGlobalConfigDir is env-FIRST, so an ambient CLAUDE_CONFIG_DIR
// (or CODEX_HOME, or any other runtime's config-location var) overrides that
// sandbox and a complete global install lands in the developer's live config
// dir. TEST_ENV_BASE cannot reach this — it only scrubs CHILD process env.
restoreConfigLocationEnv = scrubConfigLocationEnv();
});
afterEach(() => {
@@ -7916,6 +7939,7 @@ describe('bug #3571: configuration generated manifests resolve in install layout
} else {
process.env.GSD_EXPLICIT_CONFIG_DIR = savedExplicitConfigDir;
}
restoreConfigLocationEnv();
cleanup(tmpRoot);
});
@@ -8028,7 +8052,7 @@ const { install } = require('../bin/install.js');
// ─── helpers ─────────────────────────────────────────────────────────────────
const { createTempDir, cleanup } = require('./helpers.cjs');
const { createTempDir, cleanup, scrubConfigLocationEnv } = require('./helpers.cjs');
const makeTmpDir = createTempDir;
const rmTmpDir = cleanup;
@@ -8070,6 +8094,7 @@ describe('bug #3288: model-catalog.cjs install-layout resolution', () => {
let savedHome;
let savedUserProfile;
let savedExplicitConfigDir;
let restoreConfigLocationEnv;
beforeEach(() => {
tmpRoot = makeTmpDir('gsd-3288-');
@@ -8084,6 +8109,12 @@ describe('bug #3288: model-catalog.cjs install-layout resolution', () => {
// and target a different directory than tmpRoot (CR finding, PR #3293).
savedExplicitConfigDir = process.env.GSD_EXPLICIT_CONFIG_DIR;
delete process.env.GSD_EXPLICIT_CONFIG_DIR;
// #2665: this block calls the real installer IN-PROCESS with only HOME
// sandboxed. getGlobalConfigDir is env-FIRST, so an ambient CLAUDE_CONFIG_DIR
// (or CODEX_HOME, or any other runtime's config-location var) overrides that
// sandbox and a complete global install lands in the developer's live config
// dir. TEST_ENV_BASE cannot reach this — it only scrubs CHILD process env.
restoreConfigLocationEnv = scrubConfigLocationEnv();
});
afterEach(() => {
@@ -8095,6 +8126,7 @@ describe('bug #3288: model-catalog.cjs install-layout resolution', () => {
} else {
process.env.GSD_EXPLICIT_CONFIG_DIR = savedExplicitConfigDir;
}
restoreConfigLocationEnv();
rmTmpDir(tmpRoot);
});

View File

@@ -26,7 +26,7 @@ const { resolveRuntimeArtifactLayout, findInstallSourceRoot } = require('../gsd-
const capabilityRegistry = require('../gsd-core/bin/lib/capability-registry.cjs');
const installProfiles = require('../gsd-core/bin/lib/install-profiles.cjs');
const { install } = require('../bin/install.js');
const { createTempDir, cleanup } = require('./helpers.cjs');
const { createTempDir, cleanup, scrubConfigLocationEnv } = require('./helpers.cjs');
const REPO_ROOT = path.join(__dirname, '..');
@@ -687,6 +687,7 @@ describe('#1477 .gsd-source marker provisioning', () => {
let savedUserProfile;
let savedExplicitConfigDir;
let savedTestMode;
let restoreConfigLocationEnv;
function silenceConsole(fn) {
const orig = { log: console.log, warn: console.warn, error: console.error };
@@ -732,6 +733,12 @@ describe('#1477 .gsd-source marker provisioning', () => {
delete process.env.GSD_EXPLICIT_CONFIG_DIR;
savedTestMode = process.env.GSD_TEST_MODE;
process.env.GSD_TEST_MODE = '1';
// #2665: this block calls install(true, 'claude') IN-PROCESS. Redirecting
// HOME/USERPROFILE is not enough, because getGlobalConfigDir is env-FIRST:
// an ambient CLAUDE_CONFIG_DIR wins over the fixture and a full global
// install lands in the developer's live config dir. Found by the post-suite
// hermeticity guard (scripts/live-config-guard.cjs), not by inspection.
restoreConfigLocationEnv = scrubConfigLocationEnv();
});
afterEach(() => {
@@ -743,6 +750,7 @@ describe('#1477 .gsd-source marker provisioning', () => {
else process.env.GSD_EXPLICIT_CONFIG_DIR = savedExplicitConfigDir;
if (savedTestMode === undefined) delete process.env.GSD_TEST_MODE;
else process.env.GSD_TEST_MODE = savedTestMode;
restoreConfigLocationEnv();
cleanup(tmpRoot);
});