Skip to content

Commit fd2d2d9

Browse files
committed
Fix XSS in issue2550817
Note that the code that triggers that particular bug is no longer in roundup core. But the change to the templates we suggest is a *lot* safer as it always escapes the error and ok messages now. If you are upgrading: you *MUST* read doc/upgrading.txt and do the necessary changes to your templates, the escaping now happens in the template and not in the roundup code. So if you don't make the necessary changes *you are vulnerable*.
1 parent ed6ead0 commit fd2d2d9

File tree

10 files changed

+88
-54
lines changed

10 files changed

+88
-54
lines changed

CHANGES.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Fixed:
1717
discovery) (Ralf Schlatterbeck)
1818
- Pythons cgi form code can return a TypeError, we now guard for this
1919
condition. (Ralf Schlatterbeck)
20-
- Small bug-fix in SQL backends: An query (e.g. in a html menu) with a
20+
- Small bug-fix in SQL backends: A query (e.g. in a html menu) with a
2121
where-clause that always evaluates to false now will not raise a
2222
traceback. (Ralf Schlatterbeck)
2323
- Remove Python 2.3 compatibility code for i18n (anatoly techtonik)
@@ -46,6 +46,14 @@ Fixed:
4646
- Fix subtle bug when sorting by a Link that contains a Multilink from
4747
which we also search for an attribute. In that case the LEFT OUTER
4848
JOIN clause was missing in generated SQL. (Ralf Schlatterbeck)
49+
- Fix another XSS issue2550817. Note that the code that triggers that
50+
particular bug is no longer in roundup core. But the change to the
51+
templates we suggest is a *lot* safer as it always escapes the error
52+
and ok messages now.
53+
If you are upgrading: you *MUST* read doc/upgrading.txt and do the
54+
necessary changes to your templates, the escaping now happens in the
55+
template and not in the roundup code. So if you don't make the
56+
necessary changes *you are vulnerable*. (Ralf Schlatterbeck)
4957

5058

5159
2013-07-06: 1.5.0

doc/upgrading.txt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,47 @@ steps.
1313

1414
.. contents::
1515

16+
Migrating from 1.5.0 to 1.5.1
17+
=============================
18+
19+
*Important*:
20+
There was a security bug fixed in the html templates (an XSS
21+
vulnerability). So if you have a running tracker you will have to fix
22+
the file ``html/page.html`` in your tracker directory. You need to
23+
*twice* remove the ``structure`` element in the template and modify the
24+
'tal:content' attribute, you need to replace the section::
25+
26+
<td>
27+
<p tal:condition="options/error_message | nothing" class="error-message"
28+
tal:repeat="m options/error_message"
29+
tal:content="structure string:$m <br/ > " />
30+
<p tal:condition="options/ok_message | nothing" class="ok-message">
31+
<span tal:repeat="m options/ok_message"
32+
tal:content="structure string:$m <br/ > " />
33+
<a class="form-small" tal:attributes="href request/current_url"
34+
i18n:translate="">clear this message</a>
35+
</p>
36+
</td>
37+
38+
with::
39+
40+
<td>
41+
<p tal:condition="options/error_message | nothing" class="error-message"
42+
tal:repeat="m options/error_message" tal:content="m" />
43+
<p tal:condition="options/ok_message | nothing" class="ok-message">
44+
<span tal:repeat="m options/ok_message" tal:content="m" />
45+
<a class="form-small" tal:attributes="href request/current_url"
46+
i18n:translate="">clear this message</a>
47+
</p>
48+
</td>
49+
50+
if you are using the new *jinja2* base templates, we are now iterating
51+
over the error- and ok-messages and creating a paragraph for each
52+
message. In addition ``autoescape`` is turned on for the section (which
53+
is the critical security change).
54+
See ``templates/jinja2/html/layout/page.html`` for details.
55+
56+
1657
Migrating from 1.4.20 to 1.4.21
1758
===============================
1859

roundup/cgi/client.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ def initialiseSecurity(security):
4949
security.addPermissionToRole('Admin', p)
5050

5151
def clean_message(msg):
52-
return cgi.escape (msg).replace ('\n', '<br />\n')
52+
""" A multi-line message is now split at line boundaries.
53+
The templates will do the right thing to format this message.
54+
Note that we no longer need to escape the message as this is now
55+
taken care of by the template.
56+
"""
57+
return msg.split('\n')
5358

