Skip to content

Commit

Permalink
Fixed #13275 -- Modified the parsing logic of the {% url %} tag to av…
Browse files Browse the repository at this point in the history
…oid catastrophic backtracking. Thanks to SmileyChris for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12943 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
freakboy3742 committed Apr 10, 2010
1 parent f4eac3d commit b579350
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 11 deletions.
41 changes: 31 additions & 10 deletions django/template/defaulttags.py
Expand Up @@ -1065,10 +1065,6 @@ def templatetag(parser, token):
return TemplateTagNode(tag) return TemplateTagNode(tag)
templatetag = register.tag(templatetag) templatetag = register.tag(templatetag)


# Backwards compatibility check which will fail against for old comma
# separated arguments in the url tag.
url_backwards_re = re.compile(r'''(('[^']*'|"[^"]*"|[^,]+)=?)+$''')

def url(parser, token): def url(parser, token):
""" """
Returns an absolute URL matching given view with its parameters. Returns an absolute URL matching given view with its parameters.
Expand Down Expand Up @@ -1117,13 +1113,38 @@ def url(parser, token):
asvar = bits[-1] asvar = bits[-1]
bits = bits[:-2] bits = bits[:-2]


# Backwards compatibility: {% url urlname arg1,arg2 %} or # Backwards compatibility: check for the old comma separated format
# {% url urlname arg1,arg2 as foo %} cases. # {% url urlname arg1,arg2 %}
if bits: # Initial check - that the first space separated bit has a comma in it
old_args = ''.join(bits) if bits and ',' in bits[0]:
if not url_backwards_re.match(old_args): check_old_format = True
bits = old_args.split(",") # In order to *really* be old format, there must be a comma
# in *every* space separated bit, except the last.
for bit in bits[1:-1]:
if ',' not in bit:
# No comma in this bit. Either the comma we found
# in bit 1 was a false positive (e.g., comma in a string),
# or there is a syntax problem with missing commas
check_old_format = False
break
else:
# No comma found - must be new format.
check_old_format = False

if check_old_format:
# Confirm that this is old format by trying to parse the first
# argument. An exception will be raised if the comma is
# unexpected (i.e. outside of a static string).
match = kwarg_re.match(bits[0])
if match:
value = match.groups()[1]
try:
parser.compile_filter(value)
except TemplateSyntaxError:
bits = ''.join(bits).split(',')


