From c8fdffe05e5c0727e1680e407ef2de857dd83beb Mon Sep 17 00:00:00 2001 From: Rage Lopez Date: Sun, 24 May 2026 09:15:50 -0500 Subject: [PATCH] fix(browser): allow local file URLs --- CHANGELOG.md | 3 +++ scripts/lib/bundled-plugins.sh | 46 ++++++++++++++++++++++++++++++++++ tests/scripts_smoke.sh | 6 ++++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e94e5447..d7136983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ### Fixed +- Bundled Browser plugin staging now preserves `file://` URL support advertised + by the Browser plugin while keeping `data:` URLs blocked by the upstream URL + policy. - `codex-update-manager` now prunes unreferenced updater workspaces under `~/.cache/codex-update-manager/workspaces`, removing heavy build artifacts (`builder/`, `codex-app/`, `dist/`) while preserving lightweight diagnostics such as `logs/` and rebuild reports. - The Chrome native-messaging host now evicts stale browser clients when a newer Codex browser client connects, preventing old Node REPL sessions from repeatedly reattaching CDP and driving extension service-worker CPU. - The bundled Chrome plugin is now auto-installed during app startup, matching Browser Use, so the plugin page no longer falls back to an install button after restart when the Linux native host is already staged. diff --git a/scripts/lib/bundled-plugins.sh b/scripts/lib/bundled-plugins.sh index 41b15dfc..bf8aa1df 100644 --- a/scripts/lib/bundled-plugins.sh +++ b/scripts/lib/bundled-plugins.sh @@ -643,6 +643,51 @@ path.write_text(source[:match.start()] + replacement + source[match.end():], enc PY } +patch_browser_use_file_url_policy() { + local client="$1" + + if grep -q "codexLinuxFileUrlPolicy" "$client"; then + return 0 + fi + + python3 - "$client" <<'PY' +from pathlib import Path +import re +import sys + +path = Path(sys.argv[1]) +source = path.read_text(encoding="utf-8") +pattern = re.compile( + r'function\s+(?P[A-Za-z_$][\w$]*)\((?P[A-Za-z_$][\w$]*)\)\{' + r'if\((?P[A-Za-z_$][\w$]*)\.has\((?P=url)\)\)return\s*(?:true|!0);' + r'(?:const|let|var)\s+(?P[A-Za-z_$][\w$]*)=new URL\((?P=url)\);' + r'return\s+(?P=parsed)\.protocol\s*===\s*"http:"\s*\|\|\s*' + r'(?P=parsed)\.protocol\s*===\s*"https:"(?P;?)\}' +) +match = pattern.search(source) +if match is None: + print( + "WARN: Could not find Browser Use URL policy insertion point — leaving browser-client.mjs unchanged", + file=sys.stderr, + ) + raise SystemExit(0) + +parsed = match.group("parsed") +semicolon = match.group("semicolon") +old_body = match.group(0) +old_return = re.compile( + rf'return\s+{re.escape(parsed)}\.protocol\s*===\s*"http:"\s*\|\|\s*' + rf'{re.escape(parsed)}\.protocol\s*===\s*"https:"{re.escape(semicolon)}' +) +new_return = ( + f'return {parsed}.protocol==="http:"||{parsed}.protocol==="https:"||' + f'{parsed}.protocol==="file:"/*codexLinuxFileUrlPolicy*/{semicolon}' +) +new_body = old_return.sub(new_return, old_body, count=1) +path.write_text(source[:match.start()] + new_body + source[match.end():], encoding="utf-8") +PY +} + patch_browser_use_node_repl_env_guard() { local client="$1" @@ -789,6 +834,7 @@ stage_browser_plugin_from_upstream() { patch_browser_use_node_repl_env_guard "$target_client" patch_browser_use_native_pipe_import_meta_bridge "$target_client" patch_browser_use_site_status_allowlist_fallback "$target_client" + patch_browser_use_file_url_policy "$target_client" info "Browser plugin staged from upstream DMG" return 0 diff --git a/tests/scripts_smoke.sh b/tests/scripts_smoke.sh index 2a39c063..2bf774ba 100755 --- a/tests/scripts_smoke.sh +++ b/tests/scripts_smoke.sh @@ -123,7 +123,7 @@ JSON {"name":"browser","version":"0.1.0-alpha2","interface":{"category":"Engineering"}} JSON cat > "$resources_dir/plugins/openai-bundled/plugins/browser/scripts/browser-client.mjs" <<'JS' -function lu(e){let t=globalThis.nodeRepl?.env[e];return typeof t=="string"?t:void 0}function th(){let e=import.meta.__codexNativePipe;return e==null||typeof e.createConnection!="function"?null:e}class Uf{async fetchBlocked(e){let r=await bS(e.endpoint,{method:"GET"});if(!r.ok)throw new Error(ae(`Browser Use cannot determine if ${e.displayUrl} is allowed. Please try again later or use another source.`));let n=await r.json();return TF(n)}}export function setupAtlasRuntime() {} +function lu(e){let t=globalThis.nodeRepl?.env[e];return typeof t=="string"?t:void 0}function th(){let e=import.meta.__codexNativePipe;return e==null||typeof e.createConnection!="function"?null:e}var Z6=new Set(["about:blank"]);function Qv(e){if(Z6.has(e))return true;const t=new URL(e);return t.protocol==="http:"||t.protocol==="https:"}class Uf{async fetchBlocked(e){let r=await bS(e.endpoint,{method:"GET"});if(!r.ok)throw new Error(ae(`Browser Use cannot determine if ${e.displayUrl} is allowed. Please try again later or use another source.`));let n=await r.json();return TF(n)}}export function setupAtlasRuntime() {} JS } @@ -2396,6 +2396,7 @@ test_browser_use_node_repl_fallback_runtime() { assert_contains "$install_dir/resources/plugins/openai-bundled/plugins/browser/scripts/browser-client.mjs" 'globalThis.nodeRepl?.env?.\[e\]' assert_not_contains "$install_dir/resources/plugins/openai-bundled/plugins/browser/scripts/browser-client.mjs" 'globalThis.nodeRepl?.env\[e\]' assert_contains "$install_dir/resources/plugins/openai-bundled/plugins/browser/scripts/browser-client.mjs" "codexLinuxSiteStatusAllowlistFallback" + assert_contains "$install_dir/resources/plugins/openai-bundled/plugins/browser/scripts/browser-client.mjs" "codexLinuxFileUrlPolicy" assert_contains "$output_log" "Browser Use node_repl runtime is not a Linux executable for x86_64; skipping" assert_not_contains "$output_log" "WARN.*Browser Use node_repl runtime is not a Linux executable" assert_contains "$output_log" "Downloading Browser Use node_repl fallback runtime" @@ -2437,6 +2438,9 @@ test_browser_plugin_renamed_upstream_staging() { assert_contains "$browser_dir/scripts/browser-client.mjs" "nativePipe??import.meta.__codexNativePipe" assert_not_contains "$browser_dir/scripts/browser-client.mjs" "let e=import.meta.__codexNativePipe;return" assert_contains "$browser_dir/scripts/browser-client.mjs" "codexLinuxSiteStatusAllowlistFallback" + assert_contains "$browser_dir/scripts/browser-client.mjs" "codexLinuxFileUrlPolicy" + assert_contains "$browser_dir/scripts/browser-client.mjs" 'protocol==="file:"' + assert_not_contains "$browser_dir/scripts/browser-client.mjs" 'protocol==="data:"' assert_contains "$marketplace" '"name": "browser"' assert_contains "$marketplace" '"path": "./plugins/browser"' assert_contains "$output_log" "Browser plugin staged from upstream DMG"