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
+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);