@@ -3908,6 +3908,29 @@ def test_sneaky_hardlink_fallback(self):
39083908 self .expect_file ("boom" , symlink_to = '../../link_here' )
39093909 self .expect_file ("c" , symlink_to = 'b' )
39103910
3911+ def test_sneaky_hardlink_fallback_deep (self ):
3912+ # (CVE-2026-11940)
3913+ with ArchiveMaker () as arc :
3914+ arc .add ("a/b/s" , symlink_to = os .path .join (".." , "escape" ))
3915+ arc .add ("s" , hardlink_to = os .path .join ("a" , "b" , "s" ))
3916+
3917+ with self .check_context (arc .open (), 'data' ):
3918+ e = self .expect_exception (
3919+ tarfile .LinkFallbackError ,
3920+ "link 's' would be extracted as a copy of "
3921+ + "'a/b/s', which was rejected" )
3922+ self .assertIsInstance (e .__cause__ ,
3923+ tarfile .LinkOutsideDestinationError )
3924+
3925+ for filter in 'tar' , 'fully_trusted' :
3926+ with self .subTest (filter ), self .check_context (arc .open (), filter ):
3927+ if not os_helper .can_symlink ():
3928+ self .expect_file ("a/" )
3929+ self .expect_file ("a/b/" )
3930+ else :
3931+ self .expect_file ("a/b/s" , symlink_to = os .path .join ('..' , 'escape' ))
3932+ self .expect_file ("s" , symlink_to = os .path .join ('..' , 'escape' ))
3933+
39113934 def test_exfiltration_via_symlink (self ):
39123935 # (CVE-2025-4138)
39133936 # Test changing symlinks that result in a symlink pointing outside
0 commit comments