mirror of
https://github.com/ThePhaseless/Byparr.git
synced 2026-09-24 06:10:14 +01:00
Merge pull request #395 from ThePhaseless/fix/csp-json-viewer-eval
fix: /v1 must survive CSP-blocked evaluate (incl. Firefox JSON viewer)
This commit is contained in:
+11
-1
@@ -92,10 +92,20 @@ async def read_item(request: LinkRequest, dep: BrowserDep) -> LinkResponse:
|
||||
page_html=page_html,
|
||||
)
|
||||
|
||||
user_agent = (
|
||||
page_request.request.headers.get("user-agent") if page_request else None
|
||||
)
|
||||
if user_agent is None:
|
||||
try:
|
||||
user_agent = await dep.page.evaluate("navigator.userAgent")
|
||||
except Exception:
|
||||
logger.warning("Could not determine User-Agent via page.evaluate")
|
||||
user_agent = ""
|
||||
|
||||
return LinkResponse(
|
||||
message="Success",
|
||||
solution=Solution(
|
||||
user_agent=await dep.page.evaluate("navigator.userAgent"),
|
||||
user_agent=user_agent,
|
||||
url=final_url if final_url is not None else dep.page.url,
|
||||
status=status,
|
||||
cookies=cookies,
|
||||
|
||||
@@ -96,6 +96,7 @@ async def get_browser(
|
||||
proxy=proxy_config,
|
||||
humanize=True,
|
||||
locale=BROWSER_LOCALE or "auto",
|
||||
extra_prefs={"devtools.jsonview.enabled": False},
|
||||
) as browser_raw:
|
||||
# InvisiblePlaywright yields a Browser instance
|
||||
browser = cast("Browser", browser_raw)
|
||||
|
||||
+50
-1
@@ -59,6 +59,34 @@ def test_bypass(website: str):
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
|
||||
def test_json_api():
|
||||
"""JSON APIs must return 200, not crash on the UA evaluate.
|
||||
|
||||
Firefox renders application/json in a built-in viewer whose CSP blocks
|
||||
Playwright's eval-based evaluate() (issue #394). The browser must be
|
||||
launched with the viewer disabled so /v1 works and returns the raw JSON.
|
||||
"""
|
||||
url = "https://api.ipify.org?format=json"
|
||||
test_request = httpx2.get(url)
|
||||
if test_request.status_code >= HTTPStatus.INTERNAL_SERVER_ERROR:
|
||||
pytest.skip(
|
||||
f"Skipping JSON API test - upstream error ({test_request.status_code})"
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
"/v1",
|
||||
json=LinkRequest.model_construct(url=url, cmd="request.get").model_dump(),
|
||||
)
|
||||
|
||||
if response.status_code == HTTPStatus.REQUEST_TIMEOUT:
|
||||
pytest.skip("Skipping JSON API test - timed out (upstream issue)")
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
solution = response.json()["solution"]
|
||||
assert solution["userAgent"]
|
||||
assert '"ip"' in solution["response"]
|
||||
|
||||
|
||||
def test_health_check():
|
||||
"""
|
||||
Tests the health check endpoint.
|
||||
@@ -111,7 +139,9 @@ def fake_dep(*, fail_states: set[str] | None = None) -> BrowserDepClass:
|
||||
page = AsyncMock()
|
||||
page.url = "https://example.test/login"
|
||||
page.goto.return_value = MagicMock(
|
||||
status=HTTPStatus.OK, headers={"content-type": "text/html"}
|
||||
status=HTTPStatus.OK,
|
||||
headers={"content-type": "text/html"},
|
||||
request=MagicMock(headers={"user-agent": "UnitTestBrowser/1.0"}),
|
||||
)
|
||||
page.title.return_value = "Login"
|
||||
page.evaluate.return_value = "UnitTestBrowser/1.0"
|
||||
@@ -158,3 +188,22 @@ async def test_domcontentloaded_timeout_returns_408():
|
||||
)
|
||||
|
||||
assert exc.value.status_code == HTTPStatus.REQUEST_TIMEOUT
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_user_agent_survives_csp_blocked_evaluate():
|
||||
"""UA comes from request headers when page CSP blocks evaluate (#394).
|
||||
|
||||
No CSP configuration (header, meta tag, or internal viewer document) may
|
||||
turn /v1 into a 500.
|
||||
"""
|
||||
dep = fake_dep()
|
||||
dep.page.evaluate.side_effect = Exception("call to eval() blocked by CSP")
|
||||
|
||||
response = await read_item(
|
||||
LinkRequest(url="https://example.test/login"),
|
||||
dep,
|
||||
)
|
||||
|
||||
assert response.status == "ok"
|
||||
assert response.solution.user_agent == "UnitTestBrowser/1.0"
|
||||
|
||||
Reference in New Issue
Block a user