Skip to content

Commit 2654e1b

Browse files
bmispelontimgraham
authored andcommitted
[1.7.x] Fixed #24461 -- Fixed XSS issue in ModelAdmin.readonly_fields
1 parent 5a3b531 commit 2654e1b

File tree

5 files changed

+34
-4
lines changed

5 files changed

+34
-4
lines changed

django/contrib/admin/helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def contents(self):
193193
if getattr(attr, "allow_tags", False):
194194
result_repr = mark_safe(result_repr)
195195
else:
196-
result_repr = linebreaksbr(result_repr)
196+
result_repr = linebreaksbr(result_repr, autoescape=True)
197197
else:
198198
if isinstance(f.rel, ManyToManyRel) and value is not None:
199199
result_repr = ", ".join(map(six.text_type, value.all()))

docs/releases/1.7.6.txt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,23 @@
22
Django 1.7.6 release notes
33
==========================
44

5-
*Under Development*
5+
*March 9, 2015*
66

7-
Django 1.7.6 fixes several bugs in 1.7.5.
7+
Django 1.7.6 fixes a security issue and several bugs in 1.7.5.
8+
9+
Mitigated an XSS attack via properties in ``ModelAdmin.readonly_fields``
10+
========================================================================
11+
12+
The :attr:`ModelAdmin.readonly_fields
13+
<django.contrib.admin.ModelAdmin.readonly_fields>` attribute in the Django
14+
admin allows displaying model fields and model attributes. While the former
15+
were correctly escaped, the latter were not. Thus untrusted content could be
16+
injected into the admin, presenting an exploitation vector for XSS attacks.
17+
18+
In this vulnerability, every model attribute used in ``readonly_fields`` that
19+
is not an actual model field (e.g. a :class:`property`) will **fail to be
20+
escaped** even if that attribute is not marked as safe. In this release,
21+
autoescaping is now correctly applied.
822

923
Bugfixes
1024
========

tests/admin_views/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ def get_formsets_with_inlines(self, request, obj=None):
860860
site = admin.AdminSite(name="admin")
861861
site.register(Article, ArticleAdmin)
862862
site.register(CustomArticle, CustomArticleAdmin)
863-
site.register(Section, save_as=True, inlines=[ArticleInline])
863+
site.register(Section, save_as=True, inlines=[ArticleInline], readonly_fields=['name_property'])
864864
site.register(ModelWithStringPrimaryKey)
865865
site.register(Color)
866866
site.register(Thing, ThingAdmin)

tests/admin_views/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ class Section(models.Model):
2222
"""
2323
name = models.CharField(max_length=100)
2424

25+
@property
26+
def name_property(self):
27+
"""
28+
A property that simply returns the name. Used to test #24461
29+
"""
30+
return self.name
31+
2532

2633
@python_2_unicode_compatible
2734
class Article(models.Model):

tests/admin_views/tests.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3862,6 +3862,15 @@ def test_readonly_field_overrides(self):
38623862
self.assertContains(response, '<label for="id_public">Overridden public label:</label>', html=True)
38633863
self.assertNotContains(response, "Some help text for the date (with unicode ŠĐĆŽćžšđ)")
38643864

3865+
def test_correct_autoescaping(self):
3866+
"""
3867+
Make sure that non-field readonly elements are properly autoescaped (#24461)
3868+
"""
3869+
section = Section.objects.create(name='<a>evil</a>')
3870+
response = self.client.get(reverse('admin:admin_views_section_change', args=(section.pk,)))
3871+
self.assertNotContains(response, "<a>evil</a>", status_code=200)
3872+
self.assertContains(response, "&lt;a&gt;evil&lt;/a&gt;", status_code=200)
3873+
38653874

38663875
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
38673876
class LimitChoicesToInAdminTest(TestCase):

0 commit comments

Comments
 (0)