# Now all the bits are parsed into new format,
# process them as template vars
if len(bits): if len(bits):
for bit in bits: for bit in bits:
match = kwarg_re.match(bit) match = kwarg_re.match(bit)
Expand Down
12 changes: 12 additions & 0 deletions tests/regressiontests/templates/tests.py
Expand Up @@ -364,8 +364,12 @@ def test_template_loader(template_name, template_dirs=None):
settings.TEMPLATE_STRING_IF_INVALID = invalid_str settings.TEMPLATE_STRING_IF_INVALID = invalid_str
for is_cached in (False, True): for is_cached in (False, True):
try: try:
start = datetime.now()
test_template = loader.get_template(name) test_template = loader.get_template(name)
end = datetime.now()
output = self.render(test_template, vals) output = self.render(test_template, vals)
if end-start > timedelta(seconds=0.2):
failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Took too long to parse test" % (is_cached, invalid_str, name))
except ContextStackException: except ContextStackException:
failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Context stack was left imbalanced" % (is_cached, invalid_str, name)) failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Context stack was left imbalanced" % (is_cached, invalid_str, name))
continue continue
Expand Down Expand Up @@ -1175,13 +1179,20 @@ def get_template_tests(self):
# Successes # Successes
'legacyurl02': ('{% url regressiontests.templates.views.client_action id=client.id,action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'), 'legacyurl02': ('{% url regressiontests.templates.views.client_action id=client.id,action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'legacyurl02a': ('{% url regressiontests.templates.views.client_action client.id,"update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'), 'legacyurl02a': ('{% url regressiontests.templates.views.client_action client.id,"update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'legacyurl02b': ("{% url regressiontests.templates.views.client_action id=client.id,action='update' %}", {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'legacyurl02c': ("{% url regressiontests.templates.views.client_action client.id,'update' %}", {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'legacyurl10': ('{% url regressiontests.templates.views.client_action id=client.id,action="two words" %}', {'client': {'id': 1}}, '/url_tag/client/1/two%20words/'), 'legacyurl10': ('{% url regressiontests.templates.views.client_action id=client.id,action="two words" %}', {'client': {'id': 1}}, '/url_tag/client/1/two%20words/'),
'legacyurl13': ('{% url regressiontests.templates.views.client_action id=client.id, action=arg|join:"-" %}', {'client': {'id': 1}, 'arg':['a','b']}, '/url_tag/client/1/a-b/'), 'legacyurl13': ('{% url regressiontests.templates.views.client_action id=client.id, action=arg|join:"-" %}', {'client': {'id': 1}, 'arg':['a','b']}, '/url_tag/client/1/a-b/'),
'legacyurl14': ('{% url regressiontests.templates.views.client_action client.id, arg|join:"-" %}', {'client': {'id': 1}, 'arg':['a','b']}, '/url_tag/client/1/a-b/'), 'legacyurl14': ('{% url regressiontests.templates.views.client_action client.id, arg|join:"-" %}', {'client': {'id': 1}, 'arg':['a','b']}, '/url_tag/client/1/a-b/'),
'legacyurl16': ('{% url regressiontests.templates.views.client_action action="update",id="1" %}', {}, '/url_tag/client/1/update/'),
'legacyurl16a': ("{% url regressiontests.templates.views.client_action action='update',id='1' %}", {}, '/url_tag/client/1/update/'),
'legacyurl17': ('{% url regressiontests.templates.views.client_action client_id=client.my_id,action=action %}', {'client': {'my_id': 1}, 'action': 'update'}, '/url_tag/client/1/update/'),


'url01': ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'), 'url01': ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'),
'url02': ('{% url regressiontests.templates.views.client_action id=client.id action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'), 'url02': ('{% url regressiontests.templates.views.client_action id=client.id action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'url02a': ('{% url regressiontests.templates.views.client_action client.id "update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'), 'url02a': ('{% url regressiontests.templates.views.client_action client.id "update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'url02b': ("{% url regressiontests.templates.views.client_action id=client.id action='update' %}", {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'url02c': ("{% url regressiontests.templates.views.client_action client.id 'update' %}", {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'url03': ('{% url regressiontests.templates.views.index %}', {}, '/url_tag/'), 'url03': ('{% url regressiontests.templates.views.index %}', {}, '/url_tag/'),
'url04': ('{% url named.client client.id %}', {'client': {'id': 1}}, '/url_tag/named-client/1/'), 'url04': ('{% url named.client client.id %}', {'client': {'id': 1}}, '/url_tag/named-client/1/'),
'url05': (u'{% url метка_оператора v %}', {'v': u'Ω'}, '/url_tag/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'), 'url05': (u'{% url метка_оператора v %}', {'v': u'Ω'}, '/url_tag/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'),
Expand All @@ -1195,6 +1206,7 @@ def get_template_tests(self):
'url13': ('{% url regressiontests.templates.views.client_action id=client.id action=arg|join:"-" %}', {'client': {'id': 1}, 'arg':['a','b']}, '/url_tag/client/1/a-b/'), 'url13': ('{% url regressiontests.templates.views.client_action id=client.id action=arg|join:"-" %}', {'client': {'id': 1}, 'arg':['a','b']}, '/url_tag/client/1/a-b/'),
'url14': ('{% url regressiontests.templates.views.client_action client.id arg|join:"-" %}', {'client': {'id': 1}, 'arg':['a','b']}, '/url_tag/client/1/a-b/'), 'url14': ('{% url regressiontests.templates.views.client_action client.id arg|join:"-" %}', {'client': {'id': 1}, 'arg':['a','b']}, '/url_tag/client/1/a-b/'),
'url15': ('{% url regressiontests.templates.views.client_action 12 "test" %}', {}, '/url_tag/client/12/test/'), 'url15': ('{% url regressiontests.templates.views.client_action 12 "test" %}', {}, '/url_tag/client/12/test/'),
'url18': ('{% url regressiontests.templates.views.client "1,2" %}', {}, '/url_tag/client/1,2/'),


# Failures # Failures
'url-fail01': ('{% url %}', {}, template.TemplateSyntaxError), 'url-fail01': ('{% url %}', {}, template.TemplateSyntaxError),
Expand Down
3 changes: 2 additions & 1 deletion tests/regressiontests/templates/urls.py
Expand Up @@ -6,8 +6,9 @@


# Test urls for testing reverse lookups # Test urls for testing reverse lookups
(r'^$', views.index), (r'^$', views.index),
(r'^client/(\d+)/$', views.client), (r'^client/([\d,]+)/$', views.client),
(r'^client/(?P<id>\d+)/(?P<action>[^/]+)/$', views.client_action), (r'^client/(?P<id>\d+)/(?P<action>[^/]+)/$', views.client_action),
(r'^client/(?P<client_id>\d+)/(?P<action>[^/]+)/$', views.client_action),
url(r'^named-client/(\d+)/$', views.client2, name="named.client"), url(r'^named-client/(\d+)/$', views.client2, name="named.client"),


# Unicode strings are permitted everywhere. # Unicode strings are permitted everywhere.
Expand Down

0 comments on commit b579350

Please sign in to comment.