From 22506b2c162b34c4c45f748cf11ede75824a40cd Mon Sep 17 00:00:00 2001 From: haileyajohnson Date: Mon, 16 Jun 2025 14:22:34 -0700 Subject: [PATCH] Fixed #36465, Refs #35816 -- Disallowed '+' and '-' characters in template variable names. Regression in 5183f7c287a9a5d61ca1103b55166cda52d9c647. Thank you to Jon Banafato and Baptiste Mispelon for the report. --- django/template/base.py | 7 +++++++ tests/template_tests/test_base.py | 5 +---- tests/template_tests/test_parser.py | 28 ++++++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/django/template/base.py b/django/template/base.py index 140f713add..121a47d638 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -852,6 +852,13 @@ class Variable: "Variables and attributes may " "not begin with underscores: '%s'" % var ) + # Disallow characters that are allowed in numbers but not in a + # variable name. + for c in ["+", "-"]: + if c in var: + raise TemplateSyntaxError( + "Invalid character ('%s') in variable name: '%s'" % (c, var) + ) self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR)) def resolve(self, context): diff --git a/tests/template_tests/test_base.py b/tests/template_tests/test_base.py index 6457d4d4e6..33a200c362 100644 --- a/tests/template_tests/test_base.py +++ b/tests/template_tests/test_base.py @@ -78,9 +78,6 @@ class VariableTests(SimpleTestCase): def test_nonliterals(self): """Variable names that aren't resolved as literals.""" - var_names = [] - for var in ("inf", "infinity", "iNFiniTy", "nan"): - var_names.extend((var, "-" + var, "+" + var)) - for var in var_names: + for var in ["inf", "infinity", "iNFiniTy", "nan"]: with self.subTest(var=var): self.assertIsNone(Variable(var).literal) diff --git a/tests/template_tests/test_parser.py b/tests/template_tests/test_parser.py index c54f481f36..317fb88238 100644 --- a/tests/template_tests/test_parser.py +++ b/tests/template_tests/test_parser.py @@ -126,6 +126,16 @@ class ParserTests(SimpleTestCase): ): Variable({}) + # Variables should raise when invalid characters in name. + for c in ["+", "-"]: + with self.subTest(invalid_character=c): + variable_name = f"variable{c}name" + with self.assertRaisesMessage( + TemplateSyntaxError, + f"Invalid character ('{c}') in variable name: '{variable_name}'", + ): + Variable(variable_name) + def test_filter_args_count(self): parser = Parser("") register = Library() @@ -174,6 +184,7 @@ class ParserTests(SimpleTestCase): def test_filter_numeric_argument_parsing(self): p = Parser("", builtins=[filter_library]) + # Values that resolve to a numeric literal. cases = { "5": 5, "-5": -5, @@ -193,6 +204,7 @@ class ParserTests(SimpleTestCase): FilterExpression(f"0|default:{num}", p).resolve({}), expected ) + # Values that are interpreted as names of variables that do not exist. invalid_numbers = [ "abc123", "123abc", @@ -205,8 +217,6 @@ class ParserTests(SimpleTestCase): "1e2.0", "1e2a", "1e2e3", - "1e-", - "1e-a", ] for num in invalid_numbers: @@ -216,3 +226,17 @@ class ParserTests(SimpleTestCase): ) with self.assertRaises(VariableDoesNotExist): FilterExpression(f"0|default:{num}", p).resolve({}) + + # Values that are interpreted as an invalid variable name. + invalid_numbers_and_var_names = [ + "1e-", + "1e-a", + "1+1", + "1-1", + ] + for num in invalid_numbers_and_var_names: + with self.subTest(num=num): + with self.assertRaises(TemplateSyntaxError): + FilterExpression(num, p).resolve({}) + with self.assertRaises(TemplateSyntaxError): + FilterExpression(f"0|default:{num}", p).resolve({})