@@ -3853,10 +3853,19 @@ def test_parent_symlink(self):
38533853 + "which is outside the destination" )
38543854
38553855 with self .check_context (arc .open (), 'data' ):
3856- self .expect_exception (
3857- tarfile .LinkOutsideDestinationError ,
3858- """'parent' would link to ['"].*outerdir['"], """
3859- + "which is outside the destination" )
3856+ if self .dotdot_resolves_early :
3857+ # 'current/../..' normalises to '..', which is rejected.
3858+ self .expect_exception (
3859+ tarfile .LinkOutsideDestinationError ,
3860+ """'parent' would link to ['"].*outerdir['"], """
3861+ + "which is outside the destination" )
3862+ else :
3863+ # 'current/..' normalises to '.'; the rewritten link is
3864+ # created and 'parent/evil' lands harmlessly inside the
3865+ # destination.
3866+ self .expect_file ('current' , symlink_to = '.' )
3867+ self .expect_file ('parent' , symlink_to = '.' )
3868+ self .expect_file ('evil' )
38603869
38613870 else :
38623871 # No symlink support. The symlinks are ignored.
@@ -4130,6 +4139,76 @@ def test_sly_relative2(self):
41304139 + """['"].*moo['"], which is outside the """
41314140 + "destination" )
41324141
4142+ @symlink_test
4143+ @os_helper .skip_unless_symlink
4144+ def test_normpath_realpath_mismatch (self ):
4145+ # The link-target check must validate the value that will actually
4146+ # be written to disk (the normalised linkname), not the original.
4147+ # Here 'a' is a symlink to a deep nonexistent path, so realpath()
4148+ # of 'a/../../...' stays inside the destination while normpath()
4149+ # collapses 'a/..' lexically and escapes.
4150+ depth = len (self .destdir .parts ) + 5
4151+ deep = '/' .join (f'p{ i } ' for i in range (depth ))
4152+ sneaky = 'a/' + '../' * depth + 'flag'
4153+ for kind in 'symlink_to' , 'hardlink_to' :
4154+ with self .subTest (kind ):
4155+ with ArchiveMaker () as arc :
4156+ arc .add ('a' , symlink_to = deep )
4157+ arc .add ('escape' , ** {kind : sneaky })
4158+ with self .check_context (arc .open (), 'data' ):
4159+ self .expect_exception (
4160+ tarfile .LinkOutsideDestinationError )
4161+
4162+ @symlink_test
4163+ @os_helper .skip_unless_symlink
4164+ def test_symlink_trailing_slash (self ):
4165+ # A trailing slash on a symlink member's name must not cause the
4166+ # link target to be resolved relative to the wrong directory.
4167+ with ArchiveMaker () as arc :
4168+ t = tarfile .TarInfo ('x/' )
4169+ t .type = tarfile .SYMTYPE
4170+ t .linkname = '..'
4171+ arc .tar_w .addfile (t )
4172+ arc .add ('x/escaped' , content = 'hi' )
4173+
4174+ with self .check_context (arc .open (), 'data' ):
4175+ self .expect_exception (tarfile .LinkOutsideDestinationError )
4176+
4177+ @symlink_test
4178+ @os_helper .skip_unless_symlink
4179+ def test_link_at_destination (self ):
4180+ # A link member whose name resolves to the destination directory
4181+ # itself must be rejected: otherwise the destination is replaced
4182+ # by a symlink and later members can be redirected through it.
4183+ for name in '' , '.' , './' :
4184+ with ArchiveMaker () as arc :
4185+ t = tarfile .TarInfo (name )
4186+ t .type = tarfile .SYMTYPE
4187+ t .linkname = '.'
4188+ arc .tar_w .addfile (t )
4189+
4190+ with self .check_context (arc .open (), 'data' ):
4191+ self .expect_exception (tarfile .OutsideDestinationError )
4192+
4193+ @symlink_test
4194+ @os_helper .skip_unless_symlink
4195+ def test_empty_name_symlink_chain (self ):
4196+ # Regression test for a chain of empty-named symlinks that
4197+ # incrementally redirects the destination outwards.
4198+ with ArchiveMaker () as arc :
4199+ for name , target in [('' , '' ), ('a/' , '..' ),
4200+ ('' , 'dummy' ), ('' , 'a' ),
4201+ ('b/' , '..' ),
4202+ ('' , 'dummy' ), ('' , 'a/b' )]:
4203+ t = tarfile .TarInfo (name )
4204+ t .type = tarfile .SYMTYPE
4205+ t .linkname = target
4206+ arc .tar_w .addfile (t )
4207+ arc .add ('escaped' , content = 'hi' )
4208+
4209+ with self .check_context (arc .open (), 'data' ):
4210+ self .expect_exception (tarfile .FilterError )
4211+
41334212 @symlink_test
41344213 def test_deep_symlink (self ):
41354214 # Test that symlinks and hardlinks inside a directory
0 commit comments