From eafc0364764ba12babd76194d8e1f78b876471ec Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Fri, 25 Jan 2013 06:53:40 -0500 Subject: [PATCH] Fixed #19577 - Added HTML escaping to admin examples. Thanks foo@ for the report and Florian Apolloner for the review. --- django/utils/html.py | 4 ++-- docs/ref/contrib/admin/index.txt | 33 ++++++++++++++++++++++++++------ docs/ref/utils.txt | 13 +++++++++++++ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/django/utils/html.py b/django/utils/html.py index 25605bea044..ec7b28d330a 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -87,8 +87,8 @@ def format_html(format_string, *args, **kwargs): def format_html_join(sep, format_string, args_generator): """ - A wrapper format_html, for the common case of a group of arguments that need - to be formatted using the same format string, and then joined using + A wrapper of format_html, for the common case of a group of arguments that + need to be formatted using the same format string, and then joined using 'sep'. 'sep' is also passed through conditional_escape. 'args_generator' should be an iterator that returns the sequence of 'args' diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index ee04d77d321..a862d558757 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -449,17 +449,25 @@ subclass:: * If the string given is a method of the model, ``ModelAdmin`` or a callable, Django will HTML-escape the output by default. If you'd rather not escape the output of the method, give the method an - ``allow_tags`` attribute whose value is ``True``. + ``allow_tags`` attribute whose value is ``True``. However, to avoid an + XSS vulnerability, you should use :func:`~django.utils.html.format_html` + to escape user-provided inputs. Here's a full example model:: + from django.utils.html import format_html + class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) color_code = models.CharField(max_length=6) def colored_name(self): - return '%s %s' % (self.color_code, self.first_name, self.last_name) + return format_html('{1} {2}', + self.color_code, + self.first_name, + self.last_name) + colored_name.allow_tags = True class PersonAdmin(admin.ModelAdmin): @@ -500,12 +508,17 @@ subclass:: For example:: + from django.utils.html import format_html + class Person(models.Model): first_name = models.CharField(max_length=50) color_code = models.CharField(max_length=6) def colored_first_name(self): - return '%s' % (self.color_code, self.first_name) + return format_html('{1}', + self.color_code, + self.first_name) + colored_first_name.allow_tags = True colored_first_name.admin_order_field = 'first_name' @@ -817,19 +830,27 @@ subclass:: the admin interface to provide feedback on the status of the objects being edited, for example:: + from django.utils.html import format_html_join + from django.utils.safestring import mark_safe + class PersonAdmin(ModelAdmin): readonly_fields = ('address_report',) def address_report(self, instance): - return ", ".join(instance.get_full_address()) or \ - "I can't determine this address." + # assuming get_full_address() returns a list of strings + # for each line of the address and you want to separate each + # line by a linebreak + return format_html_join( + mark_safe('
'), + '{0}', + ((line,) for line in instance.get_full_address()), + ) or "I can't determine this address." # short_description functions like a model field's verbose_name address_report.short_description = "Address" # in this example, we have used HTML tags in the output address_report.allow_tags = True - .. attribute:: ModelAdmin.save_as Set ``save_as`` to enable a "save as" feature on admin change forms. diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 20192ed006a..e9b29602acf 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -541,6 +541,19 @@ escaping HTML. through :func:`conditional_escape` which (ultimately) calls :func:`~django.utils.encoding.force_text` on the values. +.. function:: format_html_join(sep, format_string, args_generator) + + A wrapper of :func:`format_html`, for the common case of a group of + arguments that need to be formatted using the same format string, and then + joined using ``sep``. ``sep`` is also passed through + :func:`conditional_escape`. + + ``args_generator`` should be an iterator that returns the sequence of + ``args`` that will be passed to :func:`format_html`. For example:: + + format_html_join('\n', "
  • {0} {1}
  • ", ((u.first_name, u.last_name) + for u in users)) + .. function:: strip_tags(value) Removes anything that looks like an html tag from the string, that is