{"id":"GHSA-7hgr-7h44-33w2","aliases":[],"url":"https://o3.security/vulnerability/GHSA-7hgr-7h44-33w2","summary":"CamoFox MCP: Unauthenticated HTTP MCP browser-control surface","details":"# Unauthenticated HTTP MCP browser-control surface in `camofox-mcp`\n\n## Summary\n\n`camofox-mcp` exposed a Streamable HTTP MCP endpoint at `/mcp` with rate limiting but no inbound MCP-layer authentication. When HTTP mode was enabled, any client that could reach `/mcp` could list and invoke browser-control tools.\n\nIf `CAMOFOX_API_KEY` was configured, the server then forwarded that server-side key to the underlying `camofox-browser` backend. That means an unauthenticated MCP caller could exercise the server's browser authority without knowing the backend browser API key.\n\nReviewed vulnerable commit: `10e3ac08cb50d830eb4ee00a789229f02f28a1a4`\nFixed commit observed on main: `599f56ee40f8062aeca541c251ed1d39fb437f50`\nFixed release observed: `v1.13.2`\nSuggested severity: High, with the caveat that default loopback-only deployments reduce practical exposure.\n\n## Root cause\n\nIn the reviewed commit, `src/http.ts` creates the Express MCP app and applies only a rate limiter to `/mcp`:\n\n```ts\nconst app = createMcpExpressApp({ host: config.httpHost });\n\nconst limiter = rateLimit({\n  windowMs: 60_000,\n  limit: config.httpRateLimit,\n  standardHeaders: true,\n  legacyHeaders: false\n});\n\napp.use(\"/mcp\", limiter);\n```\n\nThe `POST /mcp` handler then creates a server and `StreamableHTTPServerTransport` and passes the request body into the MCP transport without checking `Authorization`, an inbound API key, allowed hosts, or public-bind safety:\n\n```ts\napp.post(\"/mcp\", async (req: any, res: any) => {\n  try {\n    const { server } = createServer(config);\n    const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });\n\n    await server.connect(transport);\n    await transport.handleRequest(req, res, req.body);\n```\n\n`src/config.ts` made HTTP mode configurable and defaulted the HTTP host to loopback, but it did not require an inbound HTTP client secret:\n\n```ts\ntransport: cli.transport ?? envTransport ?? \"stdio\",\nhttpPort: cli.httpPort ?? (Number.isNaN(httpPortFromEnv) ? 3000 : httpPortFromEnv),\nhttpHost: cli.httpHost ?? env.CAMOFOX_HTTP_HOST ?? \"127.0.0.1\",\n```\n\nSeparately, `src/client.ts` forwarded `CAMOFOX_API_KEY` server-side to the browser backend:\n\n```ts\nif (this.apiKey) {\n  headers.set(\"x-api-key\", this.apiKey);\n  headers.set(\"authorization\", `Bearer ${this.apiKey}`);\n}\n```\n\nSo `CAMOFOX_API_KEY` protected the MCP server's outbound requests to the backend browser service, but did not authenticate inbound HTTP MCP clients.\n\n## Auth boundary\n\nThe vulnerable boundary was the HTTP MCP endpoint. The client did not need to provide `Authorization` or any `CAMOFOX_API_KEY` value to call MCP tools.\n\nThe default bind was `127.0.0.1`, which lowers severity for default local-only deployments. The risky cases are documented HTTP/remote-client deployments, Docker/port-forwarded deployments, or any environment where a browser page, local network client, reverse proxy, or another user can reach the `/mcp` endpoint.\n\n## Proof of concept\n\nI used a fake `camofox-browser` backend so no real browser was launched and no external navigation occurred. The harness starts the reviewed `dist/http.js` server with `CAMOFOX_API_KEY=server-side-secret`, connects an MCP SDK client to `/mcp` with no auth headers, lists tools, then calls `create_tab` and `navigate`.\n\nObserved output:\n\n```json\n{\n  \"authUsedByClient\": false,\n  \"listedToolCount\": 46,\n  \"backendRequests\": [\n    {\n      \"method\": \"POST\",\n      \"url\": \"/tabs\",\n      \"headers\": {\n        \"authorization\": \"Bearer server-side-secret\",\n        \"x-api-key\": \"server-side-secret\"\n      }\n    },\n    {\n      \"method\": \"POST\",\n      \"url\": \"/tabs/fake-tab-1/navigate\",\n      \"headers\": {\n        \"authorization\": \"Bearer server-side-secret\",\n        \"x-api-key\": \"server-side-secret\"\n      }\n    }\n  ],\n  \"observedUnauthenticatedBrowserControl\": true,\n  \"serverSideSecretForwardedToBackend\": true\n}\n```\n\nThis demonstrates both parts of the issue:\n\n1. The MCP client used no inbound authentication.\n2. The server still used its configured backend browser secret when forwarding the tool calls.\n\n## Impact\n\nAn unauthenticated client that can reach the HTTP MCP endpoint can exercise browser-control tools as the MCP server. Depending on the user's active browser profiles and configured backend, that can allow page navigation, tab creation, interaction with authenticated browser contexts, screenshot/content observation, and other browser-automation actions exposed by the MCP tool surface.\n\nThe impact is strongest when HTTP mode is intentionally exposed for remote MCP clients or through Docker/reverse-proxy deployment and the operator assumes `CAMOFOX_API_KEY` protects the whole control plane.\n\n## Fix notes\n\nThe public issue indicates this has been fixed in `599f56e` and released as `v1.13.2` by adding dedicated inbound `CAMOFOX_HTTP_API_KEY` Bearer auth, public-bind startup validation, auth before `/mcp` JSON parsing, loopback Host-header protection, and optional allowed-hosts handling. Those are the right mitigation directions.","published":"2026-05-19T20:13:35Z","modified":"2026-05-19T20:15:17.697531929Z","cvss":null,"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"camofox-mcp","fixedVersion":"1.13.2"}],"fix":{"url":"https://github.com/redf0x1/camofox-mcp/commit/599f56ee40f8062aeca541c251ed1d39fb437f50","label":"redf0x1/camofox-mcp@599f56e"},"references":[{"type":"WEB","url":"https://github.com/redf0x1/camofox-mcp/security/advisories/GHSA-7hgr-7h44-33w2"},{"type":"WEB","url":"https://github.com/redf0x1/camofox-mcp/commit/599f56ee40f8062aeca541c251ed1d39fb437f50"},{"type":"PACKAGE","url":"https://github.com/redf0x1/camofox-mcp"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-05-19T20:15:17.697531929Z"}}