Releases and security
2.1.13 - 2026-06-01
Fix — runique (admin templates)
-
Admin sort links lost active filters: clicking a column header to sort reset all active
list_filtervalues. The sort links inlist_partial.htmldid not include{{ filter_qs }}, unlike the pagination links. Fixed:filter_qsis now appended to thehrefandhx-getof both theidcolumn and dynamic column headers. -
Admin filter sidebar values unordered: generated
filter_fnclosures retrieved distinct values then re-sorted them in Rust withsort_by(numeric-then-lexicographic ascending), overriding any DB-side ordering. Fixed: the in-memorysort_byis removed; theDISTINCTquery now usesORDER BY CAST(col AS TEXT) DESCso values arrive pre-sorted from the DB — dates show newest first, string values show highest alphabetically first.
Fix — runique (admin templates, admin CSS)
-
Double-escaping of string values in admin list and detail views:
{{ value | escape }}was used inlist_partial.htmlanddetail.htmlwhile Tera's autoescape was already active. Theescapefilter converts/to/, then autoescape re-encodes&to&, so the browser rendered the literal text/instead of/. Same issue in the hidden filter inputs (value="{{ val | escape }}"), which would have caused filter comparisons to fail for values containing/. Fixed: all three occurrences replaced with plain{{ value }}/{{ val }}— autoescape alone is sufficient for XSS protection here. -
Admin filter sidebar CSS used mismatched selectors: the CSS section for the list filter panel used hyphen-style selectors (
.admin-filter-sidebar,.filter-group,.admin-list-layout, etc.) while the templates had already been refactored to BEM (.admin-filter__sidebar,.admin-filter__group,.admin-list__layout, etc.). The filter panel had no effective styling. Fixed: the entire section is rewritten with BEM selectors; mobile now uses an offcanvas pattern (position: fixed; right: -300px→.mobile-open { right: 0 }) with a backdrop overlay.
Fix — runique (templates)
-
Internal templates not autoescaped — XSS vector on admin form fields: Tera's autoescape is activated for logical keys ending in
.htmlor.xml. Internal framework templates were registered without the.htmlsuffix, so their variables were rendered raw. In particular,{{ form_fields.html }}(which holds Runique-generated form HTML) was not autoescaped and would have been misinterpreted as a missing variable. Fixed: all internal template keys now include the.htmlsuffix; the template preprocessor (process_content) rewrites{{ form_fields.html }}to{{ form_fields.html | safe }}viaADMIN_FORM_HTML_REGEX— the only variable exempt from autoescaping because it is always Runique-generated HTML, never user input. The{% extends %}keys in doc examples were updated accordingly ("admin/admin_template.html","admin_base.html"). -
resolve_og_image: CSS version hash applied to media URLs, and potential double slash: the function appended?v=<hash>to the OG image URL unconditionally when a CSS build token was present. The hash is computed from static assets (CSS/JS) at build time and has no relation to the media file's content — applying it to an uploaded image is semantically wrong and breaks scraper cache-busting when the image changes without a redeploy. Additionally, if anallowed_hostsentry contained a trailing slash, the host prefix (https://host/) concatenated with anog_imagestarting with/produced a double slash (https://host//media/...). Fixed: the?v=is removed entirely from OG image URLs;trim_end_matches('/')is applied to the host andtrim_start_matches('/')to the image path before concatenation; absolute URLs (http:///https://) are returned early without modification.