diff --git a/django/template/__init__.py b/django/template/__init__.py index edc2150af0..761c08d6c9 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -594,7 +594,7 @@ class FilterExpression(object): arg_vals = [] for lookup, arg in args: if not lookup: - arg_vals.append(arg) + arg_vals.append(mark_safe(arg)) else: arg_vals.append(arg.resolve(context)) if getattr(func, 'needs_autoescape', False): @@ -707,7 +707,7 @@ class Variable(object): # If it's wrapped with quotes (single or double), then # we're also dealing with a literal. if var[0] in "\"'" and var[0] == var[-1]: - self.literal = var[1:-1] + self.literal = mark_safe(var[1:-1]) else: # Otherwise we'll set self.lookups so that resolve() knows we're # dealing with a bonafide variable diff --git a/docs/templates.txt b/docs/templates.txt index 020586159c..07258bb46a 100644 --- a/docs/templates.txt +++ b/docs/templates.txt @@ -401,6 +401,32 @@ auto-escaping is on, these extra filters won't change the output -- any variables that use the ``escape`` filter do not have further automatic escaping applied to them. +String literals and automatic escaping +-------------------------------------- + +Sometimes you will pass a string literal as an argument to a filter. For +example:: + + {{ data|default:"This is a string literal." }} + +All string literals are inserted **without** any automatic escaping into the +template, if they are used (it's as if they were all passed through the +``safe`` filter). The reasoning behind this is that the template author is in +control of what goes into the string literal, so they can make sure the text +is correctly escaped when the template is written. + +This means you would write :: + + {{ data|default:"3 > 2" }} + +...rather than :: + + {{ data|default:"3 > 2" }} <-- Bad! Don't do this. + +This doesn't affect what happens to data coming from the variable itself. +The variable's contents are still automatically escaped, if necessary, since +they're beyond the control of the template author. + Using the built-in reference ============================ diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py index 5d7129480c..00dfbe3c35 100644 --- a/tests/regressiontests/templates/filters.py +++ b/tests/regressiontests/templates/filters.py @@ -177,18 +177,17 @@ def get_filter_tests(): 'filter-unordered_list04': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [[mark_safe("x>\n\t\n\t"), 'filter-unordered_list05': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [["x>\n\t\n\t"), - # If the input to "default" filter is marked as safe, then so is the - # output. However, if the default arg is used, auto-escaping kicks in - # (if enabled), because we cannot mark the default as safe. + # Literal string arguments to the default filter are always treated as + # safe strings, regardless of the auto-escaping state. # # Note: we have to use {"a": ""} here, otherwise the invalid template # variable string interferes with the test result. - 'filter-default01': ('{{ a|default:"x<" }}', {"a": ""}, "x<"), + 'filter-default01': ('{{ a|default:"x<" }}', {"a": ""}, "x<"), 'filter-default02': ('{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}', {"a": ""}, "x<"), 'filter-default03': ('{{ a|default:"x<" }}', {"a": mark_safe("x>")}, "x>"), 'filter-default04': ('{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}', {"a": mark_safe("x>")}, "x>"), - 'filter-default_if_none01': ('{{ a|default:"x<" }}', {"a": None}, "x<"), + 'filter-default_if_none01': ('{{ a|default:"x<" }}', {"a": None}, "x<"), 'filter-default_if_none02': ('{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}', {"a": None}, "x<"), 'filter-phone2numeric01': ('{{ a|phone2numeric }} {{ b|phone2numeric }}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>") }, "<1-800-2255-63> <1-800-2255-63>"), diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 74e6fd5cef..7eb4ae06b0 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -318,9 +318,9 @@ class Templates(unittest.TestCase): # Chained filters, with an argument to the first one 'filter-syntax09': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "Yes"}, "yes"), - # Escaped string as argument + # Literal string as argument is always "safe" from auto-escaping.. 'filter-syntax10': (r'{{ var|default_if_none:" endquote\" hah" }}', - {"var": None}, ' endquote" hah'), + {"var": None}, ' endquote" hah'), # Variable as argument 'filter-syntax11': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'), @@ -735,9 +735,10 @@ class Templates(unittest.TestCase): 'i18n12': ('{% load i18n %}{% get_available_languages as langs %}{% for lang in langs %}{% ifequal lang.0 "de" %}{{ lang.0 }}{% endifequal %}{% endfor %}', {}, 'de'), # translation of constant strings - 'i18n13': ('{{ _("Page not found") }}', {'LANGUAGE_CODE': 'de'}, 'Seite nicht gefunden'), + 'i18n13': ('{{ _("Password") }}', {'LANGUAGE_CODE': 'de'}, 'Passwort'), 'i18n14': ('{% cycle "foo" _("Password") _(\'Password\') as c %} {% cycle c %} {% cycle c %}', {'LANGUAGE_CODE': 'de'}, 'foo Passwort Passwort'), 'i18n15': ('{{ absent|default:_("Password") }}', {'LANGUAGE_CODE': 'de', 'absent': ""}, 'Passwort'), + 'i18n16': ('{{ _("<") }}', {'LANGUAGE_CODE': 'de'}, '<'), ### HANDLING OF TEMPLATE_STRING_IF_INVALID ################################### @@ -885,9 +886,9 @@ class Templates(unittest.TestCase): 'autoescape-tag06': ("{{ first }}", {"first": mark_safe("first")}, "first"), 'autoescape-tag07': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": mark_safe(u"Apple")}, u"Apple"), - # String arguments to filters, if used in the result, are escaped, - # too. - 'basic-syntax08': (r'{% autoescape on %}{{ var|default_if_none:" endquote\" hah" }}{% endautoescape %}', {"var": None}, ' endquote" hah'), + # Literal string arguments to filters, if used in the result, are + # safe. + 'basic-syntax08': (r'{% autoescape on %}{{ var|default_if_none:" endquote\" hah" }}{% endautoescape %}', {"var": None}, ' endquote" hah'), # The "safe" and "escape" filters cannot work due to internal # implementation details (fortunately, the (no)autoescape block