fix(cli): address PR #983 review feedback

- play.ts: move --remote-debugging-port parse+deps validation before any
  server setup so an invalid value exits cleanly instead of leaking a
  listening socket (the original bug — server printed 'Player running'
  and 'Press Ctrl+C to stop' before failing).
- Extract validateRemoteDebuggingPortDeps() in openBrowser.ts to keep
  preview.ts and play.ts in sync instead of copy-pasting the dep
  checks.
- Narrow parseRemoteDebuggingPort param to string | undefined; drop the
  dead null branch and the redundant String() / Number.isInteger() now
  that the regex already constrains the input.
- buildBrowserArgs: omit --remote-debugging-port when userDataDir is
  missing so a CDP endpoint cannot leak into the user's main profile
  even if a caller bypasses the CLI validation layer.
- Replace the duplicated buildBrowserArgs case with one that proves
  this defense-in-depth behaviour; add unit tests for
  validateRemoteDebuggingPortDeps.
- Drop the heavy JSDoc on parseRemoteDebuggingPort to match the file's
  surrounding style.
- Both commands: align --remote-debugging-port description (it now
  matches the actual 'requires --browser-path and --user-data-dir'
  contract) and add a CDP example to the --help output.
This commit is contained in:
AnoKno
2026-05-25 15:38:41 -04:00
committed by Miguel Ángel
parent 3902a9a82b
commit 0ea8aa4ffa
4 changed files with 137 additions and 69 deletions
+52 -8
View File
@@ -1,5 +1,9 @@
import { describe, it, expect } from "vitest";
import { buildBrowserArgs, parseRemoteDebuggingPort } from "./openBrowser.js";
import {
buildBrowserArgs,
parseRemoteDebuggingPort,
validateRemoteDebuggingPortDeps,
} from "./openBrowser.js";
describe("buildBrowserArgs", () => {
it("returns only the URL when no options are given", () => {
@@ -38,18 +42,16 @@ describe("buildBrowserArgs", () => {
).toEqual(["--user-data-dir=C:\\Documents and Settings\\profile", "http://localhost:3002"]);
});
it("prepends --remote-debugging-port before the URL", () => {
it("omits --remote-debugging-port when userDataDir is missing (defense in depth)", () => {
// The CLI validation layer rejects this combination upstream, but
// buildBrowserArgs must not leak a CDP endpoint into the user's main
// profile even if a caller bypasses that check.
expect(
buildBrowserArgs("http://localhost:3002", {
browserPath: "/usr/bin/chromium",
userDataDir: "/tmp/hf-profile",
remoteDebuggingPort: 9222,
}),
).toEqual([
"--user-data-dir=/tmp/hf-profile",
"--remote-debugging-port=9222",
"http://localhost:3002",
]);
).toEqual(["http://localhost:3002"]);
});
it("includes all flags together", () => {
@@ -114,3 +116,45 @@ describe("parseRemoteDebuggingPort", () => {
expect(() => parseRemoteDebuggingPort("22.5")).toThrow();
});
});
describe("validateRemoteDebuggingPortDeps", () => {
it("returns null when --remote-debugging-port is not set", () => {
expect(validateRemoteDebuggingPortDeps({})).toBeNull();
});
it("returns null when all required flags are present", () => {
expect(
validateRemoteDebuggingPortDeps({
browserPath: "/usr/bin/chromium",
userDataDir: "/tmp/hf-profile",
remoteDebuggingPort: "9222",
}),
).toBeNull();
});
it("requires --browser-path when --remote-debugging-port is set", () => {
expect(
validateRemoteDebuggingPortDeps({
userDataDir: "/tmp/hf-profile",
remoteDebuggingPort: "9222",
}),
).toBe("--remote-debugging-port requires --browser-path");
});
it("requires --user-data-dir when --remote-debugging-port is set", () => {
expect(
validateRemoteDebuggingPortDeps({
browserPath: "/usr/bin/chromium",
remoteDebuggingPort: "9222",
}),
).toBe("--remote-debugging-port requires --user-data-dir");
});
it("reports --browser-path first when both deps are missing", () => {
expect(
validateRemoteDebuggingPortDeps({
remoteDebuggingPort: "9222",
}),
).toBe("--remote-debugging-port requires --browser-path");
});
});
+27 -17
View File
@@ -6,29 +6,35 @@ export interface OpenBrowserOptions {
remoteDebuggingPort?: number;
}
/**
* Validate and parse a --remote-debugging-port value.
* Returns the port number or undefined if not provided.
* Throws if the value is not a valid integer in 1..65535.
*/
export function parseRemoteDebuggingPort(value: unknown): number | undefined {
if (value === undefined || value === null || value === "") return undefined;
const text = String(value);
if (!/^\d+$/.test(text)) {
export function parseRemoteDebuggingPort(value: string | undefined): number | undefined {
if (value === undefined || value === "") return undefined;
if (!/^\d+$/.test(value)) {
throw new Error("--remote-debugging-port must be an integer between 1 and 65535");
}
const port = Number(text);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
const port = Number(value);
if (port < 1 || port > 65535) {
throw new Error("--remote-debugging-port must be an integer between 1 and 65535");
}
return port;
}
export interface RemoteDebuggingPortDeps {
browserPath?: string;
userDataDir?: string;
remoteDebuggingPort?: string;
}
/**
* Returns an error message if --remote-debugging-port is set without its required
* dependencies (--browser-path and --user-data-dir), or null if everything is OK.
*/
export function validateRemoteDebuggingPortDeps(deps: RemoteDebuggingPortDeps): string | null {
if (!deps.remoteDebuggingPort) return null;
if (!deps.browserPath) return "--remote-debugging-port requires --browser-path";
if (!deps.userDataDir) return "--remote-debugging-port requires --user-data-dir";
return null;
}
/**
* Build the argument list for spawning a browser process.
*
@@ -39,7 +45,11 @@ export function buildBrowserArgs(url: string, options: OpenBrowserOptions): stri
if (options.userDataDir) {
args.push(`--user-data-dir=${options.userDataDir}`);
}
if (options.remoteDebuggingPort !== undefined) {
// Defense-in-depth: only emit --remote-debugging-port when paired with an
// isolated --user-data-dir. Without an isolated profile the CDP endpoint
// would expose the user's main browser session, which is the whole reason
// the CLI validation layer requires both flags together.
if (options.remoteDebuggingPort !== undefined && options.userDataDir) {
args.push(`--remote-debugging-port=${options.remoteDebuggingPort}`);
}
args.push(url);