@@ -392,24 +392,104 @@ func TestUnpackErrorOnUnhandledType(t *testing.T) {
392
392
393
393
func TestUnpackMaliciousSymlinks (t * testing.T ) {
394
394
tcases := []struct {
395
- desc string
396
- target string
397
- err string
395
+ desc string
396
+ headers [] * tar. Header
397
+ err string
398
398
}{
399
399
{
400
- desc : "symlink with absolute path" ,
401
- target : "/etc/shadow" ,
402
- err : "has absolute target" ,
400
+ desc : "symlink with absolute path" ,
401
+ headers : []* tar.Header {
402
+ & tar.Header {
403
+ Name : "l" ,
404
+ Linkname : "/etc/shadow" ,
405
+ Typeflag : tar .TypeSymlink ,
406
+ },
407
+ },
408
+ err : "has absolute target" ,
403
409
},
404
410
{
405
- desc : "symlink with external target" ,
406
- target : "../../../../../etc/shadow" ,
407
- err : "has external target" ,
411
+ desc : "symlink with external target" ,
412
+ headers : []* tar.Header {
413
+ & tar.Header {
414
+ Name : "l" ,
415
+ Linkname : "../../../../../etc/shadow" ,
416
+ Typeflag : tar .TypeSymlink ,
417
+ },
418
+ },
419
+ err : "has external target" ,
408
420
},
409
421
{
410
- desc : "symlink with nested external target" ,
411
- target : "foo/bar/baz/../../../../../../../../etc/shadow" ,
412
- err : "has external target" ,
422
+ desc : "symlink with nested external target" ,
423
+ headers : []* tar.Header {
424
+ & tar.Header {
425
+ Name : "l" ,
426
+ Linkname : "foo/bar/baz/../../../../../../../../etc/shadow" ,
427
+ Typeflag : tar .TypeSymlink ,
428
+ },
429
+ },
430
+ err : "has external target" ,
431
+ },
432
+ {
433
+ desc : "zipslip vulnerability" ,
434
+ headers : []* tar.Header {
435
+ & tar.Header {
436
+ Name : "subdir/parent" ,
437
+ Linkname : ".." ,
438
+ Typeflag : tar .TypeSymlink ,
439
+ },
440
+ & tar.Header {
441
+ Name : "subdir/parent/escapes" ,
442
+ Linkname : ".." ,
443
+ Typeflag : tar .TypeSymlink ,
444
+ },
445
+ },
446
+ err : `Cannot extract "subdir/parent/escapes" through symlink` ,
447
+ },
448
+ {
449
+ desc : "nested symlinks within symlinked dir" ,
450
+ headers : []* tar.Header {
451
+ & tar.Header {
452
+ Name : "subdir/parent" ,
453
+ Linkname : ".." ,
454
+ Typeflag : tar .TypeSymlink ,
455
+ },
456
+ & tar.Header {
457
+ Name : "subdir/parent/otherdir/escapes" ,
458
+ Linkname : "../.." ,
459
+ Typeflag : tar .TypeSymlink ,
460
+ },
461
+ },
462
+ err : `Cannot extract "subdir/parent/otherdir/escapes" through symlink` ,
463
+ },
464
+ {
465
+ desc : "regular file through symlink" ,
466
+ headers : []* tar.Header {
467
+ & tar.Header {
468
+ Name : "subdir/parent" ,
469
+ Linkname : ".." ,
470
+ Typeflag : tar .TypeSymlink ,
471
+ },
472
+ & tar.Header {
473
+ Name : "subdir/parent/file" ,
474
+ Typeflag : tar .TypeReg ,
475
+ },
476
+ },
477
+ err : `Cannot extract "subdir/parent/file" through symlink` ,
478
+ },
479
+ {
480
+ desc : "directory through symlink" ,
481
+ headers : []* tar.Header {
482
+ & tar.Header {
483
+ Name : "subdir/parent" ,
484
+ Linkname : ".." ,
485
+ Typeflag : tar .TypeSymlink ,
486
+ },
487
+ & tar.Header {
488
+ Name : "subdir/parent/dir" ,
489
+ Typeflag : tar .TypeDir ,
490
+ },
491
+ },
492
+ err : `Cannot extract "subdir/parent/dir" through symlink` ,
413
493
},
414
494
}
415
495
@@ -435,14 +515,86 @@ func TestUnpackMaliciousSymlinks(t *testing.T) {
435
515
// Tar the file contents
436
516
tarW := tar .NewWriter (gzipW )
437
517
438
- var hdr tar.Header
518
+ for _ , hdr := range tc .headers {
519
+ tarW .WriteHeader (hdr )
520
+ }
521
+
522
+ tarW .Close ()
523
+ gzipW .Close ()
524
+ wfh .Close ()
439
525
440
- hdr .Typeflag = tar .TypeSymlink
441
- hdr .Name = "l"
442
- hdr .Size = int64 (0 )
443
- hdr .Linkname = tc .target
526
+ // Open the slug file for reading.
527
+ fh , err := os .Open (in )
528
+ if err != nil {
529
+ t .Fatalf ("err: %v" , err )
530
+ }
444
531
445
- tarW .WriteHeader (& hdr )
532
+ // Create a dir to unpack into.
533
+ dst , err := ioutil .TempDir (dir , "" )
534
+ if err != nil {
535
+ t .Fatalf ("err: %v" , err )
536
+ }
537
+ defer os .RemoveAll (dst )
538
+
539
+ // Now try unpacking it, which should fail
540
+ err = Unpack (fh , dst )
541
+ if err == nil || ! strings .Contains (err .Error (), tc .err ) {
542
+ t .Fatalf ("expected %v, got %v" , tc .err , err )
543
+ }
544
+ })
545
+ }
546
+ }
547
+
548
+ func TestUnpackMaliciousFiles (t * testing.T ) {
549
+ tcases := []struct {
550
+ desc string
551
+ name string
552
+ err string
553
+ }{
554
+ {
555
+ desc : "filename containing path traversal" ,
556
+ name : "../../../../../../../../tmp/test" ,
557
+ err : "Invalid filename, traversal with \" ..\" outside of current directory" ,
558
+ },
559
+ {
560
+ desc : "should fail before attempting to create directories" ,
561
+ name : "../../../../../../../../Users/root" ,
562
+ err : "Invalid filename, traversal with \" ..\" outside of current directory" ,
563
+ },
564
+ }
565
+
566
+ for _ , tc := range tcases {
567
+ t .Run (tc .desc , func (t * testing.T ) {
568
+ dir , err := ioutil .TempDir ("" , "slug" )
569
+ if err != nil {
570
+ t .Fatalf ("err:%v" , err )
571
+ }
572
+ defer os .RemoveAll (dir )
573
+ in := filepath .Join (dir , "slug.tar.gz" )
574
+
575
+ // Create the output file
576
+ wfh , err := os .Create (in )
577
+ if err != nil {
578
+ t .Fatalf ("err: %v" , err )
579
+ }
580
+
581
+ // Gzip compress all the output data
582
+ gzipW := gzip .NewWriter (wfh )
583
+
584
+ // Tar the file contents
585
+ tarW := tar .NewWriter (gzipW )
586
+
587
+ hdr := & tar.Header {
588
+ Name : tc .name ,
589
+ Mode : 0600 ,
590
+ Size : int64 (0 ),
591
+ }
592
+ if err := tarW .WriteHeader (hdr ); err != nil {
593
+ t .Fatalf ("err: %v" , err )
594
+ }
595
+ if _ , err := tarW .Write ([]byte {}); err != nil {
596
+ t .Fatalf ("err: %v" , err )
597
+ }
446
598
447
599
tarW .Close ()
448
600
gzipW .Close ()
0 commit comments