Files
FabledSteward/tests/test_no_remote_assets.py
T
bvandeusenandClaude Opus 5.5 bd28d3b0e7
CI / lint (push) Successful in 3s
CI / integration (push) Successful in 48s
CI / unit (push) Successful in 1m6s
CI / publish (push) Successful in 1m11s
feat(ui): serve htmx, Chart.js, Gridstack and the house fonts from Steward itself (#5091)
#5091 "Local assets". base.html, the dashboard and the share view loaded
htmx and Chart.js from unpkg/jsdelivr, Gridstack from jsdelivr and their
fonts from Google. On an instance with no outbound network (rule 164) every
page lost htmx, and so every button and poll; and each page load told those
hosts about the visitor (rule 155).

- steward/static/vendor: htmx 2.0.4, Chart.js 4.4.4, Gridstack 12.6.0 (the
  versions the pages already used), with their licences (0BSD, MIT, MIT).
- steward/static/fonts: Inter, Fraunces and JetBrains Mono, the design
  system's faces (SIL OFL, licences beside them), every unicode-range
  subset, with fonts.css. The serif face is now Fraunces in place of
  EB Garamond (base) and Libertinus Serif (share view); applying Inter and
  the type scale is the tokens step.
- Templates use url_for('static'); /static already passes every gate.
- Guard (tests/test_no_remote_assets.py): no template or stylesheet loads a
  script, stylesheet, font or image from another origin (comments stripped,
  example URLs in help text allowed), with a self-test that it can fail;
  every url_for('static') file and every font the sheet names exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:12:31 -04:00

72 lines
3.0 KiB
Python

"""Steward draws itself only with what it serves (#5091; rules 155 and 164).
No template or stylesheet loads a script, stylesheet, font or image from
another origin (a page would quietly lose htmx, its charts or its fonts on an
instance with no outbound network, and tell a third party about every
visitor), and every file a template asks url_for('static') for exists.
Example URLs in placeholders and help text are fine: only loads count.
"""
import pathlib
import re
import steward
_REPO = pathlib.Path(steward.__file__).parent.parent
_STATIC = _REPO / "steward" / "static"
_COMMENTS = re.compile(r"\{#.*?#\}|<!--.*?-->|/\*.*?\*/", re.S)
_REMOTE = r"""["']?\s*(?:https?:)?//"""
_LOADS = [
re.compile(r"<script\b[^>]*\bsrc\s*=\s*" + _REMOTE, re.I),
re.compile(r"<link\b[^>]*\bhref\s*=\s*" + _REMOTE, re.I),
re.compile(r"<img\b[^>]*\bsrc\s*=\s*" + _REMOTE, re.I),
re.compile(r"@import\s+" + _REMOTE, re.I), # @import url(...) is the url( case below
re.compile(r"\burl\(\s*" + _REMOTE, re.I),
]
_STATIC_REF = re.compile(r"url_for\(\s*'static'\s*,\s*filename\s*=\s*'([^']+)'\s*\)")
def remote_loads(text: str) -> list[str]:
text = _COMMENTS.sub("", text)
return [m.group(0) for rx in _LOADS for m in rx.finditer(text)]
def _files():
roots = [_REPO / "steward" / "templates", _STATIC, *(_REPO / "plugins").glob("*/templates")]
for root in roots:
for path in root.rglob("*"):
if path.suffix in (".html", ".css", ".js") and "vendor" not in path.parts:
yield path
def test_the_remote_asset_guard_can_fail():
page = """
{# <script src="https://cdn.example/explained.js"></script> #}
<!-- <link href="https://fonts.example/x.css" rel="stylesheet"> -->
<input placeholder="https://discord.com/api/webhooks/...">
<a href="https://github.com/org/repo">a link is not a load</a>
<script src="https://unpkg.com/htmx.org/dist/htmx.min.js"></script>
<link rel="preconnect" href="//fonts.gstatic.com">
<style>@import url(https://fonts.example/a.css); .x { background: url('http://x/y.png') }</style>
"""
assert len(remote_loads(page)) == 4
def test_nothing_is_loaded_from_another_origin():
found = {str(p.relative_to(_REPO)): hits for p in _files() if (hits := remote_loads(p.read_text()))}
assert not found, f"loads from another origin (vendor it under steward/static): {found}"
def test_every_static_file_a_template_names_exists():
refs = {(p, ref) for p in _files() for ref in _STATIC_REF.findall(p.read_text())}
assert any(ref == "vendor/htmx-2.0.4.min.js" for _, ref in refs)
missing = sorted(f"{p.relative_to(_REPO)}: {ref}" for p, ref in refs if not (_STATIC / ref).is_file())
assert not missing, missing
def test_every_font_file_the_font_sheet_names_exists():
sheet = (_STATIC / "fonts" / "fonts.css").read_text()
names = set(re.findall(r"url\(([^)]+\.woff2)\)", sheet))
assert len(names) >= 10
assert not [n for n in names if not (_STATIC / "fonts" / n).is_file()]