diff --git a/terraform/terraform/go.mod b/terraform/terraform/go.mod index 56c5fb52bda..449bd84424e 100644 --- a/terraform/terraform/go.mod +++ b/terraform/terraform/go.mod @@ -70,7 +70,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.5.2 // indirect github.com/hashicorp/go-rootcerts v1.0.0 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect - github.com/hashicorp/go-slug v0.4.1 // indirect + github.com/hashicorp/go-slug v0.5.0 // indirect github.com/hashicorp/go-tfe v0.15.0 // indirect github.com/hashicorp/go-uuid v1.0.1 // indirect github.com/hashicorp/go-version v1.2.1 // indirect diff --git a/terraform/terraform/go.sum b/terraform/terraform/go.sum index 140cd9e1e7c..551cdc526e2 100644 --- a/terraform/terraform/go.sum +++ b/terraform/terraform/go.sum @@ -357,8 +357,9 @@ github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82k github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= -github.com/hashicorp/go-slug v0.4.1 h1:/jAo8dNuLgSImoLXaX7Od7QB4TfYCVPam+OpAt5bZqc= github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8= +github.com/hashicorp/go-slug v0.5.0 h1:WWPckGATOqthOtP8CmOKstgdZlZzLM+xOiX94W8NorI= +github.com/hashicorp/go-slug v0.5.0/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8= github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM= github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-tfe v0.15.0 h1:vdnz1NjOhvmap+cj8iPsL8SbS4iFFVuNYFkGpF5SdoA= diff --git a/terraform/terraform/vendor/github.com/hashicorp/go-slug/slug.go b/terraform/terraform/vendor/github.com/hashicorp/go-slug/slug.go index 059d267732f..9491ca5a616 100644 --- a/terraform/terraform/vendor/github.com/hashicorp/go-slug/slug.go +++ b/terraform/terraform/vendor/github.com/hashicorp/go-slug/slug.go @@ -125,7 +125,7 @@ func packWalkFn(root, src, dst string, tarW *tar.Writer, meta *Meta, dereference // If the target is within the current source, we // create the symlink using a relative path. - if strings.Contains(target, src) { + if strings.HasPrefix(target, src) { link, err := filepath.Rel(filepath.Dir(path), target) if err != nil { return fmt.Errorf("Failed to get relative path for symlink destination %q: %v", target, err) @@ -200,8 +200,9 @@ func packWalkFn(root, src, dst string, tarW *tar.Writer, meta *Meta, dereference } } -// Unpack is used to read and extract the contents of a slug to -// the dst directory. Returns any errors. +// Unpack is used to read and extract the contents of a slug to the dst +// directory. Symlinks within the slug are supported, provided their targets +// are relative and point to paths within the destination directory. func Unpack(r io.Reader, dst string) error { // Decompress as we read. uncompressed, err := gzip.NewReader(r) @@ -229,18 +230,72 @@ func Unpack(r io.Reader, dst string) error { } path = filepath.Join(dst, path) + // Check for paths outside our directory, they are forbidden + target := filepath.Clean(path) + if !strings.HasPrefix(target, dst) { + return fmt.Errorf("Invalid filename, traversal with \"..\" outside of current directory") + } + + // Ensure the destination is not through any symlinks. This prevents + // any files from being deployed through symlinks defined in the slug. + // There are malicious cases where this could be used to escape the + // slug's boundaries (zipslip), and any legitimate use is questionable + // and likely indicates a hand-crafted tar file, which we are not in + // the business of supporting here. + // + // The strategy is to Lstat each path component from dst up to the + // immediate parent directory of the file name in the tarball, checking + // the mode on each to ensure we wouldn't be passing through any + // symlinks. + currentPath := dst // Start at the root of the unpacked tarball. + components := strings.Split(header.Name, "/") + + for i := 0; i < len(components)-1; i++ { + currentPath = filepath.Join(currentPath, components[i]) + fi, err := os.Lstat(currentPath) + if os.IsNotExist(err) { + // Parent directory structure is incomplete. Technically this + // means from here upward cannot be a symlink, so we cancel the + // remaining path tests. + break + } + if err != nil { + return fmt.Errorf("Failed to evaluate path %q: %v", header.Name, err) + } + if fi.Mode()&os.ModeSymlink != 0 { + return fmt.Errorf("Cannot extract %q through symlink", + header.Name) + } + } + // Make the directories to the path. dir := filepath.Dir(path) if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("Failed to create directory %q: %v", dir, err) } - // If we have a symlink, just link it. + // Handle symlinks. if header.Typeflag == tar.TypeSymlink { + // Disallow absolute targets. + if filepath.IsAbs(header.Linkname) { + return fmt.Errorf("Invalid symlink (%q -> %q) has absolute target", + header.Name, header.Linkname) + } + + // Ensure the link target is within the destination directory. This + // disallows providing symlinks to external files and directories. + target := filepath.Join(dir, header.Linkname) + if !strings.HasPrefix(target, dst) { + return fmt.Errorf("Invalid symlink (%q -> %q) has external target", + header.Name, header.Linkname) + } + + // Create the symlink. if err := os.Symlink(header.Linkname, path); err != nil { - return fmt.Errorf("Failed creating symlink %q => %q: %v", - path, header.Linkname, err) + return fmt.Errorf("Failed creating symlink (%q -> %q): %v", + header.Name, header.Linkname, err) } + continue } diff --git a/terraform/terraform/vendor/modules.txt b/terraform/terraform/vendor/modules.txt index 05f9a8b3cf4..ce1ee7ef335 100644 --- a/terraform/terraform/vendor/modules.txt +++ b/terraform/terraform/vendor/modules.txt @@ -322,7 +322,7 @@ github.com/hashicorp/go-rootcerts # github.com/hashicorp/go-safetemp v1.0.0 ## explicit github.com/hashicorp/go-safetemp -# github.com/hashicorp/go-slug v0.4.1 +# github.com/hashicorp/go-slug v0.5.0 ## explicit; go 1.12 github.com/hashicorp/go-slug # github.com/hashicorp/go-tfe v0.15.0