Security

agent-browser includes security features to protect against credential exposure, prompt injection via untrusted page content, and unauthorized browser actions.

All security features are opt-in. By default, agent-browser imposes no restrictions on navigation, actions, or output. Enable these features as needed for your deployment -- existing workflows are unaffected until you explicitly activate a feature.

Threat Model

These features are designed to mitigate the following threats when an LLM-based agent drives a browser:

  • Credential exposure -- Passwords stored in the auth vault are never included in LLM context. The CLI handles vault operations locally; credentials do not pass through the daemon's IPC channel.
  • Prompt injection via page content -- Malicious pages can embed text that looks like tool output or system instructions. Content boundary markers (--content-boundaries) let the orchestrator distinguish trusted tool output from untrusted page content.
  • Unauthorized navigation / data exfiltration -- A compromised or manipulated agent could navigate to attacker-controlled domains to exfiltrate data. The domain allowlist (--allowed-domains) blocks navigations, sub-resource requests, WebSocket connections, EventSource streams, and sendBeacon calls to non-allowed domains.
  • Unauthorized destructive actions -- Action policy (--action-policy) and confirmation gating (--confirm-actions) prevent the agent from performing dangerous operations (eval, downloads, uploads) without explicit approval.
  • Context flooding -- Large page outputs can overwhelm an LLM's context window. Output truncation (--max-output) caps the size of page-sourced content.

Known limitations

  • WebSocket/EventSource blocking is best-effort. It works by overriding browser constructors via an init script. If the eval action category is allowed, page scripts could theoretically restore the original constructors. Deny eval via --action-policy for maximum protection.
  • Domain filter timing on remote connections. When connecting to a pre-existing browser via CDP or a cloud provider, pages may have already loaded content before the domain filter is installed. agent-browser navigates disallowed pages to about:blank after the filter is active, but resources loaded before that point are not retroactively blocked.
  • Content boundaries are defense-in-depth. They rely on the LLM and orchestrator respecting the structural markers. A sufficiently capable adversarial page could attempt to mimic the boundary format, though the per-process CSPRNG nonce makes this impractical to predict.
  • Confirmation timeout. Pending confirmations auto-deny after 60 seconds. Orchestrators must respond within that window.
  • Non-TTY auto-deny. When --confirm-interactive is set but stdin is not a terminal (e.g., piped input), actions are automatically denied to prevent accidental approval in non-interactive contexts.

Authentication Vault

Store credentials locally and reference them by name. The LLM never sees passwords.

# Save credentials (encrypted if AGENT_BROWSER_ENCRYPTION_KEY is set)
# Recommended: pipe password via stdin to avoid shell history / process listing exposure
echo "pass" | agent-browser auth save github --url https://github.com/login --username user --password-stdin

# Or pass directly (a warning will be shown)
agent-browser auth save github --url https://github.com/login --username user --password pass

# Login using saved credentials
agent-browser auth login github

# List saved profiles (names and URLs only, no secrets)
agent-browser auth list

# Show profile metadata
agent-browser auth show github

# Delete a profile
agent-browser auth delete github

Custom selectors can be specified if auto-detection fails:

agent-browser auth save myapp \
  --url https://app.example.com/login \
  --username user --password pass \
  --username-selector "#email" \
  --password-selector "#password" \
  --submit-selector "button.login"

Profiles are stored in ~/.agent-browser/auth/ and always encrypted with AES-256-GCM. If AGENT_BROWSER_ENCRYPTION_KEY is not set, a key is auto-generated at ~/.agent-browser/.encryption-key on first use. Back up this file or set the environment variable explicitly for portability.

File permissions are enforced on both Unix (chmod 600/700) and Windows (icacls restricted to the current user) to prevent other users from reading encryption keys or auth profiles.

Content Boundary Markers

When --content-boundaries is enabled, all page-sourced output is wrapped in structural markers so LLMs can distinguish tool output from untrusted page content:

--- AGENT_BROWSER_PAGE_CONTENT nonce=a1b2c3d4 origin=https://example.com ---
[snapshot / text / html / eval output here]
--- END_AGENT_BROWSER_PAGE_CONTENT nonce=a1b2c3d4 ---

The nonce is a random value generated per CLI process invocation, making it unpredictable to page content that might attempt to spoof the boundary.

Enable via flag or environment variable:

agent-browser --content-boundaries snapshot
# or
export AGENT_BROWSER_CONTENT_BOUNDARIES=1

Affected output types: snapshot, get text, get html, eval, console.

In --json mode, boundary metadata is injected into the JSON response as a _boundary object containing nonce and origin fields, allowing orchestrators to verify provenance programmatically:

{
  "success": true,
  "data": { "snapshot": "...", "origin": "https://example.com" },
  "_boundary": { "nonce": "a1b2c3d4e5f6...", "origin": "https://example.com" }
}

