@@ -143,6 +143,79 @@ def test_path_traversal_prevention(self, tmp_path):
143143
144144 with pytest .raises (UnsafeTarError ):
145145 extractor .extract (tar_file , extract_dir )
146+
147+ def test_sibling_directory_traversal (self , tmp_path ):
148+ """Test sibling directory traversal (partial path traversal)."""
149+ tar_file = tmp_path / "sibling_traversal.tar"
150+
151+ with tarfile .open (tar_file , 'w' ) as tar :
152+ # If extracting to 'extracted', a sibling directory could be 'extracted_evil'
153+ # Using absolute paths in tar is rare but we can simulate by making the
154+ # tar path resolve to a sibling directory when combined with the extract dir.
155+ # But SafeTarExtractor uses: extract_path / member.name
156+ # So if member.name is '../extracted_evil/file.txt', and extract_path is '/tmp/extracted'
157+ # target_path is '/tmp/extracted/../extracted_evil/file.txt' -> '/tmp/extracted_evil/file.txt'
158+ # The vulnerability was that this bypassed string prefix checking.
159+ info = tarfile .TarInfo (name = '../extracted_evil/evil.txt' )
160+ info .size = 10
161+ info .type = tarfile .REGTYPE
162+
163+ content = b'x' * 10
164+ fileobj = io .BytesIO (content )
165+ tar .addfile (info , fileobj )
166+
167+ extract_dir = tmp_path / "extracted"
168+ # Create the sibling directory to simulate attack scenario
169+ sibling_dir = tmp_path / "extracted_evil"
170+ sibling_dir .mkdir (exist_ok = True )
171+
172+ extractor = SafeTarExtractor ()
173+
174+ with pytest .raises (UnsafeTarError ):
175+ extractor .extract (tar_file , extract_dir )
176+
177+ def test_absolute_path_traversal (self , tmp_path ):
178+ """Test absolute path traversal prevention."""
179+ tar_file = tmp_path / "absolute_traversal.tar"
180+
181+ with tarfile .open (tar_file , 'w' ) as tar :
182+ # Create a TarInfo with an absolute path
183+ # Need to use a generic absolute path for testing
184+ info = tarfile .TarInfo (name = '/tmp/evil_absolute.txt' )
185+ info .size = 10
186+ info .type = tarfile .REGTYPE
187+
188+ content = b'x' * 10
189+ fileobj = io .BytesIO (content )
190+ tar .addfile (info , fileobj )
191+
192+ extract_dir = tmp_path / "extracted"
193+ extractor = SafeTarExtractor (allow_absolute_paths = False )
194+
195+ with pytest .raises (UnsafeTarError ):
196+ extractor .extract (tar_file , extract_dir )
197+
198+ def test_nested_valid_directories (self , tmp_path ):
199+ """Test that nested valid directories are correctly extracted."""
200+ tar_file = tmp_path / "nested_valid.tar"
201+
202+ with tarfile .open (tar_file , 'w' ) as tar :
203+ # Create nested structure
204+ info = tarfile .TarInfo (name = 'dir1/dir2/file.txt' )
205+ info .size = 10
206+ info .type = tarfile .REGTYPE
207+
208+ content = b'x' * 10
209+ fileobj = io .BytesIO (content )
210+ tar .addfile (info , fileobj )
211+
212+ extract_dir = tmp_path / "extracted"
213+ extractor = SafeTarExtractor ()
214+
215+ extracted = extractor .extract (tar_file , extract_dir )
216+
217+ assert len (extracted ) == 1
218+ assert (extract_dir / 'dir1' / 'dir2' / 'file.txt' ).exists ()
146219
147220 def test_size_limits (self , tmp_path ):
148221 """Test file size limits."""
0 commit comments