forked from pool/python312
Matej Cepl
7a215a300e
- Security - gh-118486: os.mkdir() on Windows now accepts mode of 0o700 to restrict the new directory to the current user. This fixes CVE-2024-4030 affecting tempfile.mkdtemp() in scenarios where the base temporary directory is more permissive than the default. - gh-116741: Update bundled libexpat to 2.6.2 - gh-117233: Detect BLAKE2, SHA3, Shake, & truncated SHA512 support in the OpenSSL-ish libcrypto library at build time. This allows hashlib to be used with libraries that do not to support every algorithm that upstream OpenSSL does. - Core and Builtins - gh-119821: Fix execution of annotation scopes within classes when globals is set to a non-dict. Patch by Jelle Zijlstra. - gh-118263: Speed up os.path.normpath() with a direct C call. - gh-119311: Fix bug where names are unexpectedly mangled in the bases of generic classes. - gh-119395: Fix bug where names appearing after a generic class are mangled as if they are in the generic class. - gh-118507: Fix os.path.isfile() on Windows for pipes. - gh-119213: Non-builtin modules built with argument clinic were crashing if used in a subinterpreter before the main interpreter. The objects that were causing the problem by leaking between interpreters carelessly have been fixed. - gh-119011: Fixes type.__type_params__ to return an empty tuple instead of a descriptor. - gh-118997: Fix _Py_ClearImmortal() assertion: use OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:Factory/python312?expand=0&rev=47
172 lines
7.3 KiB
Diff
172 lines
7.3 KiB
Diff
---
|
|
Lib/tempfile.py | 16 +
|
|
Lib/test/test_tempfile.py | 113 ++++++++++
|
|
Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst | 2
|
|
3 files changed, 131 insertions(+)
|
|
|
|
Index: Python-3.12.4/Lib/tempfile.py
|
|
===================================================================
|
|
--- Python-3.12.4.orig/Lib/tempfile.py
|
|
+++ Python-3.12.4/Lib/tempfile.py
|
|
@@ -285,6 +285,22 @@ def _resetperms(path):
|
|
_dont_follow_symlinks(chflags, path, 0)
|
|
_dont_follow_symlinks(_os.chmod, path, 0o700)
|
|
|
|
+def _dont_follow_symlinks(func, path, *args):
|
|
+ # Pass follow_symlinks=False, unless not supported on this platform.
|
|
+ if func in _os.supports_follow_symlinks:
|
|
+ func(path, *args, follow_symlinks=False)
|
|
+ elif _os.name == 'nt' or not _os.path.islink(path):
|
|
+ func(path, *args)
|
|
+
|
|
+def _resetperms(path):
|
|
+ try:
|
|
+ chflags = _os.chflags
|
|
+ except AttributeError:
|
|
+ pass
|
|
+ else:
|
|
+ _dont_follow_symlinks(chflags, path, 0)
|
|
+ _dont_follow_symlinks(_os.chmod, path, 0o700)
|
|
+
|
|
|
|
# User visible interfaces.
|
|
|
|
Index: Python-3.12.4/Lib/test/test_tempfile.py
|
|
===================================================================
|
|
--- Python-3.12.4.orig/Lib/test/test_tempfile.py
|
|
+++ Python-3.12.4/Lib/test/test_tempfile.py
|
|
@@ -1803,6 +1803,103 @@ class TestTemporaryDirectory(BaseTestCas
|
|
new_flags = os.stat(dir1).st_flags
|
|
self.assertEqual(new_flags, old_flags)
|
|
|
|
+ @os_helper.skip_unless_symlink
|
|
+ def test_cleanup_with_symlink_modes(self):
|
|
+ # cleanup() should not follow symlinks when fixing mode bits (#91133)
|
|
+ with self.do_create(recurse=0) as d2:
|
|
+ file1 = os.path.join(d2, 'file1')
|
|
+ open(file1, 'wb').close()
|
|
+ dir1 = os.path.join(d2, 'dir1')
|
|
+ os.mkdir(dir1)
|
|
+ for mode in range(8):
|
|
+ mode <<= 6
|
|
+ with self.subTest(mode=format(mode, '03o')):
|
|
+ def test(target, target_is_directory):
|
|
+ d1 = self.do_create(recurse=0)
|
|
+ symlink = os.path.join(d1.name, 'symlink')
|
|
+ os.symlink(target, symlink,
|
|
+ target_is_directory=target_is_directory)
|
|
+ try:
|
|
+ os.chmod(symlink, mode, follow_symlinks=False)
|
|
+ except NotImplementedError:
|
|
+ pass
|
|
+ try:
|
|
+ os.chmod(symlink, mode)
|
|
+ except FileNotFoundError:
|
|
+ pass
|
|
+ os.chmod(d1.name, mode)
|
|
+ d1.cleanup()
|
|
+ self.assertFalse(os.path.exists(d1.name))
|
|
+
|
|
+ with self.subTest('nonexisting file'):
|
|
+ test('nonexisting', target_is_directory=False)
|
|
+ with self.subTest('nonexisting dir'):
|
|
+ test('nonexisting', target_is_directory=True)
|
|
+
|
|
+ with self.subTest('existing file'):
|
|
+ os.chmod(file1, mode)
|
|
+ old_mode = os.stat(file1).st_mode
|
|
+ test(file1, target_is_directory=False)
|
|
+ new_mode = os.stat(file1).st_mode
|
|
+ self.assertEqual(new_mode, old_mode,
|
|
+ '%03o != %03o' % (new_mode, old_mode))
|
|
+
|
|
+ with self.subTest('existing dir'):
|
|
+ os.chmod(dir1, mode)
|
|
+ old_mode = os.stat(dir1).st_mode
|
|
+ test(dir1, target_is_directory=True)
|
|
+ new_mode = os.stat(dir1).st_mode
|
|
+ self.assertEqual(new_mode, old_mode,
|
|
+ '%03o != %03o' % (new_mode, old_mode))
|
|
+
|
|
+ @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags')
|
|
+ @os_helper.skip_unless_symlink
|
|
+ def test_cleanup_with_symlink_flags(self):
|
|
+ # cleanup() should not follow symlinks when fixing flags (#91133)
|
|
+ flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK
|
|
+ self.check_flags(flags)
|
|
+
|
|
+ with self.do_create(recurse=0) as d2:
|
|
+ file1 = os.path.join(d2, 'file1')
|
|
+ open(file1, 'wb').close()
|
|
+ dir1 = os.path.join(d2, 'dir1')
|
|
+ os.mkdir(dir1)
|
|
+ def test(target, target_is_directory):
|
|
+ d1 = self.do_create(recurse=0)
|
|
+ symlink = os.path.join(d1.name, 'symlink')
|
|
+ os.symlink(target, symlink,
|
|
+ target_is_directory=target_is_directory)
|
|
+ try:
|
|
+ os.chflags(symlink, flags, follow_symlinks=False)
|
|
+ except NotImplementedError:
|
|
+ pass
|
|
+ try:
|
|
+ os.chflags(symlink, flags)
|
|
+ except FileNotFoundError:
|
|
+ pass
|
|
+ os.chflags(d1.name, flags)
|
|
+ d1.cleanup()
|
|
+ self.assertFalse(os.path.exists(d1.name))
|
|
+
|
|
+ with self.subTest('nonexisting file'):
|
|
+ test('nonexisting', target_is_directory=False)
|
|
+ with self.subTest('nonexisting dir'):
|
|
+ test('nonexisting', target_is_directory=True)
|
|
+
|
|
+ with self.subTest('existing file'):
|
|
+ os.chflags(file1, flags)
|
|
+ old_flags = os.stat(file1).st_flags
|
|
+ test(file1, target_is_directory=False)
|
|
+ new_flags = os.stat(file1).st_flags
|
|
+ self.assertEqual(new_flags, old_flags)
|
|
+
|
|
+ with self.subTest('existing dir'):
|
|
+ os.chflags(dir1, flags)
|
|
+ old_flags = os.stat(dir1).st_flags
|
|
+ test(dir1, target_is_directory=True)
|
|
+ new_flags = os.stat(dir1).st_flags
|
|
+ self.assertEqual(new_flags, old_flags)
|
|
+
|
|
@support.cpython_only
|
|
def test_del_on_collection(self):
|
|
# A TemporaryDirectory is deleted when garbage collected
|
|
@@ -1977,6 +2074,22 @@ class TestTemporaryDirectory(BaseTestCas
|
|
|
|
def check_flags(self, flags):
|
|
# skip the test if these flags are not supported (ex: FreeBSD 13)
|
|
+ filename = os_helper.TESTFN
|
|
+ try:
|
|
+ open(filename, "w").close()
|
|
+ try:
|
|
+ os.chflags(filename, flags)
|
|
+ except OSError as exc:
|
|
+ # "OSError: [Errno 45] Operation not supported"
|
|
+ self.skipTest(f"chflags() doesn't support flags "
|
|
+ f"{flags:#b}: {exc}")
|
|
+ else:
|
|
+ os.chflags(filename, 0)
|
|
+ finally:
|
|
+ os_helper.unlink(filename)
|
|
+
|
|
+ def check_flags(self, flags):
|
|
+ # skip the test if these flags are not supported (ex: FreeBSD 13)
|
|
filename = os_helper.TESTFN
|
|
try:
|
|
open(filename, "w").close()
|
|
Index: Python-3.12.4/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ Python-3.12.4/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst
|
|
@@ -0,0 +1,2 @@
|
|
+Fix a bug in :class:`tempfile.TemporaryDirectory` cleanup, which now no longer
|
|
+dereferences symlinks when working around file system permission errors.
|