diff --git a/CHANGELOG.md b/CHANGELOG.md index bd214af..2d24e8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - `myst_block`: target labels (``(name)=``) are now escaped in the rendered anchor. - `attrs` / `attrs_block`: when no explicit `allowed` list is given, event-handler (`on*`) and `style` attributes are now stripped by default (removed entries are preserved in `token.meta["insecure_attrs"]`). Text spans (``[text]{...}``) now also honour the `allowed` list, which they previously bypassed entirely. This is a baseline protection, not a full sanitiser — pass an explicit `allowed` list (e.g. `("id", "class")`) to safely render untrusted input. + A categorical guard (`tests/test_xss.py`) renders injection probes through each plugin's default renderer, so a future unescaped interpolation is caught before review. + ## 0.7.0 - 2026-07-19 - ✨ NEW: Add section reference plugin (`section_ref`) (#144) diff --git a/mdit_py_plugins/texmath/index.py b/mdit_py_plugins/texmath/index.py index 19e8714..efbc451 100644 --- a/mdit_py_plugins/texmath/index.py +++ b/mdit_py_plugins/texmath/index.py @@ -47,7 +47,7 @@ def render_math_inline( env: EnvType, ) -> str: return rule_inline["tmpl"].format( # noqa: B023 - escapeHtml(render(tokens[idx].content, False, macros)) + render(tokens[idx].content, False, macros) ) md.add_render_rule(rule_inline["name"], render_math_inline) @@ -65,7 +65,7 @@ def render_math_block( env: EnvType, ) -> str: return rule_block["tmpl"].format( # noqa: B023 - escapeHtml(render(tokens[idx].content, True, macros)), + render(tokens[idx].content, True, macros), escapeHtml(tokens[idx].info), ) @@ -170,7 +170,7 @@ def dollar_post(src: str, end: int) -> bool: def render(tex: str, displayMode: bool, macros: Any) -> str: - return tex + return escapeHtml(tex) # TODO better HTML renderer port for math # try: # res = katex.renderToString(tex,{throwOnError:False,displayMode,macros}) diff --git a/tests/test_xss.py b/tests/test_xss.py new file mode 100644 index 0000000..603ee6c --- /dev/null +++ b/tests/test_xss.py @@ -0,0 +1,108 @@ +"""Categorical XSS guard: render injection probes through each plugin's defaults. + +Several plugins register default HTML render rules that interpolate +attacker-controlled token content into markup. Rather than trust review to catch +the next unescaped interpolation, this drives each plugin end-to-end with a probe +and asserts the raw marker never reaches the output (``markdown-it`` runs with raw +HTML disabled, so a correctly-escaped plugin emits ``<xss>`` instead). + +Two probe shapes cover the two sinks: + +- ``">`` for HTML/attribute contexts: a plugin that escapes turns the ``<`` + into ``<`` so the ``)", + " None: + md = MarkdownIt("commonmark") + case.configure(md) + out = md.render(case.payload) + assert case.marker not in out, ( + f"{case.id} emitted {case.marker!r} unescaped: {out!r}" + )