Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/codex/app-server-processes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ export function listWindowsSnapshots(): ProcessSnapshot[] {
// executable resolves from the trusted System32 directory (never PATH), and
// windowsHide keeps the enumeration console-less on desktop sessions (#1278).
const output = execFileSync(resolveTrustedWindowsPowerShellExe(), [
"-NoProfile", "-NoLogo", "-NonInteractive", "-WindowStyle", "Hidden",
"-NoProfile", "-NoLogo", "-NonInteractive",
"-Command",
psCommand,
], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 8_000, windowsHide: true });
Expand Down Expand Up @@ -461,7 +461,7 @@ function readDarwinProcStartMs(pid: number): number | null {
function readWindowsProcStartMs(pid: number): number | null {
try {
const out = execFileSync(resolveTrustedWindowsPowerShellExe(), [
"-NoProfile", "-NoLogo", "-NonInteractive", "-WindowStyle", "Hidden",
"-NoProfile", "-NoLogo", "-NonInteractive",
"-Command",
`(Get-CimInstance Win32_Process -Filter "ProcessId=${pid}").CreationDate.ToUniversalTime().ToString("o")`,
], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 8_000, windowsHide: true }).trim();
Expand Down Expand Up @@ -517,7 +517,7 @@ export function readProcessStartMsBatch(
try {
const filter = pids.map(pid => `ProcessId=${pid}`).join(" OR ");
const stdout = execFileSync(resolveTrustedWindowsPowerShellExe(), [
"-NoProfile", "-NoLogo", "-NonInteractive", "-WindowStyle", "Hidden",
"-NoProfile", "-NoLogo", "-NonInteractive",
"-Command",
`Get-CimInstance Win32_Process -Filter "${filter}" | ForEach-Object { "$($_.ProcessId)\t$($_.CreationDate.ToUniversalTime().ToString("o"))" }`,
], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 5_000, windowsHide: true });
Expand Down
7 changes: 3 additions & 4 deletions src/codex/user-identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ function windowsIdentityPowerShellCommand(expression: string): string[] {
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-WindowStyle",
"Hidden",
"-Command",
expression,
];
Expand Down Expand Up @@ -105,8 +103,9 @@ function powershellValue(expression: string): string {
// `windowsHide` is the popup fix (#1278): the desktop proxy parent runs
// without a console, so a console-subsystem child spawned without
// CREATE_NO_WINDOW gets a fresh visible console window at startup and on
// every config write. `-WindowStyle Hidden` alone does not stop the
// allocation; the flag behind `windowsHide` does.
// every config write. Do not add PowerShell's `-WindowStyle Hidden` here:
// Bun 1.3.14 can fail that direct CLI combination before the SID command
// executes (#1589); the process-level `windowsHide` flag is sufficient.
result = Bun.spawnSync(command, windowsIdentityPowerShellSpawnOptions());
} catch (cause) {
refuse("Windows effective-account lookup could not start.", cause);
Expand Down
2 changes: 0 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3578,8 +3578,6 @@ function readProcessCommandLine(pid: number): string | undefined {
"-NoProfile",
"-NoLogo",
"-NonInteractive",
"-WindowStyle",
"Hidden",
"-Command",
`(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`,
], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 3000, windowsHide: true });
Expand Down
2 changes: 0 additions & 2 deletions src/lib/windows-user-principal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ const POWERSHELL_ARGS = [
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-WindowStyle",
"Hidden",
"-Command",
SID_EXPRESSION,
] as const;
Expand Down
2 changes: 0 additions & 2 deletions src/tray/windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ export function windowsTrayProcessArgs(entry: WindowsTrayEntry, mode: "Run" | "S
"-NonInteractive",
"-STA",
"-ExecutionPolicy", "Bypass",
"-WindowStyle", "Hidden",
"-File", safePath(entry.script),
"-BunPath", safePath(entry.bun),
"-BunRuntimeSource", entry.bunRuntimeSource,
Expand Down Expand Up @@ -173,7 +172,6 @@ export function buildWindowsTrayPowerShellCommand(entry: WindowsTrayEntry, power
"-NonInteractive",
"-STA",
"-ExecutionPolicy", "Bypass",
"-WindowStyle", "Hidden",
"-File", quoteRunValue(entry.script),
"-BunPath", quoteRunValue(entry.bun),
"-BunRuntimeSource", entry.bunRuntimeSource,
Expand Down
4 changes: 2 additions & 2 deletions src/update/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ export function spawnGuiUpdateWorker(
].join("; ");
const launched = spawnSync(
resolveTrustedWindowsPowerShellExe(),
["-NoProfile", "-NoLogo", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", ps],
["-NoProfile", "-NoLogo", "-NonInteractive", "-Command", ps],
{ encoding: "utf8", windowsHide: true, timeout: 15_000 },
);
const pid = Number(String(launched.stdout ?? "").trim().split(/\r?\n/).pop());
Expand Down Expand Up @@ -1386,7 +1386,7 @@ function killWindowsServiceWrapperProcesses(): void {
"} | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }",
].join(" ");
spawnSync(resolveTrustedWindowsPowerShellExe(), [
"-NoProfile", "-NoLogo", "-NonInteractive", "-WindowStyle", "Hidden",
"-NoProfile", "-NoLogo", "-NonInteractive",
"-Command", ps,
], { stdio: "ignore", timeout: 5000, windowsHide: true });
} catch {
Expand Down
14 changes: 14 additions & 0 deletions structure/02_config-and-codex-home.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,20 @@ path obtains System32 from `GetSystemDirectoryW`. Windows ARM64 Bun builds that
Task Scheduler launch, never consults environment variables or `PATH`, and fails closed when the
fixed executable is absent.

Direct PowerShell children rely on the process launcher's `windowsHide`/hidden-host mechanism and
must not also receive the PowerShell CLI pair `-WindowStyle Hidden`. On affected Windows 11 systems,
Bun 1.3.14 exits that direct invocation before the command runs, which turns a valid SID or process
lookup into `EACLIDENTITY` or a failed sync. This does not apply to `Start-Process -WindowStyle
Hidden` inside an already-running PowerShell script, nor to .NET/VBS process-window settings.

[Decision Log]
- 목적과 의도: keep trusted Windows identity and process probes console-less without triggering Bun's direct PowerShell `-WindowStyle Hidden` failure.
- 기존 구현 및 제약 조건: the calls already used `windowsHide: true` or a hidden VBS host, but redundantly passed PowerShell's window-style CLI option; the same option remains valid inside `Start-Process` and must not be removed there.
- 검토한 주요 대안: decode the generic failure specially, retry after failure, remove all hidden-window controls, or remove only the redundant direct CLI pair.
- 선택한 방식: retain trusted executable resolution, non-interactive flags, timeouts, and process-level hiding; remove `-WindowStyle Hidden` only from direct PowerShell argv.
- 다른 대안 대신 이 방식을 선택한 이유: the command executes on affected Bun/Windows combinations, no console window is introduced, and working elevated/detached child-process behavior stays unchanged.
- 장점, 단점 및 영향: SID, process-owner, tray, update, and sync probes share the compatible launch contract; a future call must use launcher-level hiding rather than reintroducing the PowerShell CLI pair.

[Decision Log]
- 목적과 의도: Preserve required Windows ACL hardening on the bundled Windows ARM64 runtime without weakening executable trust.
- 기존 구현 및 제약 조건: The effective-SID query depended on the shared `GetSystemDirectoryW` FFI resolver; Bun 1.3.14 Windows ARM64 has no working `bun:ffi`, so config mutation reached `EACLIDENTITY` before PowerShell could start.
Expand Down
2 changes: 1 addition & 1 deletion tests/codex-app-server-processes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ describe("Windows Win32_Process owner enumeration (#476)", () => {
const child = spawn(
"powershell.exe",
[
"-NoProfile", "-NoLogo", "-NonInteractive", "-WindowStyle", "Hidden",
"-NoProfile", "-NoLogo", "-NonInteractive",
"-Command",
"Start-Sleep -Seconds 45 # codex app-server integration-probe",
],
Expand Down
2 changes: 2 additions & 0 deletions tests/windows-deploy-close-regressions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ describe("update-job restart avoids the shell-less .cmd EINVAL (Windows, bun/sou
expect(src).toContain("Start-Process");
expect(src).toContain("buildWindowsElevatedArgumentList");
expect(src).toContain("resolveTrustedWindowsPowerShellExe");
expect(src).toContain("windowsHide: true");
expect(src).not.toContain('["-NoProfile", "-NoLogo", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", ps]');
expect(src).toContain("spawnWorkerFn: spawnGuiUpdateWorker");
// Foreign listeners must stay fail-closed; npm rename is covered by ocx identity.
expect(src).not.toContain("killAnyListenPidOnPort");
Expand Down
7 changes: 3 additions & 4 deletions tests/windows-popup-fix.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,16 @@ afterEach(() => {
});

describe("Windows identity lookup popup fix (#1278)", () => {
test("builds a hidden non-interactive command from the trusted PowerShell path", () => {
test("builds a non-interactive command without the Bun-incompatible PowerShell window flag", () => {
setTrustedWindowsElevationExecutablesForTests({ powershell: TRUSTED_POWERSHELL });
const command = windowsIdentityPowerShellCommandForTests(
"[System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value",
);
expect(command[0]).toBe(TRUSTED_POWERSHELL);
expect(command).toContain("-NoProfile");
expect(command).toContain("-NonInteractive");
const windowStyle = command.indexOf("-WindowStyle");
expect(windowStyle).toBeGreaterThan(0);
expect(command[windowStyle + 1]).toBe("Hidden");
expect(command).not.toContain("-WindowStyle");
expect(command).not.toContain("Hidden");
expect(command[command.length - 2]).toBe("-Command");
expect(command[command.length - 1])
.toBe("[System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value");
Expand Down
5 changes: 4 additions & 1 deletion tests/windows-tray.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ describe("Windows tray packaging and command safety", () => {
}
});

test("uses fixed argv for the hidden PowerShell host", () => {
test("uses fixed argv and leaves window suppression to the process launcher", () => {
const args = windowsTrayProcessArgs(entry);
expect(args).toContain("-NoProfile");
expect(args).toContain("-NonInteractive");
Expand All @@ -98,6 +98,8 @@ describe("Windows tray packaging and command safety", () => {
expect(args).toContain(entry.bun);
expect(args).toContain(entry.cli);
expect(args).not.toContain("-Command");
expect(args).not.toContain("-WindowStyle");
expect(args).not.toContain("Hidden");
expect(windowsTrayProcessArgs(entry, "Run", 4242)).toContain("4242");
});

Expand All @@ -121,6 +123,7 @@ describe("Windows tray packaging and command safety", () => {
expect(powershellCommand).toContain(`-OpenCodexHome "${entry.opencodexHome}"`);
expect(powershellCommand).not.toContain("cmd /c");
expect(powershellCommand).not.toContain("-Command");
expect(powershellCommand).not.toContain("-WindowStyle");
const runCommand = buildWindowsTrayRunCommand({
...entry,
launcherPath: `${entry.opencodexHome}\\opencodex-tray.vbs`,
Expand Down
4 changes: 1 addition & 3 deletions tests/windows-user-principal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,14 @@ afterEach(() => {
});

describe("Windows effective ACL principal", () => {
test("builds a hidden non-interactive command from the trusted PowerShell path", () => {
test("builds a non-interactive command without the Bun-incompatible PowerShell window flag", () => {
const trusted = "C:\\trusted-system32\\WindowsPowerShell\\v1.0\\powershell.exe";
setTrustedWindowsElevationExecutablesForTests({ powershell: trusted });
expect(windowsPrincipalPowerShellCommandForTests()).toEqual([
trusted,
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-WindowStyle",
"Hidden",
"-Command",
"[System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value",
]);
Expand Down
Loading