@@ -3911,10 +3911,19 @@ def test_parent_symlink(self):
39113911 + "which is outside the destination" )
39123912
39133913 with self .check_context (arc .open (), 'data' ):
3914- self .expect_exception (
3915- tarfile .LinkOutsideDestinationError ,
3916- """'parent' would link to ['"].*outerdir['"], """
3917- + "which is outside the destination" )
3914+ if self .dotdot_resolves_early :
3915+ # 'current/../..' normalises to '..', which is rejected.
3916+ self .expect_exception (
3917+ tarfile .LinkOutsideDestinationError ,
3918+ """'parent' would link to ['"].*outerdir['"], """
3919+ + "which is outside the destination" )
3920+ else :
3921+ # 'current/..' normalises to '.'; the rewritten link is
3922+ # created and 'parent/evil' lands harmlessly inside the
3923+ # destination.
3924+ self .expect_file ('current' , symlink_to = '.' )
3925+ self .expect_file ('parent' , symlink_to = '.' )
3926+ self .expect_file ('evil' )
39183927
39193928 else :
39203929 # No symlink support. The symlinks are ignored.
@@ -4174,6 +4183,76 @@ def test_sly_relative2(self):
41744183 + """['"].*moo['"], which is outside the """
41754184 + "destination" )
41764185
4186+ @symlink_test
4187+ @os_helper .skip_unless_symlink
4188+ def test_normpath_realpath_mismatch (self ):
4189+ # The link-target check must validate the value that will actually
4190+ # be written to disk (the normalised linkname), not the original.
4191+ # Here 'a' is a symlink to a deep nonexistent path, so realpath()
4192+ # of 'a/../../...' stays inside the destination while normpath()
4193+ # collapses 'a/..' lexically and escapes.
4194+ depth = len (self .destdir .parts ) + 5
4195+ deep = '/' .join (f'p{ i } ' for i in range (depth ))
4196+ sneaky = 'a/' + '../' * depth + 'flag'
4197+ for kind in 'symlink_to' , 'hardlink_to' :
4198+ with self .subTest (kind ):
4199+ with ArchiveMaker () as arc :
4200+ arc .add ('a' , symlink_to = deep )
4201+ arc .add ('escape' , ** {kind : sneaky })
4202+ with self .check_context (arc .open (), 'data' ):
4203+ self .expect_exception (
4204+ tarfile .LinkOutsideDestinationError )
4205+
4206+ @symlink_test
4207+ @os_helper .skip_unless_symlink
4208+ def test_symlink_trailing_slash (self ):
4209+ # A trailing slash on a symlink member's name must not cause the
4210+ # link target to be resolved relative to the wrong directory.
4211+ with ArchiveMaker () as arc :
4212+ t = tarfile .TarInfo ('x/' )
4213+ t .type = tarfile .SYMTYPE
4214+ t .linkname = '..'
4215+ arc .tar_w .addfile (t )
4216+ arc .add ('x/escaped' , content = 'hi' )
4217+
4218+ with self .check_context (arc .open (), 'data' ):
4219+ self .expect_exception (tarfile .LinkOutsideDestinationError )
4220+
4221+ @symlink_test
4222+ @os_helper .skip_unless_symlink
4223+ def test_link_at_destination (self ):
4224+ # A link member whose name resolves to the destination directory
4225+ # itself must be rejected: otherwise the destination is replaced
4226+ # by a symlink and later members can be redirected through it.
4227+ for name in '' , '.' , './' :
4228+ with ArchiveMaker () as arc :
4229+ t = tarfile .TarInfo (name )
4230+ t .type = tarfile .SYMTYPE
4231+ t .linkname = '.'
4232+ arc .tar_w .addfile (t )
4233+
4234+ with self .check_context (arc .open (), 'data' ):
4235+ self .expect_exception (tarfile .OutsideDestinationError )
4236+
4237+ @symlink_test
4238+ @os_helper .skip_unless_symlink
4239+ def test_empty_name_symlink_chain (self ):
4240+ # Regression test for a chain of empty-named symlinks that
4241+ # incrementally redirects the destination outwards.
4242+ with ArchiveMaker () as arc :
4243+ for name , target in [('' , '' ), ('a/' , '..' ),
4244+ ('' , 'dummy' ), ('' , 'a' ),
4245+ ('b/' , '..' ),
4246+ ('' , 'dummy' ), ('' , 'a/b' )]:
4247+ t = tarfile .TarInfo (name )
4248+ t .type = tarfile .SYMTYPE
4249+ t .linkname = target
4250+ arc .tar_w .addfile (t )
4251+ arc .add ('escaped' , content = 'hi' )
4252+
4253+ with self .check_context (arc .open (), 'data' ):
4254+ self .expect_exception (tarfile .FilterError )
4255+
41774256 @symlink_test
41784257 def test_deep_symlink (self ):
41794258 # Test that symlinks and hardlinks inside a directory
0 commit comments