|
1 | 1 | package archiver_test
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "archive/tar" |
| 5 | + "bytes" |
4 | 6 | "io/ioutil"
|
5 | 7 | "os"
|
6 | 8 | "path"
|
| 9 | + "path/filepath" |
7 | 10 | "testing"
|
8 | 11 |
|
9 | 12 | "github.com/mholt/archiver/v3"
|
10 | 13 | )
|
11 | 14 |
|
| 15 | +func requireDoesNotExist(t *testing.T, path string) { |
| 16 | + _, err := os.Lstat(path) |
| 17 | + if err == nil { |
| 18 | + t.Fatalf("'%s' expected to not exist", path) |
| 19 | + } |
| 20 | +} |
| 21 | + |
12 | 22 | func requireRegularFile(t *testing.T, path string) os.FileInfo {
|
13 |
| - fileInfo, err := os.Stat(path) |
| 23 | + fileInfo, err := os.Lstat(path) |
14 | 24 | if err != nil {
|
15 | 25 | t.Fatalf("fileInfo on '%s': %v", path, err)
|
16 | 26 | }
|
@@ -47,6 +57,83 @@ func TestDefaultTar_Unarchive_HardlinkSuccess(t *testing.T) {
|
47 | 57 | assertSameFile(t, fileaInfo, filebInfo)
|
48 | 58 | }
|
49 | 59 |
|
| 60 | +func TestDefaultTar_Unarchive_SymlinkPathTraversal(t *testing.T) { |
| 61 | + tmp := t.TempDir() |
| 62 | + source := filepath.Join(tmp, "source.tar") |
| 63 | + createSymlinkPathTraversalSample(t, source, "./../target") |
| 64 | + destination := filepath.Join(tmp, "destination") |
| 65 | + |
| 66 | + err := archiver.DefaultTar.Unarchive(source, destination) |
| 67 | + if err != nil { |
| 68 | + t.Fatalf("unarchiving '%s' to '%s': %v", source, destination, err) |
| 69 | + } |
| 70 | + |
| 71 | + requireDoesNotExist(t, filepath.Join(tmp, "target")) |
| 72 | + requireRegularFile(t, filepath.Join(tmp, "destination", "duplicatedentry.txt")) |
| 73 | +} |
| 74 | + |
| 75 | +func TestDefaultTar_Unarchive_SymlinkPathTraversal_AbsLinkDestination(t *testing.T) { |
| 76 | + tmp := t.TempDir() |
| 77 | + source := filepath.Join(tmp, "source.tar") |
| 78 | + createSymlinkPathTraversalSample(t, source, "/tmp/thing") |
| 79 | + destination := filepath.Join(tmp, "destination") |
| 80 | + |
| 81 | + err := archiver.DefaultTar.Unarchive(source, destination) |
| 82 | + if err != nil { |
| 83 | + t.Fatalf("unarchiving '%s' to '%s': %v", source, destination, err) |
| 84 | + } |
| 85 | + |
| 86 | + requireDoesNotExist(t, "/tmp/thing") |
| 87 | + requireRegularFile(t, filepath.Join(tmp, "destination", "duplicatedentry.txt")) |
| 88 | +} |
| 89 | + |
| 90 | +func createSymlinkPathTraversalSample(t *testing.T, archivePath string, linkPath string) { |
| 91 | + t.Helper() |
| 92 | + |
| 93 | + type tarinfo struct { |
| 94 | + Name string |
| 95 | + Link string |
| 96 | + Body string |
| 97 | + Type byte |
| 98 | + } |
| 99 | + |
| 100 | + var infos = []tarinfo{ |
| 101 | + {"duplicatedentry.txt", linkPath, "", tar.TypeSymlink}, |
| 102 | + {"duplicatedentry.txt", "", "content modified!", tar.TypeReg}, |
| 103 | + } |
| 104 | + |
| 105 | + var buf bytes.Buffer |
| 106 | + var tw = tar.NewWriter(&buf) |
| 107 | + for _, ti := range infos { |
| 108 | + hdr := &tar.Header{ |
| 109 | + Name: ti.Name, |
| 110 | + Mode: 0600, |
| 111 | + Linkname: ti.Link, |
| 112 | + Typeflag: ti.Type, |
| 113 | + Size: int64(len(ti.Body)), |
| 114 | + } |
| 115 | + if err := tw.WriteHeader(hdr); err != nil { |
| 116 | + t.Fatal("Writing header: ", err) |
| 117 | + } |
| 118 | + if _, err := tw.Write([]byte(ti.Body)); err != nil { |
| 119 | + t.Fatal("Writing body: ", err) |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + f, err := os.Create(archivePath) |
| 124 | + if err != nil { |
| 125 | + t.Fatal(err) |
| 126 | + } |
| 127 | + _, err = f.Write(buf.Bytes()) |
| 128 | + if err != nil { |
| 129 | + t.Fatal(err) |
| 130 | + } |
| 131 | + |
| 132 | + if err := f.Close(); err != nil { |
| 133 | + t.Fatal(err) |
| 134 | + } |
| 135 | +} |
| 136 | + |
50 | 137 | func TestDefaultTar_Extract_HardlinkSuccess(t *testing.T) {
|
51 | 138 | source := "testdata/gnu-hardlinks.tar"
|
52 | 139 |
|
|
0 commit comments