Moderate severityNVD Advisory· Published Mar 12, 2015· Updated May 6, 2026
CVE-2015-2241
CVE-2015-2241
Description
Cross-site scripting (XSS) vulnerability in the contents function in admin/helpers.py in Django before 1.7.6 and 1.8 before 1.8b2 allows remote attackers to inject arbitrary web script or HTML via a model attribute in ModelAdmin.readonly_fields, as demonstrated by a @property.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
DjangoPyPI | < 1.7.6 | 1.7.6 |
DjangoPyPI | >= 1.8a1, < 1.8b2 | 1.8b2 |
Patches
22654e1b93923[1.7.x] Fixed #24461 -- Fixed XSS issue in ModelAdmin.readonly_fields
5 files changed · +34 −4
django/contrib/admin/helpers.py+1 −1 modified@@ -193,7 +193,7 @@ def contents(self): if getattr(attr, "allow_tags", False): result_repr = mark_safe(result_repr) else: - result_repr = linebreaksbr(result_repr) + result_repr = linebreaksbr(result_repr, autoescape=True) else: if isinstance(f.rel, ManyToManyRel) and value is not None: result_repr = ", ".join(map(six.text_type, value.all()))
docs/releases/1.7.6.txt+16 −2 modified@@ -2,9 +2,23 @@ Django 1.7.6 release notes ========================== -*Under Development* +*March 9, 2015* -Django 1.7.6 fixes several bugs in 1.7.5. +Django 1.7.6 fixes a security issue and several bugs in 1.7.5. + +Mitigated an XSS attack via properties in ``ModelAdmin.readonly_fields`` +======================================================================== + +The :attr:`ModelAdmin.readonly_fields +<django.contrib.admin.ModelAdmin.readonly_fields>` attribute in the Django +admin allows displaying model fields and model attributes. While the former +were correctly escaped, the latter were not. Thus untrusted content could be +injected into the admin, presenting an exploitation vector for XSS attacks. + +In this vulnerability, every model attribute used in ``readonly_fields`` that +is not an actual model field (e.g. a :class:`property`) will **fail to be +escaped** even if that attribute is not marked as safe. In this release, +autoescaping is now correctly applied. Bugfixes ========
tests/admin_views/admin.py+1 −1 modified@@ -860,7 +860,7 @@ def get_formsets_with_inlines(self, request, obj=None): site = admin.AdminSite(name="admin") site.register(Article, ArticleAdmin) site.register(CustomArticle, CustomArticleAdmin) -site.register(Section, save_as=True, inlines=[ArticleInline]) +site.register(Section, save_as=True, inlines=[ArticleInline], readonly_fields=['name_property']) site.register(ModelWithStringPrimaryKey) site.register(Color) site.register(Thing, ThingAdmin)
tests/admin_views/models.py+7 −0 modified@@ -22,6 +22,13 @@ class Section(models.Model): """ name = models.CharField(max_length=100) + @property + def name_property(self): + """ + A property that simply returns the name. Used to test #24461 + """ + return self.name + @python_2_unicode_compatible class Article(models.Model):
tests/admin_views/tests.py+9 −0 modified@@ -3862,6 +3862,15 @@ def test_readonly_field_overrides(self): self.assertContains(response, '<label for="id_public">Overridden public label:</label>', html=True) self.assertNotContains(response, "Some help text for the date (with unicode ŠĐĆŽćžšđ)") + def test_correct_autoescaping(self): + """ + Make sure that non-field readonly elements are properly autoescaped (#24461) + """ + section = Section.objects.create(name='<a>evil</a>') + response = self.client.get(reverse('admin:admin_views_section_change', args=(section.pk,))) + self.assertNotContains(response, "<a>evil</a>", status_code=200) + self.assertContains(response, "<a>evil</a>", status_code=200) + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class LimitChoicesToInAdminTest(TestCase):
82c9169077a0Refs #24461 -- Added test/release notes for XSS issue in ModelAdmin.readonly_fields
4 files changed · +33 −3
docs/releases/1.7.6.txt+16 −2 modified@@ -2,9 +2,23 @@ Django 1.7.6 release notes ========================== -*Under Development* +*March 9, 2015* -Django 1.7.6 fixes several bugs in 1.7.5. +Django 1.7.6 fixes a security issue and several bugs in 1.7.5. + +Mitigated an XSS attack via properties in ``ModelAdmin.readonly_fields`` +======================================================================== + +The :attr:`ModelAdmin.readonly_fields +<django.contrib.admin.ModelAdmin.readonly_fields>` attribute in the Django +admin allows displaying model fields and model attributes. While the former +were correctly escaped, the latter were not. Thus untrusted content could be +injected into the admin, presenting an exploitation vector for XSS attacks. + +In this vulnerability, every model attribute used in ``readonly_fields`` that +is not an actual model field (e.g. a :class:`property`) will **fail to be +escaped** even if that attribute is not marked as safe. In this release, +autoescaping is now correctly applied. Bugfixes ========
tests/admin_views/admin.py+1 −1 modified@@ -870,7 +870,7 @@ def get_formsets_with_inlines(self, request, obj=None): site.site_url = '/my-site-url/' site.register(Article, ArticleAdmin) site.register(CustomArticle, CustomArticleAdmin) -site.register(Section, save_as=True, inlines=[ArticleInline]) +site.register(Section, save_as=True, inlines=[ArticleInline], readonly_fields=['name_property']) site.register(ModelWithStringPrimaryKey) site.register(Color) site.register(Thing, ThingAdmin)
tests/admin_views/models.py+7 −0 modified@@ -22,6 +22,13 @@ class Section(models.Model): """ name = models.CharField(max_length=100) + @property + def name_property(self): + """ + A property that simply returns the name. Used to test #24461 + """ + return self.name + @python_2_unicode_compatible class Article(models.Model):
tests/admin_views/tests.py+9 −0 modified@@ -4644,6 +4644,15 @@ def test_readonly_field_overrides(self): self.assertContains(response, '<label for="id_public">Overridden public label:</label>', html=True) self.assertNotContains(response, "Some help text for the date (with unicode ŠĐĆŽćžšđ)") + def test_correct_autoescaping(self): + """ + Make sure that non-field readonly elements are properly autoescaped (#24461) + """ + section = Section.objects.create(name='<a>evil</a>') + response = self.client.get(reverse('admin:admin_views_section_change', args=(section.pk,))) + self.assertNotContains(response, "<a>evil</a>", status_code=200) + self.assertContains(response, "<a>evil</a>", status_code=200) + @override_settings(PASSWORD_HASHERS=['django.contrib.auth.hashers.SHA1PasswordHasher'], ROOT_URLCONF="admin_views.urls")
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
12- code.djangoproject.com/ticket/24461nvdExploitVendor AdvisoryWEB
- github.com/advisories/GHSA-6565-fg86-6jcxghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2015-2241ghsaADVISORY
- www.djangoproject.com/weblog/2015/mar/09/security-releases/nvdVendor Advisory
- www.mandriva.com/security/advisoriesnvdWEB
- github.com/django/django/commit/2654e1b93923bac55f12b4e66c5e39b16695ace5ghsaWEB
- github.com/django/django/commit/82c9169077a066995e3b00aac551bf1c8a89d98aghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/django/PYSEC-2015-8.yamlghsaWEB
- web.archive.org/web/20150523054951/http://www.mandriva.com/en/support/security/advisories/advisory/MDVSA-2015:109/ghsaWEB
- web.archive.org/web/20171112005349/http://www.securityfocus.com/bid/73095ghsaWEB
- www.djangoproject.com/weblog/2015/mar/09/security-releasesghsaWEB
- www.securityfocus.com/bid/73095nvd
News mentions
0No linked articles in our index yet.