summaryrefslogtreecommitdiff
path: root/django/forms/fields.py
diff options
context:
space:
mode:
authorNatalia <124304+nessita@users.noreply.github.com>2026-01-29 22:52:41 -0300
committerNatalia <124304+nessita@users.noreply.github.com>2026-03-03 09:22:17 -0300
commitb3e8ec8cc310489fe80174b14b11edb970d682ea (patch)
tree331fb2517ce3ade9cc2f44b3154e48678540b136 /django/forms/fields.py
parente52ff00856cce3a2b05d244ee98dc2b8d9fcf3a9 (diff)
[4.2.x] Fixed CVE-2026-25673 -- Simplified URLField scheme detection.
This simplicaftion mitigates a potential DoS in URLField on Windows. The usage of `urlsplit()` in `URLField.to_python()` was replaced with `str.partition(":")` for URL scheme detection. On Windows, `urlsplit()` performs Unicode normalization which is slow for certain characters, making `URLField` vulnerable to DoS via specially crafted POST payloads. Thanks Seokchan Yoon for the report, and Jake Howard and Shai Berger for the review. Refs #36923. Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com> Backport of 951ffb3832cd83ba672c1e3deae2bda128eb9cca from main.
Diffstat (limited to 'django/forms/fields.py')
-rw-r--r--django/forms/fields.py42
1 files changed, 16 insertions, 26 deletions
diff --git a/django/forms/fields.py b/django/forms/fields.py
index e62417f552..5c2e35091b 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -12,7 +12,6 @@ import re
import uuid
from decimal import Decimal, DecimalException
from io import BytesIO
-from urllib.parse import urlsplit, urlunsplit
from django.core import validators
from django.core.exceptions import ValidationError
@@ -757,33 +756,24 @@ class URLField(CharField):
super().__init__(strip=True, **kwargs)
def to_python(self, value):
- def split_url(url):
- """
- Return a list of url parts via urlparse.urlsplit(), or raise
- ValidationError for some malformed URLs.
- """
- try:
- return list(urlsplit(url))
- except ValueError:
- # urlparse.urlsplit can raise a ValueError with some
- # misformatted URLs.
- raise ValidationError(self.error_messages["invalid"], code="invalid")
-
value = super().to_python(value)
if value:
- url_fields = split_url(value)
- if not url_fields[0]:
- # If no URL scheme given, assume http://
- url_fields[0] = "http"
- if not url_fields[1]:
- # Assume that if no domain is provided, that the path segment
- # contains the domain.
- url_fields[1] = url_fields[2]
- url_fields[2] = ""
- # Rebuild the url_fields list, since the domain segment may now
- # contain the path too.
- url_fields = split_url(urlunsplit(url_fields))
- value = urlunsplit(url_fields)
+ # Detect scheme via partition to avoid calling urlsplit() on
+ # potentially large or slow-to-normalize inputs.
+ scheme, sep, _ = value.partition(":")
+ if (
+ not sep
+ or not scheme
+ or not scheme[0].isascii()
+ or not scheme[0].isalpha()
+ or "/" in scheme
+ ):
+ # No valid scheme found -- prepend the assumed scheme. Handle
+ # scheme-relative URLs ("//example.com") separately.
+ if value.startswith("//"):
+ value = "http:" + value
+ else:
+ value = "http://" + value
return value