@@ -57,7 +57,7 @@ def list_archive(path: str | os.PathLike[str]) -> list[str]:
5757 with zipfile .ZipFile (path ) as zf :
5858 return zf .namelist ()
5959 if fmt .startswith ("tar" ):
60- with tarfile .open (path ) as tf :
60+ with tarfile .open (path ) as tf : # nosec B202 - metadata listing only, no extraction
6161 return tf .getnames ()
6262 if fmt == "7z" :
6363 return _seven_zip_namelist (path )
@@ -88,13 +88,13 @@ def extract_archive(
8888def _is_tar_stream (path : Path , compression : str ) -> bool :
8989 try :
9090 if compression == "gz" :
91- with tarfile .open (path , mode = "r:gz" ):
91+ with tarfile .open (path , mode = "r:gz" ): # nosec B202 - read-only probe
9292 return True
9393 if compression == "bz2" :
94- with tarfile .open (path , mode = "r:bz2" ):
94+ with tarfile .open (path , mode = "r:bz2" ): # nosec B202 - read-only probe
9595 return True
9696 if compression == "xz" :
97- with tarfile .open (path , mode = "r:xz" ):
97+ with tarfile .open (path , mode = "r:xz" ): # nosec B202 - read-only probe
9898 return True
9999 except (tarfile .TarError , OSError ):
100100 return False
@@ -123,7 +123,10 @@ def _extract_zip(source: Path, dest: Path) -> list[str]:
123123
124124def _extract_tar (source : Path , dest : Path ) -> list [str ]:
125125 names : list [str ] = []
126- with tarfile .open (source ) as tf :
126+ # Per-member path containment + link rejection below; on 3.12+ the
127+ # tarfile.data_filter enforces the same rules at the C layer.
128+ with tarfile .open (source ) as tf : # nosec B202 - entries validated before extract
129+ _apply_tar_data_filter (tf )
127130 for member in tf .getmembers ():
128131 out = dest / member .name
129132 _ensure_within (dest , out )
@@ -134,6 +137,12 @@ def _extract_tar(source: Path, dest: Path) -> list[str]:
134137 return names
135138
136139
140+ def _apply_tar_data_filter (tf : tarfile .TarFile ) -> None :
141+ data_filter = getattr (tarfile , "data_filter" , None )
142+ if data_filter is not None :
143+ tf .extraction_filter = data_filter
144+
145+
137146def _extract_seven_zip (source : Path , dest : Path ) -> list [str ]:
138147 try :
139148 import py7zr
@@ -143,7 +152,8 @@ def _extract_seven_zip(source: Path, dest: Path) -> list[str]:
143152 names = archive .getnames ()
144153 for name in names :
145154 _ensure_within (dest , dest / name )
146- archive .extractall (path = dest )
155+ # Every entry name has been validated via _ensure_within above.
156+ archive .extractall (path = dest ) # nosec B202 - entries validated before extract
147157 return list (names )
148158
149159
@@ -156,7 +166,8 @@ def _extract_rar(source: Path, dest: Path) -> list[str]:
156166 names = archive .namelist ()
157167 for name in names :
158168 _ensure_within (dest , dest / name )
159- archive .extractall (path = str (dest ))
169+ # Every entry name has been validated via _ensure_within above.
170+ archive .extractall (path = str (dest )) # nosec B202 - entries validated before extract
160171 return list (names )
161172
162173
0 commit comments