Skip to content

Commit 07cd749

Browse files
authored
Merge pull request cli#5225 from cli/extract-to-root
run download: fix extracting to root path
2 parents 3f5311e + a315e68 commit 07cd749

File tree

2 files changed

+130
-7
lines changed

2 files changed

+130
-7
lines changed

pkg/cmd/run/download/zip.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,9 @@ const (
1616
)
1717

1818
func extractZip(zr *zip.Reader, destDir string) error {
19-
destDirAbs, err := filepath.Abs(destDir)
20-
if err != nil {
21-
return err
22-
}
23-
pathPrefix := destDirAbs + string(filepath.Separator)
2419
for _, zf := range zr.File {
25-
fpath := filepath.Join(destDirAbs, filepath.FromSlash(zf.Name))
26-
if !strings.HasPrefix(fpath, pathPrefix) {
20+
fpath := filepath.Join(destDir, filepath.FromSlash(zf.Name))
21+
if !filepathDescendsFrom(fpath, destDir) {
2722
continue
2823
}
2924
if err := extractZipFile(zf, fpath); err != nil {
@@ -67,3 +62,15 @@ func getPerm(m os.FileMode) os.FileMode {
6762
}
6863
return execMode
6964
}
65+
66+
func filepathDescendsFrom(p, dir string) bool {
67+
p = filepath.Clean(p)
68+
dir = filepath.Clean(dir)
69+
if dir == "." && !filepath.IsAbs(p) {
70+
return !strings.HasPrefix(p, ".."+string(filepath.Separator))
71+
}
72+
if !strings.HasSuffix(dir, string(filepath.Separator)) {
73+
dir += string(filepath.Separator)
74+
}
75+
return strings.HasPrefix(p, dir)
76+
}

pkg/cmd/run/download/zip_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,119 @@ func Test_extractZip(t *testing.T) {
3030
_, err = os.Stat(filepath.Join("src", "main.go"))
3131
require.NoError(t, err)
3232
}
33+
34+
func Test_filepathDescendsFrom(t *testing.T) {
35+
type args struct {
36+
p string
37+
dir string
38+
}
39+
tests := []struct {
40+
name string
41+
args args
42+
want bool
43+
}{
44+
{
45+
name: "root child",
46+
args: args{
47+
p: filepath.FromSlash("/hoi.txt"),
48+
dir: filepath.FromSlash("/"),
49+
},
50+
want: true,
51+
},
52+
{
53+
name: "abs descendant",
54+
args: args{
55+
p: filepath.FromSlash("/var/logs/hoi.txt"),
56+
dir: filepath.FromSlash("/"),
57+
},
58+
want: true,
59+
},
60+
{
61+
name: "abs trailing slash",
62+
args: args{
63+
p: filepath.FromSlash("/var/logs/hoi.txt"),
64+
dir: filepath.FromSlash("/var/logs/"),
65+
},
66+
want: true,
67+
},
68+
{
69+
name: "abs mismatch",
70+
args: args{
71+
p: filepath.FromSlash("/var/logs/hoi.txt"),
72+
dir: filepath.FromSlash("/var/pids"),
73+
},
74+
want: false,
75+
},
76+
{
77+
name: "abs partial prefix",
78+
args: args{
79+
p: filepath.FromSlash("/var/logs/hoi.txt"),
80+
dir: filepath.FromSlash("/var/log"),
81+
},
82+
want: false,
83+
},
84+
{
85+
name: "rel child",
86+
args: args{
87+
p: filepath.FromSlash("hoi.txt"),
88+
dir: filepath.FromSlash("."),
89+
},
90+
want: true,
91+
},
92+
{
93+
name: "rel descendant",
94+
args: args{
95+
p: filepath.FromSlash("./log/hoi.txt"),
96+
dir: filepath.FromSlash("."),
97+
},
98+
want: true,
99+
},
100+
{
101+
name: "mixed rel styles",
102+
args: args{
103+
p: filepath.FromSlash("./log/hoi.txt"),
104+
dir: filepath.FromSlash("log"),
105+
},
106+
want: true,
107+
},
108+
{
109+
name: "rel clean",
110+
args: args{
111+
p: filepath.FromSlash("cats/../dogs/pug.txt"),
112+
dir: filepath.FromSlash("dogs"),
113+
},
114+
want: true,
115+
},
116+
{
117+
name: "rel mismatch",
118+
args: args{
119+
p: filepath.FromSlash("dogs/pug.txt"),
120+
dir: filepath.FromSlash("dog"),
121+
},
122+
want: false,
123+
},
124+
{
125+
name: "rel breakout",
126+
args: args{
127+
p: filepath.FromSlash("../escape.txt"),
128+
dir: filepath.FromSlash("."),
129+
},
130+
want: false,
131+
},
132+
{
133+
name: "rel sneaky breakout",
134+
args: args{
135+
p: filepath.FromSlash("dogs/../../escape.txt"),
136+
dir: filepath.FromSlash("dogs"),
137+
},
138+
want: false,
139+
},
140+
}
141+
for _, tt := range tests {
142+
t.Run(tt.name, func(t *testing.T) {
143+
if got := filepathDescendsFrom(tt.args.p, tt.args.dir); got != tt.want {
144+
t.Errorf("filepathDescendsFrom() = %v, want %v", got, tt.want)
145+
}
146+
})
147+
}
148+
}

0 commit comments

Comments
 (0)