@@ -43,6 +43,7 @@ def md5sum(data):
43
43
xzname = os .path .join (TEMPDIR , "testtar.tar.xz" )
44
44
tmpname = os .path .join (TEMPDIR , "tmp.tar" )
45
45
dotlessname = os .path .join (TEMPDIR , "testtar" )
46
+ SPACE = b" "
46
47
47
48
md5_regtype = "65f477c818ad9e15f7feab0c6d37742f"
48
49
md5_sparse = "a54fbc4ca4f4399a90e1b27164012fc6"
@@ -4005,6 +4006,161 @@ def valueerror_filter(tarinfo, path):
4005
4006
self .expect_exception (TypeError ) # errorlevel is not int
4006
4007
4007
4008
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
+
4008
4164
def setUpModule ():
4009
4165
support .unlink (TEMPDIR )
4010
4166
os .makedirs (TEMPDIR )
0 commit comments