티스토리 수익 글 보기

티스토리 수익 글 보기

[6.0.x] Fixed CVE-2026-15920 — Made display_for_field() validate URL… · django/django@13debb6 · GitHub
Skip to content

Commit 13debb6

Browse files
committed
[6.0.x] Fixed CVE-2026-15920 — Made display_for_field() validate URLs before rendering admin links.
The admin renders URLField values as clickable links on changelists and read-only change forms. The link was built without validating the URL, so a potentially dangerous stored value could be rendered as a link that runs script in a staff member’s authenticated session when clicked. The admin renders URLField values as clickable links on changelists and read-only change forms. The link was built without validating the URL, so a stored value using a potentially dangerous value was rendered as a link, which could lead to cross-site scripting in an authenticated admin session. Refs CVE-2019-12308, #36032. Thanks to Egor Saltykov for the report, and Sarah Boyce for reviews. Backport of 47511a2 from main.
1 parent 6af5da3 commit 13debb6

4 files changed

Lines changed: 53 additions & 2 deletions

File tree

django/contrib/admin/utils.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88
from django.contrib.auth import get_user_model
99
from django.contrib.auth.templatetags.auth import render_password_as_hash
10-
from django.core.exceptions import FieldDoesNotExist
11-
from django.core.validators import EMPTY_VALUES
10+
from django.core.exceptions import FieldDoesNotExist, ValidationError
11+
from django.core.validators import EMPTY_VALUES, URLValidator
1212
from django.db import models, router
1313
from django.db.models.constants import LOOKUP_SEP
1414
from django.db.models.deletion import Collector
@@ -460,6 +460,14 @@ def display_for_field(value, field, empty_value_display, avoid_link=False):
460460
elif isinstance(field, models.FileField) and value and not avoid_link:
461461
return format_html('<a href="{}">{}</a>', value.url, value)
462462
elif isinstance(field, models.URLField) and value and not avoid_link:
463+
# Only render a clickable link for URLs with a safe scheme, so that a
464+
# potentially dangerous stored value is shown as plain text rather than
465+
# an executable link. The check is deliberately independent of the
466+
# field's own validators, which may permit such schemes.
467+
try:
468+
URLValidator()(value)
469+
except ValidationError:
470+
return display_for_value(value, empty_value_display)
463471
return format_html('<a href="{}">{}</a>', value, value)
464472
elif isinstance(field, models.JSONField) and value:
465473
try:

docs/releases/5.2.17.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,18 @@ affected.
7979

8080
This issue has severity "moderate" according to the :ref:`Django security
8181
policy <severity-levels>`.
82+
83+
CVE-2026-15920: Potential cross-site scripting via ``URLField`` values in the admin
84+
===================================================================================
85+
86+
The admin renders :class:`~django.db.models.URLField` values as clickable links
87+
on changelist views and read-only fields. The link was generated without
88+
validating the value as a safe URL, so a stored value using a potentially
89+
dangerous scheme was rendered as a link.
90+
91+
``URLField`` values shown via ``display_for_field`` are now validated using
92+
:class:`~django.core.validators.URLValidator` before a link is rendered, and
93+
displayed as plain text if validation is failed.
94+
95+
This issue has severity "moderate" according to the :ref:`Django security
96+
policy <severity-levels>`.

docs/releases/6.0.8.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,21 @@ affected.
8080
This issue has severity "moderate" according to the :ref:`Django security
8181
policy <severity-levels>`.
8282

83+
CVE-2026-15920: Potential cross-site scripting via ``URLField`` values in the admin
84+
===================================================================================
85+
86+
The admin renders :class:`~django.db.models.URLField` values as clickable links
87+
on changelist views and read-only fields. The link was generated without
88+
validating the value as a safe URL, so a stored value using a potentially
89+
dangerous scheme was rendered as a link.
90+
91+
``URLField`` values shown via ``display_for_field`` are now validated using
92+
:class:`~django.core.validators.URLValidator` before a link is rendered, and
93+
displayed as plain text if validation is failed.
94+
95+
This issue has severity "moderate" according to the :ref:`Django security
96+
policy <severity-levels>`.
97+
8398
Bugfixes
8499
========
85100

tests/admin_utils/tests.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,19 @@ def test_url_display_for_field(self):
208208
expected = '<a href="http://example.com">http://example.com</a>'
209209
self.assertHTMLEqual(display_value, expected)
210210

211+
def test_url_display_for_field_invalid_url(self):
212+
# An invalid URL, such as one with an unsafe scheme, is rendered as
213+
# plain text instead of a clickable link.
214+
model_field = models.URLField()
215+
for value in [
216+
"javascript:alert(1)",
217+
"data:text/html,<script>alert(1)</script>",
218+
]:
219+
with self.subTest(value=value):
220+
display_value = display_for_field(value, model_field, self.empty_value)
221+
self.assertNotIn("<a", display_value)
222+
self.assertEqual(display_value, value)
223+
211224
def test_number_formats_display_for_field(self):
212225
display_value = display_for_field(
213226
12345.6789, models.FloatField(), self.empty_value

0 commit comments

Comments
 (0)