@@ -43,6 +43,7 @@ def md5sum(data):
4343xzname = os .path .join (TEMPDIR , "testtar.tar.xz" )
4444tmpname = os .path .join (TEMPDIR , "tmp.tar" )
4545dotlessname = os .path .join (TEMPDIR , "testtar" )
46+ SPACE = b" "
4647
4748md5_regtype = "65f477c818ad9e15f7feab0c6d37742f"
4849md5_sparse = "a54fbc4ca4f4399a90e1b27164012fc6"
@@ -4005,6 +4006,161 @@ def valueerror_filter(tarinfo, path):
40054006 self .expect_exception (TypeError ) # errorlevel is not int
40064007
40074008
4009+ class OffsetValidationTests (unittest .TestCase ):
4010+ tarname = tmpname
4011+ invalid_posix_header = (
4012+ # name: 100 bytes
4013+ tarfile .NUL * tarfile .LENGTH_NAME
4014+ # mode, space, null terminator: 8 bytes
4015+ + b"000755" + SPACE + tarfile .NUL
4016+ # uid, space, null terminator: 8 bytes
4017+ + b"000001" + SPACE + tarfile .NUL
4018+ # gid, space, null terminator: 8 bytes
4019+ + b"000001" + SPACE + tarfile .NUL
4020+ # size, space: 12 bytes
4021+ + b"\xff " * 11 + SPACE
4022+ # mtime, space: 12 bytes
4023+ + tarfile .NUL * 11 + SPACE
4024+ # chksum: 8 bytes
4025+ + b"0011407" + tarfile .NUL
4026+ # type: 1 byte
4027+ + tarfile .REGTYPE
4028+ # linkname: 100 bytes
4029+ + tarfile .NUL * tarfile .LENGTH_LINK
4030+ # magic: 6 bytes, version: 2 bytes
4031+ + tarfile .POSIX_MAGIC
4032+ # uname: 32 bytes
4033+ + tarfile .NUL * 32
4034+ # gname: 32 bytes
4035+ + tarfile .NUL * 32
4036+ # devmajor, space, null terminator: 8 bytes
4037+ + tarfile .NUL * 6 + SPACE + tarfile .NUL
4038+ # devminor, space, null terminator: 8 bytes
4039+ + tarfile .NUL * 6 + SPACE + tarfile .NUL
4040+ # prefix: 155 bytes
4041+ + tarfile .NUL * tarfile .LENGTH_PREFIX
4042+ # padding: 12 bytes
4043+ + tarfile .NUL * 12
4044+ )
4045+ invalid_gnu_header = (
4046+ # name: 100 bytes
4047+ tarfile .NUL * tarfile .LENGTH_NAME
4048+ # mode, null terminator: 8 bytes
4049+ + b"0000755" + tarfile .NUL
4050+ # uid, null terminator: 8 bytes
4051+ + b"0000001" + tarfile .NUL
4052+ # gid, space, null terminator: 8 bytes
4053+ + b"0000001" + tarfile .NUL
4054+ # size, space: 12 bytes
4055+ + b"\xff " * 11 + SPACE
4056+ # mtime, space: 12 bytes
4057+ + tarfile .NUL * 11 + SPACE
4058+ # chksum: 8 bytes
4059+ + b"0011327" + tarfile .NUL
4060+ # type: 1 byte
4061+ + tarfile .REGTYPE
4062+ # linkname: 100 bytes
4063+ + tarfile .NUL * tarfile .LENGTH_LINK
4064+ # magic: 8 bytes
4065+ + tarfile .GNU_MAGIC
4066+ # uname: 32 bytes
4067+ + tarfile .NUL * 32
4068+ # gname: 32 bytes
4069+ + tarfile .NUL * 32
4070+ # devmajor, null terminator: 8 bytes
4071+ + tarfile .NUL * 8
4072+ # devminor, null terminator: 8 bytes
4073+ + tarfile .NUL * 8
4074+ # padding: 167 bytes
4075+ + tarfile .NUL * 167
4076+ )
4077+ invalid_v7_header = (
4078+ # name: 100 bytes
4079+ tarfile .NUL * tarfile .LENGTH_NAME
4080+ # mode, space, null terminator: 8 bytes
4081+ + b"000755" + SPACE + tarfile .NUL
4082+ # uid, space, null terminator: 8 bytes
4083+ + b"000001" + SPACE + tarfile .NUL
4084+ # gid, space, null terminator: 8 bytes
4085+ + b"000001" + SPACE + tarfile .NUL
4086+ # size, space: 12 bytes
4087+ + b"\xff " * 11 + SPACE
4088+ # mtime, space: 12 bytes
4089+ + tarfile .NUL * 11 + SPACE
4090+ # chksum: 8 bytes
4091+ + b"0010070" + tarfile .NUL
4092+ # type: 1 byte
4093+ + tarfile .REGTYPE
4094+ # linkname: 100 bytes
4095+ + tarfile .NUL * tarfile .LENGTH_LINK
4096+ # padding: 255 bytes
4097+ + tarfile .NUL * 255
4098+ )
4099+ valid_gnu_header = tarfile .TarInfo ("filename" ).tobuf (tarfile .GNU_FORMAT )
4100+ data_block = b"\xff " * tarfile .BLOCKSIZE
4101+
4102+ def _write_buffer (self , buffer ):
4103+ with open (self .tarname , "wb" ) as f :
4104+ f .write (buffer )
4105+
4106+ def _get_members (self , ignore_zeros = None ):
4107+ with open (self .tarname , "rb" ) as f :
4108+ with tarfile .open (
4109+ mode = "r" , fileobj = f , ignore_zeros = ignore_zeros
4110+ ) as tar :
4111+ return tar .getmembers ()
4112+
4113+ def _assert_raises_read_error_exception (self ):
4114+ with self .assertRaisesRegex (
4115+ tarfile .ReadError , "file could not be opened successfully"
4116+ ):
4117+ self ._get_members ()
4118+
4119+ def test_invalid_offset_header_validations (self ):
4120+ for tar_format , invalid_header in (
4121+ ("posix" , self .invalid_posix_header ),
4122+ ("gnu" , self .invalid_gnu_header ),
4123+ ("v7" , self .invalid_v7_header ),
4124+ ):
4125+ with self .subTest (format = tar_format ):
4126+ self ._write_buffer (invalid_header )
4127+ self ._assert_raises_read_error_exception ()
4128+
4129+ def test_early_stop_at_invalid_offset_header (self ):
4130+ buffer = self .valid_gnu_header + self .invalid_gnu_header + self .valid_gnu_header
4131+ self ._write_buffer (buffer )
4132+ members = self ._get_members ()
4133+ self .assertEqual (len (members ), 1 )
4134+ self .assertEqual (members [0 ].name , "filename" )
4135+ self .assertEqual (members [0 ].offset , 0 )
4136+
4137+ def test_ignore_invalid_archive (self ):
4138+ # 3 invalid headers with their respective data
4139+ buffer = (self .invalid_gnu_header + self .data_block ) * 3
4140+ self ._write_buffer (buffer )
4141+ members = self ._get_members (ignore_zeros = True )
4142+ self .assertEqual (len (members ), 0 )
4143+
4144+ def test_ignore_invalid_offset_headers (self ):
4145+ for first_block , second_block , expected_offset in (
4146+ (
4147+ (self .valid_gnu_header ),
4148+ (self .invalid_gnu_header + self .data_block ),
4149+ 0 ,
4150+ ),
4151+ (
4152+ (self .invalid_gnu_header + self .data_block ),
4153+ (self .valid_gnu_header ),
4154+ 1024 ,
4155+ ),
4156+ ):
4157+ self ._write_buffer (first_block + second_block )
4158+ members = self ._get_members (ignore_zeros = True )
4159+ self .assertEqual (len (members ), 1 )
4160+ self .assertEqual (members [0 ].name , "filename" )
4161+ self .assertEqual (members [0 ].offset , expected_offset )
4162+
4163+
40084164def setUpModule ():
40094165 support .unlink (TEMPDIR )
40104166 os .makedirs (TEMPDIR )
0 commit comments