Skip to content

Security: harden against zero-click agent hijacking (PleaseFix pattern) #4

Description

@Vitexus

Summary

Following Dark Reading's coverage of the "PleaseFix" zero-click agent-hijacking vulnerability class in agentic browsers (Zenity Labs disclosure, 2026-03-03), I did a code-level risk assessment of this server's tool surface against that threat model.

Full report: https://claude.ai/code/artifact/92beb8ed-9b37-4713-b27b-729512b0b94e

The core pattern: an agent pulls untrusted content (in our case, rendered web pages) into its context without reliably separating "data" from "instruction." An attacker who plants adversarial text in a page can hijack the agent's tool access with zero clicks, using whatever tools the agent already has standing access to. This server is exactly that kind of component — it's the tool layer an AI assistant uses to drive a real Firefox session.

No exploitation was performed; this is static analysis against server.py as of the current HEAD.

Findings

High severity

  1. Unsanitized content relaybrowser_get_source (server.py:1182), browser_get_text (:1187), browser_get_attribute (:1202), devtools_console (:793), devtools_js_errors (:768), browser_get_storage (:1601) all return page-controlled strings verbatim into the agent's context — no truncation, no HTML/script stripping, no "untrusted" framing. This is the direct injection channel the PleaseFix researchers describe.

  2. Unrestricted JS executionbrowser_execute_js (server.py:1307-1320) runs agent-supplied JS via driver.execute_script() with no allowlist or sandbox. A hijacked agent can run fetch()/XMLHttpRequest to exfiltrate data to an attacker origin, limited only by the target page's own CORS/CSP.

  3. No URL/scheme/host gating_normalise_url() (server.py:125-130) explicitly passes file:, data:, and about: URLs through unmodified rather than blocking them, and there's no host blocklist. browser_navigate/browser_open will go anywhere requested — including file://, localhost, RFC1918 addresses, or the 169.254.169.254 cloud metadata endpoint.

  4. Cookie/storage exfiltration chainbrowser_get_cookies (server.py:1565) + browser_get_storage (:1601) read secrets (including HttpOnly cookies, readable via the WebDriver API) with no redaction. browser_execute_js or browser_navigate can immediately ship those values cross-origin. Nothing in the server links, warns on, or rate-limits this sequence.

Medium severity

  1. No confirmation or audit layerreadOnlyHint/openWorldHint tool annotations are advisory metadata consumed only by the MCP client's approval UI. server.py itself has no elicitation/confirmation call, no rate limiting, and no action log.

Informational

  • The Firefox-profile lockdown (AGENTS.md:54, tests/test_server.py::TestBrowserOpenHasNoProfileParams) is a real, deliberately tested mitigation against this exact threat class — browser_open has no per-call profile override so a page can't get the agent to open the user's real logged-in profile. It just doesn't cover the four High findings above.
  • Minor doc/test drift, not a security issue: 43 tools documented vs. 42 asserted in tests/test_server.py:214-233 (devtools_performance missing from the expected set).

Suggested order of work

  1. Envelope page-derived tool output as untrusted content (cheapest, highest-leverage — addresses the root injection channel).
  2. Redact cookie/storage values by default, behind an explicit opt-in to reveal raw values.
  3. Add an opt-in scheme/host allowlist for browser_navigate/browser_open (block file://, loopback, RFC1918, link-local/metadata by default).
  4. Document the execute_js + navigate exfiltration chain in README/AGENTS.md, next to the existing profile-lockdown note.
  5. (Larger) Optional server-side confirmation hook (FastMCP elicitation) for the highest-risk tools.

References


🤖 Generated with Claude Code

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestinvalidThis doesn't seem right

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions