fix: /v1 must survive CSP-blocked evaluate (incl. Firefox JSON viewer) - #395
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
/v1500s whenever the loaded page's CSP disallowseval()— Playwright'spage.evaluaterunseval()in the page's main world, so any CSP cause can crash the response path ("closes #394" via one such cause).Root causes covered
application/jsondocuments 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.<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 withextra_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_agentcomes 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.pyneeded no change:/loadalready 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/v1withhttps://api.ipify.org?format=json→ 200, UA present, raw JSON in body.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
POST /v1→api.ipify.org?format=json→ 200, UA populated, raw JSON body (was 500)POST /v1→example.com→ 200,GET /health→ 200evaluatefails on ipify andapi.github.com;text/plainandtext/htmlfine.