Est.
FeaturesLong read

Sandbox Escapes and SSRF Risks in Headless Browser JavaScript

Headless browsers expose internal systems through SSRF gaps and JavaScript sandbox escapes.

· 8 min read
Cover illustration for “Sandbox Escapes and SSRF Risks in Headless Browser JavaScript”
Features · November 2, 2026 · 8 min read · 1,862 words

Headless browser JavaScript creates two related security problems, server-side request forgery and escapes from boundaries meant to keep untrusted code inside. Both arise from gaps between what an application checks and what the browser or runtime can actually do. Understanding those gaps shifts the goal from fixing bugs one at a time to limiting what the code can do when a check fails.

Why headless browsers are structurally predisposed to SSRF

A headless browser is a full Chromium instance with no built-in checks to reject dangerous destinations. By default, it can fetch resources the host can reach, including file://, 127.0.0.1, 169.254.169.254, and RFC1918 addresses. Removing the visible interface does not narrow that reach. A browser operating on a server therefore has access to destinations that would be internal from an outside user's perspective.

Puppeteer, the JavaScript Chrome controller, has no built-in SSRF filter. The application above it must supply the restrictions. What happens, then, when a service accepts user-supplied HTML or URLs and passes them directly to the browser? Unless it intercepts and filters browser requests, it is an SSRF relay by default. The attacker supplies the destination, while the server supplies the network access.

This is an architectural starting point. The browser is built to fetch resources, but the service that lets others use it may need a much narrower trust boundary. Those two scopes do not align automatically. Before any particular vulnerability enters the picture, the application already has to distinguish between content the browser can load and destinations the requester should be allowed to reach.

How redirect-bypass turns application-layer defenses into false assurances

Checking a URL before passing it to the browser appears to establish that distinction. But the check covers only the destination presented at that moment. Playwright and Chromium automatically follow HTTP redirects after validation has run, so a public URL can become the first step toward an internal resource. The browser determines the final destination.

The Open WebUI advisory for CVE-2026-54018 documents this gap in versions prior to 0.9.6. SafePlaywrightURLLoader applied validate_url only to the initial URL, allowing a safe-looking address to redirect the browser to localhost, a Docker container network, or a cloud metadata endpoint. The bypass worked even with ENABLE_RAG_LOCAL_WEB_FETCH set to False. Open WebUI 0.9.6 fixed the vulnerability. The configuration flag alone had not enforced the restriction developers could reasonably expect from it.

PraisonAI before 1.6.78 exposed a related but distinct problem in CVE-2026-61429. With crawl4ai installed, headless Chromium through Playwright re-resolved DNS at fetch time, defeating pre-flight IP checks. PlaywrightCapture's CVE-2026-44439 demonstrated another path, in which a JavaScript redirect inside the tab reached cloud metadata and the captured document contained temporary AWS credentials for the EC2 instance's IAM role. PlaywrightCapture 1.39.6 introduced the only_global_lookup default to address the issue. Although that advisory is for one product, the same pattern applies to capture and HTML-to-PDF services that lack browser-level interception.

The separate but parallel failure mode: JavaScript sandbox libraries that escape to the host

JavaScript sandbox libraries address a different boundary, separating untrusted code from the host runtime. They may replace dangerous constructors and intercept property access, but JavaScript exposes capabilities through prototype chains and several kinds of functions. The interception must cover every relevant path. As with URL validation, a check can be correct about the surface it examines while missing the route execution actually takes.

The GitHub advisory for CVE-2026-23830 describes that problem in @nyariv/SandboxJS before 0.8.26. The library sandboxed Function but omitted AsyncFunction, GeneratorFunction, and AsyncGeneratorFunction. An attacker could obtain the host's native AsyncFunction through (async () => {}).constructor and execute arbitrary code outside the sandbox. The vulnerability needed no credentials or user interaction and had a score of CVSS 10.0. A separate flaw, CVE-2026-43898, exposed Function.caller through sandbox-defined functions, leaking an internal callback that enabled unrestricted host control. That CVSS 10.0 issue affected versions through 0.9.5 and was patched in 0.9.6.

Four additional early 2026 CVEs show why constructor coverage alone was not enough. One vulnerability used objects that coerced to one string key during validation and another during execution, while another vulnerability overwrote Map.prototype.has to make malicious objects look safe. CVE-2026-25586 used hasOwnProperty shadowing to bypass checks and pollute Object.prototype. CVE-2026-25520 chained Object.values() or Object.entries() with Array.prototype.at() to unwrap host objects and reach the host's Function constructor. These are distinct routes through shared runtime infrastructure. Their common lesson concerns the limits of API-surface interception.

Chrome's own sandbox boundary has been breached from headless mode specifically

Correct URL filtering and patched JavaScript libraries do not remove the browser's own process-isolation surface. CVE-2026-10017 was an out-of-bounds read in Google Chrome's Headless rendering component before 148.0.7778.216. If the renderer process was already compromised, a crafted HTML page could trigger a sandbox escape and might let code run with elevated privileges. The vulnerability received CVSS 8.3 HIGH. Its precondition is important, but renderer vulnerabilities can be chained with escapes.

Headless deployments in containers and server-side services were within scope. Patching required redeploying containers or binaries with version 148.0.7778.216 or later. Disabling JavaScript did not protect against this vulnerability.

Chromium's separate OpenURLFromTab issue concerned sandbox restrictions during navigation. Headless Chrome failed to propagate sandbox flags when that path opened a new tab. An attacker in a sandboxed iframe with allow-popups could trigger it through a synthetic middle-click, bypassing the restrictions and headless popup blocker. These mechanisms work below or outside the application's URL checks. Consequently, no single application patch can stand in for isolation across all the layers involved.

