From c81669cb54f964c6e726c7f130211babe93e6991 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Tue, 14 Jan 2025 23:08:50 +0100 Subject: [PATCH] [5.1.x] Fixed #36098 -- Fixed validate_ipv6_address()/validate_ipv46_address() crash for non-string values. Regression in ca2be7724e1244a4cb723de40a070f873c6e94bf. Backport of b3c5830769d8a5dbf2f974da7116fe503c9454d9 from main. --- django/utils/ipv6.py | 10 ++++++---- docs/releases/4.2.19.txt | 14 ++++++++++++++ docs/releases/5.0.12.txt | 14 ++++++++++++++ docs/releases/5.1.6.txt | 4 +++- docs/releases/index.txt | 2 ++ tests/utils_tests/test_ipv6.py | 18 ++++++++++++++++++ tests/validators/tests.py | 11 +++++++++++ 7 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 docs/releases/4.2.19.txt create mode 100644 docs/releases/5.0.12.txt diff --git a/django/utils/ipv6.py b/django/utils/ipv6.py index 1b79d52226..c77083e3cf 100644 --- a/django/utils/ipv6.py +++ b/django/utils/ipv6.py @@ -51,12 +51,14 @@ def clean_ipv6_address( return str(addr) -def is_valid_ipv6_address(ip_str): +def is_valid_ipv6_address(ip_addr): """ - Return whether or not the `ip_str` string is a valid IPv6 address. + Return whether the `ip_addr` object is a valid IPv6 address. """ + if isinstance(ip_addr, ipaddress.IPv6Address): + return True try: - _ipv6_address_from_str(ip_str) - except ValueError: + _ipv6_address_from_str(ip_addr) + except (TypeError, ValueError): return False return True diff --git a/docs/releases/4.2.19.txt b/docs/releases/4.2.19.txt new file mode 100644 index 0000000000..91bc8e5842 --- /dev/null +++ b/docs/releases/4.2.19.txt @@ -0,0 +1,14 @@ +=========================== +Django 4.2.19 release notes +=========================== + +*Expected February 5, 2025* + +Django 4.2.19 fixes a regression in 4.2.18. + +Bugfixes +======== + +* Fixed a regression in Django 4.2.18 that caused ``validate_ipv6_address()`` + and ``validate_ipv46_address()`` to crash when handling non-string values + (:ticket:`36098`). diff --git a/docs/releases/5.0.12.txt b/docs/releases/5.0.12.txt new file mode 100644 index 0000000000..1e1f0f2a95 --- /dev/null +++ b/docs/releases/5.0.12.txt @@ -0,0 +1,14 @@ +=========================== +Django 5.0.12 release notes +=========================== + +*Expected February 5, 2025* + +Django 5.0.12 fixes a regression in 5.0.11. + +Bugfixes +======== + +* Fixed a regression in Django 5.0.11 that caused ``validate_ipv6_address()`` + and ``validate_ipv46_address()`` to crash when handling non-string values + (:ticket:`36098`). diff --git a/docs/releases/5.1.6.txt b/docs/releases/5.1.6.txt index fa60de4c8d..3b23192033 100644 --- a/docs/releases/5.1.6.txt +++ b/docs/releases/5.1.6.txt @@ -9,4 +9,6 @@ Django 5.1.6 fixes several bugs in 5.1.5. Bugfixes ======== -* ... +* Fixed a regression in Django 5.1.5 that caused ``validate_ipv6_address()`` + and ``validate_ipv46_address()`` to crash when handling non-string values + (:ticket:`36098`). diff --git a/docs/releases/index.txt b/docs/releases/index.txt index 0402246b14..caeadb87f5 100644 --- a/docs/releases/index.txt +++ b/docs/releases/index.txt @@ -38,6 +38,7 @@ versions of the documentation contain the release notes for any later releases. .. toctree:: :maxdepth: 1 + 5.0.12 5.0.11 5.0.10 5.0.9 @@ -57,6 +58,7 @@ versions of the documentation contain the release notes for any later releases. .. toctree:: :maxdepth: 1 + 4.2.19 4.2.18 4.2.17 4.2.16 diff --git a/tests/utils_tests/test_ipv6.py b/tests/utils_tests/test_ipv6.py index 1754c7b356..e43e6ca6e4 100644 --- a/tests/utils_tests/test_ipv6.py +++ b/tests/utils_tests/test_ipv6.py @@ -1,5 +1,7 @@ import traceback +from decimal import Decimal from io import StringIO +from ipaddress import IPv6Address from django.core.exceptions import ValidationError from django.test import SimpleTestCase @@ -23,6 +25,16 @@ class TestUtilsIPv6(SimpleTestCase): self.assertTrue(is_valid_ipv6_address("::ffff:254.42.16.14")) self.assertTrue(is_valid_ipv6_address("::ffff:0a0a:0a0a")) + def test_validates_correct_with_ipv6_instance(self): + cases = [ + IPv6Address("::ffff:2.125.160.216"), + IPv6Address("fe80::1"), + IPv6Address("::"), + ] + for case in cases: + with self.subTest(case=case): + self.assertIs(is_valid_ipv6_address(case), True) + def test_validates_incorrect_plain_address(self): self.assertFalse(is_valid_ipv6_address("foo")) self.assertFalse(is_valid_ipv6_address("127.0.0.1")) @@ -45,6 +57,12 @@ class TestUtilsIPv6(SimpleTestCase): self.assertFalse(is_valid_ipv6_address("::999.42.16.14")) self.assertFalse(is_valid_ipv6_address("::zzzz:0a0a")) + def test_validates_incorrect_with_non_string(self): + cases = [None, [], {}, (), Decimal("2.46"), 192.168, 42] + for case in cases: + with self.subTest(case=case): + self.assertIs(is_valid_ipv6_address(case), False) + def test_cleans_plain_address(self): self.assertEqual(clean_ipv6_address("DEAD::0:BEEF"), "dead::beef") self.assertEqual( diff --git a/tests/validators/tests.py b/tests/validators/tests.py index 4ae0f6413e..7e0db84c11 100644 --- a/tests/validators/tests.py +++ b/tests/validators/tests.py @@ -1,3 +1,4 @@ +import ipaddress import re import types from datetime import datetime, timedelta @@ -383,15 +384,25 @@ TEST_DATA = [ (validate_ipv6_address, "fe80::1", None), (validate_ipv6_address, "::1", None), (validate_ipv6_address, "1:2:3:4:5:6:7:8", None), + (validate_ipv6_address, ipaddress.IPv6Address("::ffff:2.125.160.216"), None), + (validate_ipv6_address, ipaddress.IPv6Address("fe80::1"), None), + (validate_ipv6_address, Decimal("33.1"), ValidationError), + (validate_ipv6_address, 9.22, ValidationError), (validate_ipv6_address, "1:2", ValidationError), (validate_ipv6_address, "::zzz", ValidationError), (validate_ipv6_address, "12345::", ValidationError), (validate_ipv46_address, "1.1.1.1", None), (validate_ipv46_address, "255.0.0.0", None), (validate_ipv46_address, "0.0.0.0", None), + (validate_ipv46_address, ipaddress.IPv4Address("1.1.1.1"), None), + (validate_ipv46_address, ipaddress.IPv4Address("255.0.0.0"), None), (validate_ipv46_address, "fe80::1", None), (validate_ipv46_address, "::1", None), (validate_ipv46_address, "1:2:3:4:5:6:7:8", None), + (validate_ipv46_address, ipaddress.IPv6Address("::ffff:2.125.160.216"), None), + (validate_ipv46_address, ipaddress.IPv6Address("fe80::1"), None), + (validate_ipv46_address, Decimal("33.1"), ValidationError), + (validate_ipv46_address, 9.22, ValidationError), (validate_ipv46_address, "256.1.1.1", ValidationError), (validate_ipv46_address, "25.1.1.", ValidationError), (validate_ipv46_address, "25,1,1,1", ValidationError),