fix(coding-agent): bound browser version probes
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Hardened Linux Chromium executable detection to reject non-executable files, non-browser wrappers, and candidates that hang during the version probe.
|
||||
|
||||
## [17.2.9] - 2026-08-05
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -205,11 +205,19 @@ function isExecutableFile(p: string): boolean {
|
||||
async function isChromiumExecutable(p: string): Promise<boolean> {
|
||||
if (!isExecutableFile(p)) return false;
|
||||
try {
|
||||
const probeTimeoutMs = 3000;
|
||||
const proc = Bun.spawn([p, "--version"], {
|
||||
stdout: "pipe",
|
||||
stderr: "ignore",
|
||||
signal: AbortSignal.timeout(probeTimeoutMs),
|
||||
killSignal: "SIGKILL",
|
||||
});
|
||||
const stdout = await new Response(proc.stdout).text();
|
||||
const stdout = await Promise.race([
|
||||
new Response(proc.stdout).text(),
|
||||
proc.exited.then(() => null),
|
||||
Bun.sleep(probeTimeoutMs).then(() => null),
|
||||
]);
|
||||
if (stdout === null) return false;
|
||||
await proc.exited;
|
||||
return proc.exitCode === 0 && /Chrom|Edg/i.test(stdout);
|
||||
} catch {
|
||||
|
||||
@@ -106,19 +106,38 @@ describe("browser executable selection", () => {
|
||||
try {
|
||||
const wrapper = path.join(tempDir.path(), "google-chrome");
|
||||
const chromium = path.join(tempDir.path(), "chromium");
|
||||
const nonExecutable = path.join(tempDir.path(), "not-executable");
|
||||
await Bun.write(wrapper, "#!/bin/sh\necho browser bridge\n");
|
||||
await Bun.write(chromium, "#!/bin/sh\necho Chromium 123.0\n");
|
||||
await Bun.write(nonExecutable, "#!/bin/sh\necho Chromium 123.0\n");
|
||||
fs.chmodSync(wrapper, 0o755);
|
||||
fs.chmodSync(chromium, 0o755);
|
||||
fs.chmodSync(nonExecutable, 0o644);
|
||||
|
||||
await expect(chromiumExecutableProbeForTest(wrapper)).resolves.toBe(false);
|
||||
await expect(chromiumExecutableProbeForTest(chromium)).resolves.toBe(true);
|
||||
await expect(chromiumExecutableProbeForTest(nonExecutable)).resolves.toBe(false);
|
||||
} finally {
|
||||
await tempDir.remove();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it.skipIf(process.platform === "win32")("rejects wrappers that hang during the version probe", async () => {
|
||||
const tempDir = TempDir.createSync("@browser-probe-hanging-");
|
||||
try {
|
||||
const hangingWrapper = path.join(tempDir.path(), "google-chrome");
|
||||
await Bun.write(hangingWrapper, "#!/bin/sh\nsleep 60\n");
|
||||
fs.chmodSync(hangingWrapper, 0o755);
|
||||
|
||||
const startedAt = performance.now();
|
||||
await expect(chromiumExecutableProbeForTest(hangingWrapper)).resolves.toBe(false);
|
||||
expect(performance.now() - startedAt).toBeLessThan(5000);
|
||||
} finally {
|
||||
await tempDir.remove();
|
||||
}
|
||||
});
|
||||
|
||||
it("honors PUPPETEER_EXECUTABLE_PATH before a detected Windows system Chrome", async () => {
|
||||
const tempDir = TempDir.createSync("@browser-executable-");
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user