Skip to content

Commit

Permalink
Use templatetag to escape Django syntax characters in templates
Browse files Browse the repository at this point in the history
  • Loading branch information
goosemania committed Aug 9, 2016
1 parent 6e0bc15 commit 9e8706e
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 23 deletions.
68 changes: 54 additions & 14 deletions plugins/pulp_rpm/plugins/db/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import csv
import errno
import logging
import re
import os
from collections import namedtuple
from gettext import gettext as _
Expand All @@ -9,9 +10,9 @@

import mongoengine
from django.template import Context, Template
from django.template.defaulttags import TemplateTagNode

import pulp.common.error_codes as platform_error_codes
import pulp.server.webservices.templatetags # NOQA
import pulp.server.util as server_util
from pulp.server.db.model import ContentUnit, FileContentUnit
from pulp.server.exceptions import PulpCodedException
Expand Down Expand Up @@ -764,7 +765,7 @@ def render_primary(self, checksumtype):
"""
metadata = self.repodata['primary']
for tag in self.ESCAPE_TEMPLATE_VARS_TAGS['primary']:
metadata = self._escape_django_template_vars(metadata, tag)
metadata = self._escape_django_syntax_chars(metadata, tag)
context = Context({'checksum': self.get_or_calculate_and_save_checksum(checksumtype),
'checksumtype': checksumtype})

Expand All @@ -782,7 +783,7 @@ def render_other(self, checksumtype):
"""
metadata = self.repodata['other']
for tag in self.ESCAPE_TEMPLATE_VARS_TAGS['other']:
metadata = self._escape_django_template_vars(metadata, tag)
metadata = self._escape_django_syntax_chars(metadata, tag)
context = Context({'pkgid': self.get_or_calculate_and_save_checksum(checksumtype)})
return self._render(metadata, context)

Expand Down Expand Up @@ -821,27 +822,66 @@ def _render(template, context):
return rendered

@staticmethod
def _escape_django_template_vars(template, tag_name):
def _escape_django_syntax_chars(template, tag_name):
"""
Escape Django template variables by wrapping the specified element into `verbatim`
templatetag.
Escape Django syntax characters by replacing them with the corresponding templatetag.
NOTE: This function does not handle the following XML syntax:
- namespaces in the format of namespace_alias:tag_name
- nested tag with the same name, e.g. <a><a>...</a></a>
It is unlikely that the syntax above is used in the metadata of the content units.
:param template: a Django template
:type template: basestring
:param tag_name: name of the element to wrap
:type tad_name: basestring
:type tag_name: basestring
:return: a Django template with the escaped template variables in the specified element
:return: a Django template with the escaped syntax characters in the specified element
:rype: basestring
"""
start_tag = '<%s' % tag_name
end_tag = '</%s>' % tag_name
start_templatetag = '{%% verbatim pulp_%s_escape_method %%}' % tag_name
end_templatetag = '{%% endverbatim pulp_%s_escape_method %%}' % tag_name
template = template.replace(start_tag, start_templatetag + start_tag)
template = template.replace(end_tag, end_tag + end_templatetag)
start_tag_pattern = r'<%s.*?(?<!/)>' % tag_name
end_tag_pattern = r'</%s>' % tag_name
complete_tag_pattern = r'(%s)(.*?)(%s)' % (start_tag_pattern, end_tag_pattern)
tag_re = re.compile(complete_tag_pattern, flags=re.DOTALL)
template = tag_re.sub(RpmBase._generate_tag_replacement_str, template)
return template

@staticmethod
def _generate_tag_replacement_str(mobj):
"""
Generate replacement string for the matched XML element.
:param mobj: matched object consisted of 3 groups:
opening tag, value and closing tag.
:type mobj: _sre.SRE_Match
:return: replacement string for the given match object
:rtype: basestring
"""
start_tag = mobj.group(1)
value = RpmBase._substitute_special_chars(mobj.group(2))
end_tag = mobj.group(3)
return start_tag + value + end_tag

@staticmethod
def _substitute_special_chars(template):
"""
Make the substitution of the syntax characters with the corresponding templatetag.
The syntax characters to be substituted can be found in
django.template.defaulttags.TemplateTagNode.mapping.
:param template: a piece of the template in which substitution should happen
:type template: basestring
:return: string with syntax characters substituted
:rtype: basestriing
"""
templatetag_map = dict((sym, name) for name, sym in TemplateTagNode.mapping.items())
symbols_pattern = '(%s)' % '|'.join(templatetag_map.keys())
return re.sub(symbols_pattern,
lambda mobj: '{%% templatetag %s %%}' % templatetag_map[mobj.group(1)],
template)

def modify_xml(self):
"""
Given a unit that has repodata XML snippets, modify them in several necessary ways. These
Expand Down
30 changes: 21 additions & 9 deletions plugins/test/unit/plugins/db/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,7 @@ def setUp(self):
<version epoch="0" rel="1" ver="1.0" />
<checksum pkgid="YES" type="{{ checksumtype }}">{{ checksum }}</checksum>
<summary>A dummy {{ var }} package of cat</summary>
<description>A dummy package of cat with some description of {{ template var}}</description>
<description>A dummy package of cat with some description of {% character </description>
<packager />
<url>http://tstrachota.fedorapeople.org</url>
<time build="1331831362" file="1331832453" />
Expand Down Expand Up @@ -1133,14 +1133,26 @@ def test_render_primary(self):
self.assertTrue('abc123' in ret)
self.assertTrue('sha1' in ret)

def test__escape_django_template_vars(self):
def test__escape_django_syntax_chars(self):
"""
Test that a requested element is wrapped into `verbatim` templatetag.
Test that for a requested element all syntax characters are substituted with
the corresponding templatetag.
List of characters and the corresponding names of the templatetag could be found in
django.template.defaulttags.TemplateTagNode.mapping.
For now, those characters are:
>>> from django.template.defaulttags import TemplateTagNode
>>> TemplateTagNode.mapping.values()
[u'{#', u'{{', u'%}', u'#}', u'{', u'}}', u'{%', u'}']
E.g. '{%' should be substituted with '{% templatetag openblock %}'
"""
template = '<tag>description</tag><some_tag>text</some_tag><tag>another description</tag>'
expected_template = ('{% verbatim pulp_tag_escape_method %}<tag>description</tag>'
'{% endverbatim pulp_tag_escape_method %}<some_tag>text</some_tag>'
'{% verbatim pulp_tag_escape_method %}<tag>another description</tag>'
'{% endverbatim pulp_tag_escape_method %}')
result = self.unit._escape_django_template_vars(template, 'tag')
template = ('<tag>{some {{ var }}</tag><some_tag>text</some_tag>'
'<tag>some {% tag %} {# comment #} }</tag>')
expected_template = ('<tag>{% templatetag openbrace %}some {% templatetag openvariable %}'
' var {% templatetag closevariable %}</tag><some_tag>text</some_tag>'
'<tag>some {% templatetag openblock %} tag '
'{% templatetag closeblock %} {% templatetag opencomment %} comment '
'{% templatetag closecomment %} {% templatetag closebrace %}</tag>')
result = self.unit._escape_django_syntax_chars(template, 'tag')
self.assertEqual(result, expected_template)

0 comments on commit 9e8706e

Please sign in to comment.