Skip to content

Commit 7d7fcff

Browse files
authored
Merge pull request #340 from ndycode/audit/pr4-windows-operator-safety
make Windows shell guards opt-in
2 parents 4840023 + 115d8e8 commit 7d7fcff

2 files changed

Lines changed: 139 additions & 19 deletions

File tree

scripts/codex.js

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ const POWERSHELL_PROFILE_MARKER_END = "# <<< codex-multi-auth shell guard <<<";
197197

198198
function shouldInstallWindowsBatchShimGuard() {
199199
if (process.platform !== "win32") return false;
200-
const override = (process.env.CODEX_MULTI_AUTH_WINDOWS_BATCH_SHIM_GUARD ?? "1").trim();
200+
const override = (process.env.CODEX_MULTI_AUTH_WINDOWS_BATCH_SHIM_GUARD ?? "0").trim();
201201
return override !== "0";
202202
}
203203

@@ -370,7 +370,7 @@ function ensureWindowsShellShim(filePath, desiredContent, options = {}) {
370370

371371
function shouldInstallPowerShellProfileGuard() {
372372
if (process.platform !== "win32") return false;
373-
const override = (process.env.CODEX_MULTI_AUTH_PWSH_PROFILE_GUARD ?? "1").trim();
373+
const override = (process.env.CODEX_MULTI_AUTH_PWSH_PROFILE_GUARD ?? "0").trim();
374374
return override !== "0";
375375
}
376376

@@ -455,7 +455,9 @@ function ensurePowerShellProfileGuard(shimDirectory) {
455455
}
456456

457457
function ensureWindowsShellShimGuards() {
458-
if (!shouldInstallWindowsBatchShimGuard()) return;
458+
const shouldInstallBatchGuard = shouldInstallWindowsBatchShimGuard();
459+
const shouldInstallProfileGuard = shouldInstallPowerShellProfileGuard();
460+
if (!shouldInstallBatchGuard && !shouldInstallProfileGuard) return;
459461
const shimDirectory = resolveWindowsShimDirectoryFromPath();
460462
if (!shimDirectory) return;
461463

@@ -464,23 +466,31 @@ function ensureWindowsShellShimGuards() {
464466

465467
const overwriteCustomShim =
466468
(process.env.CODEX_MULTI_AUTH_OVERWRITE_CUSTOM_BATCH_SHIM ?? "0").trim() === "1";
467-
const installedBatch = ensureWindowsShellShim(
468-
join(shimDirectory, "codex.bat"),
469-
buildWindowsBatchShimContent(),
470-
{ overwriteCustomShim },
471-
);
472-
const installedCmd = ensureWindowsShellShim(
473-
join(shimDirectory, "codex.cmd"),
474-
buildWindowsCmdShimContent(),
475-
{ overwriteCustomShim },
476-
);
477-
const installedPs1 = ensureWindowsShellShim(
478-
join(shimDirectory, "codex.ps1"),
479-
buildWindowsPowerShellShimContent(),
480-
{ overwriteCustomShim },
481-
);
469+
const installedBatch = shouldInstallBatchGuard
470+
? ensureWindowsShellShim(
471+
join(shimDirectory, "codex.bat"),
472+
buildWindowsBatchShimContent(),
473+
{ overwriteCustomShim },
474+
)
475+
: false;
476+
const installedCmd = shouldInstallBatchGuard
477+
? ensureWindowsShellShim(
478+
join(shimDirectory, "codex.cmd"),
479+
buildWindowsCmdShimContent(),
480+
{ overwriteCustomShim },
481+
)
482+
: false;
483+
const installedPs1 = shouldInstallBatchGuard
484+
? ensureWindowsShellShim(
485+
join(shimDirectory, "codex.ps1"),
486+
buildWindowsPowerShellShimContent(),
487+
{ overwriteCustomShim },
488+
)
489+
: false;
482490
const installedAny = installedBatch || installedCmd || installedPs1;
483-
const installedProfileGuard = ensurePowerShellProfileGuard(shimDirectory);
491+
const installedProfileGuard = shouldInstallProfileGuard
492+
? ensurePowerShellProfileGuard(shimDirectory)
493+
: false;
484494
if (installedAny || installedProfileGuard) {
485495
console.error(
486496
"codex-multi-auth: installed Windows shell guards to keep multi-auth routing after codex npm updates.",

test/codex-bin-wrapper.test.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ describe("codex bin wrapper", () => {
296296
const result = runWrapper(fixtureRoot, ["--version"], {
297297
CODEX_MULTI_AUTH_REAL_CODEX_BIN: fakeBin,
298298
CODEX_MULTI_AUTH_WINDOWS_BATCH_SHIM_GUARD: "1",
299+
CODEX_MULTI_AUTH_PWSH_PROFILE_GUARD: "1",
299300
PATH: `${shimDir}${delimiter}${process.env.PATH ?? ""}`,
300301
USERPROFILE: fixtureRoot,
301302
HOME: fixtureRoot,
@@ -335,6 +336,114 @@ describe("codex bin wrapper", () => {
335336
},
336337
);
337338

339+
it.skipIf(process.platform !== "win32")(
340+
"does not install Windows shell guards unless explicitly enabled",
341+
() => {
342+
const fixtureRoot = createWrapperFixture();
343+
const fakeBin = createFakeCodexBin(fixtureRoot);
344+
const shimDir = join(fixtureRoot, "shim-bin");
345+
mkdirSync(shimDir, { recursive: true });
346+
writeFileSync(
347+
join(shimDir, "codex-multi-auth.cmd"),
348+
"@ECHO OFF\r\nREM fixture codex-multi-auth shim\r\n",
349+
"utf8",
350+
);
351+
writeFileSync(
352+
join(shimDir, "codex.cmd"),
353+
'@ECHO OFF\r\necho "%dp0%\\node_modules\\@openai\\codex\\bin\\codex.js"\r\n',
354+
"utf8",
355+
);
356+
writeFileSync(
357+
join(shimDir, "codex.ps1"),
358+
'Write-Output "$basedir/node_modules/@openai/codex/bin/codex.js"' +
359+
"\r\n",
360+
"utf8",
361+
);
362+
363+
const result = runWrapper(fixtureRoot, ["--version"], {
364+
CODEX_MULTI_AUTH_REAL_CODEX_BIN: fakeBin,
365+
PATH: `${shimDir}${delimiter}${process.env.PATH ?? ""}`,
366+
USERPROFILE: fixtureRoot,
367+
HOME: fixtureRoot,
368+
});
369+
expect(result.status).toBe(0);
370+
371+
expect(() => readFileSync(join(shimDir, "codex.bat"), "utf8")).toThrow();
372+
expect(readFileSync(join(shimDir, "codex.cmd"), "utf8")).toContain(
373+
"node_modules\\@openai\\codex\\bin\\codex.js",
374+
);
375+
expect(readFileSync(join(shimDir, "codex.ps1"), "utf8")).toContain(
376+
"node_modules/@openai/codex/bin/codex.js",
377+
);
378+
expect(() =>
379+
readFileSync(
380+
join(
381+
fixtureRoot,
382+
"Documents",
383+
"PowerShell",
384+
"Microsoft.PowerShell_profile.ps1",
385+
),
386+
"utf8",
387+
),
388+
).toThrow();
389+
},
390+
);
391+
392+
it.skipIf(process.platform !== "win32")(
393+
"installs the PowerShell profile guard without requiring batch shim guards",
394+
() => {
395+
const fixtureRoot = createWrapperFixture();
396+
const fakeBin = createFakeCodexBin(fixtureRoot);
397+
const shimDir = join(fixtureRoot, "shim-bin");
398+
mkdirSync(shimDir, { recursive: true });
399+
writeFileSync(
400+
join(shimDir, "codex-multi-auth.cmd"),
401+
"@ECHO OFF\r\nREM fixture codex-multi-auth shim\r\n",
402+
"utf8",
403+
);
404+
writeFileSync(
405+
join(shimDir, "codex.cmd"),
406+
'@ECHO OFF\r\necho "%dp0%\\node_modules\\@openai\\codex\\bin\\codex.js"\r\n',
407+
"utf8",
408+
);
409+
writeFileSync(
410+
join(shimDir, "codex.ps1"),
411+
'Write-Output "$basedir/node_modules/@openai/codex/bin/codex.js"' +
412+
"\r\n",
413+
"utf8",
414+
);
415+
416+
const result = runWrapper(fixtureRoot, ["--version"], {
417+
CODEX_MULTI_AUTH_REAL_CODEX_BIN: fakeBin,
418+
CODEX_MULTI_AUTH_PWSH_PROFILE_GUARD: "1",
419+
PATH: `${shimDir}${delimiter}${process.env.PATH ?? ""}`,
420+
USERPROFILE: fixtureRoot,
421+
HOME: fixtureRoot,
422+
});
423+
expect(result.status).toBe(0);
424+
425+
expect(() => readFileSync(join(shimDir, "codex.bat"), "utf8")).toThrow();
426+
expect(readFileSync(join(shimDir, "codex.cmd"), "utf8")).toContain(
427+
"node_modules\\@openai\\codex\\bin\\codex.js",
428+
);
429+
expect(readFileSync(join(shimDir, "codex.ps1"), "utf8")).toContain(
430+
"node_modules/@openai/codex/bin/codex.js",
431+
);
432+
const pwshProfilePath = join(
433+
fixtureRoot,
434+
"Documents",
435+
"PowerShell",
436+
"Microsoft.PowerShell_profile.ps1",
437+
);
438+
expect(readFileSync(pwshProfilePath, "utf8")).toContain(
439+
"# >>> codex-multi-auth shell guard >>>",
440+
);
441+
expect(readFileSync(pwshProfilePath, "utf8")).toContain(
442+
"CodexMultiAuthShim",
443+
);
444+
},
445+
);
446+
338447
it.skipIf(process.platform !== "win32")(
339448
"prefers invocation-derived shim directory over PATH-decoy shim entries",
340449
() => {
@@ -374,6 +483,7 @@ describe("codex bin wrapper", () => {
374483
const scriptPath = join(scriptDir, "codex.js");
375484
const result = runWrapperScript(scriptPath, ["--version"], {
376485
CODEX_MULTI_AUTH_REAL_CODEX_BIN: fakeBin,
486+
CODEX_MULTI_AUTH_WINDOWS_BATCH_SHIM_GUARD: "1",
377487
PATH: `${decoyShimDir}${delimiter}${globalShimDir}${delimiter}${process.env.PATH ?? ""}`,
378488
USERPROFILE: fixtureRoot,
379489
HOME: fixtureRoot,

0 commit comments

Comments
 (0)