Implement Docker support, WebUI API, and stealth extension#719
Implement Docker support, WebUI API, and stealth extension#719aleks1k wants to merge 2 commits intobrowser-use:mainfrom
Conversation
…les, no secrets) Made-with: Cursor
|
|
There was a problem hiding this comment.
9 issues found across 16 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="scripts/startup_browser.py">
<violation number="1" location="scripts/startup_browser.py:33">
P2: startup_browser.py only strips the "--user-data-dir=..." form. If PLAYWRIGHT_LAUNCH_ARGS contains the two-token form ("--user-data-dir /path"), user_data_dir remains None and the script falls back to a default profile while still passing the raw --user-data-dir arguments into Chromium, ignoring the intended profile. Handle the two-token form as well to avoid silently using the wrong profile.</violation>
</file>
<file name="docs/CLI_AND_DOCKER_CDP.md">
<violation number="1" location="docs/CLI_AND_DOCKER_CDP.md:79">
P2: Documentation suggests pointing CDP to a server IP without warning that the remote debugging port is unauthenticated and should be restricted; this can lead to exposing full browser control if 9222 is reachable publicly.</violation>
</file>
<file name="stealth-extension/stealth.js">
<violation number="1" location="stealth-extension/stealth.js:1">
P2: The stealth overrides are wrapped in a bare arrow function that is never invoked, so none of the property overrides execute.</violation>
<violation number="2" location="stealth-extension/stealth.js:14">
P2: `navigator.permissions` getter returns a new object each access, so the later override of `query` does not persist and the notifications-specific behavior is never applied.</violation>
<violation number="3" location="stealth-extension/stealth.js:43">
P2: Syntax error: window.chrome object literal is not closed, so the script cannot parse.</violation>
<violation number="4" location="stealth-extension/stealth.js:49">
P2: Using HTMLNavigator.prototype will throw ReferenceError in standard browsers; the Navigator interface is the correct prototype for userAgent overrides.</violation>
</file>
<file name="stealth-extension/manifest.json">
<violation number="1" location="stealth-extension/manifest.json:14">
P2: manifest.json is missing the closing `}` for the top-level object, making the JSON invalid and causing the extension to fail to load.</violation>
</file>
<file name="src/webui/browser_api.py">
<violation number="1" location="src/webui/browser_api.py:137">
P2: api_screenshot promises a base64 image but can return a filesystem path when "path" is present, breaking the API contract and potentially leaking local paths to consumers expecting base64 data.</violation>
</file>
<file name="docker-compose.yml">
<violation number="1" location="docker-compose.yml:68">
P2: noVNC is exposed on 0.0.0.0 while the default VNC_PASSWORD is empty, which results in a blank VNC password being generated and allows unauthenticated access to the VNC/noVNC UI from any host.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Add one-off context when rerunning by tagging
@cubic-dev-aiwith guidance or docs links (includingllms.txt) - Ask questions if you need clarification on any suggestion
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| If the host is not the same machine as the server (e.g. you’re on a laptop and the container runs on a server), replace `127.0.0.1` with the server’s IP or hostname: | ||
|
|
||
| ```python | ||
| CDP_URL = "http://YOUR_SERVER_IP:9222" |
There was a problem hiding this comment.
P2: Documentation suggests pointing CDP to a server IP without warning that the remote debugging port is unauthenticated and should be restricted; this can lead to exposing full browser control if 9222 is reachable publicly.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/CLI_AND_DOCKER_CDP.md, line 79:
<comment>Documentation suggests pointing CDP to a server IP without warning that the remote debugging port is unauthenticated and should be restricted; this can lead to exposing full browser control if 9222 is reachable publicly.</comment>
<file context>
@@ -0,0 +1,133 @@
+If the host is not the same machine as the server (e.g. you’re on a laptop and the container runs on a server), replace `127.0.0.1` with the server’s IP or hostname:
+
+```python
+CDP_URL = "http://YOUR_SERVER_IP:9222"
+```
+
</file context>
| get: () => [1, 2, 3, 4, 5], | ||
| }); | ||
|
|
||
| Object.defineProperty(navigator, 'permissions', { |
There was a problem hiding this comment.
P2: navigator.permissions getter returns a new object each access, so the later override of query does not persist and the notifications-specific behavior is never applied.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At stealth-extension/stealth.js, line 14:
<comment>`navigator.permissions` getter returns a new object each access, so the later override of `query` does not persist and the notifications-specific behavior is never applied.</comment>
<file context>
@@ -0,0 +1,51 @@
+ get: () => [1, 2, 3, 4, 5],
+ });
+
+ Object.defineProperty(navigator, 'permissions', {
+ get: () => ({
+ query: () => Promise.resolve({ state: 'granted' }),
</file context>
| get: () => window.chrome, | ||
| }); | ||
|
|
||
| Object.defineProperty(HTMLNavigator.prototype, 'userAgent', { |
There was a problem hiding this comment.
P2: Using HTMLNavigator.prototype will throw ReferenceError in standard browsers; the Navigator interface is the correct prototype for userAgent overrides.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At stealth-extension/stealth.js, line 49:
<comment>Using HTMLNavigator.prototype will throw ReferenceError in standard browsers; the Navigator interface is the correct prototype for userAgent overrides.</comment>
<file context>
@@ -0,0 +1,51 @@
+ get: () => window.chrome,
+ });
+
+ Object.defineProperty(HTMLNavigator.prototype, 'userAgent', {
+ get: () => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
+ });
</file context>
| @@ -0,0 +1,51 @@ | |||
| () => { | |||
There was a problem hiding this comment.
P2: The stealth overrides are wrapped in a bare arrow function that is never invoked, so none of the property overrides execute.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At stealth-extension/stealth.js, line 1:
<comment>The stealth overrides are wrapped in a bare arrow function that is never invoked, so none of the property overrides execute.</comment>
<file context>
@@ -0,0 +1,51 @@
+() => {
+ Object.defineProperty(navigator, 'webdriver', {
+ get: () => false,
</file context>
| canary: false, | ||
| loadTimes: () => {}, | ||
| csi: () => {}, | ||
| ; |
There was a problem hiding this comment.
P2: Syntax error: window.chrome object literal is not closed, so the script cannot parse.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At stealth-extension/stealth.js, line 43:
<comment>Syntax error: window.chrome object literal is not closed, so the script cannot parse.</comment>
<file context>
@@ -0,0 +1,51 @@
+ canary: false,
+ loadTimes: () => {},
+ csi: () => {},
+ ;
+
+ Object.defineProperty(navigator, 'chrome', {
</file context>
| "permissions": [ | ||
| "webNavigation", | ||
| "tabs" | ||
| ] |
There was a problem hiding this comment.
P2: manifest.json is missing the closing } for the top-level object, making the JSON invalid and causing the extension to fail to load.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At stealth-extension/manifest.json, line 14:
<comment>manifest.json is missing the closing `}` for the top-level object, making the JSON invalid and causing the extension to fail to load.</comment>
<file context>
@@ -0,0 +1,14 @@
+ "permissions": [
+ "webNavigation",
+ "tabs"
+ ]
</file context>
| if isinstance(out, dict) and "base64" in out: | ||
| return out["base64"] | ||
| if hasattr(out, "get"): | ||
| return out.get("base64") or out.get("path") |
There was a problem hiding this comment.
P2: api_screenshot promises a base64 image but can return a filesystem path when "path" is present, breaking the API contract and potentially leaking local paths to consumers expecting base64 data.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/webui/browser_api.py, line 137:
<comment>api_screenshot promises a base64 image but can return a filesystem path when "path" is present, breaking the API contract and potentially leaking local paths to consumers expecting base64 data.</comment>
<file context>
@@ -0,0 +1,165 @@
+ if isinstance(out, dict) and "base64" in out:
+ return out["base64"]
+ if hasattr(out, "get"):
+ return out.get("base64") or out.get("path")
+ return None
+ except Exception as e:
</file context>
|
|
||
| # VNC Settings | ||
| - VNC_PASSWORD=${VNC_PASSWORD:-youvncpassword} | ||
| - VNC_PASSWORD=${VNC_PASSWORD:-} |
There was a problem hiding this comment.
P2: noVNC is exposed on 0.0.0.0 while the default VNC_PASSWORD is empty, which results in a blank VNC password being generated and allows unauthenticated access to the VNC/noVNC UI from any host.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docker-compose.yml, line 68:
<comment>noVNC is exposed on 0.0.0.0 while the default VNC_PASSWORD is empty, which results in a blank VNC password being generated and allows unauthenticated access to the VNC/noVNC UI from any host.</comment>
<file context>
@@ -43,30 +44,35 @@ services:
# VNC Settings
- - VNC_PASSWORD=${VNC_PASSWORD:-youvncpassword}
+ - VNC_PASSWORD=${VNC_PASSWORD:-}
volumes:
</file context>
…docker-compose.yml for clarity, improved browser launch script to handle stale profile locks, find Chromium executable, and wait for CDP port readiness. Added support for headless mode and ensured proper environment variable handling.
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="scripts/startup_browser.py">
<violation number="1" location="scripts/startup_browser.py:104">
P2: SIGTERM/SIGINT only terminates the child process; the parent keeps sleeping and polling for the port, delaying shutdown up to the full startup timeout. Consider exiting immediately or setting a shutdown flag checked by the wait loop.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| ) | ||
|
|
||
| def on_sigterm(*_args: object) -> None: | ||
| proc.terminate() |
There was a problem hiding this comment.
P2: SIGTERM/SIGINT only terminates the child process; the parent keeps sleeping and polling for the port, delaying shutdown up to the full startup timeout. Consider exiting immediately or setting a shutdown flag checked by the wait loop.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/startup_browser.py, line 104:
<comment>SIGTERM/SIGINT only terminates the child process; the parent keeps sleeping and polling for the port, delaying shutdown up to the full startup timeout. Consider exiting immediately or setting a shutdown flag checked by the wait loop.</comment>
<file context>
@@ -1,66 +1,123 @@
+ )
+
+ def on_sigterm(*_args: object) -> None:
+ proc.terminate()
+
+ signal.signal(signal.SIGTERM, on_sigterm)
</file context>
| proc.terminate() | |
| proc.terminate() | |
| raise SystemExit(0) |
OpenClaw custom: Docker, WebUI API, stealth extension, docs" -b "Custom build for OpenClaw: Docker/docker-compose tweaks, WebUI HTTP API (browser_api), startup scripts, stealth extension, docs (WEBUI_HTTP_API.md, CLI_AND_DOCKER_CDP.md). No browser profiles or secrets in repo.
Summary by cubic
Adds Dockerized startup browser with CDP forwarding, a small WebUI HTTP API for direct browser control, and a stealth extension to make automation more stable and easier to integrate. Also improves Docker startup reliability (CDP readiness wait, stale profile lock cleanup, optional headless) and docs so Python/CLI can connect on :9222 without exposing sensitive ports.
New Features
STARTUP_BROWSER_HEADLESS); localhost bindings for 7788/5901/9222; volumes for data/profiles/output.goto,get_url,get_title,screenshot) plus agent actions (run_agent,stop_agent,pause_resume_agent,clear_agent). Auto-connects toBROWSER_CDPwhen set.WEBUI_HTTP_API.mdandCLI_AND_DOCKER_CDP.md; README notes for using CDP from host.verify_playwright.pyto sanity-check Chromium.Migration
LAUNCH_BROWSER_AT_STARTUP=trueandBROWSER_CDP=http://127.0.0.1:9222to control the container’s browser; Chrome listens on 9223 internally.PLAYWRIGHT_LAUNCH_ARGS(includes--remote-debugging-port=9223and the stealth extension path) andBROWSER_USER_DATA=/profiles/agent-default; mount./profilesto persist. Optionally setSTARTUP_BROWSER_HEADLESS=1./gradio_api/call/<name>orgradio_client(seeWEBUI_HTTP_API.md).Written for commit 027783b. Summary will update on new commits.