AI agent pipelines multiply the blast radius of each architectural gap

AI agent pipelines combine unattended server-side browsers, attacker-influenced content, and credentials or session tokens for internal systems. The browser is not merely retrieving a document. It may be doing so inside a pipeline whose other capabilities make an internal destination especially valuable.

FlowiseAI's August/September 2026 advisory, GHSA-9cvr-5wv9-2gxr, illustrates how protection can miss an entire execution path. Its Cheerio, Playwright, and Puppeteer document loaders instantiated LangChain loaders that made their own requests without checkDenyList() or secureFetch, bypassing protection in httpSecurity.ts. A March 2026 SSRF fix had excluded the browser-based loaders. In executeautomation's mcp-playwright, playwright_navigate likewise opened URLs without checking scheme or destination, including loopback, private-network, and file:// resources. Tools such as playwright_get_visible_text could read the resulting content. A fix PR opened in March 2026 remained unmerged as of October 14, 2026.

BlueRock Security analyzed thousands of MCP servers and found more than a third potentially vulnerable to SSRF. In a separate study, Trend Micro found hundreds that were open to the internet without authentication. Meanwhile, Salesforce Headless 360, announced April 15, 2026 at TDX, introduced more than 60 MCP tools and 30+ preconfigured coding skills giving agents complete, live access to the Salesforce platform's data, workflows, and business logic. Its supported agents included Claude Code, Cursor, Codex, and Windsurf. That expansion shows why governing MCP access is becoming a priority as enterprise platforms choose to let agents do more.

Prompt injection and agentic browsers extend the attack from internal networks to user accounts

An authenticated browser agent introduces another form of authority, the user's existing sessions. An attacker who shapes rendered content can try to redirect the agent's actions as well as its network requests. Which instructions belong to the user, and which arrived inside a page? That distinction becomes a security boundary when the agent can act across authenticated services.

Zenity Labs' “Grand Theft Atlas” showed a newsletter-signup request being redirected by attacker-written page instructions to WhatsApp Web. The victim was already signed in, and the ChatGPT Atlas browser agent read the contact list and sent messages from the victim's account. Zenity reported the issue to OpenAI on January 11, 2026, and OpenAI acknowledged it on February 17, 2026. OpenAI described “meaningful risks associated with prompt injection in agentic environments” but issued no patch or CVE, characterizing the issue as a design property.

Zenity's “PleaseFix,” released August 5, 2026, expanded the research to Claude in Chrome, Gemini in Chrome, Comet, Atlas, and Copilot in Edge. Intent Collision caused agents to conflate attacker instructions with user intent. HistoryFixing added persistence by planting malicious information in browser history that an agent could later treat as trusted context. Spotting the injection when it happened would not, on its own, show whether later context could still be trusted.

PleaseFix also reached localhost services through Comet, Gemini in Chrome, and Edge, then abused Ollama, Open WebUI, Jupyter, and pgAdmin to execute commands or manipulate local systems. Antiy CERT separately identified a large number of malicious skills on ClawHub, the OpenClaw framework's marketplace. Channels that look legitimate can thus bring supply-chain attacks into pipelines that already take in hostile content.

Whether this is fixable by design or only manageable by defense-in-depth

The strongest objection is that these are engineering defects with engineering remedies. The fixed releases support that argument. Better input validation, browser-level request interception, and improved sandboxing can close specific paths, as the PlaywrightCapture, Open WebUI, and SandboxJS patches demonstrate.

But does closing a path eliminate the class? Zenity treats prompt injection in agentic browsers as a design property, while OpenAI said in late 2025 that such vulnerabilities were “unlikely to ever” be fully solved, although the overall danger could be reduced. This distinguishes a local repair from a guarantee about the whole system. Browsers can still fetch a wide range of resources, JavaScript still has shared prototype infrastructure, and process boundaries are still attack surfaces. Network-level defense is therefore an architectural complement to application fixes.

Defensive controls that address the structural surface rather than individual CVEs

The practical response is to enforce restrictions at each boundary where authority can expand. At the browser level, explicitly block file://, data:, and javascript: schemes in addition to application validation. Disable automatic redirect-following where the use case permits, or revalidate the destination after every hop. Treat JavaScript sandboxes as untrusted by default. Run their code in a separate process or worker with explicit capability grants instead of depending solely on in-process interception of the prototype chain.

Isolate headless browser processes so they cannot reach RFC1918 addresses, loopback, or link-local ranges, and make 169.254.169.254 unreachable from hosts running the service. Use deny-by-default outbound access to allow only the public internet destinations needed for the use case. Reachability should not depend on the browser's judgment. This gives application-layer validation a separate enforcement boundary rather than another check along the same execution path.

At the agent layer, treat every MCP tool that can navigate a browser or make HTTP requests as a possible SSRF vector. Require explicit URL allowlisting in its configuration rather than deny-listing, and apply Zero Trust principles by requiring identity verification before tool invocation. Unauthenticated MCP servers should not be internet-accessible. Browser history also needs a trust boundary, so agents should require explicit user confirmation before treating it as a source of instructions.

Finally, run headless browsers in containers or VMs with no shared memory access to GPU processes where possible, limiting the impact of CVE-2026-2441-style compositor escapes. Keep patching the individual vulnerabilities as well. These controls serve different purposes, restricting destinations, separating code, limiting tool access, and containing process escapes. Their value lies in making a failure at one layer insufficient to grant all the authority available at the next.

Sources

  1. Sandbox escape in Headless Chrome via OpenURLFromTab dropping the opener [523639090] - Chromium

More in Features