|
5 | 5 | "archive/zip" |
6 | 6 | "bytes" |
7 | 7 | "compress/gzip" |
| 8 | + "errors" |
| 9 | + "fmt" |
8 | 10 | "io" |
9 | 11 | "os" |
10 | 12 | "strings" |
@@ -569,3 +571,132 @@ func TestGetStripPrefixNpm(t *testing.T) { |
569 | 571 | } |
570 | 572 | } |
571 | 573 | } |
| 574 | + |
| 575 | +func TestOpenTarRejectsDecompressBomb(t *testing.T) { |
| 576 | + oldMax := maxDecompressedSize |
| 577 | + maxDecompressedSize = 1024 |
| 578 | + defer func() { maxDecompressedSize = oldMax }() |
| 579 | + |
| 580 | + buf := new(bytes.Buffer) |
| 581 | + gw := gzip.NewWriter(buf) |
| 582 | + tw := tar.NewWriter(gw) |
| 583 | + |
| 584 | + content := strings.Repeat("x", 2048) |
| 585 | + _ = tw.WriteHeader(&tar.Header{ |
| 586 | + Name: "big.txt", |
| 587 | + Size: int64(len(content)), |
| 588 | + Mode: 0644, |
| 589 | + }) |
| 590 | + _, _ = tw.Write([]byte(content)) |
| 591 | + _ = tw.Close() |
| 592 | + _ = gw.Close() |
| 593 | + |
| 594 | + _, err := openTar(bytes.NewReader(buf.Bytes()), "gzip") |
| 595 | + if err == nil { |
| 596 | + t.Fatal("expected error for oversized decompressed content") |
| 597 | + } |
| 598 | + if !errors.Is(err, ErrDecompressLimit) { |
| 599 | + t.Fatalf("expected ErrDecompressLimit, got: %v", err) |
| 600 | + } |
| 601 | +} |
| 602 | + |
| 603 | +func TestOpenTarAcceptsWithinLimit(t *testing.T) { |
| 604 | + oldMax := maxDecompressedSize |
| 605 | + maxDecompressedSize = 4096 |
| 606 | + defer func() { maxDecompressedSize = oldMax }() |
| 607 | + |
| 608 | + buf := new(bytes.Buffer) |
| 609 | + gw := gzip.NewWriter(buf) |
| 610 | + tw := tar.NewWriter(gw) |
| 611 | + |
| 612 | + content := strings.Repeat("x", 1024) |
| 613 | + _ = tw.WriteHeader(&tar.Header{ |
| 614 | + Name: "ok.txt", |
| 615 | + Size: int64(len(content)), |
| 616 | + Mode: 0644, |
| 617 | + }) |
| 618 | + _, _ = tw.Write([]byte(content)) |
| 619 | + _ = tw.Close() |
| 620 | + _ = gw.Close() |
| 621 | + |
| 622 | + reader, err := openTar(bytes.NewReader(buf.Bytes()), "gzip") |
| 623 | + if err != nil { |
| 624 | + t.Fatalf("unexpected error: %v", err) |
| 625 | + } |
| 626 | + |
| 627 | + files, _ := reader.List() |
| 628 | + if len(files) != 1 { |
| 629 | + t.Fatalf("expected 1 file, got %d", len(files)) |
| 630 | + } |
| 631 | + if files[0].Path != "ok.txt" { |
| 632 | + t.Errorf("expected ok.txt, got %s", files[0].Path) |
| 633 | + } |
| 634 | +} |
| 635 | + |
| 636 | +func TestOpenTarRejectsCumulativeOverflow(t *testing.T) { |
| 637 | + oldMax := maxDecompressedSize |
| 638 | + maxDecompressedSize = 1024 |
| 639 | + defer func() { maxDecompressedSize = oldMax }() |
| 640 | + |
| 641 | + buf := new(bytes.Buffer) |
| 642 | + gw := gzip.NewWriter(buf) |
| 643 | + tw := tar.NewWriter(gw) |
| 644 | + |
| 645 | + for i := 0; i < 3; i++ { |
| 646 | + content := strings.Repeat("y", 512) |
| 647 | + _ = tw.WriteHeader(&tar.Header{ |
| 648 | + Name: fmt.Sprintf("file%d.txt", i), |
| 649 | + Size: int64(len(content)), |
| 650 | + Mode: 0644, |
| 651 | + }) |
| 652 | + _, _ = tw.Write([]byte(content)) |
| 653 | + } |
| 654 | + _ = tw.Close() |
| 655 | + _ = gw.Close() |
| 656 | + |
| 657 | + _, err := openTar(bytes.NewReader(buf.Bytes()), "gzip") |
| 658 | + if err == nil { |
| 659 | + t.Fatal("expected error when cumulative size exceeds limit") |
| 660 | + } |
| 661 | + if !errors.Is(err, ErrDecompressLimit) { |
| 662 | + t.Fatalf("expected ErrDecompressLimit, got: %v", err) |
| 663 | + } |
| 664 | +} |
| 665 | + |
| 666 | +func TestOpenGemRejectsOversizedData(t *testing.T) { |
| 667 | + oldMax := maxDecompressedSize |
| 668 | + maxDecompressedSize = 512 |
| 669 | + defer func() { maxDecompressedSize = oldMax }() |
| 670 | + |
| 671 | + // Build a data.tar.gz that decompresses larger than the limit |
| 672 | + var innerBuf bytes.Buffer |
| 673 | + innerGw := gzip.NewWriter(&innerBuf) |
| 674 | + innerTw := tar.NewWriter(innerGw) |
| 675 | + |
| 676 | + content := strings.Repeat("z", 1024) |
| 677 | + _ = innerTw.WriteHeader(&tar.Header{ |
| 678 | + Name: "lib/main.rb", |
| 679 | + Size: int64(len(content)), |
| 680 | + Mode: 0644, |
| 681 | + }) |
| 682 | + _, _ = innerTw.Write([]byte(content)) |
| 683 | + _ = innerTw.Close() |
| 684 | + _ = innerGw.Close() |
| 685 | + |
| 686 | + // Wrap in outer gem tar |
| 687 | + var gemBuf bytes.Buffer |
| 688 | + outerTw := tar.NewWriter(&gemBuf) |
| 689 | + dataTarGz := innerBuf.Bytes() |
| 690 | + _ = outerTw.WriteHeader(&tar.Header{ |
| 691 | + Name: "data.tar.gz", |
| 692 | + Size: int64(len(dataTarGz)), |
| 693 | + Mode: 0644, |
| 694 | + }) |
| 695 | + _, _ = outerTw.Write(dataTarGz) |
| 696 | + _ = outerTw.Close() |
| 697 | + |
| 698 | + _, err := openGem(bytes.NewReader(gemBuf.Bytes())) |
| 699 | + if err == nil { |
| 700 | + t.Fatal("expected error for oversized gem data") |
| 701 | + } |
| 702 | +} |
0 commit comments