Skip to content

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

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

feat: add ChromeDriver proxy for WebDriver and BiDi protocol support#164
tnsardesai wants to merge 4 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 (at port 9222), 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 endpoint and request/response rewriting in the API process, plus new runtime services/ports in the browser images, which could impact connectivity and automation flows if misconfigured.

Overview
Adds ChromeDriver support to the Chromium images and runtime. Both headful/headless images now download a Chromium-matching chromedriver, run it under supervisord on 127.0.0.1:9225, and expose a new proxy port 9224 (updated run-*.sh and wrappers to start/stop/wait for ChromeDriver).

Adds a ChromeDriver proxy server to the Go API process. The API now serves a dedicated ChromeDriver proxy (configurable ports/upstream via new env vars) that intercepts POST /session and BiDi session.new to inject goog:chromeOptions.debuggerAddress, rewrites returned capabilities.webSocketUrl to route via the proxy, and otherwise reverse-proxies HTTP/WebSocket traffic.

Refactors and extends proxy/test infrastructure. WebSocket pumping is extracted into reusable wsproxy and the DevTools proxy is updated to use it; DevTools JSON discovery proxying is generalized to include /json/list with URL rewriting; e2e infra now maps/waits for the ChromeDriver port and adds comprehensive BiDi e2e tests plus node-based validation scripts.

Written by Cursor Bugbot for commit d428c05. 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 tnsardesai force-pushed the tanmay/kernel-915-validation-gate branch from 7941a36 to 433f3d1 Compare February 25, 2026 19:13
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

Read(ctx context.Context) (websocket.MessageType, []byte, error)
Write(ctx context.Context, typ websocket.MessageType, p []byte) error
Close(statusCode websocket.StatusCode, reason string) error
}
Copy link

Choose a reason for hiding this comment

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

Exported Conn interface in wsproxy appears unused externally

Low Severity

The exported Conn interface is defined for "testing and flexibility" but Pump is the only function using it, and the Proxy function passes concrete *websocket.Conn values. No external callers appear to use Conn or Pump directly — all consumers go through Proxy. This is an unused exported abstraction that adds indirection without current value.

Fix in Cursor Fix in Web

tnsardesai and others added 4 commits March 6, 2026 15:56
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>
Make the chromedriver proxy runtime-configurable and more robust, then add explicit Vibium BiDi e2e coverage using dynamic session endpoints so remote WebDriver workflows are easier and less brittle.

Made-with: Cursor
Prevent parallel e2e tests from racing pnpm setup by making playwright dependency installation single-flight, avoiding intermittent ENOENT failures in CI.

Made-with: Cursor
@rgarcia rgarcia force-pushed the tanmay/kernel-915-validation-gate branch from 6b38b7c to d428c05 Compare March 6, 2026 21:32
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 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

require.NoError(t, err, "Failed to install bidi dependencies: %v\nOutput: %s", err, string(output))
t.Log("Bidi test dependencies installed successfully")
}
}
Copy link

Choose a reason for hiding this comment

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

Concurrent npm install race in ensureBidiDeps

Medium Severity

ensureBidiDeps checks for node_modules and runs npm install without any synchronization, unlike the updated ensurePlaywrightDeps in the same PR which correctly uses sync.Once. When multiple parallel BiDi tests call ensureBidiDeps concurrently, multiple npm install processes can race, potentially corrupting node_modules.

Fix in Cursor Fix in Web

rewriteChromeURLs(item, chromeHost, proxyHost)
}
}
}
Copy link

Choose a reason for hiding this comment

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

Map rewriting doesn't recurse into nested objects

Low Severity

rewriteChromeURLs handles map[string]interface{} by rewriting known URL fields at the current level, but it never recurses into the map's other values. If a Chrome JSON endpoint ever returns nested objects (not just flat maps or arrays of flat maps), URLs at deeper levels won't be rewritten. The []interface{} case recurses, but the map case does not.

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.

2 participants