VYPR
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.

PackageAffected versionsPatched versions
DjangoPyPI
< 1.7.61.7.6
DjangoPyPI
>= 1.8a1, < 1.8b21.8b2

Patches

2
2654e1b93923

[1.7.x] Fixed #24461 -- Fixed XSS issue in ModelAdmin.readonly_fields

https://github.com/django/djangoBaptiste MispelonMar 8, 2015via ghsa
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, "&lt;a&gt;evil&lt;/a&gt;", status_code=200)
    +
     
     @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
     class LimitChoicesToInAdminTest(TestCase):
    
82c9169077a0

Refs #24461 -- Added test/release notes for XSS issue in ModelAdmin.readonly_fields

https://github.com/django/djangoBaptiste MispelonMar 8, 2015via ghsa
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, "&lt;a&gt;evil&lt;/a&gt;", 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

News mentions

0

No linked articles in our index yet.