Skip to content

fix: /v1 must survive CSP-blocked evaluate (incl. Firefox JSON viewer) - #395

Merged
ThePhaseless merged 5 commits into
mainfrom
fix/csp-json-viewer-eval
Aug 11, 2026
Merged

fix: /v1 must survive CSP-blocked evaluate (incl. Firefox JSON viewer)#395
ThePhaseless merged 5 commits into
mainfrom
fix/csp-json-viewer-eval

Conversation

@ThePhaseless

@ThePhaseless ThePhaseless commented Aug 11, 2026

Copy link
Copy Markdown
Owner

What

/v1 500s whenever the loaded page's CSP disallows eval() — Playwright's page.evaluate runs eval() in the page's main world, so any CSP cause can crash the response path ("closes #394" via one such cause).

Root causes covered

  1. Firefox JSON viewer (call to eval() blocked by CSP #394): application/json documents render in a built-in viewer whose own <meta> CSP (script-src resource:) blocks eval. No response header is involved, so the existing header-stripping route could never catch it. This is why the previous CSP fixes looked complete but weren't.
  2. General CSP independence: even non-viewer pages can block eval — most notably CSP delivered via <meta http-equiv="Content-Security-Policy">, which the header-strip route cannot remove. The only durable guarantee is to stop requiring main-world eval for the response.

Fix

  • src/utils.py: launches with extra_prefs={"devtools.jsonview.enabled": False}. JSON renders as plain text → evaluate works again and API consumers get the raw JSON body instead of the viewer's syntax-highlighted HTML.
  • src/endpoints.py: user_agent comes from the navigation request headers (what the site actually saw); page.evaluate("navigator.userAgent") is kept only as a best-effort fallback whose failure logs a warning instead of 500ing. This makes the endpoint immune to the whole "eval blocked by CSP" class, whatever the cause.
  • owui.py needed no change: /load already degrades to empty content on any extraction failure.

(Explicitly not added: stripping <meta http-equiv="Content-Security-Policy"> from fulfilled HTML bodies. Feasible, but Cloudflare-style pages can use a CSP-blocked canary script to detect tampering — with the fallback above, we no longer need to touch page CSP at all.)

Test

  • tests/main_test.py (test_json_api): exact call to eval() blocked by CSP #394 scenario — POST /v1 with https://api.ipify.org?format=json → 200, UA present, raw JSON in body.
  • New mock test (test_user_agent_survives_csp_blocked_evaluate): page.evaluate raises the exact CSP error; endpoint still returns 200 with UA from request headers — this is the future-proofing regression for the whole class.

Verification

  • Local: 9/9 mock/unit tests pass.
  • Docker container with both commits applied:
    • POST /v1api.ipify.org?format=json200, UA populated, raw JSON body (was 500)
    • POST /v1example.com → 200, GET /health → 200
    • Confirmed without the pref, viewer-on: evaluate fails on ipify and api.github.com; text/plain and text/html fine.

Firefox renders application/json documents in a built-in viewer whose own
CSP (<script-src resource:>) blocks Playwright's eval-based page.evaluate,
crashing /v1 with a 500 on JSON APIs (closes #394). Setting
devtools.jsonview.enabled=false renders JSON as plain text, which also
returns the raw JSON body instead of the viewer's syntax-highlighted HTML.
…lback

page.evaluate runs eval() in the page's main world, which fails with 'call to eval() blocked by CSP' under any CSP that disallows unsafe-eval - HTTP headers (already stripped), meta tags (not strippable), or internal viewer documents (#394).

The navigation request already carries the UA the site actually saw, so take user_agent from page_request.request.headers and keep evaluate only as a best-effort fallback whose failure can no longer 500 the request.
@ThePhaseless ThePhaseless changed the title fix: disable Firefox JSON viewer so evaluate works on JSON APIs fix: /v1 must survive CSP-blocked evaluate (incl. Firefox JSON viewer) Aug 11, 2026
@ThePhaseless
ThePhaseless merged commit 9afb3e0 into main Aug 11, 2026
7 checks passed
@ThePhaseless
ThePhaseless deleted the fix/csp-json-viewer-eval branch August 11, 2026 09:57
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.

call to eval() blocked by CSP

1 participant