Skip to content

Commit 5fc9d02

Browse files
that-github-userunknownclaude
authored
Fix DEP0190: remove all execFile+shell:true usage (#147)
Replace execFile(cmd, args, {shell:true}) with exec(cmdString) in test-runner.ts and run.ts preflightTestRun. The shell:true+args pattern is deprecated (DEP0190) because args aren't escaped, making it equivalent to string concatenation. Since we validate commands via validateTestCommand before execution, using exec directly is both safer and more honest. Closes #135 Co-authored-by: unknown <that-github-user@github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 297107b commit 5fc9d02

File tree

2 files changed

+11
-20
lines changed

2 files changed

+11
-20
lines changed

src/commands/run.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { execFile } from "node:child_process";
1+
import { exec as execCb, execFile } from "node:child_process";
22
import { mkdir, readFile, statfs, writeFile } from "node:fs/promises";
33
import { tmpdir } from "node:os";
44
import { join } from "node:path";
@@ -322,14 +322,14 @@ export async function retry(opts: RunOptions): Promise<void> {
322322
* Returns a warning string if the tests fail, or null if they pass.
323323
*/
324324
export async function preflightTestRun(testCmd: string, repoRoot: string): Promise<string | null> {
325-
const { cmd, args } = parseTestCommand(testCmd);
325+
const { cmd } = parseTestCommand(testCmd);
326326
if (!cmd) return null;
327327

328+
const execAsync = promisify(execCb);
328329
try {
329-
await execFileAsync(cmd, args, {
330+
await execAsync(testCmd, {
330331
cwd: repoRoot,
331332
timeout: 60_000,
332-
shell: true,
333333
env: { ...process.env, CI: "true" },
334334
});
335335
return null;

src/scoring/test-runner.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { execFile } from "node:child_process";
1+
import { exec as execCb } from "node:child_process";
22
import { access } from "node:fs/promises";
33
import { join } from "node:path";
44
import { promisify } from "node:util";
55
import type { TestResult } from "../types.js";
66

7-
const exec = promisify(execFile);
7+
const exec = promisify(execCb);
88

99
const DEFAULT_TEST_TIMEOUT_MS = 120_000;
1010

@@ -65,7 +65,7 @@ export async function runTests(
6565
};
6666
}
6767

68-
const { cmd, args } = parseTestCommand(testCmd);
68+
const { cmd } = parseTestCommand(testCmd);
6969

7070
if (!cmd) {
7171
return {
@@ -88,12 +88,12 @@ export async function runTests(
8888
}
8989

9090
try {
91-
// Use execFile with shell:true for cross-platform command resolution
92-
// while keeping args as an array to prevent injection via arguments.
93-
const { stdout, stderr } = await exec(cmd, args, {
91+
// Use exec (shell string) for cross-platform command resolution (npx, npm, etc.).
92+
// Safety: testCmd is validated by validateTestCommand() which rejects shell operators.
93+
// This avoids the DEP0190 deprecation from execFile + shell:true + args array.
94+
const { stdout, stderr } = await exec(testCmd, {
9495
cwd: worktreePath,
9596
timeout: timeoutMs,
96-
shell: true,
9797
env: { ...process.env, CI: "true" },
9898
});
9999
return {
@@ -120,15 +120,6 @@ export async function runTests(
120120
};
121121
}
122122

123-
if (typeof e.code === "string" && e.code === "ENOENT") {
124-
return {
125-
agentId,
126-
passed: false,
127-
output: `Command not found: ${cmd}. Is it installed?`,
128-
exitCode: 127,
129-
};
130-
}
131-
132123
return {
133124
agentId,
134125
passed: false,

0 commit comments

Comments
 (0)