Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit e54746b

Browse files
authored
Clean-up the template loading code. (#9200)
* Enables autoescape by default for HTML files. * Adds a new read_template method for reading a single template. * Some logic clean-up.
1 parent 93b6158 commit e54746b

12 files changed

+96
-38
lines changed

UPGRADE.rst

+37
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,43 @@ for example:
8585
wget https://packages.matrix.org/debian/pool/main/m/matrix-synapse-py3/matrix-synapse-py3_1.3.0+stretch1_amd64.deb
8686
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
8787
88+
Upgrading to v1.27.0
89+
====================
90+
91+
Changes to HTML templates
92+
-------------------------
93+
94+
The HTML templates for SSO and email notifications now have `Jinja2's autoescape <https://jinja.palletsprojects.com/en/2.11.x/api/#autoescaping>`_
95+
enabled for files ending in ``.html``, ``.htm``, and ``.xml``. If you hae customised
96+
these templates and see issues when viewing them you might need to update them.
97+
It is expected that most configurations will need no changes.
98+
99+
If you have customised the templates *names* for these templates it is recommended
100+
to verify they end in ``.html`` to ensure autoescape is enabled.
101+
102+
The above applies to the following templates:
103+
104+
* ``add_threepid.html``
105+
* ``add_threepid_failure.html``
106+
* ``add_threepid_success.html``
107+
* ``notice_expiry.html``
108+
* ``notice_expiry.html``
109+
* ``notif_mail.html`` (which, by default, includes ``room.html`` and ``notif.html``)
110+
* ``password_reset.html``
111+
* ``password_reset_confirmation.html``
112+
* ``password_reset_failure.html``
113+
* ``password_reset_success.html``
114+
* ``registration.html``
115+
* ``registration_failure.html``
116+
* ``registration_success.html``
117+
* ``sso_account_deactivated.html``
118+
* ``sso_auth_bad_user.html``
119+
* ``sso_auth_confirm.html``
120+
* ``sso_auth_success.html``
121+
* ``sso_error.html``
122+
* ``sso_login_idp_picker.html``
123+
* ``sso_redirect_confirm.html``
124+
88125
Upgrading to v1.26.0
89126
====================
90127

changelog.d/9200.misc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Clean-up template loading code.

synapse/config/_base.py

+26-16
Original file line numberDiff line numberDiff line change
@@ -203,36 +203,50 @@ def read_file(cls, file_path, config_name):
203203
with open(file_path) as file_stream:
204204
return file_stream.read()
205205

206+
def read_template(self, filename: str) -> jinja2.Template:
207+
"""Load a template file from disk.
208+
209+
This function will attempt to load the given template from the default Synapse
210+
template directory.
211+
212+
Files read are treated as Jinja templates. The templates is not rendered yet
213+
and has autoescape enabled.
214+
215+
Args:
216+
filename: A template filename to read.
217+
218+
Raises:
219+
ConfigError: if the file's path is incorrect or otherwise cannot be read.
220+
221+
Returns:
222+
A jinja2 template.
223+
"""
224+
return self.read_templates([filename])[0]
225+
206226
def read_templates(
207-
self,
208-
filenames: List[str],
209-
custom_template_directory: Optional[str] = None,
210-
autoescape: bool = False,
227+
self, filenames: List[str], custom_template_directory: Optional[str] = None,
211228
) -> List[jinja2.Template]:
212229
"""Load a list of template files from disk using the given variables.
213230
214231
This function will attempt to load the given templates from the default Synapse
215232
template directory. If `custom_template_directory` is supplied, that directory
216233
is tried first.
217234
218-
Files read are treated as Jinja templates. These templates are not rendered yet.
235+
Files read are treated as Jinja templates. The templates are not rendered yet
236+
and have autoescape enabled.
219237
220238
Args:
221239
filenames: A list of template filenames to read.
222240
223241
custom_template_directory: A directory to try to look for the templates
224242
before using the default Synapse template directory instead.
225243
226-
autoescape: Whether to autoescape variables before inserting them into the
227-
template.
228-
229244
Raises:
230245
ConfigError: if the file's path is incorrect or otherwise cannot be read.
231246
232247
Returns:
233248
A list of jinja2 templates.
234249
"""
235-
templates = []
236250
search_directories = [self.default_template_dir]
237251

238252
# The loader will first look in the custom template directory (if specified) for the
@@ -249,7 +263,7 @@ def read_templates(
249263
search_directories.insert(0, custom_template_directory)
250264

251265
loader = jinja2.FileSystemLoader(search_directories)
252-
env = jinja2.Environment(loader=loader, autoescape=autoescape)
266+
env = jinja2.Environment(loader=loader, autoescape=jinja2.select_autoescape(),)
253267

254268
# Update the environment with our custom filters
255269
env.filters.update(
@@ -259,12 +273,8 @@ def read_templates(
259273
}
260274
)
261275

262-
for filename in filenames:
263-
# Load the template
264-
template = env.get_template(filename)
265-
templates.append(template)
266-
267-
return templates
276+
# Load the templates
277+
return [env.get_template(filename) for filename in filenames]
268278

269279

270280
def _format_ts_filter(value: int, format: str):

synapse/config/captcha.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ def read_config(self, config, **kwargs):
2828
"recaptcha_siteverify_api",
2929
"https://www.recaptcha.net/recaptcha/api/siteverify",
3030
)
31-
self.recaptcha_template = self.read_templates(
32-
["recaptcha.html"], autoescape=True
33-
)[0]
31+
self.recaptcha_template = self.read_template("recaptcha.html")
3432

3533
def generate_config_section(self, **kwargs):
3634
return """\

synapse/config/consent_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def __init__(self, *args):
8989

9090
def read_config(self, config, **kwargs):
9191
consent_config = config.get("user_consent")
92-
self.terms_template = self.read_templates(["terms.html"], autoescape=True)[0]
92+
self.terms_template = self.read_template("terms.html")
9393

9494
if consent_config is None:
9595
return

synapse/config/registration.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,7 @@ def read_config(self, config, **kwargs):
176176
self.session_lifetime = session_lifetime
177177

178178
# The success template used during fallback auth.
179-
self.fallback_success_template = self.read_templates(
180-
["auth_success.html"], autoescape=True
181-
)[0]
179+
self.fallback_success_template = self.read_template("auth_success.html")
182180

183181
def generate_config_section(self, generate_secrets=False, **kwargs):
184182
if generate_secrets:

synapse/push/mailer.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,15 @@ def make_unsubscribe_link(
668668

669669

670670
def safe_markup(raw_html: str) -> jinja2.Markup:
671+
"""
672+
Sanitise a raw HTML string to a set of allowed tags and attributes, and linkify any bare URLs.
673+
674+
Args
675+
raw_html: Unsafe HTML.
676+
677+
Returns:
678+
A Markup object ready to safely use in a Jinja template.
679+
"""
671680
return jinja2.Markup(
672681
bleach.linkify(
673682
bleach.clean(
@@ -684,8 +693,13 @@ def safe_markup(raw_html: str) -> jinja2.Markup:
684693

685694
def safe_text(raw_text: str) -> jinja2.Markup:
686695
"""
687-
Process text: treat it as HTML but escape any tags (ie. just escape the
688-
HTML) then linkify it.
696+
Sanitise text (escape any HTML tags), and then linkify any bare URLs.
697+
698+
Args
699+
raw_text: Unsafe text which might include HTML markup.
700+
701+
Returns:
702+
A Markup object ready to safely use in a Jinja template.
689703
"""
690704
return jinja2.Markup(
691705
bleach.linkify(bleach.clean(raw_text, tags=[], attributes={}, strip=False))

synapse/res/templates/sso_auth_bad_user.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<body>
66
<div>
77
<p>
8-
We were unable to validate your <tt>{{server_name | e}}</tt> account via
8+
We were unable to validate your <tt>{{ server_name }}</tt> account via
99
single-sign-on (SSO), because the SSO Identity Provider returned
1010
different details than when you logged in.
1111
</p>

synapse/res/templates/sso_auth_confirm.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<body>
66
<div>
77
<p>
8-
A client is trying to {{ description | e }}. To confirm this action,
9-
<a href="{{ redirect_url | e }}">re-authenticate with single sign-on</a>.
8+
A client is trying to {{ description }}. To confirm this action,
9+
<a href="{{ redirect_url }}">re-authenticate with single sign-on</a>.
1010
If you did not expect this, your account may be compromised!
1111
</p>
1212
</div>

synapse/res/templates/sso_error.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<p>
1313
There was an error during authentication:
1414
</p>
15-
<div id="errormsg" style="margin:20px 80px">{{ error_description | e }}</div>
15+
<div id="errormsg" style="margin:20px 80px">{{ error_description }}</div>
1616
<p>
1717
If you are seeing this page after clicking a link sent to you via email, make
1818
sure you only click the confirmation link once, and that you open the

synapse/res/templates/sso_login_idp_picker.html

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@
33
<head>
44
<meta charset="UTF-8">
55
<link rel="stylesheet" href="/_matrix/static/client/login/style.css">
6-
<title>{{server_name | e}} Login</title>
6+
<title>{{ server_name }} Login</title>
77
</head>
88
<body>
99
<div id="container">
10-
<h1 id="title">{{server_name | e}} Login</h1>
10+
<h1 id="title">{{ server_name }} Login</h1>
1111
<div class="login_flow">
1212
<p>Choose one of the following identity providers:</p>
1313
<form>
14-
<input type="hidden" name="redirectUrl" value="{{redirect_url | e}}">
14+
<input type="hidden" name="redirectUrl" value="{{ redirect_url }}">
1515
<ul class="radiobuttons">
1616
{% for p in providers %}
1717
<li>
18-
<input type="radio" name="idp" id="prov{{loop.index}}" value="{{p.idp_id}}">
19-
<label for="prov{{loop.index}}">{{p.idp_name | e}}</label>
18+
<input type="radio" name="idp" id="prov{{ loop.index }}" value="{{ p.idp_id }}">
19+
<label for="prov{{ loop.index }}">{{ p.idp_name }}</label>
2020
{% if p.idp_icon %}
21-
<img src="{{p.idp_icon | mxc_to_http(32, 32)}}"/>
21+
<img src="{{ p.idp_icon | mxc_to_http(32, 32) }}"/>
2222
{% endif %}
2323
</li>
2424
{% endfor %}

synapse/res/templates/sso_redirect_confirm.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
<title>SSO redirect confirmation</title>
66
</head>
77
<body>
8-
<p>The application at <span style="font-weight:bold">{{ display_url | e }}</span> is requesting full access to your <span style="font-weight:bold">{{ server_name }}</span> Matrix account.</p>
8+
<p>The application at <span style="font-weight:bold">{{ display_url }}</span> is requesting full access to your <span style="font-weight:bold">{{ server_name }}</span> Matrix account.</p>
99
<p>If you don't recognise this address, you should ignore this and close this tab.</p>
1010
<p>
11-
<a href="{{ redirect_url | e }}">I trust this address</a>
11+
<a href="{{ redirect_url }}">I trust this address</a>
1212
</p>
1313
</body>
14-
</html>
14+
</html>

0 commit comments

Comments
 (0)