Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
gh-114959: tarfile: do not ignore errors when extract a directory on …
…top of a file (GH-114960) Also, add tests common to tarfile and zipfile.
- Loading branch information
1 parent
b4240fd
commit 96bce03
Showing
5 changed files
with
220 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
"""Tests common to tarfile and zipfile.""" | ||
|
||
import os | ||
import sys | ||
|
||
from test.support import os_helper | ||
|
||
class OverwriteTests: | ||
|
||
def setUp(self): | ||
os.makedirs(self.testdir) | ||
self.addCleanup(os_helper.rmtree, self.testdir) | ||
|
||
def create_file(self, path, content=b''): | ||
with open(path, 'wb') as f: | ||
f.write(content) | ||
|
||
def open(self, path): | ||
raise NotImplementedError | ||
|
||
def extractall(self, ar): | ||
raise NotImplementedError | ||
|
||
|
||
def test_overwrite_file_as_file(self): | ||
target = os.path.join(self.testdir, 'test') | ||
self.create_file(target, b'content') | ||
with self.open(self.ar_with_file) as ar: | ||
self.extractall(ar) | ||
self.assertTrue(os.path.isfile(target)) | ||
with open(target, 'rb') as f: | ||
self.assertEqual(f.read(), b'newcontent') | ||
|
||
def test_overwrite_dir_as_dir(self): | ||
target = os.path.join(self.testdir, 'test') | ||
os.mkdir(target) | ||
with self.open(self.ar_with_dir) as ar: | ||
self.extractall(ar) | ||
self.assertTrue(os.path.isdir(target)) | ||
|
||
def test_overwrite_dir_as_implicit_dir(self): | ||
target = os.path.join(self.testdir, 'test') | ||
os.mkdir(target) | ||
with self.open(self.ar_with_implicit_dir) as ar: | ||
self.extractall(ar) | ||
self.assertTrue(os.path.isdir(target)) | ||
self.assertTrue(os.path.isfile(os.path.join(target, 'file'))) | ||
with open(os.path.join(target, 'file'), 'rb') as f: | ||
self.assertEqual(f.read(), b'newcontent') | ||
|
||
def test_overwrite_dir_as_file(self): | ||
target = os.path.join(self.testdir, 'test') | ||
os.mkdir(target) | ||
with self.open(self.ar_with_file) as ar: | ||
with self.assertRaises(PermissionError if sys.platform == 'win32' | ||
else IsADirectoryError): | ||
self.extractall(ar) | ||
self.assertTrue(os.path.isdir(target)) | ||
|
||
def test_overwrite_file_as_dir(self): | ||
target = os.path.join(self.testdir, 'test') | ||
self.create_file(target, b'content') | ||
with self.open(self.ar_with_dir) as ar: | ||
with self.assertRaises(FileExistsError): | ||
self.extractall(ar) | ||
self.assertTrue(os.path.isfile(target)) | ||
with open(target, 'rb') as f: | ||
self.assertEqual(f.read(), b'content') | ||
|
||
def test_overwrite_file_as_implicit_dir(self): | ||
target = os.path.join(self.testdir, 'test') | ||
self.create_file(target, b'content') | ||
with self.open(self.ar_with_implicit_dir) as ar: | ||
with self.assertRaises(FileNotFoundError if sys.platform == 'win32' | ||
else NotADirectoryError): | ||
self.extractall(ar) | ||
self.assertTrue(os.path.isfile(target)) | ||
with open(target, 'rb') as f: | ||
self.assertEqual(f.read(), b'content') | ||
|
||
@os_helper.skip_unless_symlink | ||
def test_overwrite_file_symlink_as_file(self): | ||
# XXX: It is potential security vulnerability. | ||
target = os.path.join(self.testdir, 'test') | ||
target2 = os.path.join(self.testdir, 'test2') | ||
self.create_file(target2, b'content') | ||
os.symlink('test2', target) | ||
with self.open(self.ar_with_file) as ar: | ||
self.extractall(ar) | ||
self.assertTrue(os.path.islink(target)) | ||
self.assertTrue(os.path.isfile(target2)) | ||
with open(target2, 'rb') as f: | ||
self.assertEqual(f.read(), b'newcontent') | ||
|
||
@os_helper.skip_unless_symlink | ||
def test_overwrite_broken_file_symlink_as_file(self): | ||
# XXX: It is potential security vulnerability. | ||
target = os.path.join(self.testdir, 'test') | ||
target2 = os.path.join(self.testdir, 'test2') | ||
os.symlink('test2', target) | ||
with self.open(self.ar_with_file) as ar: | ||
self.extractall(ar) | ||
self.assertTrue(os.path.islink(target)) | ||
self.assertTrue(os.path.isfile(target2)) | ||
with open(target2, 'rb') as f: | ||
self.assertEqual(f.read(), b'newcontent') | ||
|
||
@os_helper.skip_unless_symlink | ||
def test_overwrite_dir_symlink_as_dir(self): | ||
# XXX: It is potential security vulnerability. | ||
target = os.path.join(self.testdir, 'test') | ||
target2 = os.path.join(self.testdir, 'test2') | ||
os.mkdir(target2) | ||
os.symlink('test2', target, target_is_directory=True) | ||
with self.open(self.ar_with_dir) as ar: | ||
self.extractall(ar) | ||
self.assertTrue(os.path.islink(target)) | ||
self.assertTrue(os.path.isdir(target2)) | ||
|
||
@os_helper.skip_unless_symlink | ||
def test_overwrite_dir_symlink_as_implicit_dir(self): | ||
# XXX: It is potential security vulnerability. | ||
target = os.path.join(self.testdir, 'test') | ||
target2 = os.path.join(self.testdir, 'test2') | ||
os.mkdir(target2) | ||
os.symlink('test2', target, target_is_directory=True) | ||
with self.open(self.ar_with_implicit_dir) as ar: | ||
self.extractall(ar) | ||
self.assertTrue(os.path.islink(target)) | ||
self.assertTrue(os.path.isdir(target2)) | ||
self.assertTrue(os.path.isfile(os.path.join(target2, 'file'))) | ||
with open(os.path.join(target2, 'file'), 'rb') as f: | ||
self.assertEqual(f.read(), b'newcontent') | ||
|
||
@os_helper.skip_unless_symlink | ||
def test_overwrite_broken_dir_symlink_as_dir(self): | ||
target = os.path.join(self.testdir, 'test') | ||
target2 = os.path.join(self.testdir, 'test2') | ||
os.symlink('test2', target, target_is_directory=True) | ||
with self.open(self.ar_with_dir) as ar: | ||
with self.assertRaises(FileExistsError): | ||
self.extractall(ar) | ||
self.assertTrue(os.path.islink(target)) | ||
self.assertFalse(os.path.exists(target2)) | ||
|
||
@os_helper.skip_unless_symlink | ||
def test_overwrite_broken_dir_symlink_as_implicit_dir(self): | ||
target = os.path.join(self.testdir, 'test') | ||
target2 = os.path.join(self.testdir, 'test2') | ||
os.symlink('test2', target, target_is_directory=True) | ||
with self.open(self.ar_with_implicit_dir) as ar: | ||
with self.assertRaises(FileExistsError): | ||
self.extractall(ar) | ||
self.assertTrue(os.path.islink(target)) | ||
self.assertFalse(os.path.exists(target2)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
Misc/NEWS.d/next/Library/2024-02-03-16-59-25.gh-issue-114959.dCfAG2.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
:mod:`tarfile` no longer ignores errors when trying to extract a directory on | ||
top of a file. |