Skip to main content
Glama
README.md
# browser-bridge

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Firefox Add-on](https://img.shields.io/badge/Firefox-Add--on-orange)](https://addons.mozilla.org)

Bridge opencode into your browser. Read pages, execute JavaScript, and audit for security flaws — all from your terminal. Purpose-built for bug bounty hunting.

## Why

Bug bounty hunters spend half their time switching between browser and tools. browser-bridge puts opencode directly inside the page. Browse normally, then ask opencode to read the DOM, run JS, or scan for vulnerabilities — no proxies, no manual export, no context switching.

## Why not firefox-devtools-mcp

Alternative MCP servers like [Mozilla's firefox-devtools-mcp](https://github.com/mozilla/firefox-devtools-mcp) drive Firefox externally through WebDriver BiDi. That approach works, but it leaves a detectable fingerprint:

- Sets `navigator.webdriver = true` and enables Marionette
- Alters other browser fingerprint signals
- Gets blocked by Cloudflare, Akamai, and other bot detection on the very targets you want to test

browser-bridge takes the opposite approach. Instead of driving the browser from outside, it runs **inside** the page as a Firefox addon. No WebDriver flag, no Marionette, no modified fingerprint — sites can't tell you're there. You see the page exactly as a real user does, which means the security findings you get are the ones that actually matter in the wild.

## ZAP integration

browser-bridge works wonderfully alongside [OWASP ZAP](https://www.zaproxy.org/). Run ZAP as your proxy for active/passive scanning, and use browser-bridge to navigate, read responses, and dig into DOM sinks, CORS, and CSP from inside the page. The two complement each other: ZAP finds server-side issues, browser-bridge finds client-side ones — without either tipping off the target.

## Architecture

```
opencode ──▶ MCP server (stdio) ──▶ Unix socket ──▶ host.js (native messaging) ──▶ browser addon ──▶ page
```

The MCP server talks to opencode over stdio. It forwards commands through a Unix socket to the native messaging host, which relays them into the browser's active tab. Responses flow back the same way.

## Requirements

- [omp](https://github.com/can1357/oh-my-pi) (primary) or [opencode](https://opencode.ai)
- [Firefox](https://www.mozilla.org/firefox/)
- [Node.js](https://nodejs.org) ≥ 18

## Install

One-liner (Linux/macOS, incl. WSL):

```bash
curl -fsSL https://raw.githubusercontent.com/jordandubu/browser-bridge/main/install.sh | bash
```

or from a checkout:

```bash
git clone https://github.com/jordandubu/browser-bridge
cd browser-bridge
./install.sh
```

`install.sh` registers the Firefox native messaging host and wires the MCP server
into **omp** (`~/.omp/agent/mcp.json`) — and into opencode too if its config
exists. No manual config editing.

Install the addon from the [Firefox Add-ons store](https://addons.mozilla.org),
restart omp, and the `browser_*` tools are available (`/mcp list` to confirm).

## Tools

### Page Interaction

| Tool | Description |
|------|-------------|
| `browser_read` | Extract all visible text from the active tab |
| `browser_html` | Get full page HTML |
| `browser_js` | Run arbitrary JavaScript and return the result |
| `browser_navigate` | Navigate to a URL (new tab by default, or reuse current tab) |
| `browser_tabs` | List all open tabs or switch to a specific tab by index |
| `browser_dialog` | Handle native `alert()`/`confirm()`/`prompt()` dialogs — list pending, answer (accept/dismiss/type), or clear |

### Recon & Monitoring

| Tool | Description |
|------|-------------|
| `browser_console` | Capture console.log/error/warn/info/debug output (up to 500 entries) |
| `browser_network` | Capture ALL network requests (page load + fetch/XHR) with URL, method, type |
| `browser_websocket` | Capture WebSocket connections and messages (connect, send, recv) |
| `browser_postmessage` | Capture all window.postMessage events between frames (origin, data, source) |

### Vulnerability Hunting

| Tool | Description |
|------|-------------|
| `browser_security` | Collect forms, scripts, cookies, storage, external domains, meta tags, CSP meta, inline scripts, iframes |
| `browser_dom_sinks` | Find DOM XSS sinks: innerHTML, document.write, eval, jQuery.html(), dangerouslySetInnerHTML, location.href, and more |
| `browser_storage` | Dump cookies (full values), localStorage, sessionStorage |
| `browser_csp` | Extract CSP from meta tags, flag weaknesses (unsafe-inline, unsafe-eval, wildcards, missing directives) |
| `browser_cors` | Find cross-origin resources and external origins for CORS misconfiguration testing |
| `browser_event_listeners` | Enumerate inline event handlers (onclick, onsubmit, etc.) and interactive elements |
| `browser_strip_headers` | Toggle stripping of security headers (CSP, X-XSS-Protection, X-Frame-Options, X-Content-Type-Options) for testing |

### Example: security audit

```
User: audit this page for vulnerabilities
opencode → browser_security → returns forms, scripts, cookies, external domains
opencode: "Found 3 issues:
  1. Login form submits over HTTP (no TLS)
  2. No CSP meta tag detected
  3. Inline script uses innerHTML with user-controlled input"
```

### Example: DOM XSS hunt

```
User: check for DOM XSS on this page
opencode → browser_dom_sinks → returns sink list
opencode: "Found innerHTML usage in 2 inline scripts, eval() in 1 script"
opencode → browser_postmessage → returns postMessage logs
opencode: "postMessage listener at line 42 accepts any origin — potential XSS vector"
```

### Example: CORS audit

```
User: check CORS on this page
opencode → browser_cors → returns cross-origin resources
opencode: "3 cross-origin scripts, 1 cross-origin stylesheet. Test each for misconfigured CORS"
```

### Example: native dialogs

Native `alert()`/`confirm()`/`prompt()` dialogs are browser chrome, not DOM, so they can't be clicked. The addon intercepts them in the page and queues them instead of blocking. Because `confirm()`/`prompt()` return synchronously, arm the answer **before** triggering the dialog:

```
User: the admin page will pop a confirm — accept it
opencode → browser_dialog answer type:confirm accept:true
opencode → browser_click selector:"Delete all users"
opencode → browser_dialog list → [{type: "confirm", message: "Delete all users?"}]
opencode: "confirm accepted, page JS continued"
```

## CLI

For testing without opencode:

```bash
node host/bridge.js read              # read page text
node host/bridge.js html              # get page HTML
node host/bridge.js js "document.title"  # run JS
node host/bridge.js security          # extract security data
node host/bridge.js dom_sinks         # find DOM XSS sinks
node host/bridge.js storage           # dump cookies + storage
node host/bridge.js csp               # analyze CSP
node host/bridge.js cors              # find cross-origin resources
node host/bridge.js postmessage       # capture postMessage events
node host/bridge.js websocket         # capture WebSocket messages
node host/bridge.js event_listeners   # enumerate event handlers
node host/bridge.js dialog list       # list dialogs that have fired
node host/bridge.js dialog answer alert  # arm: auto-dismiss the next alert
node host/bridge.js dialog answer confirm true  # arm: accept the next confirm
node host/bridge.js dialog answer confirm false # arm: dismiss the next confirm
node host/bridge.js dialog answer prompt "my input"  # arm: answer the next prompt
node host/bridge.js dialog clear      # drop all queued dialogs + armed answers
node host/bridge.js strip_headers true  # strip security headers
node host/bridge.js strip_headers false # restore headers
node host/bridge.js navigate "https://example.com"  # navigate
node host/bridge.js tabs list         # list tabs
node host/bridge.js tabs switch 2     # switch to tab 2
```

## Files

| Path | Role |
|------|------|
| `addon/` | Browser extension (manifest, background, content scripts) |
| `host/` | Native messaging host, MCP server, CLI client |
| `tests/` | Tool test suite + selector logic smoke test |
| `bench/ui/` | Multi-page shop (Acme Shop) used for UI navigation testing with judge/grade scripts |

## Testing

### Tool tests — `tests/`

```bash
devbox run test-tools
```

Runs 16 tests covering all 15 `browser_*` tools against a local test page. Orchestrates HTTP server, extension (temp profile), and host bridge — then tears down on exit. CI-ready.

### UI navigation bench — `bench/ui/`

Multi-page shop (catalog, cart, checkout, account) for exercising the navigation tools against realistic UI (menus, toasts, modals, tabs, pagination, late-injected elements).

```bash
devbox run bench-ui      # serve the shop on :8765
devbox run test-ui       # judge: run the shop through the MCP socket and grade each task
```

`bench/ui/judge.js` verifies the shop's interactions against a running bridge; `bench/ui/grade.js` scores a previously-run AI session from its evidence log. Regenerate the pages after editing shared markup with `node bench/ui/build.js`.

For fast dev iteration:

```bash
npm run dev          # start extension with temp profile
npm run reload-host  # restart host bridge
```

## Contributing

PRs welcome. Only opencode is supported — if you want Cursor, Claude Code, or another MCP client, add it yourself. The bridge is client-agnostic (stdio MCP), so it should just work. Test with `devbox run test-tools` and `npm run lint`.

## License

MIT