2929
3030SMALL_TEST_DATA = [('_ziptest1' , '1q2w3e4r5t' ),
3131 ('ziptest2dir/_ziptest2' , 'qawsedrftg' ),
32- ('/ ziptest2dir/ziptest3dir/_ziptest3' , 'azsxdcfvgb' ),
32+ ('ziptest2dir/ziptest3dir/_ziptest3' , 'azsxdcfvgb' ),
3333 ('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3' , '6y7u8i9o0p' )]
3434
3535
@@ -409,10 +409,7 @@ def test_extract(self):
409409 writtenfile = zipfp .extract (fpath )
410410
411411 # make sure it was written to the right place
412- if os .path .isabs (fpath ):
413- correctfile = os .path .join (os .getcwd (), fpath [1 :])
414- else :
415- correctfile = os .path .join (os .getcwd (), fpath )
412+ correctfile = os .path .join (os .getcwd (), fpath )
416413 correctfile = os .path .normpath (correctfile )
417414
418415 self .assertEqual (writtenfile , correctfile )
@@ -434,10 +431,7 @@ def test_extract_all(self):
434431 with zipfile .ZipFile (TESTFN2 , "r" ) as zipfp :
435432 zipfp .extractall ()
436433 for fpath , fdata in SMALL_TEST_DATA :
437- if os .path .isabs (fpath ):
438- outfile = os .path .join (os .getcwd (), fpath [1 :])
439- else :
440- outfile = os .path .join (os .getcwd (), fpath )
434+ outfile = os .path .join (os .getcwd (), fpath )
441435
442436 with open (outfile , "rb" ) as f :
443437 self .assertEqual (fdata .encode (), f .read ())
@@ -447,6 +441,80 @@ def test_extract_all(self):
447441 # remove the test file subdirectories
448442 shutil .rmtree (os .path .join (os .getcwd (), 'ziptest2dir' ))
449443
444+ def check_file (self , filename , content ):
445+ self .assertTrue (os .path .isfile (filename ))
446+ with open (filename , 'rb' ) as f :
447+ self .assertEqual (f .read (), content )
448+
449+ def test_extract_hackers_arcnames (self ):
450+ hacknames = [
451+ ('../foo/bar' , 'foo/bar' ),
452+ ('foo/../bar' , 'foo/bar' ),
453+ ('foo/../../bar' , 'foo/bar' ),
454+ ('foo/bar/..' , 'foo/bar' ),
455+ ('./../foo/bar' , 'foo/bar' ),
456+ ('/foo/bar' , 'foo/bar' ),
457+ ('/foo/../bar' , 'foo/bar' ),
458+ ('/foo/../../bar' , 'foo/bar' ),
459+ ('//foo/bar' , 'foo/bar' ),
460+ ('../../foo../../ba..r' , 'foo../ba..r' ),
461+ ]
462+ if os .path .sep == '\\ ' : # Windows.
463+ hacknames .extend ([
464+ (r'..\foo\bar' , 'foo/bar' ),
465+ (r'..\/foo\/bar' , 'foo/bar' ),
466+ (r'foo/\..\/bar' , 'foo/bar' ),
467+ (r'foo\/../\bar' , 'foo/bar' ),
468+ (r'C:foo/bar' , 'foo/bar' ),
469+ (r'C:/foo/bar' , 'foo/bar' ),
470+ (r'C://foo/bar' , 'foo/bar' ),
471+ (r'C:\foo\bar' , 'foo/bar' ),
472+ (r'//conky/mountpoint/foo/bar' , 'foo/bar' ),
473+ (r'\\conky\mountpoint\foo\bar' , 'foo/bar' ),
474+ (r'///conky/mountpoint/foo/bar' , 'conky/mountpoint/foo/bar' ),
475+ (r'\\\conky\mountpoint\foo\bar' , 'conky/mountpoint/foo/bar' ),
476+ (r'//conky//mountpoint/foo/bar' , 'conky/mountpoint/foo/bar' ),
477+ (r'\\conky\\mountpoint\foo\bar' , 'conky/mountpoint/foo/bar' ),
478+ (r'//?/C:/foo/bar' , 'foo/bar' ),
479+ (r'\\?\C:\foo\bar' , 'foo/bar' ),
480+ (r'C:/../C:/foo/bar' , 'C_/foo/bar' ),
481+ (r'a:b\c<d>e|f"g?h*i' , 'b/c_d_e_f_g_h_i' ),
482+ ])
483+
484+ for arcname , fixedname in hacknames :
485+ content = b'foobar' + arcname .encode ()
486+ with zipfile .ZipFile (TESTFN2 , 'w' , zipfile .ZIP_STORED ) as zipfp :
487+ zipfp .writestr (arcname , content )
488+
489+ targetpath = os .path .join ('target' , 'subdir' , 'subsub' )
490+ correctfile = os .path .join (targetpath , * fixedname .split ('/' ))
491+
492+ with zipfile .ZipFile (TESTFN2 , 'r' ) as zipfp :
493+ writtenfile = zipfp .extract (arcname , targetpath )
494+ self .assertEqual (writtenfile , correctfile )
495+ self .check_file (correctfile , content )
496+ shutil .rmtree ('target' )
497+
498+ with zipfile .ZipFile (TESTFN2 , 'r' ) as zipfp :
499+ zipfp .extractall (targetpath )
500+ self .check_file (correctfile , content )
501+ shutil .rmtree ('target' )
502+
503+ correctfile = os .path .join (os .getcwd (), * fixedname .split ('/' ))
504+
505+ with zipfile .ZipFile (TESTFN2 , 'r' ) as zipfp :
506+ writtenfile = zipfp .extract (arcname )
507+ self .assertEqual (writtenfile , correctfile )
508+ self .check_file (correctfile , content )
509+ shutil .rmtree (fixedname .split ('/' )[0 ])
510+
511+ with zipfile .ZipFile (TESTFN2 , 'r' ) as zipfp :
512+ zipfp .extractall ()
513+ self .check_file (correctfile , content )
514+ shutil .rmtree (fixedname .split ('/' )[0 ])
515+
516+ os .remove (TESTFN2 )
517+
450518 def test_writestr_compression (self ):
451519 zipfp = zipfile .ZipFile (TESTFN2 , "w" )
452520 zipfp .writestr ("a.txt" , "hello world" , compress_type = zipfile .ZIP_STORED )
0 commit comments