@@ -3523,3 +3523,38 @@ def test_module_all_attribute(self):
35233523
35243524if __name__ == '__main__' :
35253525 unittest .main ()
3526+
3527+ class TestShutilZipTraversal (unittest .TestCase ):
3528+ def setUp (self ):
3529+ self .tmp_dir = tempfile .mkdtemp ()
3530+ self .extract_dir = os .path .join (self .tmp_dir , "extract" )
3531+ os .mkdir (self .extract_dir )
3532+
3533+ def tearDown (self ):
3534+ shutil .rmtree (self .tmp_dir )
3535+
3536+ @unittest .skipUnless (sys .platform == 'win32' , 'Windows-specific traversal test' )
3537+ @support .requires_zlib ()
3538+ def test_unpack_zipfile_traversal_windows_drive (self ):
3539+ # Create a ZIP file with a drive-prefixed path
3540+ zip_path = os .path .join (self .tmp_dir , "test.zip" )
3541+ with zipfile .ZipFile (zip_path , 'w' ) as zf :
3542+ # zipfile.extractall() should sanitize this to 'D/traversal.txt'
3543+ # relative to extract_dir.
3544+ zf .writestr ("D:/traversal.txt" , "found you" )
3545+
3546+ # Prior to the fix, this might have attempted to write to D:/traversal.txt
3547+ # With the fix (using extractall()), it's safely joined.
3548+ shutil .unpack_archive (zip_path , self .extract_dir )
3549+
3550+ # Check that it didn't go to D:/
3551+ self .assertFalse (os .path .exists ("D:/traversal.txt" ))
3552+
3553+ # Check where it actually went
3554+ found = False
3555+ for root , dirs , files in os .walk (self .extract_dir ):
3556+ if "traversal.txt" in files :
3557+ found = True
3558+ break
3559+ self .assertTrue (found , "Extracted file not found within extract_dir" )
3560+
0 commit comments