@@ -49,6 +49,7 @@ def sha256sum(data):
4949xzname = os .path .join (TEMPDIR , "testtar.tar.xz" )
5050tmpname = os .path .join (TEMPDIR , "tmp.tar" )
5151dotlessname = os .path .join (TEMPDIR , "testtar" )
52+ SPACE = b" "
5253
5354sha256_regtype = (
5455 "e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce"
@@ -4273,6 +4274,193 @@ def valueerror_filter(tarinfo, path):
42734274 self .expect_exception (TypeError ) # errorlevel is not int
42744275
42754276
4277+ class OverwriteTests (archiver_tests .OverwriteTests , unittest .TestCase ):
4278+ testdir = os .path .join (TEMPDIR , "testoverwrite" )
4279+
4280+ @classmethod
4281+ def setUpClass (cls ):
4282+ p = cls .ar_with_file = os .path .join (TEMPDIR , 'tar-with-file.tar' )
4283+ cls .addClassCleanup (os_helper .unlink , p )
4284+ with tarfile .open (p , 'w' ) as tar :
4285+ t = tarfile .TarInfo ('test' )
4286+ t .size = 10
4287+ tar .addfile (t , io .BytesIO (b'newcontent' ))
4288+
4289+ p = cls .ar_with_dir = os .path .join (TEMPDIR , 'tar-with-dir.tar' )
4290+ cls .addClassCleanup (os_helper .unlink , p )
4291+ with tarfile .open (p , 'w' ) as tar :
4292+ tar .addfile (tar .gettarinfo (os .curdir , 'test' ))
4293+
4294+ p = os .path .join (TEMPDIR , 'tar-with-implicit-dir.tar' )
4295+ cls .ar_with_implicit_dir = p
4296+ cls .addClassCleanup (os_helper .unlink , p )
4297+ with tarfile .open (p , 'w' ) as tar :
4298+ t = tarfile .TarInfo ('test/file' )
4299+ t .size = 10
4300+ tar .addfile (t , io .BytesIO (b'newcontent' ))
4301+
4302+ def open (self , path ):
4303+ return tarfile .open (path , 'r' )
4304+
4305+ def extractall (self , ar ):
4306+ ar .extractall (self .testdir , filter = 'fully_trusted' )
4307+
4308+
4309+ class OffsetValidationTests (unittest .TestCase ):
4310+ tarname = tmpname
4311+ invalid_posix_header = (
4312+ # name: 100 bytes
4313+ tarfile .NUL * tarfile .LENGTH_NAME
4314+ # mode, space, null terminator: 8 bytes
4315+ + b"000755" + SPACE + tarfile .NUL
4316+ # uid, space, null terminator: 8 bytes
4317+ + b"000001" + SPACE + tarfile .NUL
4318+ # gid, space, null terminator: 8 bytes
4319+ + b"000001" + SPACE + tarfile .NUL
4320+ # size, space: 12 bytes
4321+ + b"\xff " * 11 + SPACE
4322+ # mtime, space: 12 bytes
4323+ + tarfile .NUL * 11 + SPACE
4324+ # chksum: 8 bytes
4325+ + b"0011407" + tarfile .NUL
4326+ # type: 1 byte
4327+ + tarfile .REGTYPE
4328+ # linkname: 100 bytes
4329+ + tarfile .NUL * tarfile .LENGTH_LINK
4330+ # magic: 6 bytes, version: 2 bytes
4331+ + tarfile .POSIX_MAGIC
4332+ # uname: 32 bytes
4333+ + tarfile .NUL * 32
4334+ # gname: 32 bytes
4335+ + tarfile .NUL * 32
4336+ # devmajor, space, null terminator: 8 bytes
4337+ + tarfile .NUL * 6 + SPACE + tarfile .NUL
4338+ # devminor, space, null terminator: 8 bytes
4339+ + tarfile .NUL * 6 + SPACE + tarfile .NUL
4340+ # prefix: 155 bytes
4341+ + tarfile .NUL * tarfile .LENGTH_PREFIX
4342+ # padding: 12 bytes
4343+ + tarfile .NUL * 12
4344+ )
4345+ invalid_gnu_header = (
4346+ # name: 100 bytes
4347+ tarfile .NUL * tarfile .LENGTH_NAME
4348+ # mode, null terminator: 8 bytes
4349+ + b"0000755" + tarfile .NUL
4350+ # uid, null terminator: 8 bytes
4351+ + b"0000001" + tarfile .NUL
4352+ # gid, space, null terminator: 8 bytes
4353+ + b"0000001" + tarfile .NUL
4354+ # size, space: 12 bytes
4355+ + b"\xff " * 11 + SPACE
4356+ # mtime, space: 12 bytes
4357+ + tarfile .NUL * 11 + SPACE
4358+ # chksum: 8 bytes
4359+ + b"0011327" + tarfile .NUL
4360+ # type: 1 byte
4361+ + tarfile .REGTYPE
4362+ # linkname: 100 bytes
4363+ + tarfile .NUL * tarfile .LENGTH_LINK
4364+ # magic: 8 bytes
4365+ + tarfile .GNU_MAGIC
4366+ # uname: 32 bytes
4367+ + tarfile .NUL * 32
4368+ # gname: 32 bytes
4369+ + tarfile .NUL * 32
4370+ # devmajor, null terminator: 8 bytes
4371+ + tarfile .NUL * 8
4372+ # devminor, null terminator: 8 bytes
4373+ + tarfile .NUL * 8
4374+ # padding: 167 bytes
4375+ + tarfile .NUL * 167
4376+ )
4377+ invalid_v7_header = (
4378+ # name: 100 bytes
4379+ tarfile .NUL * tarfile .LENGTH_NAME
4380+ # mode, space, null terminator: 8 bytes
4381+ + b"000755" + SPACE + tarfile .NUL
4382+ # uid, space, null terminator: 8 bytes
4383+ + b"000001" + SPACE + tarfile .NUL
4384+ # gid, space, null terminator: 8 bytes
4385+ + b"000001" + SPACE + tarfile .NUL
4386+ # size, space: 12 bytes
4387+ + b"\xff " * 11 + SPACE
4388+ # mtime, space: 12 bytes
4389+ + tarfile .NUL * 11 + SPACE
4390+ # chksum: 8 bytes
4391+ + b"0010070" + tarfile .NUL
4392+ # type: 1 byte
4393+ + tarfile .REGTYPE
4394+ # linkname: 100 bytes
4395+ + tarfile .NUL * tarfile .LENGTH_LINK
4396+ # padding: 255 bytes
4397+ + tarfile .NUL * 255
4398+ )
4399+ valid_gnu_header = tarfile .TarInfo ("filename" ).tobuf (tarfile .GNU_FORMAT )
4400+ data_block = b"\xff " * tarfile .BLOCKSIZE
4401+
4402+ def _write_buffer (self , buffer ):
4403+ with open (self .tarname , "wb" ) as f :
4404+ f .write (buffer )
4405+
4406+ def _get_members (self , ignore_zeros = None ):
4407+ with open (self .tarname , "rb" ) as f :
4408+ with tarfile .open (
4409+ mode = "r" , fileobj = f , ignore_zeros = ignore_zeros
4410+ ) as tar :
4411+ return tar .getmembers ()
4412+
4413+ def _assert_raises_read_error_exception (self ):
4414+ with self .assertRaisesRegex (
4415+ tarfile .ReadError , "file could not be opened successfully"
4416+ ):
4417+ self ._get_members ()
4418+
4419+ def test_invalid_offset_header_validations (self ):
4420+ for tar_format , invalid_header in (
4421+ ("posix" , self .invalid_posix_header ),
4422+ ("gnu" , self .invalid_gnu_header ),
4423+ ("v7" , self .invalid_v7_header ),
4424+ ):
4425+ with self .subTest (format = tar_format ):
4426+ self ._write_buffer (invalid_header )
4427+ self ._assert_raises_read_error_exception ()
4428+
4429+ def test_early_stop_at_invalid_offset_header (self ):
4430+ buffer = self .valid_gnu_header + self .invalid_gnu_header + self .valid_gnu_header
4431+ self ._write_buffer (buffer )
4432+ members = self ._get_members ()
4433+ self .assertEqual (len (members ), 1 )
4434+ self .assertEqual (members [0 ].name , "filename" )
4435+ self .assertEqual (members [0 ].offset , 0 )
4436+
4437+ def test_ignore_invalid_archive (self ):
4438+ # 3 invalid headers with their respective data
4439+ buffer = (self .invalid_gnu_header + self .data_block ) * 3
4440+ self ._write_buffer (buffer )
4441+ members = self ._get_members (ignore_zeros = True )
4442+ self .assertEqual (len (members ), 0 )
4443+
4444+ def test_ignore_invalid_offset_headers (self ):
4445+ for first_block , second_block , expected_offset in (
4446+ (
4447+ (self .valid_gnu_header ),
4448+ (self .invalid_gnu_header + self .data_block ),
4449+ 0 ,
4450+ ),
4451+ (
4452+ (self .invalid_gnu_header + self .data_block ),
4453+ (self .valid_gnu_header ),
4454+ 1024 ,
4455+ ),
4456+ ):
4457+ self ._write_buffer (first_block + second_block )
4458+ members = self ._get_members (ignore_zeros = True )
4459+ self .assertEqual (len (members ), 1 )
4460+ self .assertEqual (members [0 ].name , "filename" )
4461+ self .assertEqual (members [0 ].offset , expected_offset )
4462+
4463+
42764464def setUpModule ():
42774465 os_helper .unlink (TEMPDIR )
42784466 os .makedirs (TEMPDIR )
0 commit comments