Browser agents need a hard boundary.
A local, read-only architecture review of browser-use: how page content reaches the model, how actions reach Chromium, and where a production deployment still needs deterministic policy.
Review snapshot
- Commit
- 6e1977daa0f67c9de0bc0e16aaec8b5833eeb8e0
- Python files
- 274 bounded · 0 syntax errors
- Engine findings
- 408 total · 244 info · 161 low · 3 medium
- Coverage note
- 273 generic parser limitations — that is, 273 of the 274 files matched no framework-specific adapter matched and were analysed by the generic heuristic adapter instead. All 274 files parsed, with 0 syntax errors; “uncovered” means unrecognised by a framework adapter, not unread, which is why 118 of them still carry findings. A generic-adapter result is weaker evidence than a framework-adapter one, a parser limitation is not a finding, and a file that was not covered by a framework adapter was not concluded to be safe.
- Scan scope id
tree_sha419ab9358e627eae — this is a scan-scope and tree-shape identifier, not a content hash and not a cryptographic digest: it is not collision-resistant, and AxiomGate's own verification script demonstrates a collision between differing trees of equal file sizes. The upstream git commit above is what binds the content.- Published artifact digest
- SHA-256 of the published
findings.jsonartifact:fc2a27944c3df9390e2968d5406af51f7f4ffd930cd6a9db3d8388ed01d975b2— this identifies the published evidence file itself. It is a different kind of identifier from the upstream git commit (which binds the analysed source), from the scannertree_shaabove (a scan-scope identifier, not a content hash), from the demo fixture digest, and from the formal artifact hashes. Verify it withsha256sum findings.json. - Raw evidence
- findings.json — every automated finding with its rule id, rule version, source location, confidence and explicit “cannot claim” statement, plus the exact analyzer configuration used. Automated static output only; the manual review section below is separate and is not analyzer output.
- Review themes
- 5 architectural hardening gaps
- Revalidated
- 2026-09-18 · analyzer checkpoint 21419c03
- Publication
- Local only
Where authority travels
The reviewed flow is capable by design. The missing piece is a single, final decision that distinguishes page data from agent instructions and intent from authority.
DOM, titles, popups, links, redirects
Browser state, task, history, sensitive state
Model-selected action and parameters
Click, input, files, evaluate, MCP
Chromium, cookies, profiles, egress
Five hardening priorities
Manual source review, read against the same frozen commit. The engine emitted none of these: they are architectural readings, not analyzer detections. None is an unauthenticated RCE claim; the risk depends on task trust, deployment mode, and authenticated browser state.
No universal capability decision at the action boundary
The model can select navigation, click, input, upload, file, page-JavaScript, and MCP actions. Domain metadata shapes the model schema, but the final invocation does not visibly carry a universal capability decision.
tools/service.py:1833-1850, 2178-2253tools/registry/views.py:14-29
tools/registry/service.py:331-360, 539-544
Page text shares the instruction channel
DOM, tabs, titles, events, history, task state, and sensitive-data state are assembled into one user message. Replacement redaction helps, but uncertainty is not a terminal deny.
agent/prompts.py:252-334, 353-359, 410-428agent/message_manager/service.py:556-600
Optional isolation and partial egress policy
Allowlists and IP blocking are opt-in. The watchdog allows data and blob schemes, and URL checks do not demonstrate request-level egress enforcement or DNS destination pinning.
browser/profile.py:625-639, 917-939watchdogs/security_watchdog.py:194-220
Authenticated state has a wide blast radius
Storage-state export can return decrypted cookies from all domains and write them to a caller-selected path. Persistent profile reuse is powerful, but scope, retention, and encryption are not enforced here.
browser/session.py:1435-1476browser/profile.py:557, 850-905
Fail-soft recovery is not universal fail-closed
Timeouts exist, yet broad step and action handlers can convert failures into model-visible results and continue. Security denial should be a typed terminal state, separate from operational retry.
agent/service.py:1035-1085, 1258-1307tools/service.py:2189-2253
From source to control
Taxonomy is a lens for prioritization, not a certification or a legal conformity opinion.
| Reference | What the review shows | Deterministic response |
|---|---|---|
| OWASP LLM01 Prompt Injection | External page material reaches the instruction-bearing model context. | Typed untrusted-data envelope; raw DOM never grants an action. |
| OWASP LLM02 Sensitive Disclosure | Secret state and browser cookies have model and durable-storage paths. | Opaque origin-scoped handles; fail closed on redaction uncertainty. |
| OWASP LLM06 Excessive Agency | Broad model-selected browser, file, JavaScript, and MCP surface. | Final deny-by-default capability broker with budgets and approvals. |
| EU AI Act Article 15 | Robustness and cybersecurity depend on configuration and recovery semantics. | Mandatory network policy, sandbox preservation, typed terminal denials. |
A small boundary with a large payoff
The recommended fixes preserve the useful automation plane while making authority explicit, testable, and bounded.
Sanitize the input
Strip executable and hidden markup, attach provenance and a content hash, and label page content as data only.
Broker every action
Authorize at final invocation against origin, capability, data scope, approval, and remaining budget.
Pin the destination
Require HTTPS origins, resolve and pin approved IPs, and enforce redirects and egress at the network boundary.
Minimize identity
Use ephemeral profiles and opaque, origin-scoped cookie handles instead of all-domain export.
Terminate on doubt
Make policy, redaction, sandbox, identity, and egress uncertainty terminal rather than model-retryable.
Declare extensions
Require risk and capability declarations for custom actions and dynamically discovered MCP tools.
Read the full evidence ledger and the concrete Python capability-broker proposal in the local teardown package.
Open the full teardown ↗Read-only means read-only
The reviewed snapshot was acquired once, then analyzed offline. No target behavior was invoked.
What was verified
- Shallow clone at a fixed commit, isolated under Codex scratch storage.
- 274 Python files parsed locally with Python AST at the 300,000-byte bound; 0 syntax errors.
- Automated static analysis issued 0/100, D — AT RISK, with 273 generic parser limitations disclosed.
- BU-01 – BU-05 are manual source review of the same snapshot, reported separately from the engine findings.
- AxiomGate linter returned zero findings, retained as supplementary evidence only.
- Scratch clone was removed after extraction; only local report artifacts remain.
What was not done
- No target imports, browser launches, subprocesses, LLM calls, or provider calls.
- No exploit attempts, dynamic page interaction, or external API traffic during analysis.
- No GitHub issue, advisory, social post, commit, push, or deployment.
- No claim that a specific deployment is exploitable or legally non-compliant.