Domain Allowlist

Restrict which domains the browser can interact with, preventing redirect-based attacks and data exfiltration:

agent-browser --allowed-domains "example.com,*.example.com,github.com" open https://example.com
# or
export AGENT_BROWSER_ALLOWED_DOMAINS="example.com,*.example.com"

Supports exact match (github.com) and wildcard prefix (*.example.com, which also matches the bare domain example.com). Both page navigations and sub-resource requests (scripts, images, fetch, XHR, etc.) to non-allowed domains are blocked, preventing data exfiltration. WebSocket and EventSource connections are also blocked via constructor-level patching. Non-http(s) sub-resources (data URIs, blobs) are still allowed. When a request is blocked, the command returns an error.

Note: The WebSocket/EventSource blocking is best-effort -- it works by overriding the browser constructors via an init script. If the eval action category is allowed, page scripts could theoretically restore the original constructors. For maximum protection, deny the eval category via --action-policy when using --allowed-domains.

Config file:

{
  "allowedDomains": ["example.com", "*.example.com", "github.com"]
}

CDN and third-party resources: The domain filter blocks all sub-resource requests (scripts, stylesheets, images, fonts, fetch/XHR) to non-allowed domains. Most websites load assets from CDN domains. Include these in your allowlist or pages will break. For example:

--allowed-domains "myapp.com,*.myapp.com,cdn.jsdelivr.net,fonts.googleapis.com,fonts.gstatic.com"

Action Policy

Gate actions using a static policy file. The policy is enforced by the daemon -- denied actions fail immediately.

agent-browser --action-policy ./policy.json open https://example.com
# or
export AGENT_BROWSER_ACTION_POLICY=./policy.json

Example policy (permissive with specific denials):

{
  "default": "allow",
  "deny": ["eval", "download", "upload"]
}

Example policy (restrictive):

{
  "default": "deny",
  "allow": ["navigate", "snapshot", "click", "scroll", "wait", "get"]
}
CategoryActions
navigateopen, back, forward, reload, tab new
clickclick, dblclick, tap
fillfill, type, keyboard type/inserttext, select, check, uncheck
evaleval, evalhandle, addscript, addinitscript, addstyle, expose, setcontent
downloaddownload, waitfordownload
uploadupload
snapshotsnapshot, screenshot, pdf, diff
scrollscroll, scrollintoview
waitwait, waitforurl, waitforloadstate, waitforfunction
getget text/html/url/title, count, isvisible, getbyrole, getbytext, getbylabel, etc.
interacthover, focus, drag, press, keydown, keyup, mousemove, dispatch
networknetwork route/unroute, requests
statestate save/load, cookies set, storage set

Auth vault operations (auth save, auth login, auth list, auth show, auth delete) and other internal/meta operations bypass action policy enforcement since they are trusted local operations. Domain allowlist restrictions still apply to auth login navigations.

Action Confirmation

For actions that require explicit approval, use --confirm-actions to specify categories that require confirmation:

# Orchestrator mode: returns confirmation_required response
agent-browser --confirm-actions eval,download eval "document.title"

# Then approve or deny:
agent-browser confirm c_8f3a1234
agent-browser deny c_8f3a1234

For interactive (human-in-the-loop) confirmation:

agent-browser --confirm-actions eval,download --confirm-interactive eval "document.title"
# Prompts: Allow? [y/N]

Pending confirmations auto-deny after 60 seconds.

Non-TTY behavior: When --confirm-interactive is set but stdin is not a TTY (e.g., piped input or running inside an automated pipeline), actions are automatically denied. This prevents accidental approval in non-interactive contexts.

Output Length Limits

Prevent context flooding by truncating large page outputs:

agent-browser --max-output 50000 get text body
# or
export AGENT_BROWSER_MAX_OUTPUT=50000

Affected output types: snapshot, get text, get html, eval, console.

Environment Variables

VariableDescription
AGENT_BROWSER_CONTENT_BOUNDARIESWrap page output in boundary markers
AGENT_BROWSER_MAX_OUTPUTMax characters for page output
AGENT_BROWSER_ALLOWED_DOMAINSComma-separated allowed domain patterns
AGENT_BROWSER_ACTION_POLICYPath to action policy JSON file
AGENT_BROWSER_CONFIRM_ACTIONSComma-separated action categories requiring confirmation
AGENT_BROWSER_CONFIRM_INTERACTIVEEnable interactive confirmation prompts
AGENT_BROWSER_ENCRYPTION_KEY64-char hex key for AES-256-GCM encryption (auth vault + sessions)

For production AI agent deployments:

{
  "contentBoundaries": true,
  "maxOutput": 50000,
  "allowedDomains": ["your-app.com", "*.your-app.com"],
  "actionPolicy": "./policy.json"
}