refactor: convert Follow.html from macro to template for better security and type safety
#12634 opened on May 5, 2026
Description
Context
Follow.html is currently a macro (openlibrary/macros/Follow.html) that accepts a link_track parameter and injects it directly into an HTML attribute string. This was flagged during review of #12367.
Jim Champ (jimchamp) noted:
If a macro receives an argument of an object, maybe it should be a template (i.e. security, avoid untypable/uncheckable args)
Problem
-
Attribute injection: The macro builds a raw attribute string via
'data-ol-link-track="%s"' % link_track— iflink_trackever contains",<, or>, it can break markup. (#12367 adds a.replace('"', '"')bandage, but this pattern is fragile.) -
Uncheckable args: Macros in web.py accept positional/keyword args but have no type annotations or input validation — any caller can pass arbitrary objects and the error won't surface until render time.
-
Injection surface: As a macro callable from any template,
Follow.htmlis harder to audit for misuse than a template, which has a defined call signature and is invoked viarender_template().
Proposed Fix
Convert openlibrary/macros/Follow.html → openlibrary/templates/follow/follow.html (or similar path under templates).
Benefits:
- Native template attribute rendering (
data-ol-link-track="$link_track") auto-escapes the value via web.py's$interpolation, eliminating the attribute injection risk without a manual workaround - Templates are rendered via
render_template()which is more auditable - Type-checkable signature
Related
- Flagged during review of PR #12367 (adds
link_trackparameter to Follow macro) - Copilot thread: interprets as XSS vector