diff options
author | Joram Wilander <jwawilander@gmail.com> | 2017-09-01 09:00:27 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-01 09:00:27 -0400 |
commit | 899ab31fff9b34bc125faf75b79a89e390deb2cf (patch) | |
tree | 41dc5832268504e54a0b2188eedcf89b7828dd12 /utils | |
parent | 74b5e52c4eb54000dcb5a7b46c0977d732bce80f (diff) | |
download | chat-899ab31fff9b34bc125faf75b79a89e390deb2cf.tar.gz chat-899ab31fff9b34bc125faf75b79a89e390deb2cf.tar.bz2 chat-899ab31fff9b34bc125faf75b79a89e390deb2cf.zip |
Implement experimental REST API endpoints for plugins (#7279)
* Implement experimental REST API endpoints for plugins
* Updates per feedback and rebase
* Update tests
* Further updates
* Update extraction of plugins
* Use OS temp dir for plugins instead of search path
* Fail extraction on paths that attempt to traverse upward
* Update pluginenv ActivePlugins()
Diffstat (limited to 'utils')
-rw-r--r-- | utils/extract.go | 83 | ||||
-rw-r--r-- | utils/path.go | 15 | ||||
-rw-r--r-- | utils/path_test.go | 31 |
3 files changed, 129 insertions, 0 deletions
diff --git a/utils/extract.go b/utils/extract.go new file mode 100644 index 000000000..0559c6ce8 --- /dev/null +++ b/utils/extract.go @@ -0,0 +1,83 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package utils + +import ( + "archive/tar" + "compress/gzip" + "fmt" + "io" + "os" + "path/filepath" +) + +// ExtractTarGz takes in an io.Reader containing the bytes for a .tar.gz file and +// a destination string to extract to. A list of the file and directory names that +// were extracted is returned. +func ExtractTarGz(gzipStream io.Reader, dst string) ([]string, error) { + uncompressedStream, err := gzip.NewReader(gzipStream) + if err != nil { + return nil, fmt.Errorf("ExtractTarGz: NewReader failed: %s", err.Error()) + } + defer uncompressedStream.Close() + + tarReader := tar.NewReader(uncompressedStream) + + filenames := []string{} + + for true { + header, err := tarReader.Next() + + if err == io.EOF { + break + } + + if err != nil { + return nil, fmt.Errorf("ExtractTarGz: Next() failed: %s", err.Error()) + } + + switch header.Typeflag { + case tar.TypeDir: + if PathTraversesUpward(header.Name) { + return nil, fmt.Errorf("ExtractTarGz: path attempts to traverse upwards") + } + + path := filepath.Join(dst, header.Name) + if err := os.Mkdir(path, 0744); err != nil && !os.IsExist(err) { + return nil, fmt.Errorf("ExtractTarGz: Mkdir() failed: %s", err.Error()) + } + + filenames = append(filenames, header.Name) + case tar.TypeReg: + if PathTraversesUpward(header.Name) { + return nil, fmt.Errorf("ExtractTarGz: path attempts to traverse upwards") + } + + path := filepath.Join(dst, header.Name) + dir := filepath.Dir(path) + + if err := os.MkdirAll(dir, 0744); err != nil { + return nil, fmt.Errorf("ExtractTarGz: MkdirAll() failed: %s", err.Error()) + } + + outFile, err := os.Create(path) + if err != nil { + return nil, fmt.Errorf("ExtractTarGz: Create() failed: %s", err.Error()) + } + defer outFile.Close() + if _, err := io.Copy(outFile, tarReader); err != nil { + return nil, fmt.Errorf("ExtractTarGz: Copy() failed: %s", err.Error()) + } + + filenames = append(filenames, header.Name) + default: + return nil, fmt.Errorf( + "ExtractTarGz: unknown type: %v in %v", + header.Typeflag, + header.Name) + } + } + + return filenames, nil +} diff --git a/utils/path.go b/utils/path.go new file mode 100644 index 000000000..5f921b41d --- /dev/null +++ b/utils/path.go @@ -0,0 +1,15 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package utils + +import ( + "path/filepath" + "strings" +) + +// PathTraversesUpward will return true if the path attempts to traverse upwards by using +// ".." in the path. +func PathTraversesUpward(path string) bool { + return strings.HasPrefix(filepath.Clean(path), "..") +} diff --git a/utils/path_test.go b/utils/path_test.go new file mode 100644 index 000000000..70b7c24fc --- /dev/null +++ b/utils/path_test.go @@ -0,0 +1,31 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPathTraversesUpward(t *testing.T) { + cases := []struct { + input string + expected bool + }{ + {"../test/path", true}, + {"../../test/path", true}, + {"../../test/../path", true}, + {"test/../../path", true}, + {"test/path/../../", false}, + {"test", false}, + {"test/path", false}, + {"test/path/", false}, + {"test/path/file.ext", false}, + } + + for _, c := range cases { + assert.Equal(t, c.expected, PathTraversesUpward(c.input), c.input) + } +} |