@@ -1747,7 +1747,11 @@ def _is_bad_path(path, base):
17471747 bool: True if the path is not rooted under the base directory, False otherwise.
17481748 """
17491749 # joinpath will ignore base if path is absolute
1750- return not _get_resolved_path (joinpath (base , path )).startswith (base )
1750+ resolved = _get_resolved_path (joinpath (base , path ))
1751+ try :
1752+ return os .path .commonpath ([resolved , base ]) != base
1753+ except ValueError :
1754+ return True
17511755
17521756
17531757def _is_bad_link (info , base ):
@@ -1767,19 +1771,18 @@ def _is_bad_link(info, base):
17671771 return _is_bad_path (info .linkname , base = tip )
17681772
17691773
1770- def _get_safe_members (members ):
1774+ def _get_safe_members (members , base ):
17711775 """A generator that yields members that are safe to extract.
17721776
17731777 It filters out bad paths and bad links.
17741778
17751779 Args:
17761780 members (list): A list of members to check.
1781+ base (str): The base directory for extraction.
17771782
17781783 Yields:
17791784 tarfile.TarInfo: The tar file info.
17801785 """
1781- base = _get_resolved_path ("" )
1782-
17831786 for file_info in members :
17841787 if _is_bad_path (file_info .name , base ):
17851788 logger .error ("%s is blocked (illegal path)" , file_info .name )
@@ -1810,15 +1813,23 @@ def _validate_extracted_paths(extract_path):
18101813 for dir_name in dirs :
18111814 dir_path = os .path .join (root , dir_name )
18121815 resolved = _get_resolved_path (dir_path )
1813- if not resolved .startswith (base ):
1816+ try :
1817+ is_within_base = os .path .commonpath ([resolved , base ]) == base
1818+ except ValueError :
1819+ is_within_base = False
1820+ if not is_within_base :
18141821 logger .error ("Extracted directory escaped extraction path: %s" , dir_path )
18151822 raise ValueError (f"Extracted path outside expected directory: { dir_path } " )
18161823
18171824 # Check files
18181825 for file_name in files :
18191826 file_path = os .path .join (root , file_name )
18201827 resolved = _get_resolved_path (file_path )
1821- if not resolved .startswith (base ):
1828+ try :
1829+ is_within_base = os .path .commonpath ([resolved , base ]) == base
1830+ except ValueError :
1831+ is_within_base = False
1832+ if not is_within_base :
18221833 logger .error ("Extracted file escaped extraction path: %s" , file_path )
18231834 raise ValueError (f"Extracted path outside expected directory: { file_path } " )
18241835
@@ -1842,7 +1853,8 @@ def custom_extractall_tarfile(tar, extract_path):
18421853 if hasattr (tarfile , "data_filter" ):
18431854 tar .extractall (path = extract_path , filter = "data" )
18441855 else :
1845- tar .extractall (path = extract_path , members = _get_safe_members (tar ))
1856+ base = _get_resolved_path (extract_path )
1857+ tar .extractall (path = extract_path , members = _get_safe_members (tar .getmembers (), base ))
18461858 # Re-validate extracted paths to catch symlink race conditions
18471859 _validate_extracted_paths (extract_path )
18481860
0 commit comments