authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-04 15:25:46-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-04 15:25:46-07:00
log300f7fd11d61704617119634a99563e16c0e6913
treebf12b09e5ad47747ee5189b3c9af967f537e6011
parent53042f47f85a986257573773d9854cf59b957514
signature Signed by PGP key B5690EEEBB952194

[codex] Avoid shell for system executables (#2950)

Co-authored-by: Julius Marminge <julius@mac.lan>

8 files changed, 10 insertions(+), 23 deletions(-)

apps/server/src/diagnostics/ProcessDiagnostics.ts-1
...@@ -280,7 +280,6 @@ const runProcess = Effect.fn("runProcess")(...@@ -280,7 +280,6 @@ const runProcess = Effect.fn("runProcess")(
280 const child = yield* spawner.spawn(280 const child = yield* spawner.spawn(
281 ChildProcess.make(input.command, input.args, {281 ChildProcess.make(input.command, input.args, {
282 cwd: process.cwd(),282 cwd: process.cwd(),
283 shell: process.platform === "win32",
284 }),283 }),
285 );284 );
286 const [stdout, stderr, exitCode] = yield* Effect.all(285 const [stdout, stderr, exitCode] = yield* Effect.all(
apps/server/src/environment/Layers/ServerEnvironmentLabel.ts-1
...@@ -59,7 +59,6 @@ const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* (...@@ -59,7 +59,6 @@ const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* (
59 command,59 command,
60 args,60 args,
61 timeoutBehavior: "timedOutResult",61 timeoutBehavior: "timedOutResult",
62 shell: process.platform === "win32",
63 })62 })
64 .pipe(Effect.option);63 .pipe(Effect.option);
6564
apps/server/src/project/Layers/RepositoryIdentityResolver.test.ts-1
...@@ -23,7 +23,6 @@ const git = (cwd: string, args: ReadonlyArray<string>) =>...@@ -23,7 +23,6 @@ const git = (cwd: string, args: ReadonlyArray<string>) =>
23 return yield* processRunner.run({23 return yield* processRunner.run({
24 command: "git",24 command: "git",
25 args: ["-C", cwd, ...args],25 args: ["-C", cwd, ...args],
26 shell: process.platform === "win32",
27 });26 });
28 }).pipe(Effect.provide(ProcessRunner.layer));27 }).pipe(Effect.provide(ProcessRunner.layer));
2928
apps/server/src/project/Layers/RepositoryIdentityResolver.ts-2
...@@ -94,7 +94,6 @@ const resolveRepositoryIdentityCacheKey = Effect.fn("resolveRepositoryIdentityCa...@@ -94,7 +94,6 @@ const resolveRepositoryIdentityCacheKey = Effect.fn("resolveRepositoryIdentityCa
94 command: "git",94 command: "git",
95 args: ["-C", cwd, "rev-parse", "--show-toplevel"],95 args: ["-C", cwd, "rev-parse", "--show-toplevel"],
96 timeoutBehavior: "timedOutResult",96 timeoutBehavior: "timedOutResult",
97 shell: process.platform === "win32",
98 })97 })
99 .pipe(Effect.option);98 .pipe(Effect.option);
100 if (topLevelResult._tag === "None" || topLevelResult.value.code !== 0) {99 if (topLevelResult._tag === "None" || topLevelResult.value.code !== 0) {
...@@ -119,7 +118,6 @@ const resolveRepositoryIdentityFromCacheKey = Effect.fn("resolveRepositoryIdenti...@@ -119,7 +118,6 @@ const resolveRepositoryIdentityFromCacheKey = Effect.fn("resolveRepositoryIdenti
119 command: "git",118 command: "git",
120 args: ["-C", cacheKey, "remote", "-v"],119 args: ["-C", cacheKey, "remote", "-v"],
121 timeoutBehavior: "timedOutResult",120 timeoutBehavior: "timedOutResult",
122 shell: process.platform === "win32",
123 })121 })
124 .pipe(Effect.option);122 .pipe(Effect.option);
125 if (remoteResult._tag === "None" || remoteResult.value.code !== 0) {123 if (remoteResult._tag === "None" || remoteResult.value.code !== 0) {
apps/server/src/terminal/Layers/Manager.ts-1
...@@ -519,7 +519,6 @@ function windowsInspectSubprocess(...@@ -519,7 +519,6 @@ function windowsInspectSubprocess(
519 timeout: "1500 millis",519 timeout: "1500 millis",
520 maxOutputBytes: 32_768,520 maxOutputBytes: 32_768,
521 outputMode: "truncate",521 outputMode: "truncate",
522 shell: process.platform === "win32",
523 timeoutBehavior: "timedOutResult",522 timeoutBehavior: "timedOutResult",
524 });523 });
525 }).pipe(524 }).pipe(
packages/ssh/src/command.ts+4-4
...@@ -16,6 +16,7 @@ import { SshCommandError, SshInvalidTargetError } from "./errors.ts";...@@ -16,6 +16,7 @@ import { SshCommandError, SshInvalidTargetError } from "./errors.ts";
16const PUBLISHABLE_T3_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;16const PUBLISHABLE_T3_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
17const DEFAULT_SSH_COMMAND_TIMEOUT_MS = 60_000;17const DEFAULT_SSH_COMMAND_TIMEOUT_MS = 60_000;
18const MAX_SSH_ERROR_OUTPUT_LENGTH = 4_000;18const MAX_SSH_ERROR_OUTPUT_LENGTH = 4_000;
19export const SSH_COMMAND = process.platform === "win32" ? "ssh.exe" : "ssh";
1920
20const encoder = new TextEncoder();21const encoder = new TextEncoder();
2122
...@@ -192,15 +193,14 @@ const runSshCommandInScope = Effect.fn("ssh/command.runSshCommand.inScope")(func...@@ -192,15 +193,14 @@ const runSshCommandInScope = Effect.fn("ssh/command.runSshCommand.inScope")(func
192 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;193 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
193 yield* Effect.logDebug("ssh.command.start", {194 yield* Effect.logDebug("ssh.command.start", {
194 ...sshTargetLogFields(target),195 ...sshTargetLogFields(target),
195 command: ["ssh", ...args],196 command: [SSH_COMMAND, ...args],
196 hasStdin: input.stdin !== undefined,197 hasStdin: input.stdin !== undefined,
197 timeoutMs: input.timeoutMs ?? DEFAULT_SSH_COMMAND_TIMEOUT_MS,198 timeoutMs: input.timeoutMs ?? DEFAULT_SSH_COMMAND_TIMEOUT_MS,
198 });199 });
199 const child = yield* spawner200 const child = yield* spawner
200 .spawn(201 .spawn(
201 ChildProcess.make("ssh", args, {202 ChildProcess.make(SSH_COMMAND, args, {
202 env: environment,203 env: environment,
203 shell: process.platform === "win32",
204 stdin: {204 stdin: {
205 stream: stdinStream(input.stdin),205 stream: stdinStream(input.stdin),
206 endOnDone: true,206 endOnDone: true,
...@@ -212,7 +212,7 @@ const runSshCommandInScope = Effect.fn("ssh/command.runSshCommand.inScope")(func...@@ -212,7 +212,7 @@ const runSshCommandInScope = Effect.fn("ssh/command.runSshCommand.inScope")(func
212 Effect.mapError(212 Effect.mapError(
213 (cause) =>213 (cause) =>
214 new SshCommandError({214 new SshCommandError({
215 command: ["ssh", ...args],215 command: [SSH_COMMAND, ...args],
216 exitCode: null,216 exitCode: null,
217 stderr: "",217 stderr: "",
218 message:218 message:
packages/ssh/src/tunnel.ts+3-3
...@@ -36,6 +36,7 @@ import {...@@ -36,6 +36,7 @@ import {
36 remoteStateKey,36 remoteStateKey,
37 resolveSshTarget,37 resolveSshTarget,
38 runSshCommand,38 runSshCommand,
39 SSH_COMMAND,
39 targetConnectionKey,40 targetConnectionKey,
40} from "./command.ts";41} from "./command.ts";
41import {42import {
...@@ -1068,7 +1069,7 @@ const startSshTunnel = Effect.fn("ssh/tunnel.startSshTunnel")(function* (input:...@@ -1068,7 +1069,7 @@ const startSshTunnel = Effect.fn("ssh/tunnel.startSshTunnel")(function* (input:
1068 `${input.localPort}:127.0.0.1:${input.remotePort}`,1069 `${input.localPort}:127.0.0.1:${input.remotePort}`,
1069 hostSpec,1070 hostSpec,
1070 ];1071 ];
1071 const tunnelCommand = ["ssh", ...args];1072 const tunnelCommand = [SSH_COMMAND, ...args];
1072 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;1073 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
1073 const scope = yield* Scope.Scope;1074 const scope = yield* Scope.Scope;
1074 yield* Effect.logDebug("ssh.tunnel.spawn.start", {1075 yield* Effect.logDebug("ssh.tunnel.spawn.start", {
...@@ -1081,9 +1082,8 @@ const startSshTunnel = Effect.fn("ssh/tunnel.startSshTunnel")(function* (input:...@@ -1081,9 +1082,8 @@ const startSshTunnel = Effect.fn("ssh/tunnel.startSshTunnel")(function* (input:
1081 });1082 });
1082 const child = yield* spawner1083 const child = yield* spawner
1083 .spawn(1084 .spawn(
1084 ChildProcess.make("ssh", args, {1085 ChildProcess.make(SSH_COMMAND, args, {
1085 env: childEnvironment,1086 env: childEnvironment,
1086 shell: process.platform === "win32",
1087 stdin: {1087 stdin: {
1088 stream: Stream.empty,1088 stream: Stream.empty,
1089 endOnDone: true,1089 endOnDone: true,
packages/tailscale/src/tailscale.ts+3-10
...@@ -10,6 +10,7 @@ export const DEFAULT_TAILSCALE_SERVE_PORT = 443;...@@ -10,6 +10,7 @@ export const DEFAULT_TAILSCALE_SERVE_PORT = 443;
10export const TAILSCALE_STATUS_TIMEOUT_MS = 1_500;10export const TAILSCALE_STATUS_TIMEOUT_MS = 1_500;
11export const TAILSCALE_SERVE_TIMEOUT_MS = 10_000;11export const TAILSCALE_SERVE_TIMEOUT_MS = 10_000;
12export const TAILSCALE_PROBE_TIMEOUT_MS = 2_500;12export const TAILSCALE_PROBE_TIMEOUT_MS = 2_500;
13const TAILSCALE_COMMAND = process.platform === "win32" ? "tailscale.exe" : "tailscale";
1314
14export class TailscaleCommandError extends Data.TaggedError("TailscaleCommandError")<{15export class TailscaleCommandError extends Data.TaggedError("TailscaleCommandError")<{
15 readonly command: readonly string[];16 readonly command: readonly string[];
...@@ -136,11 +137,7 @@ export const readTailscaleStatus: Effect.Effect<...@@ -136,11 +137,7 @@ export const readTailscaleStatus: Effect.Effect<
136 const args = ["status", "--json"];137 const args = ["status", "--json"];
137 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;138 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
138 const child = yield* spawner139 const child = yield* spawner
139 .spawn(140 .spawn(ChildProcess.make(TAILSCALE_COMMAND, args))
140 ChildProcess.make("tailscale", args, {
141 shell: process.platform === "win32",
142 }),
143 )
144 .pipe(141 .pipe(
145 Effect.mapError((cause) =>142 Effect.mapError((cause) =>
146 tailscaleCommandError(143 tailscaleCommandError(
...@@ -215,11 +212,7 @@ const runTailscaleCommand = (...@@ -215,11 +212,7 @@ const runTailscaleCommand = (
215 Effect.gen(function* () {212 Effect.gen(function* () {
216 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;213 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
217 const child = yield* spawner214 const child = yield* spawner
218 .spawn(215 .spawn(ChildProcess.make(TAILSCALE_COMMAND, args))
219 ChildProcess.make("tailscale", args, {
220 shell: process.platform === "win32",
221 }),
222 )
223 .pipe(216 .pipe(
224 Effect.mapError((cause) =>217 Effect.mapError((cause) =>
225 tailscaleCommandError(218 tailscaleCommandError(