Atlas / Skills / browser-use / Browser Harness ♞

Browser Harness ♞BLOCK

skills/browser-use/browser

Let Claude Code, Codex or Cursor use your real Chrome, logged in as you. One prompt to set up. Open source, MIT.

Verdict
BLOCK
Grade
F
Trust score
47 /100
Version
—
Hosts
7 documented
License
MIT
Stars
18,513
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

Browser Harness lets Claude Code, Codex or Cursor use your real Chrome browser: open pages, click, type, read and download, logged in as you. Paste one prompt into your coding agent to set it up. It talks to Chrome over the DevTools Protocol (CDP), and the agent writes any missing helper as it works, so the harness gets better with each task. Open source, MIT licensed.

Try browser-harness in Browser Use Cloud or paste the setup prompt into your coding agent.

● agent: wants to upload a file
│
● agent-workspace/agent_helpers.py → helper missing
│
● agent writes it                         agent_helpers.py
│                                                       + custom helper
✓ file uploaded

You will never use the browser again.

See it work

Task: "Open my X profile, find my latest 20 video posts, and download them."

[](https://browser-use.com/showcase/videos/download-latest-20-x-videos.mp4)

Setup prompt

Paste into Claude Code or Codex (Cursor: use the MCP config below):

Install or upgrade browser-harness to the latest stable version with uv using Python 3.12, register the skill from `browser-harness skill`, and connect it to my browser. Ask whether I want local browser recordings enabled; default to no and preserve my existing preference on upgrades. Follow https://github.com/browser-use/browser-harness/blob/main/install.md if setup or connection fails.

The agent will open chrome://inspect/#remote-debugging. On first setup, tick the checkbox so the agent can connect to your browser:

Read from source at commit 850b2014fd27OBSERVED · 2026-10-20
Advertisement
02

Install

Commands as the repository documents them. They are shown, not run.

uv run --with pytest python -m pytest tests/unit -q
uvx --from 'browser-harness[mcp]' browser-harness-mcp
claude mcp add browser-harness \
uvx --from 'browser-harness[mcp]' browser-harness-mcp
uvx --from 'browser-harness[mcp]' browser-harness-mcp
uvx --from 'browser-harness[mcp]' browser-harness-mcp
03

Host compatibility

What the documentation claims. We have not run a compatibility test.

HostStatusNotes
claude-codementioned
claude-desktopmentioned
codexmentioned
copilotmentioned
cursormentioned
gemini-climentioned
openclawmentioned
04

What it tells the agent

The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.

---
name: browser-harness
description: "Control a real browser via CDP: clicking, typing, navigation, logged-in sessions, JS-rendered or bot-protected pages. Not for plain HTTP fetches of public content - use curl for those."
---

# browser-harness

Direct browser control via CDP. For task-specific edits, use `agent-workspace/agent_helpers.py`. For setup, install, or connection problems, read https://github.com/browser-use/browser-harness/blob/main/install.md.

## When Not to Use

A basic fetch of public information needs no browser. If a plain HTTP request can read it — a public page, an API, docs — use `curl` or your fetch tool, and leave the browser alone. Use browser-harness when the task needs interaction (click, type, navigate), the user's logged-in session, JS rendering, or a bot-protected page. If a direct fetch fails or returns a shell page, then escalate to the browser.

Domain skills are off by default. Set `BH_DOMAIN_SKILLS=1` to enable them; see the bottom section.

**If `BH_DOMAIN_SKILLS=1` and the task is site-specific, read every file in the matching `$BH_AGENT_WORKSPACE/domain-skills/<site>/` directory before inventing an approach.**

## Usage

```bash
browser-harness <<'PY'
print(page_info())
PY
```

- Invoke as `browser-harness`. Use heredocs for multi-line commands.
- Helpers are pre-imported. `run.py` calls `ensure_daemon()` before `exec`.
- First navigation for a task is `new_tab(url)`, not `goto_url(url)`. The daemon
  preserves the attached tab across separate CLI invocations, so do not call
  `new_tab()` again in every script.
- Keep one working tab per task/site. Before opening another, inspect
  `current_tab()` and `list_tabs()` and use `switch_tab()` to reuse a matching
  tab. Do not leave duplicate tabs on the same URL or close tabs you did not
  create.
- At task completion, close tabs created for the task that are no longer needed.
  Keep a tab open if the user needs to see it, it is needed for a known follow-up,
  or closing it could discard unsaved work or other important state.
- `new_tab()` and `switch_tab()` attach and move the horse marker without
  changing Chrome's visible tab. Screenshots and normal CDP input work in the
  background. Never call `activate_tab(target)` automatically: it brings Chrome
  to the foreground. Call it only when the user explicitly asks to see or
  visibly switch to that tab. Do not pair `switch_tab()` with `activate_tab()`.
- A local daemon is a connection to the whole Chrome instance, not to one site,
  task, card, or agent. Omit `BU_NAME` and reuse the default daemon for normal
  sequential local work across websites, tabs, screenshots, and Codex turns.
  Do not invent per-job names such as `gmail1375` or `slack1371`: every new
  local daemon opens another browser-level CDP connection and Chrome may show
  another Allow prompt.
- Set `BH_TAB_MARKER=0` before starting the daemon to leave page titles unchanged.
  The horse marker remains enabled by default.
- A timeout or page that pauses while hidden is not permission to foreground
  Chrome. Keep using background CDP operations. For a focus-gated page,
  temporarily call `cdp("Emulation.setFocusEmulationEnabled", enabled=True)`,
  perform and verify the operation, then disable it in a `finally` block. If
  background control still cannot work, report that limitation instead of
  activating the tab. Do not invent a `Runtime.evaluate` scroll replacement or
  a cross-frame JS walker.
- The normal local flow attaches to the running Chrome/Chromium CDP endpoint. No browser ids or local profile selection.

## Local Chrome

The default daemon can keep many tabs and visit many sites; browser-harness has
no per-site, screenshot, or result-count limit that requires a new daemon.
Chrome memory and page complexity are the practical limits. Reuse matching tabs
with `list_tabs()` and `switch_tab()`.

One daemon has one mutable attached/current tab. Many agents can share it when
their browser operations are serialized: treat local Chrome as one shared
browser lane while non-browser work continues in parallel. Sequential tab
switching, input, and screenshot capture are safe. Do not create another local
daemon merely because several agents exist.

Two agents that switch tabs and act simultaneously can race, causing one to act
on or capture the other's tab. For truly simultaneous interactive work, use
separate remote browsers when Browser Use Cloud authentication is already
available. Otherwise serialize browser operations through the default local
daemon. A named local daemon is a last resort when simultaneous isolation is
required, remote auth is unavailable or unsuitable, and the extra Chrome
approval prompt is acceptable. It creates another controller and dedicated tab
in the same local Chrome profile, not another Chrome profile or process.

If the default daemon becomes stale, use its built-in reattachment/recovery
first. A command timeout, truncated output, site change, closed tab, or new task
is not a reason to create another daemon. Run `browser-harness --doctor` and
restart or replace the default daemon only when it is actually dead or cannot
recover.

If the daemon cannot connect, run diagnostics:

```bash
browser-harness --doctor
```

If Chrome is not running at all, the harness launches it automatically and retries.

If Chrome is running but remote debugging is not enabled, the harness opens:

```text
chrome://inspect/#remote-debugging
```

On macOS, when local Chrome asks for remote-debugging permission, keep the
original browser command running and call `mac-approve` in another shell/tool
call. Preserve the exact daemon name: if the waiting command used
`BU_NAME=r7k2`, run:

```text
BU_NAME=r7k2 browser-harness mac-approve
```

For the default daemon, omit the `BU_NAME` prefix. The original command resumes
when the helper returns `ready`; do not rerun it. If the helper reports
`accessibility-required`, ask the user once to grant the app launching
browser-harness (for example
05

Trust audit

BLOCKgrade F · trust 47/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.

LayerWhat it checksResult
L0Provenance & inventoryWARN
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)FAIL
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
none-observed
Network
declared (11 observation(s))
Shell
declared (1 observation(s))
Dependencies
pinned
Secrets in source
found

Findings (25)

HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/browser_harness/run.py:409
exec(code, globals())
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHPrompt injection · prompt.zero_width · CWE-94, CWE-1427
agent-workspace/domain-skills/feishu/docx-scraping.md:110
t = t.replace("","").replace("","").replace("","")
Why it matters. invisible characters in instruction text
Fix. strip non-printing characters
HIGHPrompt injection · prompt.zero_width · CWE-94, CWE-1427
agent-workspace/domain-skills/itch-io/scraping.md:436
10. **Unicode zero-width space in some titles.** `\u200b` (zero-width space) appears at the start of certain titles (e.g. "Our Life: Beginnings & Always"). Strip with `.replace('\u200b', '').strip()`
Why it matters. invisible characters in instruction text
Fix. strip non-printing characters
MEDIUMInventory / provenance · inv.symlink · CWE-1104
skills/browser-harness/SKILL.md
skills/browser-harness/SKILL.md
Why it matters. link not followed
MEDIUMInventory / provenance · inv.symlink · CWE-1104
src/browser_harness/SKILL.md
src/browser_harness/SKILL.md
Why it matters. link not followed
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/browser_harness/daemon.py:308
return json.loads(urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=1).read())["webSocketDebuggerUrl"]
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/browser_harness/daemon.py:333
with urllib.request.urlopen(f"http://127.0.0.1:{probe_port}/json/version", timeout=1) as r:
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/browser_harness/run.py:85
with urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=0.3) as response:
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
agent-workspace/domain-skills/metacritic/scraping.md:14
API_KEY = "1MOZgmNFxvmljaQR1X9KAij9Mo4xAY3u"
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
agent-workspace/domain-skills/metacritic/scraping.md:93
API_KEY = "1MOZgmNFxvmljaQR1X9KAij9Mo4xAY3u"
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
agent-workspace/domain-skills/metacritic/scraping.md:141
API_KEY = "1MOZgmNFxvmljaQR1X9KAij9Mo4xAY3u"
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
agent-workspace/domain-skills/metacritic/scraping.md:182
API_KEY = "1MOZgmNFxvmljaQR1X9KAij9Mo4xAY3u"
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
agent-workspace/domain-skills/metacritic/scraping.md:223
API_KEY = "1MOZgmNFxvmljaQR1X9KAij9Mo4xAY3u"
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
agent-workspace/domain-skills/qbo/report-export.md:29
BU_NAME=qbo BU_CDP_URL=http://127.0.0.1:9223 browser-harness <<'PY'
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
skills/browser-harness/references/install.md:46
- **Way 2 (isolated profile, no popups):** launch Chrome with `--remote-debugging-port=9222 --user-data-dir=<non-default path>`, then set `BU_CDP_URL=http://127.0.0.1:9222`. Best for unattended automa
LOWObfuscation / stealth · obf.decode_call · CWE-506, CWE-94
src/browser_harness/helpers.py:346
open(path, "wb").write(base64.b64decode(r["data"]))
LOWObfuscation / stealth · obf.decode_call · CWE-506, CWE-94
src/browser_harness/recorder.py:288
data = base64.b64decode(shot["data"])
LOWPrompt injection · prompt.credential_read · CWE-94, CWE-1427
agent-workspace/domain-skills/stripe/account-sandboxes.md:13
2. Open Test mode and its API keys page. Do not infer account identity from the URL alone.
Why it matters. asks the agent to read credentials
LOWSupply chain · prompt.pipe_to_shell · CWE-829, CWE-1357
agent-workspace/domain-skills/browser-use-cloud/cloud.md:181
`curl -fsSL https://browser-use.com/profile.sh | sh`.
LOWSupply chain · prompt.pipe_to_shell · CWE-829, CWE-1357
interaction-skills/profile-sync.md:8
curl -fsSL https://browser-use.com/profile.sh | sh
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
agent-workspace/domain-skills/atlas/overview.md:51
POST `https://my.recruitwithatlas.com/graphql` using the tab's own cookies:
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
agent-workspace/domain-skills/github/repo-actions.md:3
`https://github.com/{owner}/{repo}` — user-triggered actions on the repo header (Star, Unstar, Watch, Unwatch) are HTML forms that POST back to GitHub with the session's CSRF token already rendered in
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
agent-workspace/domain-skills/howlongtobeat/scraping.md:9
HLTB runs a token-gated POST endpoint at `/api/find`. You must first fetch a session token from `/api/find/init`, then include it in the search request. Both steps are plain HTTP — no browser required
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
agent-workspace/domain-skills/howlongtobeat/scraping.md:439
- **Token system**: Every search requires a fresh token from `/api/find/init`. Token encodes `timestamp::IP|UA|hpKey|hmacHash`. The server validates that the UA used to fetch the token matches the UA
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
agent-workspace/domain-skills/howlongtobeat/scraping.md:441
- **Required headers on search POST**: `Origin: https://howlongtobeat.com` AND `Referer: https://howlongtobeat.com/` — missing either causes HTTP 403 or 404. `x-auth-token`, `x-hp-key`, `x-hp-val` are
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine

