"""The app draws itself only from its own origin. A font, stylesheet or script linked from another host hands that host every visitor's address on every page load, sign-in page included, and when the host is slow or blocked the page quietly renders in fallback type with nothing in the console. Everything the frontend needs ships in the image instead, and the Content-Security-Policy names no other host, so a remote link that slips back in is refused by the browser as well as by this check. """ from __future__ import annotations import pathlib import re ROOT = pathlib.Path(__file__).resolve().parents[1] FRONTEND = ROOT / "frontend" CSS_COMMENT = re.compile(r"/\*.*?\*/", re.S) HTML_COMMENT = re.compile(r"", re.S) REMOTE = r"""(?:https?:)?//""" REMOTE_REFS = [ re.compile(rf"""@import\s+(?:url\(\s*)?['"]?{REMOTE}""", re.I), re.compile(rf"""url\(\s*['"]?{REMOTE}""", re.I), re.compile(rf"""]*\bhref\s*=\s*['"]{REMOTE}""", re.I), re.compile(rf"""]*\bsrc\s*=\s*['"]{REMOTE}""", re.I), re.compile(r"""\brel\s*=\s*['"](?:preconnect|dns-prefetch)""", re.I), ] def remote_refs(text: str) -> list[str]: """The remote asset references in a stylesheet, page or component.""" text = HTML_COMMENT.sub("", CSS_COMMENT.sub("", text)) return [m.group(0) for pattern in REMOTE_REFS for m in pattern.finditer(text)] def frontend_sources() -> dict[str, str]: paths = [FRONTEND / "index.html"] for suffix in ("*.css", "*.vue", "*.html"): paths += (FRONTEND / "src").rglob(suffix) return {str(p.relative_to(ROOT)): p.read_text() for p in paths} def test_no_frontend_source_links_a_remote_asset(): sources = frontend_sources() assert len(sources) > 1, f"no frontend sources under {FRONTEND}" found = {name: refs for name, text in sources.items() if (refs := remote_refs(text))} assert found == {}, "vendor the asset and serve it from the app's own origin" def test_the_content_security_policy_names_no_other_host(): app = (ROOT / "src" / "grimoire" / "app.py").read_text() policy = re.search(r'"Content-Security-Policy",\s*((?:"[^"]*"\s*)+)\)', app) assert policy, "the Content-Security-Policy header is no longer set in app.py" assert "//" not in policy.group(1) def test_the_guard_can_fail(): assert remote_refs("@import url('https://fonts.example.com/css2?family=X');") assert remote_refs("@font-face { src: url(//cdn.example.com/x.woff2); }") assert remote_refs('') assert remote_refs('') assert remote_refs('') assert not remote_refs("/* no @import url('https://x.example.com') here */") assert not remote_refs("@font-face { src: url(./inter-normal-latin.woff2); }") assert not remote_refs('docs')