GHSA-9395-2g46-rj3f
djust: Six template-layer defects emit attacker-controlled markup unescaped (XSS)
Summary
Five independent defects in djust's template auto-escaping cause attacker-controlled input to be rendered as live markup where Django escapes it. All four are present in shipped 1.1.0 and are fixed in 1.1.1. They share one shape: **a filter or grant that escapes nothing itself and relies on the render-time auto-escape, which something downstream then removes.** They are grouped into a single advisory because the mitigation is identical — upgrade to 1.1.1 — and because no single one of them is meaningfully actionable in isolation. ## 1. `linenumbers` never escaped its input (#2291) ``` {{ p|linenumbers|safe }} with p = ' ' djust '1. ' ' trusted ') -> ' trusted ' correct render 2: p = ' ' -> executes ``` A view that renders trusted markup into a variable and later renders user input into the same variable emits it live. ## 5. A custom tag handler's return was emitted raw — including djust's own `{% render_slot %}` (#2379) **Reachable with no `|safe`, no `mark_safe`, and no application code: using component slots is enough.** Django's `SimpleNode.render` runs `conditional_escape` over a `simple_tag`'s return unless it carries `__html__`. djust inserted the return verbatim, so a handler as ordinary as `return f"Hello {name}"` emitted attacker markup live. Of the 221 handlers djust registers, one echoes a context value unescaped — `render_slot`, the framework's own function-component/slot tag: ``` {% render_slot p %} p = ' ' djust ' ' `/` ` but neither escaped its content nor reported its output safe. The plain spelling therefore escaped the filter's **own** tags and printed a literal ` ` on the page, so `|safe` was the only form
Affected packages
| Ecosystem | Package | Affected versions | Fixed versions |
|---|---|---|---|
| PyPI | djust | — | 1.1.1 |
Remediation: Upgrade to 1.1.1 or later.
References
Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.