Security Advisory — Sensitive Data Handling Analysis — browser-use/browser-use Reviewed: browser-use @ main  ·  June 2026  ·  Scope: DOM extraction, LLM I/O, logging

Security Advisory

Does browser-use protect sensitive data from your LLM?

Critical findings — no automatic redaction. Browser-use does not automatically scrub, redact, or filter passwords, credit card numbers, SSNs, or any other sensitive data from the browser state it sends to LLM APIs.

Verdict
No automatic redaction
Critical findings
3
High-severity findings
3
Total findings
7
SECTION A

Executive Summary

Browser-use does not automatically scrub, redact, or filter passwords, credit card numbers, SSNs, or any other sensitive data from the browser state it sends to LLM APIs. The library provides an opt-in sensitive_data parameter that can mask values you explicitly list, but it performs no structural detection of sensitive fields — it does not examine input type="password", apply Luhn-check filtering for card numbers, or pattern-match SSNs. The DOM representation includes the value attribute in its default attribute list with no type-based exclusions.

Several GitHub issues confirm real-world credential leakage in logs and LLM context. A peer-reviewed privacy study found the library disables Chromium password manager encryption on Linux, amplifying the risk surface.

SECTION B

Evidence — Findings in Order of Severity

01 — No automatic password field detection or redaction Critical
Finding The DOM extraction layer includes 'value' in its DEFAULT_INCLUDE_ATTRIBUTES list — the set of HTML attributes serialized into the LLM context. There is no conditional logic that excludes value when the element's type="password". The get_meaningful_text_for_llm() method prioritizes value as its first attribute to read, before aria-label, placeholder, or any fallback — with no password-type awareness.
# browser_use/dom/views.py — extracted directly from source # DEFAULT_INCLUDE_ATTRIBUTES (44 items, no type-conditional exclusions) DEFAULT_INCLUDE_ATTRIBUTES = [..., 'type', 'value', 'name', 'placeholder', ...] # get_meaningful_text_for_llm() — value is read FIRST, no type=password check def get_meaningful_text_for_llm(self): # priority: value → aria-label → title → placeholder → alt → text # ↑ No branch: if self.type == 'password': skip value ...

This means if a user fills a login form before the agent act, or if any form field has a pre-populated password value, that value travels to the LLM verbatim. Note: browsers typically do not expose password field .value via DOM inspection for security reasons, but the risk is real for any non-masked sensitive input.

02 — sensitive_data is opt-in only — nothing is scrubbed by default Critical
Finding Browser-use's only protection mechanism is the sensitive_data parameter, which must be explicitly passed at agent construction. It works by replacing values you pre-specify with <secret>KEY</secret> placeholders — the real values are injected into form fields after the LLM call. Without this parameter, all browser state is sent to the LLM unfiltered.
# browser_use/utils.py — the complete redaction function def redact_sensitive_string(value: str, sensitive_values: dict[str, str]) -> str: """Replace sensitive values with placeholders, longest matches first.""" for key, secret in sorted(sensitive_values.items(), key=lambda item: len(item[1]), reverse=True): value = value.replace(secret, f'<secret>{key}</secret>') return value # No automatic detection: no Luhn check for card numbers, no SSN regex, # no IBAN pattern, no input[type=password] introspection.
Critical limitation

If you don't know in advance what sensitive value the page will contain — a dynamically generated card number, a fetched account balance, an OTP — you cannot pre-register it. The library has no mechanism to detect and redact unknown sensitive patterns at runtime.

03 — Screenshots bypass text-layer filtering — vision mode exposes everything Critical
Finding Screenshots are always captured, regardless of the use_vision setting. When use_vision=True (the default in many configurations), the screenshot is sent directly to the LLM as an image — after which all text-layer filtering is meaningless. A password field that is visually unmasked on screen, a credit card form with pre-filled data, or any other visible sensitive content will be readable by the model from the image.
# browser_use/agent/service.py browser_state_summary = await self.browser_session.get_browser_state_summary( include_screenshot=True, # always captured ... ) # Screenshot is sent to LLM only when vision is enabled — but # use_vision=True is common. Docs say to disable it for sensitive pages.
Note

The documentation recommends disabling vision with use_vision=False to prevent screenshot-based exposure. This is not the default and must be manually set. It also recommends storage_state='./auth.json' for login cookies instead of passing passwords at all.

