Skip to content

feat: add ChromeDriver proxy for WebDriver and BiDi protocol support#164

Open
tnsardesai wants to merge 2 commits intomainfrom
tanmay/kernel-915-validation-gate
Open

feat: add ChromeDriver proxy for WebDriver and BiDi protocol support#164
tnsardesai wants to merge 2 commits intomainfrom
tanmay/kernel-915-validation-gate

Conversation

@tnsardesai
Copy link
Contributor

@tnsardesai tnsardesai commented Feb 25, 2026

Install ChromeDriver matching the Chromium version in both headful and headless images, managed via supervisord on internal port 9225. A new ChromeDriver proxy (exposed on port 9224) intercepts session creation to inject goog:chromeOptions.debuggerAddress so ChromeDriver attaches to the already-running browser, rewrites webSocketUrl in responses to route BiDi traffic back through the proxy, and transparently proxies all other HTTP and WebSocket traffic.

Extracts the shared WebSocket bidirectional pump logic from devtoolsproxy into a reusable wsproxy package with a MessageTransform hook, used by both the existing DevTools proxy and the new ChromeDriver proxy. Expands CDP discovery endpoint proxying to include /json/list and makes proxy ports configurable via environment variables.

Includes comprehensive unit tests for the ChromeDriver proxy and manual BiDi validation scripts (raw WebSocket, Puppeteer, Selenium).

Checklist

  • A link to a related issue in our repository
  • A description of the changes proposed in the pull request.
  • @mentions of the person or team responsible for reviewing proposed changes.

Note

Medium Risk
Introduces a new externally exposed proxy and changes WebSocket proxy plumbing plus runtime container networking; failures could break remote automation/CDP connectivity but the change is well-covered by unit/e2e tests.

Overview
Adds first-class ChromeDriver/WebDriver + BiDi support to the Chromium images by installing a version-matched chromedriver, running it under supervisord on internal port 9225, and exposing a new external proxy on 9224 (Docker + unikernel scripts updated to publish the port).

The server now starts a dedicated ChromeDriver proxy (chromedriverproxy) that injects goog:chromeOptions.debuggerAddress on session creation so ChromeDriver attaches to the existing Chromium instance, rewrites returned webSocketUrl to route BiDi traffic back through the proxy, and forwards all other HTTP/WebSocket traffic transparently.

Refactors shared WebSocket pumping into a reusable wsproxy package (used by both DevTools and ChromeDriver proxies), expands CDP JSON discovery proxying to include /json/list, and makes proxy ports configurable via DEVTOOLS_PROXY_PORT/CHROMEDRIVER_PROXY_PORT. Adds unit tests for the proxy plus new BiDi e2e coverage and JS validation scripts (Puppeteer + Selenium).

Written by Cursor Bugbot for commit 433f3d1. This will update automatically on new commits. Configure here.

@tnsardesai tnsardesai marked this pull request as ready for review February 25, 2026 18:46
tnsardesai and others added 2 commits February 25, 2026 11:12
Install ChromeDriver matching the Chromium version in both headful and
headless images, managed via supervisord on internal port 9225. A new
ChromeDriver proxy (exposed on port 9224) intercepts session creation to
inject goog:chromeOptions.debuggerAddress so ChromeDriver attaches to
the already-running browser, rewrites webSocketUrl in responses to route
BiDi traffic back through the proxy, and transparently proxies all other
HTTP and WebSocket traffic.

Extracts the shared WebSocket bidirectional pump logic from
devtoolsproxy into a reusable wsproxy package with a MessageTransform
hook, used by both the existing DevTools proxy and the new ChromeDriver
proxy. Expands CDP discovery endpoint proxying to include /json/list and
makes proxy ports configurable via environment variables.

Includes comprehensive unit tests for the ChromeDriver proxy and manual
BiDi validation scripts (raw WebSocket, Puppeteer, Selenium).

Co-authored-by: Cursor <cursoragent@cursor.com>
@tnsardesai tnsardesai force-pushed the tanmay/kernel-915-validation-gate branch from 7941a36 to 433f3d1 Compare February 25, 2026 19:13
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

var (
chromeDriverAddr = "127.0.0.1:9225"
debuggerAddr = "127.0.0.1:9222"
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded debuggerAddr ignores configurable DevTools proxy port

Medium Severity

The debuggerAddr variable is hardcoded to "127.0.0.1:9222", but the DevTools proxy port is configurable via the DEVTOOLS_PROXY_PORT environment variable (in config.go). If DEVTOOLS_PROXY_PORT is set to a non-default value, the DevTools proxy listens on the new port, but the ChromeDriver proxy still injects debuggerAddress: 127.0.0.1:9222, causing ChromeDriver to connect to a port where nothing is listening. The Handler function doesn't accept the DevTools proxy port as a parameter.

Additional Locations (1)

Fix in Cursor Fix in Web

return msg
}
return maybeInjectBidiSession(msg, logger)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BiDi WebSocket session response webSocketUrl not rewritten

Medium Severity

The WebSocket message transform only processes the -> (client-to-upstream) direction for injecting debuggerAddress into session.new. It does not intercept <- (upstream-to-client) messages, so the webSocketUrl in the BiDi session.new response is not rewritten. This URL continues to point to the internal ChromeDriver address (127.0.0.1:9225) instead of the proxy, which is inconsistent with the HTTP POST /session flow where rewriteWebSocketURL correctly rewrites it.

Fix in Cursor Fix in Web

func isWebSocketUpgrade(r *http.Request) bool {
return strings.EqualFold(r.Header.Get("Connection"), "upgrade") &&
strings.EqualFold(r.Header.Get("Upgrade"), "websocket")
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isWebSocketUpgrade fails on multi-value Connection headers

Low Severity

The isWebSocketUpgrade function uses strings.EqualFold for exact comparison on the Connection header. Per HTTP/1.1, the Connection header can contain comma-separated tokens (e.g., Connection: keep-alive, Upgrade). This exact match would fail for multi-valued headers, causing valid WebSocket upgrades to fall through to the httputil.ReverseProxy instead of the BiDi-aware proxyWebSocket handler, thereby skipping the debuggerAddress injection for session.new commands.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant