(Follow up to ticket 33631, where I was asked to discuss this here)
Short summary: The documentation gives this example usage of blocktranslate asvar
:
{% blocktranslate asvar the_title %}The title is {{ title }}.{% endblocktranslate %}
And then uses {{ the_title }}
in the rest of the template. This example is broken regarding HTML character escaping, because the_title
is a str
instead of a SafeString
. Thus, any further use of {{ the_title }}
will lead to double-escaping as long as {{ title }}
produced any characters that would be changed by HTML escaping.
In the ticket, we considered whether blocktranslate asvar
should return a SafeString
instead, which I think it should.
However the ticket was closed as wontfix,
- claiming that it was not clear whether the output assigned to
the_title
could reliably be marked safe and - asking me to rather use
{{ the_title|safe }}
in any following usage.
I think both these points are wrong or misleading:
-
If I removed the
asvar
part, the output would be rendered without additional escaping, so theblocktranslate
result is already implicitly marked safe. Only when I add theasvar
argument, because the result is then stored in astr
instead of being rendered, this information gets lost. Consider this code:from django.template import Template, Context template_content = "{% blocktranslate %}Title: &{{ title }}{% endblocktranslate %}" rendered = Template(template_content).render(Context({"title": "amp; & Title"})) assert "Title: & & Title" in rendered # assertion successful
Notice that the inner
&
is escaped exactly once, and the html-escape character&
that is formed accidentally, is kept exactly as is, without the additional escaping.I argue that these two templates should produce exactly the same output (which is also what the example in the documentation implies), but they don’t:
{% blocktranslate asvar the_title %}Title: &{{ title }}{% endblocktranslate %}{{ the_title }}
{% blocktranslate %}Title: &{{ title }}{% endblocktranslate %}
-
Obviously, the example from the documentation is a toy-example. In a real-world scenario, you’d have usages much later, or even in different (included) files. For XSS safety, strings should be marked safe at the source whenever possible, so code auditors can directly reason about correctness and safety. Storing the string marked as unsafe and expecting all later uses to use the
|safe
template tag is a XSS vulnerability waiting to happen.
Note that any workaround we ask users to apply here (such as manually adding |safe
on later uses, or computing the title in the view, or something else) basically means that we disagree with the (very sane) assumptions that the template in the documentation makes.