forked from pool/python-Django
75 lines
3.0 KiB
Diff
75 lines
3.0 KiB
Diff
From 3a7091babcb19f213a25dd5bf8ad90fd63c3cba0 Mon Sep 17 00:00:00 2001
|
|
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
|
|
Date: Tue, 16 Sep 2025 17:13:36 +0200
|
|
Subject: [PATCH 2/2] [5.2.x] Fixed CVE-2025-59682 -- Fixed potential partial
|
|
directory-traversal via archive.extract().
|
|
|
|
Thanks stackered for the report.
|
|
|
|
Follow up to 05413afa8c18cdb978fcdf470e09f7a12b234a23.
|
|
---
|
|
django/utils/archive.py | 6 +++++-
|
|
docs/releases/4.2.25.txt | 8 ++++++++
|
|
docs/releases/5.1.13.txt | 8 ++++++++
|
|
docs/releases/5.2.7.txt | 8 ++++++++
|
|
tests/utils_tests/test_archive.py | 22 ++++++++++++++++++++++
|
|
5 files changed, 51 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/django/utils/archive.py b/django/utils/archive.py
|
|
index 56f34c0038..c05fbcdc97 100644
|
|
--- a/django/utils/archive.py
|
|
+++ b/django/utils/archive.py
|
|
@@ -145,7 +145,11 @@ class BaseArchive:
|
|
def target_filename(self, to_path, name):
|
|
target_path = os.path.abspath(to_path)
|
|
filename = os.path.abspath(os.path.join(target_path, name))
|
|
- if not filename.startswith(target_path):
|
|
+ try:
|
|
+ if os.path.commonpath([target_path, filename]) != target_path:
|
|
+ raise SuspiciousOperation("Archive contains invalid path: '%s'" % name)
|
|
+ except ValueError:
|
|
+ # Different drives on Windows raises ValueError.
|
|
raise SuspiciousOperation("Archive contains invalid path: '%s'" % name)
|
|
return filename
|
|
|
|
diff --git a/tests/utils_tests/test_archive.py b/tests/utils_tests/test_archive.py
|
|
index 89a45bc072..24e60039a5 100644
|
|
--- a/tests/utils_tests/test_archive.py
|
|
+++ b/tests/utils_tests/test_archive.py
|
|
@@ -3,6 +3,7 @@ import stat
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
+import zipfile
|
|
|
|
from django.core.exceptions import SuspiciousOperation
|
|
from django.test import SimpleTestCase
|
|
@@ -94,3 +95,24 @@ class TestArchiveInvalid(SimpleTestCase):
|
|
with self.subTest(entry), tempfile.TemporaryDirectory() as tmpdir:
|
|
with self.assertRaisesMessage(SuspiciousOperation, msg % invalid_path):
|
|
archive.extract(os.path.join(archives_dir, entry), tmpdir)
|
|
+
|
|
+ def test_extract_function_traversal_startswith(self):
|
|
+ with tempfile.TemporaryDirectory() as tmpdir:
|
|
+ base = os.path.abspath(tmpdir)
|
|
+ tarfile_handle = tempfile.NamedTemporaryFile(suffix=".zip", delete=False)
|
|
+ tar_path = tarfile_handle.name
|
|
+
|
|
+ try:
|
|
+ tarfile_handle.close()
|
|
+ malicious_member = os.path.join(base + "abc", "evil.txt")
|
|
+
|
|
+ with zipfile.ZipFile(tar_path, "w") as zf:
|
|
+ zf.writestr(malicious_member, "evil\n")
|
|
+ zf.writestr("test.txt", "data\n")
|
|
+
|
|
+ with self.assertRaisesMessage(
|
|
+ SuspiciousOperation, "Archive contains invalid path"
|
|
+ ):
|
|
+ archive.extract(tar_path, base)
|
|
+ finally:
|
|
+ os.remove(tar_path)
|
|
--
|
|
2.39.5 (Apple Git-154)
|
|
|