Gates applied: no_behavioural_pass.

Audited 2026-10-20 · audit v0.4.1 · source sha 850b2014fd27full audit observations/trust-audit/skill/browser-use__browser.json · Report an issue / request a re-scan
06

Audit history

Every audit this skill has had.

DateSourceVerdictGradeScoreChange
2026-10-20850b2014fd27BLOCKF47first audit
07

Questions

What does the browser — browser-use/browser-harness skill do?

Let Claude Code, Codex or Cursor use your real Chrome, logged in as you. One prompt to set up. Open source, MIT.

Is browser — browser-use/browser-harness safe to install?

No — not without reading the findings first. The audit graded it F (47/100) and found 3 critical or high issues in the source. Each one is listed on this page with the file and line it is on.

What can browser — browser-use/browser-harness access on my machine?

The audit observed that it reaches the network and runs shell commands. Each of those is consistent with what it says it does. Secrets in the source: found — see the findings.

Which assistants does browser — browser-use/browser-harness work with?

Its documentation mentions claude-code, claude-desktop, codex, copilot, cursor, gemini-cli and openclaw. That is what the text claims, not a compatibility test we ran.

How current is this page?

The grade is for one exact copy of the source (850b2014fd27), read on 2026-10-20. The repository is watched, and a new audit runs when it changes — this is the first audit.

Add this badge to your README

Shows this page's audit result and links back to it. It updates when the audit does.

ModelsAtlas audit badge

Markdown
[![ModelsAtlas audit: Browser Harness ♞](https://modelsatlas.com/skills/browser-use/browser/badge.svg)](https://modelsatlas.com/skills/browser-use/browser/)

Not your listing, or something out of date? Claim or update your listing.

Advertisement