@@ -48,6 +48,7 @@ def sha256sum(data):
48
48
xzname = os .path .join (TEMPDIR , "testtar.tar.xz" )
49
49
tmpname = os .path .join (TEMPDIR , "tmp.tar" )
50
50
dotlessname = os .path .join (TEMPDIR , "testtar" )
51
+ SPACE = b" "
51
52
52
53
sha256_regtype = (
53
54
"e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce"
@@ -4234,6 +4235,193 @@ def valueerror_filter(tarinfo, path):
4234
4235
self .expect_exception (TypeError ) # errorlevel is not int
4235
4236
4236
4237
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
+
4237
4425
def setUpModule ():
4238
4426
support .unlink (TEMPDIR )
4239
4427
os .makedirs (TEMPDIR )
0 commit comments