Commit Graph

4 Commits

Author SHA1 Message Date
lukas 0c6c536d07 Ban SafeText-as-child: only Safe nodes render unescaped
Tightens the child model so the type is honest end to end. Previously a
``SafeText``/``mark_safe`` string passed as a child rendered unescaped — a
trusted-HTML-as-string backdoor that ``Child = Node | str`` couldn't express
(every ``SafeText`` is a ``str``). Now ``_child_key`` escapes *every* string
child; the only way to put trusted pre-rendered HTML into the tree is a
``Safe`` node. So a ``str`` child is always untrusted text — which is exactly
what the renderer escapes.

Converted the trusted-HTML children that relied on the old passthrough:

- ``CsrfInput`` and the Alpine selectors (``GameStatusSelector`` /
  ``SessionDeviceSelector``) now return ``Safe`` nodes instead of ``mark_safe``
  strings — they are always tree children.
- ``popover_content`` is now a ``Child`` (it is rendered as a child); the one
  HTML caller (``LinkedPurchase``) passes ``Safe(...)``.
- View-side children that were ``mark_safe`` strings → ``Safe(...)``:
  ``_played_row`` (game detail), the stat SVGs and `` `` spacer (game),
  the login table (auth), the manual session-form field/label markup
  (session), and ``_purchase_name`` (stats).
- ``SimpleTable.header_action`` typed ``Child``.

The script-tag string helpers (``ModuleScript`` / ``StaticScript`` /
``ExternalScript``) stay ``SafeText`` strings: they are only ever joined into
the ``scripts=`` string, never used as tree children.

``Children`` regains a bare ``Node`` member (a single node child is valid);
the one ``*children`` site (``Popover``) normalises via ``as_children`` first.
Tests that asserted the old SafeText-passthrough now assert the new rule
(mark_safe child escaped; ``Safe`` node passes through). Full suite green
(445; +2 new escaping tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 18:35:43 +02:00
lukas 9c42d85f52 Migrate remaining Component() callers to Element; delete the shim
The legacy back-compat ``Component(tag_name=...)`` function (a thin
string-returning wrapper over ``Element``) was the last piece of the
pre-node-tree API. Migrate its ~18 call sites across the views to the node
builders and remove it:

- stats_content.py: the table helpers now use the whitelisted ``Td`` / ``Th``
  / ``Tr`` builders and ``Element`` for table/tbody/thead/h1; helper return
  types are ``Node``.
- auth.py / statuschange.py / game.py / purchase.py: the hand-built
  ``<form>`` / ``<button>`` / ``<h1>`` / ``<h2>`` / ``<table>`` markup now uses
  ``Element("tag", ...)``.
- core.py: drop the ``Component()`` function and its back-compat note;
  ``common/components/__init__`` no longer exports it.
- Tests that exercised the shim now target ``Element`` directly
  (test_components cache/escaping/edge-case classes; test_node_tree drops the
  legacy-parity and legacy-bridge cases, which ``Element`` coverage subsumes).
- CLAUDE.md: drop the "legacy Component retained for back-compat" notes.

Full suite green (443; one obsolete legacy-bridge test removed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:51:27 +02:00
lukas ab079cb447 Use adhoc Component() less 2026-06-12 22:45:25 +02:00
lukas d101aecd70 Move from HTML templates to pure Python
Remove cruft
2026-06-06 07:51:10 +02:00