fix(coding-agent): validate Linux browser executables
This commit is contained in:
@@ -133,7 +133,7 @@ let chromiumExecutablePromise: Promise<string | undefined> | undefined;
|
||||
export async function ensureChromiumExecutable(): Promise<string | undefined> {
|
||||
const envPath = process.env.PUPPETEER_EXECUTABLE_PATH;
|
||||
if (envPath) return envPath;
|
||||
const sysChrome = resolveSystemChromium();
|
||||
const sysChrome = await resolveSystemChromium();
|
||||
if (sysChrome) return sysChrome;
|
||||
if (chromiumExecutablePromise) return chromiumExecutablePromise;
|
||||
|
||||
@@ -193,7 +193,25 @@ let resolvedChromium: string | null | undefined; // undefined = unchecked; null
|
||||
function isExecutableFile(p: string): boolean {
|
||||
try {
|
||||
const st = fs.statSync(p);
|
||||
return st.isFile();
|
||||
if (!st.isFile()) return false;
|
||||
if (process.platform === "win32") return true;
|
||||
fs.accessSync(p, fs.constants.X_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function isChromiumExecutable(p: string): Promise<boolean> {
|
||||
if (!isExecutableFile(p)) return false;
|
||||
try {
|
||||
const proc = Bun.spawn([p, "--version"], {
|
||||
stdout: "pipe",
|
||||
stderr: "ignore",
|
||||
});
|
||||
const stdout = await new Response(proc.stdout).text();
|
||||
await proc.exited;
|
||||
return proc.exitCode === 0 && /Chrom|Edg/i.test(stdout);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
@@ -278,13 +296,13 @@ function systemChromiumCandidates(
|
||||
return candidates;
|
||||
}
|
||||
|
||||
function resolveSystemChromium(): string | undefined {
|
||||
async function resolveSystemChromium(): Promise<string | undefined> {
|
||||
if (resolvedChromium !== undefined) return resolvedChromium ?? undefined;
|
||||
const seen = new Set<string>();
|
||||
for (const candidate of systemChromiumCandidates()) {
|
||||
if (!candidate || seen.has(candidate)) continue;
|
||||
seen.add(candidate);
|
||||
if (isExecutableFile(candidate)) {
|
||||
if (await isChromiumExecutable(candidate)) {
|
||||
resolvedChromium = candidate;
|
||||
logger.debug("Using system Chrome/Chromium", { path: candidate });
|
||||
return candidate;
|
||||
@@ -894,6 +912,10 @@ export function systemChromiumCandidatesForTest(
|
||||
return systemChromiumCandidates(platform, home, which);
|
||||
}
|
||||
|
||||
export async function chromiumExecutableProbeForTest(executablePath: string): Promise<boolean> {
|
||||
return isChromiumExecutable(executablePath);
|
||||
}
|
||||
|
||||
export function stealthIgnoreDefaultArgsForTest(executablePath: string | undefined): string[] {
|
||||
return stealthIgnoreDefaultArgs(executablePath);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import {
|
||||
chromiumExecutableProbeForTest,
|
||||
stealthIgnoreDefaultArgsForTest,
|
||||
systemChromiumCandidatesForTest,
|
||||
} from "@oh-my-pi/pi-coding-agent/tools/browser/launch";
|
||||
@@ -97,6 +99,26 @@ describe("system Chromium candidates", () => {
|
||||
});
|
||||
|
||||
describe("browser executable selection", () => {
|
||||
it.skipIf(process.platform === "win32")(
|
||||
"rejects executable wrappers that are not Chromium-family browsers",
|
||||
async () => {
|
||||
const tempDir = TempDir.createSync("@browser-probe-");
|
||||
try {
|
||||
const wrapper = path.join(tempDir.path(), "google-chrome");
|
||||
const chromium = path.join(tempDir.path(), "chromium");
|
||||
await Bun.write(wrapper, "#!/bin/sh\necho browser bridge\n");
|
||||
await Bun.write(chromium, "#!/bin/sh\necho Chromium 123.0\n");
|
||||
fs.chmodSync(wrapper, 0o755);
|
||||
fs.chmodSync(chromium, 0o755);
|
||||
|
||||
await expect(chromiumExecutableProbeForTest(wrapper)).resolves.toBe(false);
|
||||
await expect(chromiumExecutableProbeForTest(chromium)).resolves.toBe(true);
|
||||
} 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