@@ -48,6 +48,7 @@ def sha256sum(data):
4848xzname = os .path .join (TEMPDIR , "testtar.tar.xz" )
4949tmpname = os .path .join (TEMPDIR , "tmp.tar" )
5050dotlessname = os .path .join (TEMPDIR , "testtar" )
51+ SPACE = b" "
5152
5253sha256_regtype = (
5354 "e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce"
@@ -4234,6 +4235,193 @@ def valueerror_filter(tarinfo, path):
42344235 self .expect_exception (TypeError ) # errorlevel is not int
42354236
42364237
4238+ class OverwriteTests (archiver_tests .OverwriteTests , unittest .TestCase ):
4239+ testdir = os .path .join (TEMPDIR , "testoverwrite" )
4240+
4241+ @classmethod
4242+ def setUpClass (cls ):
4243+ p = cls .ar_with_file = os .path .join (TEMPDIR , 'tar-with-file.tar' )
4244+ cls .addClassCleanup (os_helper .unlink , p )
4245+ with tarfile .open (p , 'w' ) as tar :
4246+ t = tarfile .TarInfo ('test' )
4247+ t .size = 10
4248+ tar .addfile (t , io .BytesIO (b'newcontent' ))
4249+
4250+ p = cls .ar_with_dir = os .path .join (TEMPDIR , 'tar-with-dir.tar' )
4251+ cls .addClassCleanup (os_helper .unlink , p )
4252+ with tarfile .open (p , 'w' ) as tar :
4253+ tar .addfile (tar .gettarinfo (os .curdir , 'test' ))
4254+
4255+ p = os .path .join (TEMPDIR , 'tar-with-implicit-dir.tar' )
4256+ cls .ar_with_implicit_dir = p
4257+ cls .addClassCleanup (os_helper .unlink , p )
4258+ with tarfile .open (p , 'w' ) as tar :
4259+ t = tarfile .TarInfo ('test/file' )
4260+ t .size = 10
4261+ tar .addfile (t , io .BytesIO (b'newcontent' ))
4262+
4263+ def open (self , path ):
4264+ return tarfile .open (path , 'r' )
4265+
4266+ def extractall (self , ar ):
4267+ ar .extractall (self .testdir , filter = 'fully_trusted' )
4268+
4269+
4270+ class OffsetValidationTests (unittest .TestCase ):
4271+ tarname = tmpname
4272+ invalid_posix_header = (
4273+ # name: 100 bytes
4274+ tarfile .NUL * tarfile .LENGTH_NAME
4275+ # mode, space, null terminator: 8 bytes
4276+ + b"000755" + SPACE + tarfile .NUL
4277+ # uid, space, null terminator: 8 bytes
4278+ + b"000001" + SPACE + tarfile .NUL
4279+ # gid, space, null terminator: 8 bytes
4280+ + b"000001" + SPACE + tarfile .NUL
4281+ # size, space: 12 bytes
4282+ + b"\xff " * 11 + SPACE
4283+ # mtime, space: 12 bytes
4284+ + tarfile .NUL * 11 + SPACE
4285+ # chksum: 8 bytes
4286+ + b"0011407" + tarfile .NUL
4287+ # type: 1 byte
4288+ + tarfile .REGTYPE
4289+ # linkname: 100 bytes
4290+ + tarfile .NUL * tarfile .LENGTH_LINK
4291+ # magic: 6 bytes, version: 2 bytes
4292+ + tarfile .POSIX_MAGIC
4293+ # uname: 32 bytes
4294+ + tarfile .NUL * 32
4295+ # gname: 32 bytes
4296+ + tarfile .NUL * 32
4297+ # devmajor, space, null terminator: 8 bytes
4298+ + tarfile .NUL * 6 + SPACE + tarfile .NUL
4299+ # devminor, space, null terminator: 8 bytes
4300+ + tarfile .NUL * 6 + SPACE + tarfile .NUL
4301+ # prefix: 155 bytes
4302+ + tarfile .NUL * tarfile .LENGTH_PREFIX
4303+ # padding: 12 bytes
4304+ + tarfile .NUL * 12
4305+ )
4306+ invalid_gnu_header = (
4307+ # name: 100 bytes
4308+ tarfile .NUL * tarfile .LENGTH_NAME
4309+ # mode, null terminator: 8 bytes
4310+ + b"0000755" + tarfile .NUL
4311+ # uid, null terminator: 8 bytes
4312+ + b"0000001" + tarfile .NUL
4313+ # gid, space, null terminator: 8 bytes
4314+ + b"0000001" + tarfile .NUL
4315+ # size, space: 12 bytes
4316+ + b"\xff " * 11 + SPACE
4317+ # mtime, space: 12 bytes
4318+ + tarfile .NUL * 11 + SPACE
4319+ # chksum: 8 bytes
4320+ + b"0011327" + tarfile .NUL
4321+ # type: 1 byte
4322+ + tarfile .REGTYPE
4323+ # linkname: 100 bytes
4324+ + tarfile .NUL * tarfile .LENGTH_LINK
4325+ # magic: 8 bytes
4326+ + tarfile .GNU_MAGIC
4327+ # uname: 32 bytes
4328+ + tarfile .NUL * 32
4329+ # gname: 32 bytes
4330+ + tarfile .NUL * 32
4331+ # devmajor, null terminator: 8 bytes
4332+ + tarfile .NUL * 8
4333+ # devminor, null terminator: 8 bytes
4334+ + tarfile .NUL * 8
4335+ # padding: 167 bytes
4336+ + tarfile .NUL * 167
4337+ )
4338+ invalid_v7_header = (
4339+ # name: 100 bytes
4340+ tarfile .NUL * tarfile .LENGTH_NAME
4341+ # mode, space, null terminator: 8 bytes
4342+ + b"000755" + SPACE + tarfile .NUL
4343+ # uid, space, null terminator: 8 bytes
4344+ + b"000001" + SPACE + tarfile .NUL
4345+ # gid, space, null terminator: 8 bytes
4346+ + b"000001" + SPACE + tarfile .NUL
4347+ # size, space: 12 bytes
4348+ + b"\xff " * 11 + SPACE
4349+ # mtime, space: 12 bytes
4350+ + tarfile .NUL * 11 + SPACE
4351+ # chksum: 8 bytes
4352+ + b"0010070" + tarfile .NUL
4353+ # type: 1 byte
4354+ + tarfile .REGTYPE
4355+ # linkname: 100 bytes
4356+ + tarfile .NUL * tarfile .LENGTH_LINK
4357+ # padding: 255 bytes
4358+ + tarfile .NUL * 255
4359+ )
4360+ valid_gnu_header = tarfile .TarInfo ("filename" ).tobuf (tarfile .GNU_FORMAT )
4361+ data_block = b"\xff " * tarfile .BLOCKSIZE
4362+
4363+ def _write_buffer (self , buffer ):
4364+ with open (self .tarname , "wb" ) as f :
4365+ f .write (buffer )
4366+
4367+ def _get_members (self , ignore_zeros = None ):
4368+ with open (self .tarname , "rb" ) as f :
4369+ with tarfile .open (
4370+ mode = "r" , fileobj = f , ignore_zeros = ignore_zeros
4371+ ) as tar :
4372+ return tar .getmembers ()
4373+
4374+ def _assert_raises_read_error_exception (self ):
4375+ with self .assertRaisesRegex (
4376+ tarfile .ReadError , "file could not be opened successfully"
4377+ ):
4378+ self ._get_members ()
4379+
4380+ def test_invalid_offset_header_validations (self ):
4381+ for tar_format , invalid_header in (
4382+ ("posix" , self .invalid_posix_header ),
4383+ ("gnu" , self .invalid_gnu_header ),
4384+ ("v7" , self .invalid_v7_header ),
4385+ ):
4386+ with self .subTest (format = tar_format ):
4387+ self ._write_buffer (invalid_header )
4388+ self ._assert_raises_read_error_exception ()
4389+
4390+ def test_early_stop_at_invalid_offset_header (self ):
4391+ buffer = self .valid_gnu_header + self .invalid_gnu_header + self .valid_gnu_header
4392+ self ._write_buffer (buffer )
4393+ members = self ._get_members ()
4394+ self .assertEqual (len (members ), 1 )
4395+ self .assertEqual (members [0 ].name , "filename" )
4396+ self .assertEqual (members [0 ].offset , 0 )
4397+
4398+ def test_ignore_invalid_archive (self ):
4399+ # 3 invalid headers with their respective data
4400+ buffer = (self .invalid_gnu_header + self .data_block ) * 3
4401+ self ._write_buffer (buffer )
4402+ members = self ._get_members (ignore_zeros = True )
4403+ self .assertEqual (len (members ), 0 )
4404+
4405+ def test_ignore_invalid_offset_headers (self ):
4406+ for first_block , second_block , expected_offset in (
4407+ (
4408+ (self .valid_gnu_header ),
4409+ (self .invalid_gnu_header + self .data_block ),
4410+ 0 ,
4411+ ),
4412+ (
4413+ (self .invalid_gnu_header + self .data_block ),
4414+ (self .valid_gnu_header ),
4415+ 1024 ,
4416+ ),
4417+ ):
4418+ self ._write_buffer (first_block + second_block )
4419+ members = self ._get_members (ignore_zeros = True )
4420+ self .assertEqual (len (members ), 1 )
4421+ self .assertEqual (members [0 ].name , "filename" )
4422+ self .assertEqual (members [0 ].offset , expected_offset )
4423+
4424+
42374425def setUpModule ():
42384426 support .unlink (TEMPDIR )
42394427 os .makedirs (TEMPDIR )
0 commit comments