Always autoescape jinja environments unless overridden

We were only rendering .html files before, so the old _guess_autoescape function
had the effect of always autoescaping .render() (from a file) but never
autoescaping .from_string(). However, most places using .from_string() actually
render (Qt-)HTML via jinja, so they should escape stuff!

Now, we always autoescape, except when the caller uses the
jinja.environment.no_autoescape() context manager, which places rendering
stylesheets now do.

This impacted:

- Confirm quit texts (no HTML here)
- config.py loading errors
  (where this was found because of an error containing - a <keybinding>)
- Certificate error prompts
  (should be fine from what I can tell, as the only user-controllable output is
  the hostname, which cannot contain HTML)
This commit is contained in:
Florian Bruhin 2017-09-16 08:30:19 +02:00
parent 337d57b940
commit 3179e8c7b9
4 changed files with 22 additions and 23 deletions

View File

@ -194,7 +194,8 @@ class CompletionItemDelegate(QStyledItemDelegate):
color: {{ conf.colors.completion.match.fg }};
}
"""
template = jinja.environment.from_string(stylesheet)
with jinja.environment.no_autoescape():
template = jinja.environment.from_string(stylesheet)
self._doc.setDefaultStyleSheet(template.render(conf=config.val))
if index.parent().isValid():

View File

@ -585,7 +585,8 @@ def _render_stylesheet(stylesheet):
"""Render the given stylesheet jinja template."""
# Imported here to avoid a Python 3.4 circular import
from qutebrowser.utils import jinja
template = jinja.environment.from_string(stylesheet)
with jinja.environment.no_autoescape():
template = jinja.environment.from_string(stylesheet)
return template.render(conf=val)

View File

@ -21,6 +21,7 @@
import os
import os.path
import contextlib
import traceback
import mimetypes
import html
@ -82,21 +83,19 @@ class Environment(jinja2.Environment):
def __init__(self):
super().__init__(loader=Loader('html'),
autoescape=self._guess_autoescape,
autoescape=lambda _name: self._autoescape,
undefined=jinja2.StrictUndefined)
self.globals['resource_url'] = self._resource_url
self.globals['file_url'] = urlutils.file_url
self.globals['data_url'] = self._data_url
self._autoescape = True
def _guess_autoescape(self, template_name):
"""Turn auto-escape on/off based on the file type.
Based on http://jinja.pocoo.org/docs/dev/api/#autoescaping
"""
if template_name is None or '.' not in template_name:
return False
ext = template_name.rsplit('.', 1)[1]
return ext in ['html', 'htm', 'xml']
@contextlib.contextmanager
def no_autoescape(self):
"""Context manager to temporarily turn off autoescaping."""
self._autoescape = False
yield
self._autoescape = True
def _resource_url(self, path):
"""Load images from a relative path (to qutebrowser).

View File

@ -146,14 +146,12 @@ def test_attribute_error():
jinja.render('attributeerror.html', obj=object())
@pytest.mark.parametrize('name, expected', [
(None, False),
('foo', False),
('foo.html', True),
('foo.htm', True),
('foo.xml', True),
('blah/bar/foo.html', True),
('foo.bar.html', True),
])
def test_autoescape(name, expected):
assert jinja.environment._guess_autoescape(name) == expected
@pytest.mark.parametrize('escape', [True, False])
def test_autoescape(escape):
if not escape:
with jinja.environment.no_autoescape():
template = jinja.environment.from_string("{{ v }}")
assert template.render(v='<foo') == '<foo'
template = jinja.environment.from_string("{{ v }}")
assert template.render(v='<foo') == '&lt;foo'