@@ -533,6 +533,7 @@ def _extract(
533
533
targets : Optional [List [str ]] = None ,
534
534
return_dict : bool = False ,
535
535
callback : Optional [ExtractCallback ] = None ,
536
+ enable_symlink : bool = False ,
536
537
) -> Optional [Dict [str , IO [Any ]]]:
537
538
if callback is None :
538
539
pass
@@ -598,7 +599,12 @@ def _extract(
598
599
elif f .is_socket :
599
600
pass # TODO: implement me.
600
601
elif f .is_symlink or f .is_junction :
601
- self .worker .register_filelike (f .id , outfilename )
602
+ if enable_symlink :
603
+ self .worker .register_filelike (f .id , outfilename )
604
+ else :
605
+ # Archive has symlink or junction
606
+ # this has security consequences.
607
+ raise ValueError ("Archive has symbolic link that is not explicitly enabled." )
602
608
else :
603
609
self .worker .register_filelike (f .id , outfilename )
604
610
target_files .append ((outfilename , f .file_properties ()))
@@ -701,9 +707,9 @@ def _write_header(self):
701
707
self .sig_header .calccrc (header_len , header_crc )
702
708
self .sig_header .write (self .fp )
703
709
704
- def _writeall (self , path , arcname ):
710
+ def _writeall (self , path , arcname , dereference : bool = False ):
705
711
try :
706
- if path .is_symlink () and not self . dereference :
712
+ if path .is_symlink () and not dereference :
707
713
self .write (path , arcname )
708
714
elif path .is_file ():
709
715
self .write (path , arcname )
@@ -712,14 +718,14 @@ def _writeall(self, path, arcname):
712
718
self .write (path , arcname )
713
719
for nm in sorted (os .listdir (str (path ))):
714
720
arc = os .path .join (arcname , nm ) if arcname is not None else None
715
- self ._writeall (path .joinpath (nm ), arc )
721
+ self ._writeall (path .joinpath (nm ), arc , dereference = dereference )
716
722
else :
717
723
return # pathlib ignores ELOOP and return False for is_*().
718
724
except OSError as ose :
719
- if self . dereference and ose .errno in [errno .ELOOP ]:
725
+ if dereference and ose .errno in [errno .ELOOP ]:
720
726
return # ignore ELOOP here, this resulted to stop looped symlink reference.
721
- elif self . dereference and sys .platform == "win32" and ose .errno in [errno .ENOENT ]:
722
- return # ignore ENOENT which is happened when a case of ELOOP on windows .
727
+ elif dereference and sys .platform == "win32" and ose .errno in [errno .ENOENT ]:
728
+ return # ignore ENOENT which is happened when a case of ELOOP on Windows .
723
729
else :
724
730
raise
725
731
@@ -954,20 +960,22 @@ def readall(self) -> Optional[Dict[str, IO[Any]]]:
954
960
self ._dict = {}
955
961
return self ._extract (path = None , return_dict = True )
956
962
957
- def extractall (self , path : Optional [Any ] = None , callback : Optional [ExtractCallback ] = None ) -> None :
963
+ def extractall (
964
+ self , path : Optional [Any ] = None , callback : Optional [ExtractCallback ] = None , enable_symlink : bool = True
965
+ ) -> None :
958
966
"""Extract all members from the archive to the current working
959
967
directory and set owner, modification time and permissions on
960
968
directories afterwards. ``path`` specifies a different directory
961
969
to extract to.
962
970
"""
963
- self ._extract (path = path , return_dict = False , callback = callback )
971
+ self ._extract (path = path , return_dict = False , callback = callback , enable_symlink = enable_symlink )
964
972
965
973
def read (self , targets : Optional [List [str ]] = None ) -> Optional [Dict [str , IO [Any ]]]:
966
974
self ._dict = {}
967
975
return self ._extract (path = None , targets = targets , return_dict = True )
968
976
969
- def extract (self , path : Optional [Any ] = None , targets : Optional [List [str ]] = None ) -> None :
970
- self ._extract (path , targets , return_dict = False )
977
+ def extract (self , path : Optional [Any ] = None , targets : Optional [List [str ]] = None , enable_symlink : bool = True ) -> None :
978
+ self ._extract (path , targets , return_dict = False , enable_symlink = enable_symlink )
971
979
972
980
def reporter (self , callback : ExtractCallback ):
973
981
while True :
@@ -992,18 +1000,18 @@ def reporter(self, callback: ExtractCallback):
992
1000
pass
993
1001
self .q .task_done ()
994
1002
995
- def writeall (self , path : Union [pathlib .Path , str ], arcname : Optional [str ] = None ):
1003
+ def writeall (self , path : Union [pathlib .Path , str ], arcname : Optional [str ] = None , dereference : bool = False ):
996
1004
"""Write files in target path into archive."""
997
1005
if isinstance (path , str ):
998
1006
path = pathlib .Path (path )
999
1007
if not path .exists ():
1000
1008
raise ValueError ("specified path does not exist." )
1001
1009
if path .is_dir () or path .is_file ():
1002
- self ._writeall (path , arcname )
1010
+ self ._writeall (path , arcname , dereference or self . dereference )
1003
1011
else :
1004
1012
raise ValueError ("specified path is not a directory or a file" )
1005
1013
1006
- def write (self , file : Union [pathlib .Path , str ], arcname : Optional [str ] = None ):
1014
+ def write (self , file : Union [pathlib .Path , str ], arcname : Optional [str ] = None , dereference : bool = False ):
1007
1015
"""Write single target file into archive."""
1008
1016
if isinstance (file , str ):
1009
1017
path = pathlib .Path (file )
@@ -1012,11 +1020,11 @@ def write(self, file: Union[pathlib.Path, str], arcname: Optional[str] = None):
1012
1020
else :
1013
1021
raise ValueError ("Unsupported file type." )
1014
1022
folder = self .header .initialize ()
1015
- file_info = self ._make_file_info (path , arcname , self .dereference )
1023
+ file_info = self ._make_file_info (path , arcname , dereference or self .dereference )
1016
1024
self .header .files_info .files .append (file_info )
1017
1025
self .header .files_info .emptyfiles .append (file_info ["emptystream" ])
1018
1026
self .files .append (file_info )
1019
- self .worker .archive (self .fp , self .files , folder , deref = self .dereference )
1027
+ self .worker .archive (self .fp , self .files , folder , deref = dereference or self .dereference )
1020
1028
1021
1029
def writed (self , targets : Dict [str , IO [Any ]]) -> None :
1022
1030
for target , input in targets .items ():
0 commit comments