mirror of https://github.com/python/cpython
gh-94692: Only catch OSError in shutil.rmtree() (#112756)
Previously a symlink attack resistant version of shutil.rmtree() could ignore or pass to the error handler arbitrary exception when invalid arguments were provided.
This commit is contained in:
parent
bc68f4a4ab
commit
563ccded6e
|
@ -768,13 +768,13 @@ def rmtree(path, ignore_errors=False, onerror=None, *, onexc=None, dir_fd=None):
|
|||
# lstat()/open()/fstat() trick.
|
||||
try:
|
||||
orig_st = os.lstat(path, dir_fd=dir_fd)
|
||||
except Exception as err:
|
||||
except OSError as err:
|
||||
onexc(os.lstat, path, err)
|
||||
return
|
||||
try:
|
||||
fd = os.open(path, os.O_RDONLY, dir_fd=dir_fd)
|
||||
fd_closed = False
|
||||
except Exception as err:
|
||||
except OSError as err:
|
||||
onexc(os.open, path, err)
|
||||
return
|
||||
try:
|
||||
|
|
|
@ -317,7 +317,7 @@ class TestRmTree(BaseTest, unittest.TestCase):
|
|||
self.assertTrue(os.path.exists(dir3))
|
||||
self.assertTrue(os.path.exists(file1))
|
||||
|
||||
def test_rmtree_errors_onerror(self):
|
||||
def test_rmtree_errors(self):
|
||||
# filename is guaranteed not to exist
|
||||
filename = tempfile.mktemp(dir=self.mkdtemp())
|
||||
self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
|
||||
|
@ -326,8 +326,8 @@ class TestRmTree(BaseTest, unittest.TestCase):
|
|||
|
||||
# existing file
|
||||
tmpdir = self.mkdtemp()
|
||||
write_file((tmpdir, "tstfile"), "")
|
||||
filename = os.path.join(tmpdir, "tstfile")
|
||||
write_file(filename, "")
|
||||
with self.assertRaises(NotADirectoryError) as cm:
|
||||
shutil.rmtree(filename)
|
||||
self.assertEqual(cm.exception.filename, filename)
|
||||
|
@ -335,6 +335,19 @@ class TestRmTree(BaseTest, unittest.TestCase):
|
|||
# test that ignore_errors option is honored
|
||||
shutil.rmtree(filename, ignore_errors=True)
|
||||
self.assertTrue(os.path.exists(filename))
|
||||
|
||||
self.assertRaises(TypeError, shutil.rmtree, None)
|
||||
self.assertRaises(TypeError, shutil.rmtree, None, ignore_errors=True)
|
||||
exc = TypeError if shutil.rmtree.avoids_symlink_attacks else NotImplementedError
|
||||
with self.assertRaises(exc):
|
||||
shutil.rmtree(filename, dir_fd='invalid')
|
||||
with self.assertRaises(exc):
|
||||
shutil.rmtree(filename, dir_fd='invalid', ignore_errors=True)
|
||||
|
||||
def test_rmtree_errors_onerror(self):
|
||||
tmpdir = self.mkdtemp()
|
||||
filename = os.path.join(tmpdir, "tstfile")
|
||||
write_file(filename, "")
|
||||
errors = []
|
||||
def onerror(*args):
|
||||
errors.append(args)
|
||||
|
@ -350,23 +363,9 @@ class TestRmTree(BaseTest, unittest.TestCase):
|
|||
self.assertEqual(errors[1][2][1].filename, filename)
|
||||
|
||||
def test_rmtree_errors_onexc(self):
|
||||
# filename is guaranteed not to exist
|
||||
filename = tempfile.mktemp(dir=self.mkdtemp())
|
||||
self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
|
||||
# test that ignore_errors option is honored
|
||||
shutil.rmtree(filename, ignore_errors=True)
|
||||
|
||||
# existing file
|
||||
tmpdir = self.mkdtemp()
|
||||
write_file((tmpdir, "tstfile"), "")
|
||||
filename = os.path.join(tmpdir, "tstfile")
|
||||
with self.assertRaises(NotADirectoryError) as cm:
|
||||
shutil.rmtree(filename)
|
||||
self.assertEqual(cm.exception.filename, filename)
|
||||
self.assertTrue(os.path.exists(filename))
|
||||
# test that ignore_errors option is honored
|
||||
shutil.rmtree(filename, ignore_errors=True)
|
||||
self.assertTrue(os.path.exists(filename))
|
||||
write_file(filename, "")
|
||||
errors = []
|
||||
def onexc(*args):
|
||||
errors.append(args)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
:func:`shutil.rmtree` now only catches OSError exceptions. Previously a
|
||||
symlink attack resistant version of ``shutil.rmtree()`` could ignore or pass
|
||||
to the error handler arbitrary exception when invalid arguments were
|
||||
provided.
|
Loading…
Reference in New Issue