5459
error_message = ''"""<html><head><title>An error has occurred</title></head>
5560
<body><h1>An error has occurred</h1>
@@ -877,9 +882,9 @@ def determine_context(self, dre=re.compile(r'([^\d]+)0*(\d+)')):
877882

878883
# see if we were passed in a message
879884
if ok_message:
880-
self.ok_message.append(ok_message)
885+
self.ok_message.extend(ok_message)
881886
if error_message:
882-
self.error_message.append(error_message)
887+
self.error_message.extend(error_message)
883888

884889
# determine the classname and possibly nodeid
885890
path = self.path.split('/')

share/roundup/templates/classic/html/page.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,9 @@ <h2><span metal:define-slot="body_title">body title</span></h2>
170170
</td>
171171
<td>
172172
<p tal:condition="options/error_message | nothing" class="error-message"
173-
tal:repeat="m options/error_message"
174-
tal:content="structure string:$m <br/ > " />
173+
tal:repeat="m options/error_message" tal:content="m" />
175174
<p tal:condition="options/ok_message | nothing" class="ok-message">
176-
<span tal:repeat="m options/ok_message"
177-
tal:content="structure string:$m <br/ > " />
175+
<span tal:repeat="m options/ok_message" tal:content="m" />
178176
<a class="form-small" tal:attributes="href request/current_url"
179177
i18n:translate="">clear this message</a>
180178
</p>

share/roundup/templates/devel/html/page.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,9 @@ <h1><a href="/">Roundup Demo Tracker</a></h1>
234234
<div class="content">
235235
<h1 id="breadcrumb"><span metal:define-slot="body_title">body title</span></h1>
236236
<p tal:condition="options/error_message | nothing" class="error-message"
237-
tal:repeat="m options/error_message"
238-
tal:content="structure string:$m <br/ > " />
237+
tal:repeat="m options/error_message" tal:content="m" />
239238
<p tal:condition="options/ok_message | nothing" class="ok-message">
240-
<span tal:repeat="m options/ok_message"
241-
tal:content="structure string:$m <br/ > " />
239+
<span tal:repeat="m options/ok_message" tal:content="m" />
242240
<a class="form-small" tal:attributes="href request/current_url"
243241
i18n:translate="">clear this message</a>
244242
</p>

share/roundup/templates/jinja2/html/layout/page.html

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,31 @@
2525
{% endblock %}
2626
</div>
2727

28+
{% autoescape true %}
2829
<div class='span9'>
2930
<div class='page-header'>
3031
<h1> {% block page_header %} {% endblock %} </h1>
3132
{% if options.error_message %}
32-
<p class='alert alert-block alert-error fade in'>
33-
{{ options.error_message }}
34-
<button type='button' class='close' data-dismiss='alert'>X</button>
35-
</p>
33+
{% for m in options.error_message %}
34+
<p class='alert alert-block alert-error fade in'>
35+
{{ m }}
36+
<button type='button' class='close' data-dismiss='alert'>X</button>
37+
</p>
38+
{% endfor %}
3639
{% endif %}
3740
{% if options.ok_message %}
38-
<p class='alert alert-block alert-success fade in'>
39-
{{ options.ok_message }}
40-
<button type='button' class='close' data-dismiss='alert'>X</button>
41-
</p>
41+
{% for m in options.ok_message %}
42+
<p class='alert alert-block alert-success fade in'>
43+
{{ m }}
44+
<button type='button' class='close' data-dismiss='alert'>X</button>
45+
</p>
46+
{% endfor %}
4247
{% endif %}
4348
</div>
4449
{% block content %}
4550
{% endblock %}
4651
</div>
52+
{% endautoescape %}
4753
</div>
4854
</div>
4955
{% endblock %}

share/roundup/templates/minimal/html/page.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,9 @@ <h2><span metal:define-slot="body_title">body title</span></h2>
157157
</td>
158158
<td>
159159
<p tal:condition="options/error_message | nothing" class="error-message"
160-
tal:repeat="m options/error_message"
161-
tal:content="structure string:$m <br/ > " />
160+
tal:repeat="m options/error_message" tal:content="m" />
162161
<p tal:condition="options/ok_message | nothing" class="ok-message">
163-
<span tal:repeat="m options/ok_message"
164-
tal:content="structure string:$m <br/ > " />
162+
<span tal:repeat="m options/ok_message" tal:content="m" />
165163
<a class="form-small" tal:attributes="href request/current_url"
166164
i18n:translate="">clear this message</a>
167165
</p>

share/roundup/templates/responsive/html/page.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,11 +250,9 @@
250250
<div class="span8">
251251
<h1><span metal:define-slot="body_title">body title</span></h1>
252252
<p tal:condition="options/error_message | nothing" class="alert alert-error"
253-
tal:repeat="m options/error_message"
254-
tal:content="structure string:$m <br/ > " />
253+
tal:repeat="m options/error_message" tal:content="m" />
255254
<p tal:condition="options/ok_message | nothing" class="alert alert-success">
256-
<span tal:repeat="m options/ok_message"
257-
tal:content="structure string:$m <br/ > " />
255+
<span tal:repeat="m options/ok_message" tal:content="m" />
258256
<a class="form-small" tal:attributes="href request/current_url"
259257
i18n:translate="">clear this message</a>
260258
</p>

test/test_cgi.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,13 @@ def makeForm(args):
4343
cm = client.clean_message
4444
class MessageTestCase(unittest.TestCase):
4545
# Note: We used to allow some html tags in error message. Now *only*
46-
# newlines are allowed which are translated to <br />.
47-
# All other tags are escaped.
46+
# newlines are allowed and messages are split at newlines.
47+
# Note that tags are no longer escaped, see doc/upgrading.txt for
48+
# the changes needed in the templates (Migrating from 1.5.0 to 1.5.1)
4849
def testCleanMessageOK(self):
49-
self.assertEqual(cm('a\nb'), 'a<br />\nb')
50-
self.assertEqual(cm('a\nb\nc\n'), 'a<br />\nb<br />\nc<br />\n')
51-
52-
def testCleanMessageBAD(self):
53-
self.assertEqual(cm('<script>x</script>'),
54-
'&lt;script&gt;x&lt;/script&gt;')
55-
self.assertEqual(cm('<iframe>x</iframe>'),
56-
'&lt;iframe&gt;x&lt;/iframe&gt;')
57-
self.assertEqual(cm('<<script >>alert(42);5<</script >>'),
58-
'&lt;&lt;script &gt;&gt;alert(42);5&lt;&lt;/script &gt;&gt;')
59-
self.assertEqual(cm('<a href="y">x</a>'),
60-
'&lt;a href="y"&gt;x&lt;/a&gt;')
61-
self.assertEqual(cm('<A HREF="y">x</A>'),
62-
'&lt;A HREF="y"&gt;x&lt;/A&gt;')
63-
self.assertEqual(cm('<br>x<br />'), '&lt;br&gt;x&lt;br /&gt;')
64-
self.assertEqual(cm('<i>x</i>'), '&lt;i&gt;x&lt;/i&gt;')
65-
self.assertEqual(cm('<b>x</b>'), '&lt;b&gt;x&lt;/b&gt;')
66-
self.assertEqual(cm('<BR>x<BR />'), '&lt;BR&gt;x&lt;BR /&gt;')
67-
self.assertEqual(cm('<I>x</I>'), '&lt;I&gt;x&lt;/I&gt;')
68-
self.assertEqual(cm('<B>x</B>'), '&lt;B&gt;x&lt;/B&gt;')
50+
self.assertEqual(cm('a'), ['a'])
51+
self.assertEqual(cm('a\nb'), ['a','b'])
52+
self.assertEqual(cm('a\nb\nc\n'), ['a','b','c',''])
6953

7054
class FormTestCase(unittest.TestCase):
7155
def setUp(self):

website/issues/html/page.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,9 @@ <h1><a href="/">Roundup Tracker - Issues</a></h1>
185185
<div class="content">
186186
<h1 id="breadcrumb"><span metal:define-slot="body_title">body title</span></h1>
187187
<p tal:condition="options/error_message | nothing" class="error-message"
188-
tal:repeat="m options/error_message"
189-
tal:content="structure string:$m <br/ > " />
188+
tal:repeat="m options/error_message" tal:content="m" />
190189
<p tal:condition="options/ok_message | nothing" class="ok-message">
191-
<span tal:repeat="m options/ok_message"
192-
tal:content="structure string:$m <br/ > " />
190+
<span tal:repeat="m options/ok_message" tal:content="m" />
193191
<a class="form-small" tal:attributes="href request/current_url"
194192
i18n:translate="">clear this message</a>
195193
</p>

0 commit comments

Comments
 (0)