"""No template puts a rendered value into inline JavaScript (#5099). An onclick/onsubmit attribute is HTML-decoded before its JavaScript is parsed, so autoescaping does not protect a name placed inside it: a monitor called x');fetch(…);// ran as script when an admin clicked Delete. A prompt goes in data-confirm="…" (base.html asks with it); any other value a handler needs is an integer, rendered with |int. """ import re from tests._templates import REPO as _REPO, template_files as _templates _HANDLER = re.compile(r"""\bon[a-z]+\s*=\s*("[^"]*"|'[^']*')""", re.I) _VALUE = re.compile(r"\{\{(.*?)\}\}", re.S) def unsafe_handler_values(text: str) -> list[str]: """Each {{ … }} inside an inline handler that is not an |int.""" return [m.group(0) for h in _HANDLER.finditer(text) for m in _VALUE.finditer(h.group(1)) if not re.search(r"\|\s*int\s*$", m.group(1).strip())] def test_the_guard_can_fail(): page = """
""" assert unsafe_handler_values(page) == ["{{ rule.name }}", "{{ msg|tojson }}"] def test_no_template_renders_a_value_into_an_inline_handler(): files = _templates() assert len(files) > 100 found = {str(p.relative_to(_REPO)): hits for p in files if (hits := unsafe_handler_values(p.read_text()))} assert not found, f"use data-confirm, or render an integer with |int: {found}" def test_base_asks_before_a_data_confirm_form_submits(): """#5314: in Steward's own dialog, filled as text, never the browser's confirm().""" base = (_REPO / "steward" / "templates" / "base.html").read_text() js = (_REPO / "steward" / "static" / "confirm.js").read_text() assert "filename='confirm.js'" in base and '' in base assert re.search(r'