Critical severity9.8NVD Advisory· Published May 6, 2026· Updated May 7, 2026
CVE-2026-43575
CVE-2026-43575
Description
OpenClaw versions 2026.2.21 before 2026.4.10 contain an authentication bypass vulnerability in the sandbox noVNC helper route that exposes interactive browser session credentials. Attackers can access the noVNC helper route without bridge authentication to gain unauthorized access to the interactive browser session.
Affected products
2Patches
18dfbf3268bd2fix(browser): gate sandbox noVNC helper auth
9 files changed · +41 −19
CHANGELOG.md+1 −0 modified@@ -121,6 +121,7 @@ Docs: https://docs.openclaw.ai - Cron/isolated agent: run scheduled agent turns as non-owner senders so owner-only tools stay unavailable during cron execution. (#63878) - Voice Call/realtime: reject oversized realtime WebSocket frames before bridge setup so large pre-start payloads cannot crash the gateway. (#63890) Thanks @mmaps. +- Browser/sandbox: gate `/sandbox/novnc` behind bridge auth and stop surfacing sandbox observer URLs in model-visible prompt context. (#63882) Thanks @eleqtrizit. - Discord/sandbox: include `image` in sandbox media param normalization so Discord event cover images cannot bypass sandbox path rewriting. (#64377) Thanks @mmaps. ## 2026.4.9
extensions/browser/src/browser/bridge-server.auth.test.ts+10 −1 modified@@ -83,10 +83,12 @@ describe("startBrowserBridgeServer auth", () => { }); it("serves noVNC bootstrap html without leaking password in Location header", async () => { + let resolveCalls = 0; const bridge = await startBrowserBridgeServer({ resolved: buildResolvedConfig(), authToken: "secret-token", resolveSandboxNoVncToken: (token) => { + resolveCalls += 1; if (token !== "valid-token") { return null; } @@ -95,8 +97,15 @@ describe("startBrowserBridgeServer auth", () => { }); servers.push({ stop: () => stopBrowserBridgeServer(bridge.server) }); - const res = await fetch(`${bridge.baseUrl}/sandbox/novnc?token=valid-token`); + const unauth = await fetch(`${bridge.baseUrl}/sandbox/novnc?token=valid-token`); + expect(unauth.status).toBe(401); + expect(resolveCalls).toBe(0); + + const res = await fetch(`${bridge.baseUrl}/sandbox/novnc?token=valid-token`, { + headers: { Authorization: "Bearer secret-token" }, + }); expect(res.status).toBe(200); + expect(resolveCalls).toBe(1); expect(res.headers.get("location")).toBeNull(); expect(res.headers.get("cache-control")).toContain("no-store"); expect(res.headers.get("referrer-policy")).toBe("no-referrer");
extensions/browser/src/browser/bridge-server.ts+12 −7 modified@@ -13,6 +13,7 @@ import { type ProfileContext, } from "./server-context.js"; import { + hasVerifiedBrowserAuth, installBrowserAuthMiddleware, installBrowserCommonMiddleware, } from "./server-middleware.js"; @@ -76,8 +77,19 @@ export async function startBrowserBridgeServer(params: { const app = express(); installBrowserCommonMiddleware(app); + const authToken = normalizeOptionalString(params.authToken); + const authPassword = normalizeOptionalString(params.authPassword); + if (!authToken && !authPassword) { + throw new Error("bridge server requires auth (authToken/authPassword missing)"); + } + installBrowserAuthMiddleware(app, { token: authToken, password: authPassword }); + if (params.resolveSandboxNoVncToken) { app.get("/sandbox/novnc", (req, res) => { + if (!hasVerifiedBrowserAuth(req)) { + res.status(401).send("Unauthorized"); + return; + } res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); res.setHeader("Pragma", "no-cache"); res.setHeader("Expires", "0"); @@ -96,13 +108,6 @@ export async function startBrowserBridgeServer(params: { }); } - const authToken = normalizeOptionalString(params.authToken); - const authPassword = normalizeOptionalString(params.authPassword); - if (!authToken && !authPassword) { - throw new Error("bridge server requires auth (authToken/authPassword missing)"); - } - installBrowserAuthMiddleware(app, { token: authToken, password: authPassword }); - const state: BrowserServerState = { server: null as unknown as Server, port,
extensions/browser/src/browser/server-middleware.ts+16 −1 modified@@ -1,8 +1,22 @@ -import type { Express } from "express"; +import type { Express, Request } from "express"; import express from "express"; import { browserMutationGuardMiddleware } from "./csrf.js"; import { isAuthorizedBrowserRequest } from "./http-auth.js"; +export const BROWSER_AUTH_VERIFIED_FLAG = "__openclawBrowserAuthVerified"; + +type BrowserAuthMarkedRequest = Request & { + [BROWSER_AUTH_VERIFIED_FLAG]?: boolean; +}; + +export function hasVerifiedBrowserAuth(req: Request): boolean { + return (req as BrowserAuthMarkedRequest)[BROWSER_AUTH_VERIFIED_FLAG] === true; +} + +function markVerifiedBrowserAuth(req: Request) { + (req as BrowserAuthMarkedRequest)[BROWSER_AUTH_VERIFIED_FLAG] = true; +} + export function installBrowserCommonMiddleware(app: Express) { app.use((req, res, next) => { const ctrl = new AbortController(); @@ -30,6 +44,7 @@ export function installBrowserAuthMiddleware( } app.use((req, res, next) => { if (isAuthorizedBrowserRequest(req, auth)) { + markVerifiedBrowserAuth(req); return next(); } res.status(401).send("Unauthorized");
src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts+0 −1 modified@@ -54,7 +54,6 @@ describe("buildEmbeddedSandboxInfo", () => { workspaceAccess: "none", agentWorkspaceMount: undefined, browserBridgeUrl: "http://localhost:9222", - browserNoVncUrl: "http://localhost:6080", hostBrowserAllowed: true, }); });
src/agents/pi-embedded-runner/sandbox-info.ts+0 −1 modified@@ -17,7 +17,6 @@ export function buildEmbeddedSandboxInfo( workspaceAccess: sandbox.workspaceAccess, agentWorkspaceMount: sandbox.workspaceAccess === "ro" ? "/agent" : undefined, browserBridgeUrl: sandbox.browser?.bridgeUrl, - browserNoVncUrl: sandbox.browser?.noVncUrl, hostBrowserAllowed: sandbox.browserAllowHostControl, ...(elevatedAllowed ? {
src/agents/pi-embedded-runner/types.ts+0 −1 modified@@ -99,7 +99,6 @@ export type EmbeddedSandboxInfo = { workspaceAccess?: "none" | "ro" | "rw"; agentWorkspaceMount?: string; browserBridgeUrl?: string; - browserNoVncUrl?: string; hostBrowserAllowed?: boolean; elevated?: { allowed: boolean;
src/agents/sanitize-for-prompt.test.ts+2 −4 modified@@ -32,7 +32,7 @@ describe("buildAgentSystemPrompt uses sanitized workspace/sandbox strings", () = expect(prompt).not.toContain("\u2028"); }); - it("sanitizes sandbox workspace/mount/url strings", () => { + it("sanitizes sandbox workspace and mount strings", () => { const prompt = buildAgentSystemPrompt({ workspaceDir: "/tmp/test", sandboxInfo: { @@ -41,16 +41,14 @@ describe("buildAgentSystemPrompt uses sanitized workspace/sandbox strings", () = workspaceDir: "/host\nspace", workspaceAccess: "rw", agentWorkspaceMount: "/mnt\u2028mount", - browserNoVncUrl: "http://example.test/\nui", }, }); expect(prompt).toContain("Sandbox container workdir: /workspace"); expect(prompt).toContain( "Sandbox host mount source (file tools bridge only; not valid inside sandbox exec): /hostspace", ); expect(prompt).toContain("(mounted at /mntmount)"); - expect(prompt).toContain("Sandbox browser observer (noVNC): http://example.test/ui"); - expect(prompt).not.toContain("\nui"); + expect(prompt).not.toContain("Sandbox browser observer (noVNC):"); }); });
src/agents/system-prompt.ts+0 −3 modified@@ -647,9 +647,6 @@ export function buildAgentSystemPrompt(params: { }` : "", params.sandboxInfo.browserBridgeUrl ? "Sandbox browser: enabled." : "", - params.sandboxInfo.browserNoVncUrl - ? `Sandbox browser observer (noVNC): ${sanitizeForPromptLiteral(params.sandboxInfo.browserNoVncUrl)}` - : "", params.sandboxInfo.hostBrowserAllowed === true ? "Host browser control: allowed." : params.sandboxInfo.hostBrowserAllowed === false
Vulnerability mechanics
AI mechanics synthesis has not run for this CVE yet.
References
3- github.com/openclaw/openclaw/commit/8dfbf3268bd224b7377d1ecca77a445100746085nvdPatch
- github.com/openclaw/openclaw/security/advisories/GHSA-92jp-89mq-4374nvdMitigationVendor Advisory
- www.vulncheck.com/advisories/openclaw-authentication-bypass-in-sandbox-novnc-helper-routenvdThird Party Advisory
News mentions
0No linked articles in our index yet.