Skip to content

Commit b5edae5

Browse files
committed
Add symbolic link options
- Add enable_symlink boolean option for read, extractall, and extract - Default to be True. It will be False in future. - Add dereference boolean option for write and writeall - SevenZipFile constructor option dereference is marked deprecated and will be removed in future version Signed-off-by: Hiroshi Miura <[email protected]>
1 parent 3d4af46 commit b5edae5

File tree

1 file changed

+24
-16
lines changed

1 file changed

+24
-16
lines changed

py7zr/py7zr.py

+24-16
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@ def _extract(
533533
targets: Optional[List[str]] = None,
534534
return_dict: bool = False,
535535
callback: Optional[ExtractCallback] = None,
536+
enable_symlink: bool = False,
536537
) -> Optional[Dict[str, IO[Any]]]:
537538
if callback is None:
538539
pass
@@ -598,7 +599,12 @@ def _extract(
598599
elif f.is_socket:
599600
pass # TODO: implement me.
600601
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.")
602608
else:
603609
self.worker.register_filelike(f.id, outfilename)
604610
target_files.append((outfilename, f.file_properties()))
@@ -701,9 +707,9 @@ def _write_header(self):
701707
self.sig_header.calccrc(header_len, header_crc)
702708
self.sig_header.write(self.fp)
703709

704-
def _writeall(self, path, arcname):
710+
def _writeall(self, path, arcname, dereference: bool = False):
705711
try:
706-
if path.is_symlink() and not self.dereference:
712+
if path.is_symlink() and not dereference:
707713
self.write(path, arcname)
708714
elif path.is_file():
709715
self.write(path, arcname)
@@ -712,14 +718,14 @@ def _writeall(self, path, arcname):
712718
self.write(path, arcname)
713719
for nm in sorted(os.listdir(str(path))):
714720
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)
716722
else:
717723
return # pathlib ignores ELOOP and return False for is_*().
718724
except OSError as ose:
719-
if self.dereference and ose.errno in [errno.ELOOP]:
725+
if dereference and ose.errno in [errno.ELOOP]:
720726
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.
723729
else:
724730
raise
725731

@@ -954,20 +960,22 @@ def readall(self) -> Optional[Dict[str, IO[Any]]]:
954960
self._dict = {}
955961
return self._extract(path=None, return_dict=True)
956962

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:
958966
"""Extract all members from the archive to the current working
959967
directory and set owner, modification time and permissions on
960968
directories afterwards. ``path`` specifies a different directory
961969
to extract to.
962970
"""
963-
self._extract(path=path, return_dict=False, callback=callback)
971+
self._extract(path=path, return_dict=False, callback=callback, enable_symlink=enable_symlink)
964972

965973
def read(self, targets: Optional[List[str]] = None) -> Optional[Dict[str, IO[Any]]]:
966974
self._dict = {}
967975
return self._extract(path=None, targets=targets, return_dict=True)
968976

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)
971979

972980
def reporter(self, callback: ExtractCallback):
973981
while True:
@@ -992,18 +1000,18 @@ def reporter(self, callback: ExtractCallback):
9921000
pass
9931001
self.q.task_done()
9941002

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):
9961004
"""Write files in target path into archive."""
9971005
if isinstance(path, str):
9981006
path = pathlib.Path(path)
9991007
if not path.exists():
10001008
raise ValueError("specified path does not exist.")
10011009
if path.is_dir() or path.is_file():
1002-
self._writeall(path, arcname)
1010+
self._writeall(path, arcname, dereference or self.dereference)
10031011
else:
10041012
raise ValueError("specified path is not a directory or a file")
10051013

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):
10071015
"""Write single target file into archive."""
10081016
if isinstance(file, str):
10091017
path = pathlib.Path(file)
@@ -1012,11 +1020,11 @@ def write(self, file: Union[pathlib.Path, str], arcname: Optional[str] = None):
10121020
else:
10131021
raise ValueError("Unsupported file type.")
10141022
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)
10161024
self.header.files_info.files.append(file_info)
10171025
self.header.files_info.emptyfiles.append(file_info["emptystream"])
10181026
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)
10201028

10211029
def writed(self, targets: Dict[str, IO[Any]]) -> None:
10221030
for target, input in targets.items():

0 commit comments

Comments
 (0)