"""CORS preflight + response headers for moz-extension:// + chrome-extension://. Operator-flagged 2026-05-26: extension's first 'Test connection' tap returned `NetworkError` because /api/credentials had no OPTIONS handler and no Access-Control-Allow-Origin response header. Browser preflight failed → fetch blocked. These tests pin the contract: any request from a moz-extension:// or chrome-extension:// origin gets the right ACL headers. Plain browser requests (no Origin header, or a regular https:// Origin) get nothing — we don't want to open CORS up generally. """ import pytest pytestmark = pytest.mark.integration @pytest.mark.asyncio async def test_extension_preflight_returns_204_with_acl_headers(client): resp = await client.options( "/api/credentials", headers={ "Origin": "moz-extension://abcd1234-uuid-fake", "Access-Control-Request-Method": "GET", "Access-Control-Request-Headers": "X-Extension-Key", }, ) assert resp.status_code == 204 assert resp.headers["Access-Control-Allow-Origin"] == "moz-extension://abcd1234-uuid-fake" assert "OPTIONS" in resp.headers["Access-Control-Allow-Methods"] assert "X-Extension-Key" in resp.headers["Access-Control-Allow-Headers"] @pytest.mark.asyncio async def test_extension_get_carries_acl_headers(client): # The actual response (post-preflight) also needs ACL headers — the # browser checks them again before exposing the response body. resp = await client.get( "/api/credentials", headers={"Origin": "moz-extension://abcd1234-uuid-fake"}, ) # 200 with empty list (no creds seeded) — what matters here is the # CORS header is present. assert resp.headers["Access-Control-Allow-Origin"] == "moz-extension://abcd1234-uuid-fake" @pytest.mark.asyncio async def test_chrome_extension_origin_also_allowed(client): resp = await client.options( "/api/credentials", headers={ "Origin": "chrome-extension://abcd1234-uuid-fake", "Access-Control-Request-Method": "POST", }, ) assert resp.status_code == 204 assert resp.headers["Access-Control-Allow-Origin"] == "chrome-extension://abcd1234-uuid-fake" @pytest.mark.asyncio async def test_normal_browser_request_gets_no_cors_headers(client): # A regular browser tab (https://example.com / file:// / no Origin # at all) should NOT get any Access-Control-Allow-* — the extension # whitelist is intentionally narrow. resp = await client.get( "/api/credentials", headers={"Origin": "https://evil.example.com"}, ) assert "Access-Control-Allow-Origin" not in resp.headers @pytest.mark.asyncio async def test_no_origin_header_unaffected(client): # Same-origin requests (no Origin header) — unaffected. resp = await client.get("/api/credentials") assert "Access-Control-Allow-Origin" not in resp.headers