04 — Confirmed credential leak in controller logs (Issue #713, fixed) High
Finding Even when sensitive_data was configured and agent logs were redacted correctly, the controller logs in browser_use/controller/service.py exposed credentials in plaintext — visible in output as ⌨️ Input <actual_password> into index 3. This was reported in Issue #713 and patched in PR #724.
Detail A parallel bug was reported in Issue #1907 (version 0.2.4): the sensitive_data dictionary keys were being passed to login forms instead of the values, so the field name itself was entered as the credential. Patched in PR #1909. These are not isolated — they indicate a pattern of the sensitive data path being an afterthought rather than a core guarantee.
05 — Library disables Chromium password encryption on Linux High
Finding A 2024 academic study of browser agent privacy practices (arXiv:2512.07725) found that browser-use disables encryption for passwords stored in Chromium's password manager on Linux. This is a secondary risk: if the browser profile used by the agent contains saved credentials, those are stored unencrypted on disk, amplifying the potential damage from a prompt injection attack that exfiltrates them.
06 — No credit card, SSN, or IBAN pattern detection — anywhere in the codebase High
Finding There is no Luhn-algorithm check, no 16-digit number pattern filter, no SSN regex (XXX-XX-XXXX), and no IBAN or account number detection in the browser-use codebase. If an agent visits a page displaying a credit card number, account balance, or tax identifier — in any DOM element that is serialized — that data passes to the LLM in full. The redact_sensitive_string function only replaces strings you have pre-registered; it does not pattern-match unknown PII.
Warning

No scrubbing of dynamically presented PII exists. If a banking portal shows account numbers inline in the DOM, and the agent is navigating it, those numbers enter the LLM context. The library provides no guardrail against this.

07 — Off-device LLMs receive full browser state; prompt injection can trigger exfiltration Medium
Finding The academic privacy study confirmed that when using cloud LLM APIs (OpenAI, Anthropic, etc.), browser state including webpage content is transmitted to external servers, with users having little to no control over how that data is processed and shared. The library also fails to block navigation to self-signed or revoked TLS certificate sites — meaning an agent visiting a MitM-intercepted site could submit credentials through a spoofed form.
Detail Prompt injection via malicious page content is a documented attack vector: injected instructions in page DOM can direct the agent to exfiltrate data found elsewhere in the browser state. With no structural restriction on what DOM content reaches the LLM, there is no line of defense between what a page says and what the agent does with secrets already in its context.

"little to no control over how that data is processed and shared."

— arXiv 2512.07725, Privacy Practices of Browser Agents (off-device data sharing)
SECTION C

Protection Coverage at a Glance

Data TypeAuto-DetectedOpt-In ProtectionCoverage Note
Password field (input type=password)▲ PartialMust pre-register; screenshots still expose value if vision on
Credit card numbersNo Luhn check or 16-digit pattern filtering
SSN / Tax IDsNo regex pattern matching for XXX-XX-XXXX or EIN
Bank account numbersNo IBAN, ABA, or account number detection
Pre-registered credentials (username/password)✓ Opt-inWorks via sensitive_data dict; bypassed by screenshots
API keys / tokens (pre-registered)✓ Opt-inSame sensitive_data mechanism applies
Dynamically-rendered PII on pageNo mechanism exists for unknown sensitive content
SECTION D

Verdict

No automatic filtering of sensitive data

Browser-use provides no automatic filtering of sensitive data. Protection exists only for values you explicitly pre-register via sensitive_data=, and even that is defeated by vision mode, historically leaked through controller logs, and has shipped with implementation bugs. No pattern-based scrubbing of credit cards, SSNs, or account numbers exists anywhere in the codebase.

SECTION E

Mitigations if you must use it with sensitive data

1 — Disable vision unconditionally

  • Set use_vision=False for any task that touches authenticated pages or forms. Vision bypasses all text-layer filtering.

2 — Never pass raw credentials in the task prompt

  • Use sensitive_data= with allowed_domains= locked down. Omitting allowed_domains generates a warning; a prompt injection on any visited page can exfiltrate registered secrets.

3 — Prefer cookie-based auth

  • Use storage_state='./auth.json' to authenticate via session cookies so credentials never need to cross the LLM boundary.

4 — Add an LLM gateway layer

  • Place a PII-scrubbing proxy (e.g., a regex + NER gateway) between browser-use and the model API to catch card numbers, SSNs, and account identifiers the library will not.

5 — Do not use with financial, medical, or legal data

  • Without independent security review. The library has no design-time guarantees about PII isolation, and dynamically rendered sensitive content on any page in scope will reach the LLM.

Sources

[S1] browser_use/utils.py — redact_sensitive_string, collect_sensitive_data_values [S2] browser_use/dom/views.py — DEFAULT_INCLUDE_ATTRIBUTES, get_meaningful_text_for_llm [S3] browser_use/agent/service.py — always-screenshot, sensitive_data initialization [S4] browser_use/agent/views.py — AgentHistory sensitive_data filtering [S5] docs.browser-use.com — Sensitive Data documentation [S6] docs.browser-use.com — Secure Setup documentation [S7] GitHub Issue #713 — Sensitive data leaking in controller logs [S8] GitHub Issue #1062 — Sensitive data not working with OpenAI [S9] GitHub Issue #1907 — Sensitive data uses key instead of value (v0.2.4) [S10] arXiv:2512.07725 — Privacy Practices of Browser Agents (peer-reviewed, 2024)