mirror of
https://github.com/c4milo/unpackit.git
synced 2025-09-26 19:01:11 +08:00

To reduce confusion regarding destination path passed as argument versus returned, and prevent accidental misuse. The destination path is now always required.
251 lines
5.5 KiB
Go
251 lines
5.5 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
package unpackit
|
|
|
|
import (
|
|
"archive/tar"
|
|
"archive/zip"
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/bradfitz/iter"
|
|
"github.com/hooklift/assert"
|
|
)
|
|
|
|
func TestUnpack(t *testing.T) {
|
|
tests := []struct {
|
|
filepath string
|
|
files int
|
|
}{
|
|
{"./fixtures/test.tar.bzip2", 2},
|
|
{"./fixtures/test.tar.gz", 2},
|
|
{"./fixtures/test.tar.xz", 2},
|
|
{"./fixtures/test.zip", 2},
|
|
{"./fixtures/filetest.zip", 3},
|
|
{"./fixtures/test.tar", 2},
|
|
{"./fixtures/cfgdrv.iso", 1},
|
|
{"./fixtures/test2.tar.gz", 4},
|
|
{"./fixtures/tar-without-directory-entries.tar.gz", 1},
|
|
{"./fixtures/testfolder.zip", 3},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.filepath, func(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp(os.TempDir(), "unpackit-tests-"+path.Base(test.filepath)+"-")
|
|
assert.Ok(t, err)
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
file, err := os.Open(test.filepath)
|
|
assert.Ok(t, err)
|
|
defer file.Close()
|
|
|
|
err = Unpack(file, tempDir)
|
|
assert.Ok(t, err)
|
|
|
|
length := calcNumberOfFiles(t, tempDir)
|
|
assert.Equals(t, test.files, length)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMagicNumber(t *testing.T) {
|
|
tests := []struct {
|
|
filepath string
|
|
offset int
|
|
ftype string
|
|
}{
|
|
{"./fixtures/test.tar.bzip2", 0, "bzip"},
|
|
{"./fixtures/test.tar.gz", 0, "gzip"},
|
|
{"./fixtures/test.tar.xz", 0, "xz"},
|
|
{"./fixtures/test.zip", 0, "zip"},
|
|
{"./fixtures/test.tar", 257, "tar"},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
file, err := os.Open(test.filepath)
|
|
assert.Ok(t, err)
|
|
|
|
ftype, err := magicNumber(bufio.NewReader(file), test.offset)
|
|
file.Close()
|
|
assert.Ok(t, err)
|
|
|
|
assert.Equals(t, test.ftype, ftype)
|
|
}
|
|
}
|
|
|
|
func TestUntar(t *testing.T) {
|
|
// Create a buffer to write our archive to.
|
|
buf := new(bytes.Buffer)
|
|
|
|
// Create a new tar archive.
|
|
tw := tar.NewWriter(buf)
|
|
|
|
// Add some files to the archive.
|
|
files := []struct {
|
|
Name, Body string
|
|
}{
|
|
{"readme.txt", "This archive contains some text files."},
|
|
{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
|
|
{"todo.txt", "Get animal handling licence."},
|
|
}
|
|
for _, file := range files {
|
|
hdr := &tar.Header{
|
|
Name: file.Name,
|
|
Size: int64(len(file.Body)),
|
|
}
|
|
err := tw.WriteHeader(hdr)
|
|
assert.Ok(t, err)
|
|
|
|
_, err = tw.Write([]byte(file.Body))
|
|
assert.Ok(t, err)
|
|
}
|
|
|
|
// Make sure to check the error on Close.
|
|
err := tw.Close()
|
|
assert.Ok(t, err)
|
|
|
|
// Open the tar archive for reading.
|
|
r := bytes.NewReader(buf.Bytes())
|
|
destDir, err := os.MkdirTemp(os.TempDir(), "terraform-vix")
|
|
assert.Ok(t, err)
|
|
defer os.RemoveAll(destDir)
|
|
|
|
err = Untar(r, destDir)
|
|
assert.Ok(t, err)
|
|
}
|
|
|
|
func TestUntarOpenFileResourceLeak(t *testing.T) {
|
|
// Create a buffer to write our archive to.
|
|
buf := new(bytes.Buffer)
|
|
|
|
// Create a new tar archive.
|
|
tw := tar.NewWriter(buf)
|
|
|
|
// Add some files to the archive.
|
|
files := make([]struct {
|
|
Name, Body string
|
|
}, 2000)
|
|
|
|
for fileNum := range iter.N(2000) {
|
|
files[fileNum] = struct {
|
|
Name, Body string
|
|
}{
|
|
Name: fmt.Sprintf("file-%d.txt", fileNum),
|
|
Body: fmt.Sprintf("This is file number %d\n", fileNum),
|
|
}
|
|
}
|
|
|
|
for _, file := range files {
|
|
hdr := &tar.Header{
|
|
Name: file.Name,
|
|
Size: int64(len(file.Body)),
|
|
}
|
|
err := tw.WriteHeader(hdr)
|
|
assert.Ok(t, err)
|
|
|
|
_, err = tw.Write([]byte(file.Body))
|
|
assert.Ok(t, err)
|
|
}
|
|
|
|
// Make sure to check the error on Close.
|
|
err := tw.Close()
|
|
assert.Ok(t, err)
|
|
|
|
// Open the tar archive for reading.
|
|
r := bytes.NewReader(buf.Bytes())
|
|
destDir, err := os.MkdirTemp(os.TempDir(), "terraform-vix")
|
|
assert.Ok(t, err)
|
|
defer os.RemoveAll(destDir)
|
|
|
|
err = Untar(r, destDir)
|
|
assert.Ok(t, err)
|
|
}
|
|
|
|
func TestUnzipOpenFileResourceLeak(t *testing.T) {
|
|
tempPath, err := os.MkdirTemp(os.TempDir(), "unzip-resource-leak-test")
|
|
assert.Ok(t, err)
|
|
defer os.RemoveAll(tempPath)
|
|
|
|
testFile, err := os.Create(filepath.Join(tempPath, "test.zip"))
|
|
assert.Ok(t, err)
|
|
defer testFile.Close()
|
|
|
|
zw := zip.NewWriter(testFile)
|
|
|
|
// Add some files to the archive.
|
|
files := make([]struct {
|
|
Name, Body string
|
|
}, 2000)
|
|
|
|
for fileNum := range iter.N(2000) {
|
|
files[fileNum] = struct {
|
|
Name, Body string
|
|
}{
|
|
Name: fmt.Sprintf("file-%d.txt", fileNum),
|
|
Body: fmt.Sprintf("This is file number %d\n", fileNum),
|
|
}
|
|
}
|
|
|
|
for _, file := range files {
|
|
f, err := zw.Create(file.Name)
|
|
assert.Ok(t, err)
|
|
|
|
_, err = f.Write([]byte(file.Body))
|
|
assert.Ok(t, err)
|
|
}
|
|
|
|
// Make sure to check the error on Close.
|
|
err = zw.Close()
|
|
assert.Ok(t, err)
|
|
|
|
// Open the zip archive for reading.
|
|
destPath := filepath.Join(tempPath, "out")
|
|
os.MkdirAll(destPath, 0o777)
|
|
err = Unzip(testFile, destPath)
|
|
assert.Ok(t, err)
|
|
}
|
|
|
|
func TestSanitize(t *testing.T) {
|
|
tests := []struct {
|
|
malicious string
|
|
sanitized string
|
|
}{
|
|
{"../../.././etc/passwd", "etc/passwd"},
|
|
{"../../etc/passwd", "etc/passwd"},
|
|
{"./etc/passwd", "etc/passwd"},
|
|
{"./././etc/passwd", "etc/passwd"},
|
|
{"nonexistant/b/../file.txt", "nonexistant/file.txt"},
|
|
{"abc../def", "abc../def"},
|
|
{"a/b/c/../d", "a/b/d"},
|
|
{"a/../../c", "c"},
|
|
{"...../etc/password", "...../etc/password"},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
a := sanitize(test.malicious)
|
|
assert.Equals(t, test.sanitized, a)
|
|
}
|
|
}
|
|
|
|
func calcNumberOfFiles(t *testing.T, searchDir string) int {
|
|
fileList := []string{}
|
|
|
|
err := filepath.Walk(searchDir, func(path string, f os.FileInfo, err error) error {
|
|
if !f.IsDir() {
|
|
fileList = append(fileList, path)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
|
|
return len(fileList)
|
|
}
|