Skip to content

Commit a0f4648

Browse files
committed
Disallow '..' decompression outside of parent directory
1 parent 64040d9 commit a0f4648

File tree

7 files changed

+56
-0
lines changed

7 files changed

+56
-0
lines changed

decompress.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
package getter
22

3+
import (
4+
"strings"
5+
)
6+
37
// Decompressor defines the interface that must be implemented to add
48
// support for decompressing a type.
9+
//
10+
// Important: if you're implementing a decompressor, please use the
11+
// containsDotDot helper in this file to ensure that files can't be
12+
// decompressed outside of the specified directory.
513
type Decompressor interface {
614
// Decompress should decompress src to dst. dir specifies whether dst
715
// is a directory or single file. src is guaranteed to be a single file
@@ -31,3 +39,20 @@ func init() {
3139
"zip": new(ZipDecompressor),
3240
}
3341
}
42+
43+
// containsDotDot checks if the filepath value v contains a ".." entry.
44+
// This will check filepath components by splitting along / or \. This
45+
// function is copied directly from the Go net/http implementation.
46+
func containsDotDot(v string) bool {
47+
if !strings.Contains(v, "..") {
48+
return false
49+
}
50+
for _, ent := range strings.FieldsFunc(v, isSlashRune) {
51+
if ent == ".." {
52+
return true
53+
}
54+
}
55+
return false
56+
}
57+
58+
func isSlashRune(r rune) bool { return r == '/' || r == '\\' }

decompress_tar.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ func untar(input io.Reader, dst, src string, dir bool) error {
3535

3636
path := dst
3737
if dir {
38+
// Disallow parent traversal
39+
if containsDotDot(hdr.Name) {
40+
return fmt.Errorf("entry contains '..': %s", hdr.Name)
41+
}
42+
3843
path = filepath.Join(path, hdr.Name)
3944
}
4045

decompress_tgz_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,17 @@ func TestTarGzipDecompressor(t *testing.T) {
7474
"",
7575
nil,
7676
},
77+
78+
// Tests that a tar.gz can't contain references with "..".
79+
// GNU `tar` also disallows this.
80+
{
81+
"outside_parent.tar.gz",
82+
true,
83+
true,
84+
nil,
85+
"",
86+
nil,
87+
},
7788
}
7889

7990
for i, tc := range cases {

decompress_zip.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ func (d *ZipDecompressor) Decompress(dst, src string, dir bool) error {
4242
for _, f := range zipR.File {
4343
path := dst
4444
if dir {
45+
// Disallow parent traversal
46+
if containsDotDot(f.Name) {
47+
return fmt.Errorf("entry contains '..': %s", f.Name)
48+
}
49+
4550
path = filepath.Join(path, f.Name)
4651
}
4752

decompress_zip_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ func TestZipDecompressor(t *testing.T) {
7878
"",
7979
nil,
8080
},
81+
82+
// Tests that a zip can't contain references with "..".
83+
{
84+
"outside_parent.zip",
85+
true,
86+
true,
87+
nil,
88+
"",
89+
nil,
90+
},
8191
}
8292

8393
for i, tc := range cases {
192 Bytes
Binary file not shown.
237 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)