/** * Validate that a string is an http(s) URL and return it trimmed. * * This is the front-line guard against yt-dlp *argument injection*: the URL is * passed to yt-dlp as a positional argv element, so a value beginning with `-` * (e.g. `--config-locations=…`, `--load-info-json=…`, `--exec`) would be parsed * as an OPTION rather than a URL — which can lead to arbitrary command * execution. A string that `new URL()` accepts with an http/https protocol * necessarily begins with its scheme, so it can never be read as an option. * (Call sites also pass `--` before the positional URL as defence in depth.) * * Throws a user-friendly Error on anything that isn't an http(s) URL. * * Returns the parser-NORMALISED URL (`u.href`), not the raw input (audit F5). * The WHATWG URL parser silently tolerates interior tab/newline characters and * leading C0 control bytes (which `String.trim()` does not strip), so returning * the raw string could hand a downstream consumer — argv, or the sign-in * window's loadURL — a value subtly different from the one actually validated. * Emitting `u.href` guarantees callers use exactly the URL that passed the check. */ export function assertHttpUrl(raw: string): string { let u: URL try { u = new URL((raw ?? '').trim()) } catch { throw new Error('That doesn’t look like a valid URL.') } if (u.protocol !== 'http:' && u.protocol !== 'https:') { throw new Error('Only http and https links are supported.') } return u.href }