@@ -108,24 +108,44 @@ your database.
108
108
Migrating from 2.4.0 to 2.5.0
109
109
=============================
110
110
111
- .. _CVE-2025-pending :
111
+ .. _CVE-2025-53865 :
112
112
113
113
XSS security issue with devel and responsive templates (recommended)
114
114
--------------------------------------------------------------------
115
115
116
+ There are actually two different issues under this heading.
117
+
118
+ 1. incorrect use of the ``structure`` keyword with
119
+ ``tal:content``
120
+ 2. use of ``tal:replace`` on unsafe input
121
+
122
+ In the discussion below, the :term:`html directory` means one or
123
+ more directories listed in the ``templates`` key of your
124
+ tracker's ``config.ini`` file.
125
+
126
+ These directions can be used to solve the XSS security issue with
127
+ any version of Roundup. Even if you used a classic or minimal
128
+ template, you should check your trackers for these issues. The
129
+ classic template fixed most of these many years ago, but the
130
+ updates were not made to the devel and responsive templates. No
131
+ report of similar issues with the jinja template has been seen.
132
+
133
+ Incorrect use of structure in templates
134
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
135
+
116
136
The devel and responsive templates prior to Roundup 2.5 used this
117
137
construct::
118
138
119
139
tal:content="structure context/MUMBLE/plain"
120
140
121
141
Where ``MUMBLE`` is a property of your issues (e.g. title).
122
142
123
- This construct allows a URL with a carefully crafted query parameter
124
- to execute arbitrary JavaScript.
143
+ This construct allows a URL with a carefully crafted query
144
+ parameter to execute arbitrary JavaScript.
125
145
126
- You should check all your trackers. The classic template has not used
127
- this construct since at least 2009, but your tracker's templates may
128
- use the offending construct anyway.
146
+ You should check all your trackers. The classic template has not
147
+ used this construct since at least 2009, but your tracker's
148
+ templates may use the offending construct anyway.
129
149
130
150
This fix will apply if your tracker is based on the responsive or
131
151
devel template. Check the TEMPLATE-INFO.txt file in your tracker
@@ -138,53 +158,190 @@ field. For example a Name like::
138
158
139
159
shows that tracker is based on the responsive or devel templates.
140
160
141
- .. _cve-2025-pending -fixed:
161
+ .. _cve-2025-53865 -fixed:
142
162
143
- To fix this, remove the ``structure`` declaration when it is used with
144
- a plain representation. So fixing the code by replacing the example
145
- above with::
163
+ To fix this, remove the ``structure`` declaration when it is used
164
+ with a plain representation. So fixing the code by replacing the
165
+ example above with::
146
166
147
167
tal:content="context/MUMBLE/plain"
148
168
149
169
prevents the attack.
150
170
151
171
To check for this issue, search for ``structure`` followed by
152
172
``/plain`` in all your html templates. If you are on a Linux/Unix
153
- system you can search the html subdirectory of your tracker with the
154
- following::
173
+ system you can search the html subdirectory of your tracker with
174
+ the following::
155
175
156
176
grep 'structure.*/plain' *.html
157
177
158
178
which should return any lines with issues.
159
179
160
180
.. warning::
161
181
162
- Backup the files in the ``html`` subdirectory of your tracker in case
163
- an edit goes wrong.
182
+ Backup the files in the ``html`` subdirectory of your tracker
183
+ in case an edit goes wrong.
164
184
165
- You can fix this issue using the GNU sed command::
185
+ As an example, you could fix this issue using the GNU sed
186
+ command::
166
187
167
188
sed -i.bak -e '/structure.*\/plain/s/structure.//' *.html
168
189
169
- to edit the files in place and remove the structure keyword. It will
170
- create a ``.bak`` file with the original contents of the file. If you
171
- are on windows, some text editors support search and replace using a
172
- regular expression.
190
+ to edit the files in place and remove the structure keyword. It
191
+ will create a ``.bak`` file with the original contents of the
192
+ file. If your templates were changed, this might still miss some
193
+ entries. If you are on windows, some text editors support search
194
+ and replace using a regular expression.
173
195
174
196
If the construct is split across lines::
175
197
176
198
tal:content="structure
177
199
context/MUMBLE/plain"
178
200
179
- the commands above will miss the construct. So you should also search
180
- the html files using ``grep /plain *.html`` and verify that all of the
181
- ``context/MUMBLE/plain`` include ``tal:content`` as in the `fixed
182
- example above <#cve-2025-pending -fixed>`_. Any lines that have
183
- ``context/MUMBLE/plain`` without ``tal:content=`` before it need to be
184
- manually verified/fixed.
201
+ the commands above will miss the construct. So you should also
202
+ search the html files using ``grep /plain *.html`` and verify
203
+ that all of the ``context/MUMBLE/plain`` include ``tal:content``
204
+ as in the `fixed example above <#cve-2025-53865 -fixed>`_. Any
205
+ lines that have ``context/MUMBLE/plain`` without ``tal:content=``
206
+ before it need to be manually verified/fixed.
185
207
186
208
The distributed devel and responsive templates do not split the
187
- construct across lines, but if you changed the files it may be split.
209
+ construct across lines, but if you changed the files it may be
210
+ split.
211
+
212
+ tal:replace used with unsafe input
213
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
214
+
215
+ The problem was caused by the following markup::
216
+
217
+ <span tal:replace="context/MUMBLE" />
218
+
219
+ in the head of the ``bug.item.html``, ``task.item.html`` and
220
+ other files in the devel and responsive templates.
221
+
222
+ This was fixed many years ago in the classic template's
223
+ ``index.item.html``. The classic template replaces the above
224
+ construct with::
225
+
226
+ <tal:x tal:content="context/MUMBLE" />
227
+
228
+ ``tal:content`` explicitly escapes the result unless the
229
+ ``structure`` directive is used. ``tal:replace`` expects the
230
+ result to be safe and usable in an HTML context.
231
+
232
+ TAL drops any tags that it doesn't know about from the output.
233
+ ``<tal:x tal:content="..." />`` results in the value of the
234
+ content expression without a surrounding html tag. (Effectively
235
+ replacing the construct.)
236
+
237
+ The following diff for ``bug.item.html`` in the devel template
238
+ shows the change to make things safe (remove lines starting with
239
+ ``-`` and add lines staring with ``+``)::
240
+
241
+ <tal:block metal:use-macro="templates/page/macros/frame">
242
+ <title metal:fill-slot="head_title">
243
+ <tal:block condition="context/id" i18n:translate=""
244
+ - >Bug <span tal:replace="context/id" i18n:name="id"
245
+ - />: <span tal:replace="context/title" i18n:name="title"
246
+ - /> - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
247
+ + >Bug <tal:x tal:content="context/id" i18n:name="id"
248
+ + />: <tal:x tal:content="context/title" i18n:name="title"
249
+ + /> - <tal:x tal:content="config/TRACKER_NAME" i18n:name="tracker"
250
+ /></tal:block>
251
+ <tal:block condition="not:context/id" i18n:translate=""
252
+ >New Bug report - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
253
+
254
+ A similar change was applied in the following html files in the
255
+ devel or responsive templates:
256
+
257
+ .. rst-class:: multicol
258
+
259
+ * _generic.collision.html
260
+ * bug.item.html
261
+ * keyword.item.html
262
+ * milestone.item.html
263
+ * msg.item.html
264
+ * task.item.html
265
+ * user.item.html
266
+
267
+ Also ``page.html`` should be changed from::
268
+
269
+ <p class="label"><b tal:replace="request/user/username">username</b></p>
270
+
271
+ to::
272
+
273
+ <p class="label"><b tal:replace="python:request.user.username.plain(escape=1)">username</b></p>
274
+
275
+ The code audit found the ``tal:replace`` construct is used with
276
+ ``context/id`` and ``context/designator`` paths. The references
277
+ to these paths have been changed to use ``tal:x`` in the classic
278
+ template's ``msg.item.html`` file and the classic and minimal
279
+ template's ``_generic.collision.html`` file.
280
+
281
+ These paths are critical to navigation in Roundup and are set
282
+ from the path part of the URL. Roundup's URL path validation
283
+ makes it unlikely that an attacker could exploit them. If you
284
+ wish you can change your templates or copy the corresponding
285
+ files from the template if you haven't made local changes.
286
+
287
+ Also you may have used copies of these insecure templates
288
+ elsewhere in your tracker (e.g. to create a feature class). To
289
+ find other possible issues you can use the command::
290
+
291
+ grep -r "tal:replace=" *.html
292
+
293
+ in your tracker's :term:`html directory`. Check each occurrence
294
+ and if needed, change it to the safer form. You should consider
295
+ any reference to ``context`` to be under the user's (attacker's)
296
+ control. Also ``db`` (excluding ``db/config``) and ``request``
297
+ references that use user supplied content
298
+ (e.g. ``request/user/username`` above) should be changed to
299
+ ``tal:x`` form
300
+
301
+ .. comment:
302
+ As part of the analysis, the following command was used to find
303
+ potentially vulnerable stuff in the templates. Each grep -v was
304
+ removed to display items in that category and they were checked::
305
+
306
+ grep -r 'tal:replace' . | grep -v 'replace="batch' | \
307
+ grep -v 'replace="config' | grep -v 'replace="db/config' | \
308
+ grep -v 'replace="structure' | grep -v 'replace="python:' | \
309
+ grep -v 'replace="request/'
310
+
311
+
312
+ context/id, context/designator:
313
+ assume safe if used in an class.item.html page as the page
314
+ wouldn't be shown if they weren't valid numbers/designators.
315
+
316
+ Might not be ok referenced in a _generic fallback page though.
317
+
318
+ config, db/config, batch, nothing:
319
+ should be safe as they are not under user control
320
+
321
+ request/classname (python:request._classname), request/template:
322
+ should be safe as they are needed to navigate to a display page,
323
+ so if they are invalid nothing will be displayed.
324
+
325
+ utils, python:
326
+ assume it's written correctly and is safe (could use some new
327
+ tests for the shipped utility functions). The intent of these
328
+ can be to deliver blocks of <script> or other html markup.
329
+
330
+ db, request:
331
+ might be dangerous when accessing user supplied values.
332
+
333
+ request/user/username:
334
+ Escape these. If the username is an XSS issue, an attacker could
335
+ use it to compromise a user.
336
+
337
+ request/dispname:
338
+ should be quoted and is by the existing python: code.
339
+
340
+ Open question: why does there have to be an error generated by the
341
+ url @sort=1. Without invalid sort param, the exploit url doesn't
342
+ work and the context appears to use the database's title not the one
343
+ in the url. Also its not positional @sort=1 can appear anywhere in
344
+ the url.
188
345
189
346
Deprecation Notices (required)
190
347
------------------------------
0 commit comments