VYPR
Moderate severityNVD Advisory· Published Jun 3, 2020· Updated Aug 4, 2024

CVE-2020-13596

CVE-2020-13596

Description

An issue was discovered in Django 2.2 before 2.2.13 and 3.0 before 3.0.7. Query parameters generated by the Django admin ForeignKeyRawIdWidget were not properly URL encoded, leading to a possibility of an XSS attack.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Django ForeignKeyRawIdWidget in admin improperly encodes query parameters, enabling stored XSS.

Vulnerability

Overview

CVE-2020-13596 is a cross-site scripting (XSS) vulnerability in Django's admin interface, specifically in the ForeignKeyRawIdWidget. The widget generates query parameters for related-object lookups but fails to properly URL-encode them, allowing an attacker to inject arbitrary HTML or JavaScript into the admin page. The issue affects Django versions 2.2 before 2.2.13 and 3.0 before 3.0.7 [1][2].

Exploitation

Scenario

An attacker with the ability to supply crafted input (e.g., through a related model's field value) can cause the malformed query parameter to be rendered in the admin's pop-up window. No special privileges are required beyond basic admin access; the attack surface is the admin interface where the widget is used. Because the parameter is not sanitized before being placed into a link, a stored XSS payload can be executed in the context of the admin user's session [2][3].

Impact

Successful exploitation allows an attacker to execute arbitrary JavaScript in the browser of an admin user. This can lead to session hijacking, unauthorized actions taken on behalf of the admin, or theft of sensitive data displayed in the admin interface. The CVSSv3.1 base score is 6.1 (Medium), reflecting a low attack complexity but requiring user interaction (clicking the widget's link) [1][2].

Mitigation

The Django project released patched versions (2.2.13 and 3.0.7) that properly URL-encode the query parameters. Users should upgrade immediately. No workaround is available if the widget is used. The vulnerability is not known to be exploited in the wild, but upgrading is strongly recommended [1][3][4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
DjangoPyPI
>= 2.2a1, < 2.2.132.2.13
DjangoPyPI
>= 3.0a1, < 3.0.73.0.7

Affected products

300

Patches

2
6d61860b2287

[2.0.x] Fixed CVE-2020-13596 -- Fixed potential XSS in admin ForeignKeyRawIdWidget.

https://github.com/django/djangoJon DufresneMay 26, 2020via ghsa
4 files changed · +29 3
  • django/contrib/admin/widgets.py+3 3 modified
    @@ -12,7 +12,7 @@
     from django.urls import reverse
     from django.urls.exceptions import NoReverseMatch
     from django.utils.html import smart_urlquote
    -from django.utils.safestring import mark_safe
    +from django.utils.http import urlencode
     from django.utils.text import Truncator
     from django.utils.translation import get_language, gettext as _
     
    @@ -150,8 +150,8 @@ def get_context(self, name, value, attrs):
     
                 params = self.url_parameters()
                 if params:
    -                related_url += '?' + '&amp;'.join('%s=%s' % (k, v) for k, v in params.items())
    -            context['related_url'] = mark_safe(related_url)
    +                related_url += '?' + urlencode(params)
    +            context['related_url'] = related_url
                 context['link_title'] = _('Lookup')
                 # The JavaScript code looks for this class.
                 context['widget']['attrs'].setdefault('class', 'vForeignKeyRawIdAdminField')
    
  • docs/releases/2.2.13.txt+7 0 modified
    @@ -6,6 +6,13 @@ Django 2.2.13 release notes
     
     Django 2.2.13 fixes two security issues and a regression in 2.2.12.
     
    +CVE-2020-13596: Possible XSS via admin ``ForeignKeyRawIdWidget``
    +================================================================
    +
    +Query parameters for the admin ``ForeignKeyRawIdWidget`` were not properly URL
    +encoded, posing an XSS attack vector. ``ForeignKeyRawIdWidget`` now
    +ensures query parameters are correctly URL encoded.
    +
     Bugfixes
     ========
     
    
  • tests/admin_widgets/models.py+8 0 modified
    @@ -27,6 +27,14 @@ def __str__(self):
             return self.name
     
     
    +class UnsafeLimitChoicesTo(models.Model):
    +    band = models.ForeignKey(
    +        Band,
    +        models.CASCADE,
    +        limit_choices_to={'name': '"&><escapeme'},
    +    )
    +
    +
     class Album(models.Model):
         band = models.ForeignKey(Band, models.CASCADE)
         featuring = models.ManyToManyField(Band, related_name='featured')
    
  • tests/admin_widgets/tests.py+11 0 modified
    @@ -22,6 +22,7 @@
     from .models import (
         Advisor, Album, Band, Bee, Car, Company, Event, Honeycomb, Individual,
         Inventory, Member, MyFileField, Profile, School, Student,
    +    UnsafeLimitChoicesTo,
     )
     from .widgetadmin import site as widget_admin_site
     
    @@ -586,6 +587,16 @@ def test_proper_manager_for_label_lookup(self):
                 'Hidden</a></strong>' % {'pk': hidden.pk}
             )
     
    +    def test_render_unsafe_limit_choices_to(self):
    +        rel = UnsafeLimitChoicesTo._meta.get_field('band').remote_field
    +        w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
    +        self.assertHTMLEqual(
    +            w.render('test', None),
    +            '<input type="text" name="test" class="vForeignKeyRawIdAdminField">\n'
    +            '<a href="/admin_widgets/band/?name=%22%26%3E%3Cescapeme&amp;_to_field=id" '
    +            'class="related-lookup" id="lookup_id_test" title="Lookup"></a>'
    +        )
    +
     
     @override_settings(ROOT_URLCONF='admin_widgets.urls')
     class ManyToManyRawIdWidgetTest(TestCase):
    
1f2dd37f6fce

[3.0.x] Fixed CVE-2020-13596 -- Fixed potential XSS in admin ForeignKeyRawIdWidget.

https://github.com/django/djangoJon DufresneMay 26, 2020via ghsa
5 files changed · +36 3
  • django/contrib/admin/widgets.py+3 3 modified
    @@ -12,7 +12,7 @@
     from django.urls import reverse
     from django.urls.exceptions import NoReverseMatch
     from django.utils.html import smart_urlquote
    -from django.utils.safestring import mark_safe
    +from django.utils.http import urlencode
     from django.utils.text import Truncator
     from django.utils.translation import get_language, gettext as _
     
    @@ -150,8 +150,8 @@ def get_context(self, name, value, attrs):
     
                 params = self.url_parameters()
                 if params:
    -                related_url += '?' + '&amp;'.join('%s=%s' % (k, v) for k, v in params.items())
    -            context['related_url'] = mark_safe(related_url)
    +                related_url += '?' + urlencode(params)
    +            context['related_url'] = related_url
                 context['link_title'] = _('Lookup')
                 # The JavaScript code looks for this class.
                 context['widget']['attrs'].setdefault('class', 'vForeignKeyRawIdAdminField')
    
  • docs/releases/2.2.13.txt+7 0 modified
    @@ -6,6 +6,13 @@ Django 2.2.13 release notes
     
     Django 2.2.13 fixes two security issues and a regression in 2.2.12.
     
    +CVE-2020-13596: Possible XSS via admin ``ForeignKeyRawIdWidget``
    +================================================================
    +
    +Query parameters for the admin ``ForeignKeyRawIdWidget`` were not properly URL
    +encoded, posing an XSS attack vector. ``ForeignKeyRawIdWidget`` now
    +ensures query parameters are correctly URL encoded.
    +
     Bugfixes
     ========
     
    
  • docs/releases/3.0.7.txt+7 0 modified
    @@ -6,6 +6,13 @@ Django 3.0.7 release notes
     
     Django 3.0.7 fixes two security issues and several bugs in 3.0.6.
     
    +CVE-2020-13596: Possible XSS via admin ``ForeignKeyRawIdWidget``
    +================================================================
    +
    +Query parameters for the admin ``ForeignKeyRawIdWidget`` were not properly URL
    +encoded, posing an XSS attack vector. ``ForeignKeyRawIdWidget`` now
    +ensures query parameters are correctly URL encoded.
    +
     Bugfixes
     ========
     
    
  • tests/admin_widgets/models.py+8 0 modified
    @@ -27,6 +27,14 @@ def __str__(self):
             return self.name
     
     
    +class UnsafeLimitChoicesTo(models.Model):
    +    band = models.ForeignKey(
    +        Band,
    +        models.CASCADE,
    +        limit_choices_to={'name': '"&><escapeme'},
    +    )
    +
    +
     class Album(models.Model):
         band = models.ForeignKey(Band, models.CASCADE)
         featuring = models.ManyToManyField(Band, related_name='featured')
    
  • tests/admin_widgets/tests.py+11 0 modified
    @@ -22,6 +22,7 @@
     from .models import (
         Advisor, Album, Band, Bee, Car, Company, Event, Honeycomb, Individual,
         Inventory, Member, MyFileField, Profile, School, Student,
    +    UnsafeLimitChoicesTo,
     )
     from .widgetadmin import site as widget_admin_site
     
    @@ -586,6 +587,16 @@ def test_proper_manager_for_label_lookup(self):
                 'Hidden</a></strong>' % {'pk': hidden.pk}
             )
     
    +    def test_render_unsafe_limit_choices_to(self):
    +        rel = UnsafeLimitChoicesTo._meta.get_field('band').remote_field
    +        w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
    +        self.assertHTMLEqual(
    +            w.render('test', None),
    +            '<input type="text" name="test" class="vForeignKeyRawIdAdminField">\n'
    +            '<a href="/admin_widgets/band/?name=%22%26%3E%3Cescapeme&amp;_to_field=id" '
    +            'class="related-lookup" id="lookup_id_test" title="Lookup"></a>'
    +        )
    +
     
     @override_settings(ROOT_URLCONF='admin_widgets.urls')
     class ManyToManyRawIdWidgetTest(TestCase):
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

21

News mentions

0

No linked articles in our index yet.