You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by gc...@apache.org on 2024/02/23 21:46:03 UTC

(allura) branch master updated (e47ddb85c -> b5333e288)

This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git


    from e47ddb85c bump cryptography 42.0.2 -> 42.0.4
     new e80a4cae6 [#8536] use Markup's own interpolation
     new 0b90f747e [#8536] remove old unused oembed templates
     new 5fb7c3eca [#8536] move/improve |safe usage
     new f0226ee0d [#8536] remove unnecessary |safe usages
     new 0917b82b7 [#8536] improve safety
     new cc9a338fc [#8536] more move/improve |safe
     new 63f12b9a7 [#8536] don't use jinja for site notifications; add autoescape
     new b5333e288 [#8536] use h.clean_html and |safe_html

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Allura/allura/app.py                               |  7 ++---
 Allura/allura/config/app_cfg.py                    |  1 +
 Allura/allura/controllers/search.py                |  2 +-
 .../allura/ext/admin/templates/project_groups.html |  2 +-
 .../allura/ext/admin/templates/project_trove.html  |  2 +-
 .../templates/sections/projects.html               |  4 +--
 .../user_profile/templates/sections/projects.html  |  2 +-
 Allura/allura/lib/app_globals.py                   | 19 +++++--------
 Allura/allura/lib/diff.py                          |  3 ++-
 Allura/allura/lib/helpers.py                       | 31 +++++++++++++++++++---
 Allura/allura/lib/search.py                        |  2 +-
 Allura/allura/lib/utils.py                         | 10 +++----
 Allura/allura/lib/widgets/forms.py                 | 30 ++++++++++-----------
 Allura/allura/lib/widgets/search.py                |  6 +++--
 Allura/allura/public/nf/js/allura-base.js          |  2 +-
 Allura/allura/tasks/mail_tasks.py                  |  2 +-
 Allura/allura/templates/jinja_master/master.html   |  6 ++---
 .../templates/jinja_master/sidebar_menu.html       |  2 +-
 .../templates/jinja_master/theme_macros.html       |  2 +-
 .../templates/neighborhood_project_list.html       |  2 +-
 Allura/allura/templates/oauth_applications.html    |  4 +--
 Allura/allura/templates/oauth_authorize.html       |  2 +-
 Allura/allura/templates/oembed/__init__.py         | 16 -----------
 Allura/allura/templates/oembed/generic.html        | 23 ----------------
 Allura/allura/templates/oembed/html_tpl.html       | 25 -----------------
 Allura/allura/templates/oembed/link.html           | 26 ------------------
 Allura/allura/templates/oembed/link_opera.html     | 27 -------------------
 Allura/allura/templates/oembed/link_twitter.html   | 29 --------------------
 Allura/allura/templates/oembed/photo.html          | 25 -----------------
 Allura/allura/templates/project_list.html          |  4 ---
 Allura/allura/templates/repo/barediff.html         |  2 +-
 Allura/allura/templates/repo/diff.html             |  2 +-
 Allura/allura/templates/repo/merge_request.html    |  2 +-
 Allura/allura/templates/user_prefs.html            |  2 +-
 Allura/allura/templates/widgets/include.html       |  2 +-
 Allura/allura/templates/widgets/lightbox.html      |  2 +-
 Allura/allura/templates/widgets/post_widget.html   |  4 +--
 .../templates_responsive/jinja_master/master.html  |  6 ++---
 .../jinja_master/sidebar_menu.html                 |  2 +-
 .../jinja_master/theme_macros.html                 |  2 +-
 Allura/allura/tests/test_globals.py                |  1 +
 Allura/allura/tests/test_helpers.py                | 24 +++++++++++++++++
 ForgeActivity/forgeactivity/templates/macros.html  |  2 +-
 .../templates/blog_widgets/preview_post.html       |  2 +-
 .../templates/blog_widgets/view_post.html          |  2 +-
 ForgeChat/forgechat/templates/chat/day.html        |  2 +-
 .../templates/discussion_widgets/forum_header.html |  2 +-
 .../templates/discussionforums/admin_forums.html   |  2 +-
 ForgeTracker/forgetracker/model/ticket.py          |  2 +-
 .../forgetracker/templates/tracker/ticket.html     |  2 +-
 .../templates/tracker_widgets/ticket_form.html     | 20 +++++++-------
 ForgeTracker/forgetracker/tracker_main.py          |  6 +++--
 ForgeTracker/forgetracker/widgets/ticket_form.py   |  2 +-
 ForgeWiki/forgewiki/templates/wiki/browse.html     |  2 +-
 ForgeWiki/forgewiki/wiki_main.py                   |  5 ++--
 55 files changed, 147 insertions(+), 273 deletions(-)
 delete mode 100644 Allura/allura/templates/oembed/__init__.py
 delete mode 100644 Allura/allura/templates/oembed/generic.html
 delete mode 100644 Allura/allura/templates/oembed/html_tpl.html
 delete mode 100644 Allura/allura/templates/oembed/link.html
 delete mode 100644 Allura/allura/templates/oembed/link_opera.html
 delete mode 100644 Allura/allura/templates/oembed/link_twitter.html
 delete mode 100644 Allura/allura/templates/oembed/photo.html


(allura) 01/08: [#8536] use Markup's own interpolation

Posted by gc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit e80a4cae61866b6435916a4885c6e443f015c318
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Fri Feb 9 11:23:44 2024 -0500

    [#8536] use Markup's own interpolation
---
 Allura/allura/lib/app_globals.py                  | 15 +++++--------
 Allura/allura/lib/search.py                       |  2 +-
 Allura/allura/lib/utils.py                        | 10 ++++-----
 Allura/allura/lib/widgets/forms.py                | 27 ++++++++++-------------
 Allura/allura/tasks/mail_tasks.py                 |  2 +-
 Allura/allura/tests/test_globals.py               |  1 +
 ForgeActivity/forgeactivity/templates/macros.html |  2 +-
 ForgeTracker/forgetracker/model/ticket.py         |  2 +-
 ForgeTracker/forgetracker/widgets/ticket_form.py  |  2 +-
 9 files changed, 28 insertions(+), 35 deletions(-)

diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index eadabd9bd..9cc3d86bb 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -99,17 +99,14 @@ class ForgeMarkdown:
             # if text is too big, markdown can take a long time to process it,
             # so we return it as a plain text
             log.info('Text is too big. Skipping markdown processing')
-            escaped = html.escape(h.really_unicode(source))
-            return Markup('<pre>%s</pre>' % escaped)
+            return Markup('<pre>{}</pre>').format(h.really_unicode(source))
         try:
             return self.make_markdown_instance(**self.forge_ext_kwargs).convert(source)
         except Exception:
             log.info('Invalid markdown: %s  Upwards trace is %s', source,
                      ''.join(traceback.format_stack()), exc_info=True)
-            escaped = h.really_unicode(source)
-            escaped = html.escape(escaped)
             return Markup("""<p><strong>ERROR!</strong> The markdown supplied could not be parsed correctly.
-            Did you forget to surround a code snippet with "~~~~"?</p><pre>%s</pre>""" % escaped)
+            Did you forget to surround a code snippet with "~~~~"?</p><pre>%s</pre>""") % h.really_unicode(source)
 
     @LazyProperty
     def uncacheable_macro_regex(self):
@@ -471,10 +468,8 @@ class Globals:
             lexer = pygments.lexers.get_lexer_by_name(lexer, encoding='chardet')
 
         if lexer is None or len(text) >= asint(config.get('scm.view.max_syntax_highlight_bytes', 500000)):
-            # no highlighting, but we should escape, encode, and wrap it in
-            # a <pre>
-            text = html.escape(text)
-            return Markup('<pre>' + text + '</pre>')
+            # no highlighting, but we should wrap it in a <pre> safely
+            return Markup('<pre>{}</pre>').format(text)
         else:
             return Markup(pygments.highlight(text, lexer, formatter))
 
@@ -686,7 +681,7 @@ class Icon:
         if tag == 'a':
             attrs['href'] = '#'
         attrs.update(kw)
-        attrs = ew._Jinja2Widget().j2_attrs(attrs)
+        attrs = ew._Jinja2Widget().j2_attrs(attrs)  # this escapes them
         visible_title = ''
         if show_title:
             visible_title = f'&nbsp;{Markup.escape(title)}'
diff --git a/Allura/allura/lib/search.py b/Allura/allura/lib/search.py
index 27a29f738..388384798 100644
--- a/Allura/allura/lib/search.py
+++ b/Allura/allura/lib/search.py
@@ -409,4 +409,4 @@ def mapped_artifacts_from_index_ids(index_ids, model, objectid_id=True):
     map = {}
     for m in models:
         map[str(m._id)] = m
-    return map
\ No newline at end of file
+    return map
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index 683a7fcae..0cf6b8c3c 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -211,10 +211,10 @@ def chunked_iter(iterable, max_size):
 class AntiSpam:
 
     '''Helper class for bot-protecting forms'''
-    honey_field_template = string.Template('''<p class="$honey_class">
-    <label for="$fld_id">You seem to have CSS turned off.
+    honey_field_template = '''<p class="{honey_class}">
+    <label for="{fld_id}">You seem to have CSS turned off.
         Please don't fill out this field.</label><br>
-    <input id="$fld_id" name="$fld_name" type="text"><br></p>''')
+    <input id="{fld_id}" name="{fld_name}" type="text"><br></p>'''
 
     def __init__(self, request=None, num_honey=2, timestamp=None, spinner=None):
         self.num_honey = num_honey
@@ -307,10 +307,10 @@ class AntiSpam:
         for fldno in range(self.num_honey):
             fld_name = self.enc('honey%d' % (fldno))
             fld_id = self.enc('honey%d%d' % (self.counter, fldno))
-            yield Markup(self.honey_field_template.substitute(
+            yield Markup(self.honey_field_template).format(
                 honey_class=self.honey_class,
                 fld_id=fld_id,
-                fld_name=fld_name))
+                fld_name=fld_name)
         self.counter += 1
 
     def make_spinner(self, timestamp=None):
diff --git a/Allura/allura/lib/widgets/forms.py b/Allura/allura/lib/widgets/forms.py
index 5252819e1..134cd6f40 100644
--- a/Allura/allura/lib/widgets/forms.py
+++ b/Allura/allura/lib/widgets/forms.py
@@ -102,8 +102,7 @@ class ForgeForm(ew.SimpleForm):
             or ctx.get('label')
             or getattr(field, 'label', None)
             or ctx['name'])
-        html = '<label for="{}">{}</label>'.format(html_escape(ctx['id']), html_escape(label_text))
-        return Markup(html)
+        return Markup('<label for="{}">{}</label>').format(ctx['id'], label_text)
 
     def context_for(self, field):
         ctx = super().context_for(field)
@@ -115,9 +114,8 @@ class ForgeForm(ew.SimpleForm):
         ctx = self.context_for(field)
         display = field.display(**ctx)
         if ctx['errors'] and field.show_errors and not ignore_errors:
-            display = "{}<div class='error'>{}</div>".format(display,
-                                                             ctx['errors'])
-        return Markup(display)
+            display += Markup("<div class='error'>{}</div>").format(ctx['errors'])
+        return display
 
 
 class ForgeFormResponsive(ForgeForm):
@@ -852,18 +850,18 @@ class NeighborhoodOverviewForm(ForgeForm):
 
     def display_field(self, field, ignore_errors=False):
         if field.name == "css" and self.list_color_inputs:
-            display = '<table class="table_class">'
+            display = Markup('<table class="table_class">')
             ctx = self.context_for(field)
             for inp in self.color_inputs:
                 additional_inputs = inp.get('additional', '')
                 empty_val = False
                 if inp['value'] is None or inp['value'] == '':
                     empty_val = True
-                display += '<tr><td class="left"><label>%(label)s</label></td>' \
-                           '<td><input type="checkbox" name="%(ctx_name)s-%(inp_name)s-def" %(def_checked)s>default</td>' \
-                           '<td class="right"><div class="%(ctx_name)s-%(inp_name)s-inp"><table class="input_inner">' \
-                           '<tr><td><input type="text" class="%(inp_type)s" name="%(ctx_name)s-%(inp_name)s" ' \
-                           'value="%(inp_value)s"></td><td>%(inp_additional)s</td></tr></table></div></td></tr>\n' % {
+                display += Markup('<tr><td class="left"><label>%(label)s</label></td>'
+                           '<td><input type="checkbox" name="%(ctx_name)s-%(inp_name)s-def" %(def_checked)s>default</td>'
+                           '<td class="right"><div class="%(ctx_name)s-%(inp_name)s-inp"><table class="input_inner">'
+                           '<tr><td><input type="text" class="%(inp_type)s" name="%(ctx_name)s-%(inp_name)s" '
+                           'value="%(inp_value)s"></td><td>%(inp_additional)s</td></tr></table></div></td></tr>\n') % {
                                'ctx_name': ctx['name'],
                                'inp_name': inp['name'],
                                'inp_value': inp['value'],
@@ -871,13 +869,12 @@ class NeighborhoodOverviewForm(ForgeForm):
                                'inp_type': inp['type'],
                                'def_checked': 'checked="checked"' if empty_val else '',
                                'inp_additional': additional_inputs}
-            display += '</table>'
+            display += Markup('</table>')
 
             if ctx['errors'] and field.show_errors and not ignore_errors:
-                display = "{}<div class='error'>{}</div>".format(display,
-                                                                 ctx['errors'])
+                display += Markup("<div class='error'>{}</div>").format(ctx['errors'])
 
-            return Markup(display)
+            return display
         else:
             return super().display_field(field, ignore_errors)
 
diff --git a/Allura/allura/tasks/mail_tasks.py b/Allura/allura/tasks/mail_tasks.py
index 8aae497c4..dd16f7caa 100644
--- a/Allura/allura/tasks/mail_tasks.py
+++ b/Allura/allura/tasks/mail_tasks.py
@@ -48,7 +48,7 @@ def mail_meta_content(metalink):
       <meta itemprop="name" content="View"></meta>
     </div>
     <meta itemprop="description" content="View"></meta>
-    </div>""" % metalink)
+    </div>""") % metalink
 
 
 @task
diff --git a/Allura/allura/tests/test_globals.py b/Allura/allura/tests/test_globals.py
index 472c1f3a1..27a2ba266 100644
--- a/Allura/allura/tests/test_globals.py
+++ b/Allura/allura/tests/test_globals.py
@@ -441,6 +441,7 @@ class Test():
         text = 'a' * 40001
         assert g.markdown.convert(text) == '<pre>%s</pre>' % text
         assert g.markdown_wiki.convert(text) == '<pre>%s</pre>' % text
+        assert g.markdown.convert('<b>' + text) == '<pre>&lt;b&gt;%s</pre>' % text
 
     def test_markdown_basics(self):
         with h.push_context('test', 'wiki', neighborhood='Projects'):
diff --git a/ForgeActivity/forgeactivity/templates/macros.html b/ForgeActivity/forgeactivity/templates/macros.html
index 8b08adbf0..970da40d0 100644
--- a/ForgeActivity/forgeactivity/templates/macros.html
+++ b/ForgeActivity/forgeactivity/templates/macros.html
@@ -66,4 +66,4 @@
         </ul>
         <a class="view-all" href="{{activity_app.url}}">View All</a>
     {% endif %}
-{%- endmacro %}
\ No newline at end of file
+{%- endmacro %}
diff --git a/ForgeTracker/forgetracker/model/ticket.py b/ForgeTracker/forgetracker/model/ticket.py
index bc6910df8..de099a372 100644
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -679,7 +679,7 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
     def link_text(self):
         text = super().link_text()
         if self.is_closed:
-            return markupsafe.Markup('<s>') + text + markupsafe.Markup('</s>')
+            return markupsafe.Markup('<s>{}</s>').format(text)
         return text
 
     @property
diff --git a/ForgeTracker/forgetracker/widgets/ticket_form.py b/ForgeTracker/forgetracker/widgets/ticket_form.py
index 1781eb804..7b960c1f9 100644
--- a/ForgeTracker/forgetracker/widgets/ticket_form.py
+++ b/ForgeTracker/forgetracker/widgets/ticket_form.py
@@ -79,7 +79,7 @@ class GenericTicketForm(ew.SimpleForm):
 
         display = field.display(**ctx)
         if ctx['errors'] and field.show_errors and not ignore_errors:
-            display += Markup("<div class='error'>") + ctx['errors'] + Markup("</div>")
+            display += Markup("<div class='error'>{}</div>").format(ctx['errors'])
         return display
 
     def _add_current_value_to_user_field(self, field, user):


(allura) 06/08: [#8536] more move/improve |safe

Posted by gc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit cc9a338fcf6478d41a6267d594c314af9641e9c6
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Mon Feb 12 18:16:21 2024 -0500

    [#8536] more move/improve |safe
---
 Allura/allura/lib/widgets/search.py           | 5 +++--
 Allura/allura/templates/user_prefs.html       | 2 +-
 Allura/allura/templates/widgets/lightbox.html | 2 +-
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/Allura/allura/lib/widgets/search.py b/Allura/allura/lib/widgets/search.py
index 2bef28700..cec39ad44 100644
--- a/Allura/allura/lib/widgets/search.py
+++ b/Allura/allura/lib/widgets/search.py
@@ -18,6 +18,7 @@
 import ew as ew_core
 import ew.jinja2_ew as ew
 import jinja2
+from markupsafe import Markup
 
 from allura.lib.widgets import form_fields as ffw
 
@@ -53,8 +54,8 @@ class SearchHelp(ffw.Lightbox):
         # can't use g.jinja2_env since this widget gets imported too early :(
         jinja2_env = jinja2.Environment(
             loader=jinja2.PackageLoader('allura', 'templates/widgets'))
-        self.content = jinja2_env.get_template('search_help.html').render(dict(
+        self.content = Markup(jinja2_env.get_template('search_help.html').render(dict(
             comments=comments,
             history=history,
             fields=fields,
-        ))
+        )))
diff --git a/Allura/allura/templates/user_prefs.html b/Allura/allura/templates/user_prefs.html
index 6610ad19f..62d95352d 100644
--- a/Allura/allura/templates/user_prefs.html
+++ b/Allura/allura/templates/user_prefs.html
@@ -85,7 +85,7 @@
           </p>
         </div>
       </form>
-      {{c.enter_password.display(content='<h1>Enter password</h1>')}}
+      {{c.enter_password.display(content='<h1>Enter password</h1>'|safe)}}
       <!-- end popup -->
 
       {% endif %}  {# allow_edit_prefs #}
diff --git a/Allura/allura/templates/widgets/lightbox.html b/Allura/allura/templates/widgets/lightbox.html
index 82972260f..624a49f2d 100644
--- a/Allura/allura/templates/widgets/lightbox.html
+++ b/Allura/allura/templates/widgets/lightbox.html
@@ -21,6 +21,6 @@
   {% if content_template %}
     {% include content_template with context %}
   {% else %}
-    {{content|safe}}
+    {{content}}
   {% endif %}
 </div>


(allura) 05/08: [#8536] improve safety

Posted by gc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 0917b82b7f7ad91f74416547b858066afad2985d
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Fri Feb 9 16:17:26 2024 -0500

    [#8536] improve safety
---
 Allura/allura/ext/personal_dashboard/templates/sections/projects.html | 4 ++--
 Allura/allura/ext/user_profile/templates/sections/projects.html       | 2 +-
 Allura/allura/lib/widgets/forms.py                                    | 3 ++-
 Allura/allura/templates/jinja_master/master.html                      | 2 +-
 Allura/allura/templates_responsive/jinja_master/master.html           | 2 +-
 5 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/Allura/allura/ext/personal_dashboard/templates/sections/projects.html b/Allura/allura/ext/personal_dashboard/templates/sections/projects.html
index ccbd270a3..b65f7971d 100644
--- a/Allura/allura/ext/personal_dashboard/templates/sections/projects.html
+++ b/Allura/allura/ext/personal_dashboard/templates/sections/projects.html
@@ -43,7 +43,7 @@
                 {%- endif -%}
                 <span class="project-info">
                 <a href="{{ project.url() }}">{{ project.name }}</a>
-                {{ project.summary or '&nbsp;'|safe }}
+                {{ project.summary or ('&nbsp;'|safe) }}
             </span>
                 <span class="project-last-updated">
                 Last Updated:
@@ -71,4 +71,4 @@
         $(this).hide().closest('.section-body').find('li.hidden').show();
     });
     </script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/Allura/allura/ext/user_profile/templates/sections/projects.html b/Allura/allura/ext/user_profile/templates/sections/projects.html
index e774111d6..3b306ec8d 100644
--- a/Allura/allura/ext/user_profile/templates/sections/projects.html
+++ b/Allura/allura/ext/user_profile/templates/sections/projects.html
@@ -43,7 +43,7 @@
             {%- endif -%}
             <span class="project-info">
                 <a href="{{project.url()}}">{{project.name}}</a>
-                {{project.summary or '&nbsp;'|safe}}
+                {{project.summary or ('&nbsp;'|safe)}}
             </span>
             <span class="project-last-updated">
                 Last Updated:
diff --git a/Allura/allura/lib/widgets/forms.py b/Allura/allura/lib/widgets/forms.py
index 134cd6f40..65121ed5c 100644
--- a/Allura/allura/lib/widgets/forms.py
+++ b/Allura/allura/lib/widgets/forms.py
@@ -18,6 +18,7 @@
 import logging
 from html import escape as html_escape
 
+import html
 from tg import app_globals as g, tmpl_context as c
 from formencode import validators as fev
 import formencode
@@ -616,7 +617,7 @@ class RemoveTroveCategoryForm(ForgeForm):
                         text=cat.fullname,
                         href="/categories/%s" % cat.trove_cat_id),
                     ew.HTMLField(
-                        text=cat.shortname,
+                        text=html.escape(cat.shortname),
                         attrs={'disabled': True, 'value': cat.shortname}),
                     ew.SubmitButton(
                         show_errors=False,
diff --git a/Allura/allura/templates/jinja_master/master.html b/Allura/allura/templates/jinja_master/master.html
index 72c03bc2d..6d0d829c1 100644
--- a/Allura/allura/templates/jinja_master/master.html
+++ b/Allura/allura/templates/jinja_master/master.html
@@ -173,7 +173,7 @@
 {{ theme_macros.custom_js() }}
 
 {% if flash %}
-    <script type="text/javascript">{{ flash | safe }}</script>
+    <script type="text/javascript">{{ flash | safe }}</script>{# comes from flash.static_template in root.py and escaped by tg.flash allow_html setting #}
 {% endif %}
 <script>
     $(document).ready(function () {
diff --git a/Allura/allura/templates_responsive/jinja_master/master.html b/Allura/allura/templates_responsive/jinja_master/master.html
index be687919c..3786e2b88 100644
--- a/Allura/allura/templates_responsive/jinja_master/master.html
+++ b/Allura/allura/templates_responsive/jinja_master/master.html
@@ -161,7 +161,7 @@
 {% endif %}
 {{ theme_macros.custom_js() }}
 {% if flash %}
-    <script type="text/javascript">{{ flash | safe }}</script>
+    <script type="text/javascript">{{ flash | safe }}</script>{# comes from flash.static_template in root.py and escaped by tg.flash allow_html setting #}
 {% endif %}
 </body>
 </html>


(allura) 08/08: [#8536] use h.clean_html and |safe_html

Posted by gc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit b5333e288be17b6e2fcdd2d486f0883d4fdc7141
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Mon Feb 12 12:20:06 2024 -0500

    [#8536] use h.clean_html and |safe_html
---
 Allura/allura/config/app_cfg.py                             | 1 +
 Allura/allura/ext/admin/templates/project_trove.html        | 2 +-
 Allura/allura/lib/helpers.py                                | 9 ++++++++-
 Allura/allura/templates/jinja_master/master.html            | 4 ++--
 Allura/allura/templates/neighborhood_project_list.html      | 2 +-
 Allura/allura/templates_responsive/jinja_master/master.html | 4 ++--
 Allura/allura/tests/test_helpers.py                         | 5 +++++
 7 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/Allura/allura/config/app_cfg.py b/Allura/allura/config/app_cfg.py
index 33e5148c4..e0a5cfb4c 100644
--- a/Allura/allura/config/app_cfg.py
+++ b/Allura/allura/config/app_cfg.py
@@ -143,6 +143,7 @@ class AlluraJinjaRenderer(JinjaRenderer):
         jinja2_env.filters['filter'] = lambda s, t=None: list(filter(t and jinja2_env.tests[t], s))
         jinja2_env.filters['nl2br'] = helpers.nl2br_jinja_filter
         jinja2_env.filters['subrender'] = helpers.subrender_jinja_filter
+        jinja2_env.filters['safe_html'] = helpers.clean_html
         jinja2_env.globals.update({
             'hasattr': hasattr,
             'h': helpers,
diff --git a/Allura/allura/ext/admin/templates/project_trove.html b/Allura/allura/ext/admin/templates/project_trove.html
index 0d1a9da78..1fbff6e95 100644
--- a/Allura/allura/ext/admin/templates/project_trove.html
+++ b/Allura/allura/ext/admin/templates/project_trove.html
@@ -27,7 +27,7 @@
   {% set help_text = config.get('trovecategories.admin.help.'+base.shortname, '') %}
   {% if help_text %}
     <div class="grid-19">
-      {{ help_text|safe }}
+      {{ help_text|safe_html }}
       <br><br>
     </div>
   {% endif %}
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index 26dd2d94f..f0675e443 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -809,7 +809,7 @@ def subrender_jinja_filter(context, html_tmpl: str) -> Markup:
             log.exception(f'Could not replace {var} in jinja "subrender" for site notification')
             continue
         html_tmpl = html_tmpl.replace(var, val)
-    return Markup(html_tmpl)
+    return clean_html(html_tmpl)
 
 
 def nl2br_jinja_filter(value):
@@ -1378,3 +1378,10 @@ def pluralize_tool_name(tool_name: string, count: int):
 def parse_fediverse_address(username: str):
     pieces = username.split('@')
     return f'https://{pieces[-1]}/@{pieces[1]}'
+
+
+def clean_html(value: str) -> Markup:
+    from allura.lib.markdown_extensions import HTMLSanitizer
+    return Markup(
+        HTMLSanitizer().run(value)
+    )
diff --git a/Allura/allura/templates/jinja_master/master.html b/Allura/allura/templates/jinja_master/master.html
index 6d0d829c1..19cb43ca1 100644
--- a/Allura/allura/templates/jinja_master/master.html
+++ b/Allura/allura/templates/jinja_master/master.html
@@ -56,11 +56,11 @@
 
     {% if c.project and c.project.neighborhood.css %}
         <style type="text/css">
-            {{c.project.neighborhood.get_custom_css()|safe}}
+            {{ c.project.neighborhood.get_custom_css()|safe_html }}
         </style>
     {% elif neighborhood|default and neighborhood.css %}
         <style type="text/css">
-            {{neighborhood.get_custom_css()}}
+            {{ neighborhood.get_custom_css()|safe_html }}
         </style>
     {% endif %}
     {% block extra_css %}{% endblock %}
diff --git a/Allura/allura/templates/neighborhood_project_list.html b/Allura/allura/templates/neighborhood_project_list.html
index 91fecd345..53e33b3e1 100644
--- a/Allura/allura/templates/neighborhood_project_list.html
+++ b/Allura/allura/templates/neighborhood_project_list.html
@@ -45,7 +45,7 @@
       {{ text }}
     {% endif %}
     {% if neighborhood.homepage %}
-      {{neighborhood.homepage|safe}}
+      {{neighborhood.homepage|safe_html}}
     {% endif %}
     {% if neighborhood.allow_browse %}
       {% if not projects %}
diff --git a/Allura/allura/templates_responsive/jinja_master/master.html b/Allura/allura/templates_responsive/jinja_master/master.html
index 3786e2b88..5d28d00dc 100644
--- a/Allura/allura/templates_responsive/jinja_master/master.html
+++ b/Allura/allura/templates_responsive/jinja_master/master.html
@@ -58,11 +58,11 @@
 
     {% if c.project and c.project.neighborhood.css %}
         <style type="text/css">
-            {{c.project.neighborhood.get_custom_css()|safe}}
+            {{ c.project.neighborhood.get_custom_css()|safe_html }}
         </style>
     {% elif neighborhood|default and neighborhood.css %}
         <style type="text/css">
-            {{neighborhood.get_custom_css()}}
+            {{ neighborhood.get_custom_css()|safe_html }}
         </style>
     {% endif %}
     {% block extra_css %}{% endblock %}
diff --git a/Allura/allura/tests/test_helpers.py b/Allura/allura/tests/test_helpers.py
index bb7908c9b..99eae9ce4 100644
--- a/Allura/allura/tests/test_helpers.py
+++ b/Allura/allura/tests/test_helpers.py
@@ -707,3 +707,8 @@ def test_querystring():
             'https://mysite.com/p/test/foobar/p/test/foobar?page=2&limit=5&count=100')
     assert (h.querystring(req, dict(page=5, limit=2, count=None)) ==
             'https://mysite.com/p/test/foobar/p/test/foobar?page=5&limit=2')
+
+def test_clean_html():
+    assert h.clean_html('<script>alert(1)</script>') == '&lt;script&gt;alert(1)&lt;/script&gt;'
+    assert h.clean_html('<b style="color: red; right: 0">ok</b>') == '<b style="color: red;">ok</b>'
+    assert isinstance(h.clean_html('foo'), Markup)


(allura) 04/08: [#8536] remove unnecessary |safe usages

Posted by gc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit f0226ee0dc93f6f7ff54944b6cb5f40fbcf64267
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Fri Feb 9 16:13:42 2024 -0500

    [#8536] remove unnecessary |safe usages
---
 Allura/allura/controllers/search.py                  |  2 +-
 Allura/allura/lib/app_globals.py                     |  4 ++--
 Allura/allura/templates/oauth_applications.html      |  4 ++--
 Allura/allura/templates/oauth_authorize.html         |  2 +-
 Allura/allura/templates/project_list.html            |  4 ----
 Allura/allura/templates/repo/merge_request.html      |  2 +-
 Allura/allura/templates/widgets/include.html         |  2 +-
 Allura/allura/templates/widgets/post_widget.html     |  4 ++--
 .../templates/blog_widgets/preview_post.html         |  2 +-
 .../forgeblog/templates/blog_widgets/view_post.html  |  2 +-
 ForgeChat/forgechat/templates/chat/day.html          |  2 +-
 .../templates/discussion_widgets/forum_header.html   |  2 +-
 .../templates/discussionforums/admin_forums.html     |  2 +-
 .../forgetracker/templates/tracker/ticket.html       |  2 +-
 .../templates/tracker_widgets/ticket_form.html       | 20 ++++++++++----------
 ForgeWiki/forgewiki/templates/wiki/browse.html       |  2 +-
 16 files changed, 27 insertions(+), 31 deletions(-)

diff --git a/Allura/allura/controllers/search.py b/Allura/allura/controllers/search.py
index f0db69f73..31c5f1e48 100644
--- a/Allura/allura/controllers/search.py
+++ b/Allura/allura/controllers/search.py
@@ -130,4 +130,4 @@ class ProjectBrowseController(BaseController):
         projects, count = self._find_projects()
         title = self._build_title()
         c.custom_sidebar_menu = self._build_nav()
-        return dict(projects=projects, title=title, text=None)
+        return dict(projects=projects, title=title)
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index 9cc3d86bb..0bdf8be08 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -94,7 +94,7 @@ class ForgeMarkdown:
                         'markdown_checklist.extension'],
             output_format='html')
 
-    def convert(self, source, render_limit=True):
+    def convert(self, source, render_limit=True) -> Markup:
         if render_limit and len(source) > asint(config.get('markdown_render_max_length', 80000)):
             # if text is too big, markdown can take a long time to process it,
             # so we return it as a plain text
@@ -113,7 +113,7 @@ class ForgeMarkdown:
         regex_names = '|'.join(uncacheable_macros_names())
         return re.compile(rf"\[\[\s*({regex_names})\b")
 
-    def cached_convert(self, artifact: MappedClass, field_name: str) -> str:
+    def cached_convert(self, artifact: MappedClass, field_name: str) -> Markup:
         """
         Convert ``artifact.field_name`` markdown source to html, caching
         the result if the render time is greater than the defined threshold.
diff --git a/Allura/allura/templates/oauth_applications.html b/Allura/allura/templates/oauth_applications.html
index 9114f3d3e..c788e7722 100644
--- a/Allura/allura/templates/oauth_applications.html
+++ b/Allura/allura/templates/oauth_applications.html
@@ -83,7 +83,7 @@
             <th>Name:</th><td>{{access_token.consumer_token.name}}</td>
         </tr>
         <tr class="description">
-            <th>Description:</th><td>{{access_token.consumer_token.description_html | safe}}</td>
+            <th>Description:</th><td>{{access_token.consumer_token.description_html }}</td>
         </tr>
         {% if access_token.is_bearer %}
         <tr class="bearer_token">
@@ -117,7 +117,7 @@
     {% for consumer_token in consumer_tokens %}
     <table class="registered_app">
         <tr><th>Name:</th><td>{{consumer_token.name}}</td></tr>
-        <tr class="description"><th>Description:</th><td>{{consumer_token.description_html | safe}}</td></tr>
+        <tr class="description"><th>Description:</th><td>{{consumer_token.description_html }}</td></tr>
         <tr class="consumer_key"><th>Consumer Key:</th><td>{{consumer_token.api_key}}</td></tr>
         <tr class="consumer_secret"><th>Consumer Secret:</th><td>{{consumer_token.secret_key}}</td></tr>
         <tr class="controls">
diff --git a/Allura/allura/templates/oauth_authorize.html b/Allura/allura/templates/oauth_authorize.html
index cd8f9655e..d3afc8b3d 100644
--- a/Allura/allura/templates/oauth_authorize.html
+++ b/Allura/allura/templates/oauth_authorize.html
@@ -51,7 +51,7 @@
 <br style="clear:both"/>
 <div class="flex-container">
     <p><strong>App Name:</strong> {{consumer.name}}</p>
-    <p><strong>Description:</strong> <br> {{consumer.description_html|safe}}</p>
+    <p><strong>Description:</strong> <br> {{consumer.description_html}}</p>
 </div>
 <br style="clear:both"/>
 <div class="flex-container">
diff --git a/Allura/allura/templates/project_list.html b/Allura/allura/templates/project_list.html
index 6232a2411..9d68850ae 100644
--- a/Allura/allura/templates/project_list.html
+++ b/Allura/allura/templates/project_list.html
@@ -51,10 +51,6 @@
 {% block content %}
           {% set old_project = c.project %}
           
-          {% if text %}
-            {{text|safe}}
-          {% endif %}
-          
           {% if not projects %}
             <p>No projects found</p>
           {% else %}
diff --git a/Allura/allura/templates/repo/merge_request.html b/Allura/allura/templates/repo/merge_request.html
index 59168eefd..6f562b8b4 100644
--- a/Allura/allura/templates/repo/merge_request.html
+++ b/Allura/allura/templates/repo/merge_request.html
@@ -135,7 +135,7 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
         To merge the commits, please execute the following commands in your working copy:
       </div>
       <div class="grid-19">
-        <textarea class="merge-instructions" readonly>{{ c.app.repo.merge_command(req) | safe }}</textarea>
+        <textarea class="merge-instructions" readonly>{{ c.app.repo.merge_command(req) }}</textarea>
       </div>
       {% if merge_status not in ('ready', 'busy') %}
         {{ c.mr_dispose_form.display(action="save", value=dict(status=req.status)) }}
diff --git a/Allura/allura/templates/widgets/include.html b/Allura/allura/templates/widgets/include.html
index db9fcaf64..a28ac88ca 100644
--- a/Allura/allura/templates/widgets/include.html
+++ b/Allura/allura/templates/widgets/include.html
@@ -18,7 +18,7 @@
 -#}
 <div{{attrs|default({}, true)|xmlattr}}>
   {% if artifact %}
-    {{artifact.html_text|safe}}
+    {{artifact.html_text}}
   {% else %}
     {{text}}
   {% endif %}
diff --git a/Allura/allura/templates/widgets/post_widget.html b/Allura/allura/templates/widgets/post_widget.html
index ef205bcba..a234a8bc2 100644
--- a/Allura/allura/templates/widgets/post_widget.html
+++ b/Allura/allura/templates/widgets/post_widget.html
@@ -94,7 +94,7 @@
                 <b>{{value.subject or '(no subject)'}}<br/></b>
             {% endif %}
 
-            <div{% if can_moderate_post %} class="active-md" data-markdownlink="{{value.url()}}" {% endif %}>{{g.markdown.cached_convert(value, 'text')|safe}}</div>&nbsp;
+            <div{% if can_moderate_post %} class="active-md" data-markdownlink="{{value.url()}}" {% endif %}>{{g.markdown.cached_convert(value, 'text')}}</div>&nbsp;
             <div class='reactions{% if not c.user.is_anonymous() %} reactions-active{% endif %}' style='user-select: none; cursor: default'>
               {% for reaction in value.react_counts %}<div class="reaction{% if current_reaction == reaction %} reaction-current{% endif %}" data-react="{{ reaction }}"><div class="emoj">{{ h.emojize(reaction) }}</div><div class="emoj-count">{{ value.react_counts[reaction] }}</div></div>{% endfor %}
             </div>
@@ -194,4 +194,4 @@ $('.attachment_toolbar #remove-attachment').on('click', function(e){
     $(this).closest('form').submit();
 })
 </script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/ForgeBlog/forgeblog/templates/blog_widgets/preview_post.html b/ForgeBlog/forgeblog/templates/blog_widgets/preview_post.html
index 81e1d850d..6e0ed4f1a 100644
--- a/ForgeBlog/forgeblog/templates/blog_widgets/preview_post.html
+++ b/ForgeBlog/forgeblog/templates/blog_widgets/preview_post.html
@@ -21,7 +21,7 @@
   <h3>
     <a href="{{value.url()}}">{{value.title}}</a>
   </h3>
-  {{value.html_text_preview|safe}}
+  {{value.html_text_preview}}
   <em class="grid-19">
     Posted by {{lib.user_link(value.author(), avatar=True)}} {{lib.abbr_date(value.timestamp)}}
     {% if value.state != 'published' %}
diff --git a/ForgeBlog/forgeblog/templates/blog_widgets/view_post.html b/ForgeBlog/forgeblog/templates/blog_widgets/view_post.html
index 16e281bfb..7dbdfeae2 100644
--- a/ForgeBlog/forgeblog/templates/blog_widgets/view_post.html
+++ b/ForgeBlog/forgeblog/templates/blog_widgets/view_post.html
@@ -18,7 +18,7 @@
 -#}
 {% import 'allura:templates/jinja_master/lib.html' as lib with context %}
 
-<div{% if h.has_access(value, 'edit') %} class="active-md" data-markdownlink="" {% endif %}>{{value.html_text|safe}}</div>
+<div{% if h.has_access(value, 'edit') %} class="active-md" data-markdownlink="" {% endif %}>{{value.html_text}}</div>
 {{lib.related_artifacts(value, c.user)}}
 <em class="grid-19">
   Posted by {{lib.user_link(base_post.author(), avatar=True)}} {{lib.abbr_date(value.timestamp)}}
diff --git a/ForgeChat/forgechat/templates/chat/day.html b/ForgeChat/forgechat/templates/chat/day.html
index 26ddcacc7..81d396b4a 100644
--- a/ForgeChat/forgechat/templates/chat/day.html
+++ b/ForgeChat/forgechat/templates/chat/day.html
@@ -44,7 +44,7 @@
             {{msg.sender_short}}
           </div>
         </div>
-        <div style='margin-left:13em'>{{g.markdown.cached_convert(msg, 'text') | safe}}</div>
+        <div style='margin-left:13em'>{{g.markdown.cached_convert(msg, 'text')}}</div>
       </div>
       <div style='clear:both'></div>
     </li>
diff --git a/ForgeDiscussion/forgediscussion/templates/discussion_widgets/forum_header.html b/ForgeDiscussion/forgediscussion/templates/discussion_widgets/forum_header.html
index 18b6e483e..c06a94112 100644
--- a/ForgeDiscussion/forgediscussion/templates/discussion_widgets/forum_header.html
+++ b/ForgeDiscussion/forgediscussion/templates/discussion_widgets/forum_header.html
@@ -18,7 +18,7 @@
 -#}
 {% import 'allura:templates/jinja_master/lib.html' as lib with context %}
 
-{{g.markdown.cached_convert(value, 'description')|safe}}
+{{g.markdown.cached_convert(value, 'description')}}
 
 {% if value.announcements %}
   <h3>Announcements</h3>
diff --git a/ForgeDiscussion/forgediscussion/templates/discussionforums/admin_forums.html b/ForgeDiscussion/forgediscussion/templates/discussionforums/admin_forums.html
index ddfad95c5..9048bdb18 100644
--- a/ForgeDiscussion/forgediscussion/templates/discussionforums/admin_forums.html
+++ b/ForgeDiscussion/forgediscussion/templates/discussionforums/admin_forums.html
@@ -69,7 +69,7 @@
             </span>
           </div>
           <div class="editable viewing">
-            <div class="viewer">{{g.markdown.cached_convert(forum, 'description')|safe}}</div>
+            <div class="viewer">{{g.markdown.cached_convert(forum, 'description')}}</div>
             <span class="editor multiline">
               {{lib.text_area('forum-%s.description' % loop.index0, 'Description', h.really_unicode(forum.description))}}
             </span>
diff --git a/ForgeTracker/forgetracker/templates/tracker/ticket.html b/ForgeTracker/forgetracker/templates/tracker/ticket.html
index e17020bf7..8af471d70 100644
--- a/ForgeTracker/forgetracker/templates/tracker/ticket.html
+++ b/ForgeTracker/forgetracker/templates/tracker/ticket.html
@@ -151,7 +151,7 @@
   {{ c.vote_form.display(artifact=ticket) }}
 {% endif %}
 <div id="ticket_content">
-  <div{% if h.has_access(ticket, 'update') %} class="active-md" data-markdownlink=""{% endif %}>{{g.markdown.cached_convert(ticket, 'description')|safe}}</div>
+  <div{% if h.has_access(ticket, 'update') %} class="active-md" data-markdownlink=""{% endif %}>{{g.markdown.cached_convert(ticket, 'description')}}</div>
   {% if ticket.attachments %}
     <strong class="grid-18">{{ticket.attachments|length}} Attachments</strong>
     <div class="clear">
diff --git a/ForgeTracker/forgetracker/templates/tracker_widgets/ticket_form.html b/ForgeTracker/forgetracker/templates/tracker_widgets/ticket_form.html
index 3afc9eb49..39d514503 100644
--- a/ForgeTracker/forgetracker/templates/tracker_widgets/ticket_form.html
+++ b/ForgeTracker/forgetracker/templates/tracker_widgets/ticket_form.html
@@ -18,28 +18,28 @@
 -#}
 {% import 'allura:templates/jinja_master/lib.html' as lib with context %}
 <form method="POST" action="{{action}}" enctype="multipart/form-data" id="ticket-form">
-  {{widget.display_field_by_name('ticket_num')|safe}}
+  {{widget.display_field_by_name('ticket_num')}}
   {% if help_msg %}
     <div id="new-ticket-help-msg" class="grid-19 info">{{g.markdown.convert(help_msg)}}</div>
   {% endif %}
   <div class="grid-18">
     <div class="new-ticket-title">
-      <label>Create Ticket:</label>{{widget.display_field_by_name('summary')|safe}}
+      <label>Create Ticket:</label>{{widget.display_field_by_name('summary')}}
     </div>
   </div>
     {% if widget.fields['_milestone'] %}
       <div class="grid-6">
         <label class="cr">Milestone:</label>
-        {{widget.display_field_by_name('_milestone')|safe}}
+        {{widget.display_field_by_name('_milestone')}}
       </div>
     {% endif %}
     <div class="grid-6">
       <label class="cr">Status:</label>
-      {{widget.display_field_by_name('status')|safe}}
+      {{widget.display_field_by_name('status')}}
     </div>
     <div class="grid-6">
       <label class="cr">Owner:</label>
-      {{widget.display_field_by_name('assigned_to')|safe}}
+      {{widget.display_field_by_name('assigned_to')}}
     </div>
   <div style="clear:both">&nbsp;</div>
   {% set min_c_fields = widget.fields['_milestone'] and 1 or 0 %}
@@ -49,7 +49,7 @@
   {% endif %}
   <div class="grid-12">
     <label class="cr">Labels:</label>
-    {{widget.display_field_by_name('labels')|safe}}
+    {{widget.display_field_by_name('labels')}}
   </div>
     <div class="grid-6">
         {{widget.display_field_by_name('private')}}
@@ -86,7 +86,7 @@
   </div>
   <div style="clear:both">&nbsp;</div>
   <div class="grid-18">
-    {{widget.display_field_by_name('description')|safe}}
+    {{widget.display_field_by_name('description')}}
   </div>
   <div style="clear:both">&nbsp;</div>
   <div class="grid-18">
@@ -106,7 +106,7 @@
       {% endfor %}
       </div>
     {% endif %}
-      {{widget.display_field_by_name('attachment')|safe}}
+      {{widget.display_field_by_name('attachment')}}
   </div>
   <div style="clear:both">&nbsp;</div>
   <div class="reply" style="position:relative;{% if not show_comment %} display:none{% endif %}">
@@ -119,13 +119,13 @@
       </p>
     </div>
     <div class="grid-16">
-      {{widget.display_field_by_name('comment')|safe}}
+      {{widget.display_field_by_name('comment')}}
       <span class="arw"><span></span></span>
     </div>
   </div>
   <div style="clear:both">&nbsp;</div>
   <div class="grid-18">
-    {{widget.display_field_by_name('submit')|safe}}
+    {{widget.display_field_by_name('submit')}}
     {% if c.user and c.user != c.user.anonymous() and not ticket and not subscribed_to_tool|default %}
         <label class="subscribe">
           <input type="checkbox" checked name="ticket_form.subscribe" class="subscribe-checkbox">Subscribe to this ticket
diff --git a/ForgeWiki/forgewiki/templates/wiki/browse.html b/ForgeWiki/forgewiki/templates/wiki/browse.html
index 3a96aa719..c384f0f1c 100644
--- a/ForgeWiki/forgewiki/templates/wiki/browse.html
+++ b/ForgeWiki/forgewiki/templates/wiki/browse.html
@@ -45,7 +45,7 @@
   <tbody>
     {% for page in pages %}
       <tr class="{{'deleted ' if page.deleted else ''}}{{loop.cycle('', 'even')}}">
-        <td><a href="{{page.url|safe}}">{{page['title']}}</a></td>
+        <td><a href="{{page.url}}">{{page['title']}}</a></td>
       
         {% if 'user_name' in page %}
             <td><a href="{{ h.username_project_url(page['user_name']) }}" class="user-mention">{{ page['user_name'] }}</a></td>


(allura) 03/08: [#8536] move/improve |safe usage

Posted by gc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 5fb7c3eca208a29cdb98bd4ad042234024df98ff
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Fri Feb 9 16:12:37 2024 -0500

    [#8536] move/improve |safe usage
---
 Allura/allura/app.py                                              | 7 ++++---
 Allura/allura/ext/admin/templates/project_groups.html             | 2 +-
 Allura/allura/lib/diff.py                                         | 3 ++-
 Allura/allura/templates/jinja_master/sidebar_menu.html            | 2 +-
 Allura/allura/templates/repo/barediff.html                        | 2 +-
 Allura/allura/templates/repo/diff.html                            | 2 +-
 Allura/allura/templates_responsive/jinja_master/sidebar_menu.html | 2 +-
 ForgeTracker/forgetracker/tracker_main.py                         | 6 ++++--
 ForgeWiki/forgewiki/wiki_main.py                                  | 5 +++--
 9 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index 1beddf562..23f18d1b3 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -24,6 +24,7 @@ from xml.etree import ElementTree as ET
 from copy import copy
 
 import pkg_resources
+from markupsafe import Markup
 from tg import expose, redirect, flash, validate
 from tg.decorators import without_trailing_slash
 from tg import config as tg_config
@@ -619,13 +620,13 @@ class Application(ActivityObject):
         """
         return []
 
-    def sidebar_menu_js(self):
+    def sidebar_menu_js(self) -> Markup:
         """Return Javascript needed by the sidebar menu of this Application.
 
-        :return: a string of Javascript code
+        :return: Markup string of Javascript code
 
         """
-        return ""
+        return Markup("")
 
     @LazyProperty
     def _webhooks(self):
diff --git a/Allura/allura/ext/admin/templates/project_groups.html b/Allura/allura/ext/admin/templates/project_groups.html
index bb5571010..ed941dd9d 100644
--- a/Allura/allura/ext/admin/templates/project_groups.html
+++ b/Allura/allura/ext/admin/templates/project_groups.html
@@ -76,7 +76,7 @@
           {% for r in role.users_with_role() %}
           <li class="deleter" data-user="{{r.user.username}}">
             {{ g.icons['perm_delete'].render(
-                title=('<span>%s</span> (%s)' % (r.user.display_name | escape, r.user.username | escape)) | safe,
+                title=('<span>%s</span> (%s)'|safe) % (r.user.display_name, r.user.username),
                 show_title=True,
                 extra_css='deleter',
                 **{'data-user': r.user.username}) }}
diff --git a/Allura/allura/lib/diff.py b/Allura/allura/lib/diff.py
index 000ecce01..ee9087253 100644
--- a/Allura/allura/lib/diff.py
+++ b/Allura/allura/lib/diff.py
@@ -24,6 +24,7 @@ from collections.abc import Iterable, Generator
 import sxsdiff
 from diff_match_patch import diff_match_patch
 import six
+from markupsafe import Markup
 from sxsdiff.calculator import LineChange, ElementsHolder, PlainElement, AdditionElement, DeletionElement
 
 log = logging.getLogger(__name__)
@@ -67,7 +68,7 @@ class SxsOutputGenerator(sxsdiff.BaseGenerator):
     def run(self, diff_result: Iterable[LineChange | None]):
         self.out = ''
         super().run(diff_result)
-        return self.out
+        return Markup(self.out)  # "safe" because we use html.escape in a few key places below
 
     def visit_row(self, line_change: LineChange | None):
         if line_change is None:
diff --git a/Allura/allura/templates/jinja_master/sidebar_menu.html b/Allura/allura/templates/jinja_master/sidebar_menu.html
index 6097e7132..d9db22f24 100644
--- a/Allura/allura/templates/jinja_master/sidebar_menu.html
+++ b/Allura/allura/templates/jinja_master/sidebar_menu.html
@@ -95,7 +95,7 @@
     {% endif %}
     {% if c.app and c.app.sidebar_menu_js() %}
       <script>
-        {{c.app.sidebar_menu_js()|safe}}
+        {{c.app.sidebar_menu_js()}}
       </script>
     {% endif %}
 </div>
diff --git a/Allura/allura/templates/repo/barediff.html b/Allura/allura/templates/repo/barediff.html
index babccfa61..84085149f 100644
--- a/Allura/allura/templates/repo/barediff.html
+++ b/Allura/allura/templates/repo/barediff.html
@@ -25,7 +25,7 @@
        title="{{h.text.truncate(b.commit._id, 10)}}"/>
 {% else %}
   {% if session.diformat == 'sidebyside' %}
-    {{diff|safe}}
+    {{diff}}
   {% else %}
     {{g.highlight(diff, lexer='diff')}}
   {% endif%}
diff --git a/Allura/allura/templates/repo/diff.html b/Allura/allura/templates/repo/diff.html
index df74c6c66..fdbec5663 100644
--- a/Allura/allura/templates/repo/diff.html
+++ b/Allura/allura/templates/repo/diff.html
@@ -63,7 +63,7 @@
         <a rel="nofollow" href="{{ switch_url }}">Switch to {{ switch_text }} view</a>
       </h3>
     {% if session.diformat == 'sidebyside' %}
-      {{diff|safe}}
+      {{diff}}
     {% else %}
       {{g.highlight(diff, lexer='diff')}}
     {% endif %}
diff --git a/Allura/allura/templates_responsive/jinja_master/sidebar_menu.html b/Allura/allura/templates_responsive/jinja_master/sidebar_menu.html
index 74b73ebd4..d7ec8664f 100644
--- a/Allura/allura/templates_responsive/jinja_master/sidebar_menu.html
+++ b/Allura/allura/templates_responsive/jinja_master/sidebar_menu.html
@@ -95,7 +95,7 @@
     {% endif %}
     {% if c.app and c.app.sidebar_menu_js() %}
       <script>
-        {{c.app.sidebar_menu_js()|safe}}
+        {{c.app.sidebar_menu_js()}}
       </script>
     {% endif %}
 </div>
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index a1267ef3b..e70a61d7c 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -19,6 +19,8 @@ import logging
 import re
 from datetime import datetime, timedelta
 from urllib.parse import urlencode, unquote
+
+from markupsafe import Markup
 from webob import exc
 import json
 import os
@@ -396,7 +398,7 @@ class ForgeTrackerApp(Application):
         return links
 
     def sidebar_menu_js(self):
-        return """\
+        return Markup("""\
         $(function() {
             $.ajax({
                 url:'%(app_url)sbin_counts',
@@ -426,7 +428,7 @@ class ForgeTrackerApp(Application):
                     }
                 });
             }
-        });""" % {'app_url': c.app.url}
+        });""") % {'app_url': c.app.url}
 
     def has_custom_field(self, field):
         """Checks if given custom field is defined.
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index f1014f180..bac4d36da 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -23,6 +23,7 @@ from pprint import pformat
 import six
 from urllib.parse import unquote, urlencode
 
+from markupsafe import Markup
 # Non-stdlib imports
 from tg import expose, validate, redirect, flash, jsonify
 from tg.decorators import with_trailing_slash, without_trailing_slash
@@ -305,7 +306,7 @@ The wiki uses [Markdown](%s) syntax.
         return self.create_common_wiki_menu(has_create_access=has_access(self, 'create'))
 
     def sidebar_menu_js(self):
-        return '''
+        return Markup('''
         $('#sidebar').on('click', 'a[href$="#toggle-subscribe"]', function(e) {
             e.preventDefault();
             var link = this;
@@ -332,7 +333,7 @@ The wiki uses [Markdown](%s) syntax.
                 $(link).attr('href', $(link).attr('href').replace('-unsubscribe','-subscribe'));
             });
         });
-        '''
+        ''')
 
     def install(self, project):
         'Set up any default permissions and roles here'


(allura) 02/08: [#8536] remove old unused oembed templates

Posted by gc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 0b90f747e80b9b6f79a39ae6cff54b57b7e957b5
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Fri Feb 9 16:10:46 2024 -0500

    [#8536] remove old unused oembed templates
---
 Allura/allura/templates/oembed/__init__.py       | 16 -------------
 Allura/allura/templates/oembed/generic.html      | 23 -------------------
 Allura/allura/templates/oembed/html_tpl.html     | 25 --------------------
 Allura/allura/templates/oembed/link.html         | 26 ---------------------
 Allura/allura/templates/oembed/link_opera.html   | 27 ----------------------
 Allura/allura/templates/oembed/link_twitter.html | 29 ------------------------
 Allura/allura/templates/oembed/photo.html        | 25 --------------------
 7 files changed, 171 deletions(-)

diff --git a/Allura/allura/templates/oembed/__init__.py b/Allura/allura/templates/oembed/__init__.py
deleted file mode 100644
index 144e29886..000000000
--- a/Allura/allura/templates/oembed/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
diff --git a/Allura/allura/templates/oembed/generic.html b/Allura/allura/templates/oembed/generic.html
deleted file mode 100644
index 40a9f0fe2..000000000
--- a/Allura/allura/templates/oembed/generic.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{#-
-       Licensed to the Apache Software Foundation (ASF) under one
-       or more contributor license agreements.  See the NOTICE file
-       distributed with this work for additional information
-       regarding copyright ownership.  The ASF licenses this file
-       to you under the Apache License, Version 2.0 (the
-       "License"); you may not use this file except in compliance
-       with the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-       Unless required by applicable law or agreed to in writing,
-       software distributed under the License is distributed on an
-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-       KIND, either express or implied.  See the License for the
-       specific language governing permissions and limitations
-       under the License.
--#}
-<html>
-  <body>
-    <a href="{{href}}">{{href}}</a> (cannot be embedded)
-  </body>
-</html>
diff --git a/Allura/allura/templates/oembed/html_tpl.html b/Allura/allura/templates/oembed/html_tpl.html
deleted file mode 100644
index 0b063a2c0..000000000
--- a/Allura/allura/templates/oembed/html_tpl.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{#-
-       Licensed to the Apache Software Foundation (ASF) under one
-       or more contributor license agreements.  See the NOTICE file
-       distributed with this work for additional information
-       regarding copyright ownership.  The ASF licenses this file
-       to you under the Apache License, Version 2.0 (the
-       "License"); you may not use this file except in compliance
-       with the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-       Unless required by applicable law or agreed to in writing,
-       software distributed under the License is distributed on an
-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-       KIND, either express or implied.  See the License for the
-       specific language governing permissions and limitations
-       under the License.
--#}
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html>
-  <body>
-    {{data.html|safe}}
-  </body>
-</html>
diff --git a/Allura/allura/templates/oembed/link.html b/Allura/allura/templates/oembed/link.html
deleted file mode 100644
index f3517a821..000000000
--- a/Allura/allura/templates/oembed/link.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{#-
-       Licensed to the Apache Software Foundation (ASF) under one
-       or more contributor license agreements.  See the NOTICE file
-       distributed with this work for additional information
-       regarding copyright ownership.  The ASF licenses this file
-       to you under the Apache License, Version 2.0 (the
-       "License"); you may not use this file except in compliance
-       with the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-       Unless required by applicable law or agreed to in writing,
-       software distributed under the License is distributed on an
-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-       KIND, either express or implied.  See the License for the
-       specific language governing permissions and limitations
-       under the License.
--#}
-<html>
-  <body>
-    <pre>
-      href: {{href}}
-      data: {{data|pprint}}
-    </pre>
-  </body>
-</html>
diff --git a/Allura/allura/templates/oembed/link_opera.html b/Allura/allura/templates/oembed/link_opera.html
deleted file mode 100644
index ec98f729d..000000000
--- a/Allura/allura/templates/oembed/link_opera.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{#-
-       Licensed to the Apache Software Foundation (ASF) under one
-       or more contributor license agreements.  See the NOTICE file
-       distributed with this work for additional information
-       regarding copyright ownership.  The ASF licenses this file
-       to you under the Apache License, Version 2.0 (the
-       "License"); you may not use this file except in compliance
-       with the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-       Unless required by applicable law or agreed to in writing,
-       software distributed under the License is distributed on an
-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-       KIND, either express or implied.  See the License for the
-       specific language governing permissions and limitations
-       under the License.
--#}
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html>
-  <body>
-    <h2><a href="{{href}}" target="_blank">{{data.title}}</a></h2>
-    By <a href="{{data.author_url}}" target="_blank">{{data.author_name}}</a>
-    Contains {{data.images.image|length}} images.
-  </body>
-</html>
diff --git a/Allura/allura/templates/oembed/link_twitter.html b/Allura/allura/templates/oembed/link_twitter.html
deleted file mode 100644
index 76489c1a3..000000000
--- a/Allura/allura/templates/oembed/link_twitter.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{#-
-       Licensed to the Apache Software Foundation (ASF) under one
-       or more contributor license agreements.  See the NOTICE file
-       distributed with this work for additional information
-       regarding copyright ownership.  The ASF licenses this file
-       to you under the Apache License, Version 2.0 (the
-       "License"); you may not use this file except in compliance
-       with the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-       Unless required by applicable law or agreed to in writing,
-       software distributed under the License is distributed on an
-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-       KIND, either express or implied.  See the License for the
-       specific language governing permissions and limitations
-       under the License.
--#}
-<html>
-  <body>
-    <a href="{{href}}" target="_blank">
-      <img alt="{{data.author_name}}"
-           src="{{data.thumbnail_url}}"
-           width="{{data.thumbnail_width}}"
-           height="{{data.thumbnail_height}}"/>
-      {{data.title}}
-    </a>
-  </body>
-</html>
diff --git a/Allura/allura/templates/oembed/photo.html b/Allura/allura/templates/oembed/photo.html
deleted file mode 100644
index 251192332..000000000
--- a/Allura/allura/templates/oembed/photo.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{#-
-       Licensed to the Apache Software Foundation (ASF) under one
-       or more contributor license agreements.  See the NOTICE file
-       distributed with this work for additional information
-       regarding copyright ownership.  The ASF licenses this file
-       to you under the Apache License, Version 2.0 (the
-       "License"); you may not use this file except in compliance
-       with the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-       Unless required by applicable law or agreed to in writing,
-       software distributed under the License is distributed on an
-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-       KIND, either express or implied.  See the License for the
-       specific language governing permissions and limitations
-       under the License.
--#}
-<html>
-  <body>
-    <a href="{{href}}" target="_blank">
-      <img alt="{{data.title}}" title="{{data.title}}" src="{{data.url}}" width="{{data.width}}" height="{{data.height}}"/>
-    </a>
-  </body>
-</html>


(allura) 07/08: [#8536] don't use jinja for site notifications; add autoescape

Posted by gc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 63f12b9a7d754e5bf02a1aa9e4e1f9c17b7aa87e
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Thu Feb 15 12:05:00 2024 -0500

    [#8536] don't use jinja for site notifications; add autoescape
---
 Allura/allura/lib/helpers.py                       | 24 ++++++++++++++++++----
 Allura/allura/lib/widgets/search.py                |  1 +
 Allura/allura/public/nf/js/allura-base.js          |  2 +-
 .../templates/jinja_master/theme_macros.html       |  2 +-
 .../jinja_master/theme_macros.html                 |  2 +-
 Allura/allura/tests/test_helpers.py                | 19 +++++++++++++++++
 6 files changed, 43 insertions(+), 7 deletions(-)

diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index bc52a5638..26dd2d94f 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -790,10 +790,26 @@ def render_any_markup(name, txt, code_mode=False, linenumbers_style=TABLE):
 
 
 @pass_context
-def subrender_jinja_filter(context, value):
-    _template = context.eval_ctx.environment.from_string(value)
-    result = _template.render(**context)
-    return result
+def subrender_jinja_filter(context, html_tmpl: str) -> Markup:
+    # jinja templates can execute potentially dangerous things
+    #   _template = context.eval_ctx.environment.from_string(html_tmpl)
+    #   return _template.render(**context)
+
+    # so instead, support just a few things
+
+    limited_vars = {
+        '{{ c.project.url() }}': lambda: c.project.url(),
+    }
+    for var, fn in limited_vars.items():
+        if var not in html_tmpl:
+            continue
+        try:
+            val = fn()
+        except Exception:
+            log.exception(f'Could not replace {var} in jinja "subrender" for site notification')
+            continue
+        html_tmpl = html_tmpl.replace(var, val)
+    return Markup(html_tmpl)
 
 
 def nl2br_jinja_filter(value):
diff --git a/Allura/allura/lib/widgets/search.py b/Allura/allura/lib/widgets/search.py
index cec39ad44..c772992d1 100644
--- a/Allura/allura/lib/widgets/search.py
+++ b/Allura/allura/lib/widgets/search.py
@@ -53,6 +53,7 @@ class SearchHelp(ffw.Lightbox):
         super().__init__()
         # can't use g.jinja2_env since this widget gets imported too early :(
         jinja2_env = jinja2.Environment(
+            autoescape=True,
             loader=jinja2.PackageLoader('allura', 'templates/widgets'))
         self.content = Markup(jinja2_env.get_template('search_help.html').render(dict(
             comments=comments,
diff --git a/Allura/allura/public/nf/js/allura-base.js b/Allura/allura/public/nf/js/allura-base.js
index 779f4ebca..839408dee 100644
--- a/Allura/allura/public/nf/js/allura-base.js
+++ b/Allura/allura/public/nf/js/allura-base.js
@@ -209,7 +209,7 @@ $(function(){
     });
 
     $('#site-notification .btn-close').click(function(e) {
-        var $note = $(this).parent();
+        var $note = $(this).parents('section:first');
         $note.hide();
         var note_id = $note.attr('data-notification-id');
         var cookie = $.cookie('site-notification');
diff --git a/Allura/allura/templates/jinja_master/theme_macros.html b/Allura/allura/templates/jinja_master/theme_macros.html
index e06f5d7a2..c9ee789b3 100644
--- a/Allura/allura/templates/jinja_master/theme_macros.html
+++ b/Allura/allura/templates/jinja_master/theme_macros.html
@@ -178,7 +178,7 @@ http://stackoverflow.com/questions/26582731/redefining-imported-jinja-macros
     {% if note %}
         <div id="site-notification">
             <section class="site-message info" data-notification-id="{{note._id}}">
-                {{ note.content|subrender|safe }}
+                {{ note.content|subrender }}
                 <a href="" class="btn btn-close">Close</a>
             </section>
         </div>
diff --git a/Allura/allura/templates_responsive/jinja_master/theme_macros.html b/Allura/allura/templates_responsive/jinja_master/theme_macros.html
index 5c639115d..dd0e70ed1 100644
--- a/Allura/allura/templates_responsive/jinja_master/theme_macros.html
+++ b/Allura/allura/templates_responsive/jinja_master/theme_macros.html
@@ -195,7 +195,7 @@ http://stackoverflow.com/questions/26582731/redefining-imported-jinja-macros
     {% if note %}
         <div id="site-notification">
             <section class="callout primary" data-notification-id="{{note._id}}">
-                {{note.content|safe}}
+                {{ note.content|subrender }}
                 <button class="close-button btn-close" aria-label="Dismiss alert" type="button">
                   {#  .btn-close instead of data-close, since allura-base.js handles closing it, not Foundation #}
                   <span aria-hidden="true">&times;</span>
diff --git a/Allura/allura/tests/test_helpers.py b/Allura/allura/tests/test_helpers.py
index 9a12062bd..bb7908c9b 100644
--- a/Allura/allura/tests/test_helpers.py
+++ b/Allura/allura/tests/test_helpers.py
@@ -24,6 +24,8 @@ import time
 import PIL
 from mock import Mock, patch
 from tg import tmpl_context as c
+from tg import config
+
 from alluratest.tools import module_not_available
 from webob import Request
 from webob.exc import HTTPUnauthorized
@@ -346,6 +348,23 @@ def test_nl2br_jinja_filter():
             Markup('foo&lt;script&gt;alert(1)&lt;/script&gt;<br>\nbar<br>\nbaz'))
 
 
+def test_subrender_jinja_filter():
+    # if we need a real ctx, have to do all this which probably isn't even quite right
+    # from allura.config.app_cfg import AlluraJinjaRenderer
+    # j2env = AlluraJinjaRenderer.create(config, None)['jinja'].jinja2_env
+    # from jinja2.runtime import new_context
+    # j_ctx = new_context(j2env, template_name=None, blocks={})
+    j_ctx = None
+
+    # HTML, but no jinja:
+    assert h.subrender_jinja_filter(j_ctx, '<b>hi</b>{% foo %}') == '<b>hi</b>{% foo %}'
+    # var does not error if no `c.project`
+    assert h.subrender_jinja_filter(j_ctx, '<a href="{{ c.project.url() }}"></a>') == '<a href="{{ c.project.url() }}"></a>'
+    # with `c.project` set, replacement of this var works:
+    with h.push_context('test', neighborhood='Projects'):
+        assert h.subrender_jinja_filter(j_ctx, '<a href="{{ c.project.url() }}"></a>') == '<a href="/p/test/"></a>'
+
+
 def test_split_select_field_options():
     assert (h.split_select_field_options('"test message" test2') ==
             ['test message', 'test2'])