@@ -49,6 +49,7 @@ def sha256sum(data):
49
49
xzname = os .path .join (TEMPDIR , "testtar.tar.xz" )
50
50
tmpname = os .path .join (TEMPDIR , "tmp.tar" )
51
51
dotlessname = os .path .join (TEMPDIR , "testtar" )
52
+ SPACE = b" "
52
53
53
54
sha256_regtype = (
54
55
"e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce"
@@ -4273,6 +4274,193 @@ def valueerror_filter(tarinfo, path):
4273
4274
self .expect_exception (TypeError ) # errorlevel is not int
4274
4275
4275
4276
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
+
4276
4464
def setUpModule ():
4277
4465
os_helper .unlink (TEMPDIR )
4278
4466
os .makedirs (TEMPDIR )
0 commit comments