Thanks Florian Apolloner, Shai Berger, and Simon Charette for reviews. Thanks Wang Baohua for the report. Backport of 05413afa8c18cdb978fcdf470e09f7a12b234a23 from master.
69 lines
3.0 KiB
Python
69 lines
3.0 KiB
Python
import os
|
|
import stat
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
from django.core.exceptions import SuspiciousOperation
|
|
from django.test import SimpleTestCase
|
|
from django.utils import archive
|
|
|
|
|
|
class TestArchive(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
self.testdir = os.path.join(os.path.dirname(__file__), 'archives')
|
|
self.old_cwd = os.getcwd()
|
|
os.chdir(self.testdir)
|
|
|
|
def tearDown(self):
|
|
os.chdir(self.old_cwd)
|
|
|
|
def test_extract_function(self):
|
|
for entry in os.scandir(self.testdir):
|
|
with self.subTest(entry.name), tempfile.TemporaryDirectory() as tmpdir:
|
|
archive.extract(entry.path, tmpdir)
|
|
self.assertTrue(os.path.isfile(os.path.join(tmpdir, '1')))
|
|
self.assertTrue(os.path.isfile(os.path.join(tmpdir, '2')))
|
|
self.assertTrue(os.path.isfile(os.path.join(tmpdir, 'foo', '1')))
|
|
self.assertTrue(os.path.isfile(os.path.join(tmpdir, 'foo', '2')))
|
|
self.assertTrue(os.path.isfile(os.path.join(tmpdir, 'foo', 'bar', '1')))
|
|
self.assertTrue(os.path.isfile(os.path.join(tmpdir, 'foo', 'bar', '2')))
|
|
|
|
@unittest.skipIf(sys.platform == 'win32', 'Python on Windows has a limited os.chmod().')
|
|
def test_extract_file_permissions(self):
|
|
"""archive.extract() preserves file permissions."""
|
|
mask = stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO
|
|
umask = os.umask(0)
|
|
os.umask(umask) # Restore the original umask.
|
|
for entry in os.scandir(self.testdir):
|
|
if entry.name.startswith('leadpath_'):
|
|
continue
|
|
with self.subTest(entry.name), tempfile.TemporaryDirectory() as tmpdir:
|
|
archive.extract(entry.path, tmpdir)
|
|
# An executable file in the archive has executable permissions.
|
|
filepath = os.path.join(tmpdir, 'executable')
|
|
self.assertEqual(os.stat(filepath).st_mode & mask, 0o775)
|
|
# A file is readable even if permission data is missing.
|
|
filepath = os.path.join(tmpdir, 'no_permissions')
|
|
self.assertEqual(os.stat(filepath).st_mode & mask, 0o666 & ~umask)
|
|
|
|
|
|
class TestArchiveInvalid(SimpleTestCase):
|
|
def test_extract_function_traversal(self):
|
|
archives_dir = os.path.join(os.path.dirname(__file__), 'traversal_archives')
|
|
tests = [
|
|
('traversal.tar', '..'),
|
|
('traversal_absolute.tar', '/tmp/evil.py'),
|
|
]
|
|
if sys.platform == 'win32':
|
|
tests += [
|
|
('traversal_disk_win.tar', 'd:evil.py'),
|
|
('traversal_disk_win.zip', 'd:evil.py'),
|
|
]
|
|
msg = "Archive contains invalid path: '%s'"
|
|
for entry, invalid_path in tests:
|
|
with self.subTest(entry), tempfile.TemporaryDirectory() as tmpdir:
|
|
with self.assertRaisesMessage(SuspiciousOperation, msg % invalid_path):
|
|
archive.extract(os.path.join(archives_dir, entry), tmpdir)
|