@@ -3701,10 +3701,19 @@ def test_parent_symlink(self):
37013701 + "which is outside the destination" )
37023702
37033703 with self .check_context (arc .open (), 'data' ):
3704- self .expect_exception (
3705- tarfile .LinkOutsideDestinationError ,
3706- """'parent' would link to ['"].*outerdir['"], """
3707- + "which is outside the destination" )
3704+ if self .dotdot_resolves_early :
3705+ # 'current/../..' normalises to '..', which is rejected.
3706+ self .expect_exception (
3707+ tarfile .LinkOutsideDestinationError ,
3708+ """'parent' would link to ['"].*outerdir['"], """
3709+ + "which is outside the destination" )
3710+ else :
3711+ # 'current/..' normalises to '.'; the rewritten link is
3712+ # created and 'parent/evil' lands harmlessly inside the
3713+ # destination.
3714+ self .expect_file ('current' , symlink_to = '.' )
3715+ self .expect_file ('parent' , symlink_to = '.' )
3716+ self .expect_file ('evil' )
37083717
37093718 else :
37103719 # No symlink support. The symlinks are ignored.
@@ -3978,6 +3987,76 @@ def test_sly_relative2(self):
39783987 + """['"].*moo['"], which is outside the """
39793988 + "destination" )
39803989
3990+ @symlink_test
3991+ @os_helper .skip_unless_symlink
3992+ def test_normpath_realpath_mismatch (self ):
3993+ # The link-target check must validate the value that will actually
3994+ # be written to disk (the normalised linkname), not the original.
3995+ # Here 'a' is a symlink to a deep nonexistent path, so realpath()
3996+ # of 'a/../../...' stays inside the destination while normpath()
3997+ # collapses 'a/..' lexically and escapes.
3998+ depth = len (self .destdir .parts ) + 5
3999+ deep = '/' .join (f'p{ i } ' for i in range (depth ))
4000+ sneaky = 'a/' + '../' * depth + 'flag'
4001+ for kind in 'symlink_to' , 'hardlink_to' :
4002+ with self .subTest (kind ):
4003+ with ArchiveMaker () as arc :
4004+ arc .add ('a' , symlink_to = deep )
4005+ arc .add ('escape' , ** {kind : sneaky })
4006+ with self .check_context (arc .open (), 'data' ):
4007+ self .expect_exception (
4008+ tarfile .LinkOutsideDestinationError )
4009+
4010+ @symlink_test
4011+ @os_helper .skip_unless_symlink
4012+ def test_symlink_trailing_slash (self ):
4013+ # A trailing slash on a symlink member's name must not cause the
4014+ # link target to be resolved relative to the wrong directory.
4015+ with ArchiveMaker () as arc :
4016+ t = tarfile .TarInfo ('x/' )
4017+ t .type = tarfile .SYMTYPE
4018+ t .linkname = '..'
4019+ arc .tar_w .addfile (t )
4020+ arc .add ('x/escaped' , content = 'hi' )
4021+
4022+ with self .check_context (arc .open (), 'data' ):
4023+ self .expect_exception (tarfile .LinkOutsideDestinationError )
4024+
4025+ @symlink_test
4026+ @os_helper .skip_unless_symlink
4027+ def test_link_at_destination (self ):
4028+ # A link member whose name resolves to the destination directory
4029+ # itself must be rejected: otherwise the destination is replaced
4030+ # by a symlink and later members can be redirected through it.
4031+ for name in '' , '.' , './' :
4032+ with ArchiveMaker () as arc :
4033+ t = tarfile .TarInfo (name )
4034+ t .type = tarfile .SYMTYPE
4035+ t .linkname = '.'
4036+ arc .tar_w .addfile (t )
4037+
4038+ with self .check_context (arc .open (), 'data' ):
4039+ self .expect_exception (tarfile .OutsideDestinationError )
4040+
4041+ @symlink_test
4042+ @os_helper .skip_unless_symlink
4043+ def test_empty_name_symlink_chain (self ):
4044+ # Regression test for a chain of empty-named symlinks that
4045+ # incrementally redirects the destination outwards.
4046+ with ArchiveMaker () as arc :
4047+ for name , target in [('' , '' ), ('a/' , '..' ),
4048+ ('' , 'dummy' ), ('' , 'a' ),
4049+ ('b/' , '..' ),
4050+ ('' , 'dummy' ), ('' , 'a/b' )]:
4051+ t = tarfile .TarInfo (name )
4052+ t .type = tarfile .SYMTYPE
4053+ t .linkname = target
4054+ arc .tar_w .addfile (t )
4055+ arc .add ('escaped' , content = 'hi' )
4056+
4057+ with self .check_context (arc .open (), 'data' ):
4058+ self .expect_exception (tarfile .FilterError )
4059+
39814060 @symlink_test
39824061 def test_deep_symlink (self ):
39834062 # Test that symlinks and hardlinks inside a directory
0 commit comments