Skip to content

fix: require API key authentication on /tabs/:tabId/evaluate endpoint#3087

Open
sebastiondev wants to merge 1 commit into
jo-inc:masterfrom
sebastiondev:fix/cwe94-server-javascript-bb80
Open

fix: require API key authentication on /tabs/:tabId/evaluate endpoint#3087
sebastiondev wants to merge 1 commit into
jo-inc:masterfrom
sebastiondev:fix/cwe94-server-javascript-bb80

Conversation

@sebastiondev
Copy link
Copy Markdown

Summary

The POST /tabs/:tabId/evaluate endpoint accepts a user-supplied expression string and passes it directly to Playwright's page.evaluate(), executing arbitrary JavaScript in the browser page context. Unlike the /sessions/:userId/cookies endpoint (which requires a Bearer token via CAMOFOX_API_KEY), the evaluate endpoint had no authentication, allowing any network-reachable attacker to run arbitrary JS in any open tab.

CWE-94 — Improper Control of Generation of Code ("Code Injection")
Severity: High — an unauthenticated attacker with network access can exfiltrate cookies, localStorage, session tokens, and DOM content from authenticated browser sessions, or perform arbitrary same-origin actions.

Data flow

  1. Attacker sends POST /tabs/:tabId/evaluate with { "expression": "document.cookie" } — no auth required
  2. server.js passes expression directly to page.evaluate(expression) (Playwright)
  3. Arbitrary JavaScript executes in the authenticated page context
  4. Result (cookies, tokens, DOM data) is returned in the HTTP response

What changed

server.js — Extracted a reusable requireApiKey middleware (identical logic to the existing cookie-import auth check) and applied it to the /tabs/:tabId/evaluate route. The middleware:

  • Requires a valid Bearer <CAMOFOX_API_KEY> header when the API key is configured
  • Uses timing-safe comparison to prevent token leakage via timing side-channels
  • Falls back to allowing loopback-only requests in non-production environments (matching the existing policy)
  • Returns 403 Forbidden otherwise

tests/unit/evaluate-auth.test.js — Three new integration tests verifying:

  1. Requests without a token are rejected (403)
  2. Requests with a wrong token are rejected (403)
  3. Requests with the correct token succeed (200)

Why this is exploitable

The README documents production deployment via Docker and Fly.io with secrets, meaning the server is internet-exposable. An attacker who can reach the server only needs a valid tabId (a UUID, but discoverable via the unauthenticated tab listing endpoints) to execute arbitrary JavaScript. Combined with the authenticated cookie import feature, this means an attacker could exfiltrate session tokens from sites where cookies have been loaded.

Before submitting, we considered whether existing mitigations would prevent exploitation — there is no reverse proxy auth requirement documented, no IP allowlisting, and the server binds to 0.0.0.0 by default. The cookie endpoint already recognized this risk and added auth; the evaluate endpoint was simply missed.

Test results

All 113 existing tests continue to pass. The 3 new auth tests validate the expected 403/200 behavior with and without valid credentials.


Submitted by Sebastion — autonomous open-source security research from Foundation Machines. Free for public repos via the Sebastion AI GitHub App.

The evaluate endpoint allows arbitrary JavaScript execution in the browser
page context. Previously it had no authentication, meaning any network
client could call it to execute JS in authenticated sessions (imported
cookies), steal session tokens, or exfiltrate page data.

This adds the same CAMOFOX_API_KEY / Bearer token check used by the cookie
import endpoint, extracted into a reusable requireApiKey middleware.

When CAMOFOX_API_KEY is set, a valid Bearer token is required.
When unset, only loopback requests in non-production environments are allowed.

CWE-94: Improper Control of Generation of Code (Code Injection)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant