Skip to content

Commit b94ad8f

Browse files
committed
Fix path traversal
1 parent 6c24c49 commit b94ad8f

File tree

2 files changed

+137
-2
lines changed

2 files changed

+137
-2
lines changed

policies/recipes/steps.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,46 @@ func zipIsDir(name string) bool {
127127
return strings.HasSuffix(name, "/")
128128
}
129129

130+
func ensureFilePathBelongsToDir(dirPath string, filePath string) error {
131+
dirAbs, err := filepath.Abs(dirPath)
132+
if err != nil {
133+
return err
134+
}
135+
fileAbs, err := filepath.Abs(filePath)
136+
if err != nil {
137+
return err
138+
}
139+
140+
rel, err := filepath.Rel(dirAbs, fileAbs)
141+
if err != nil {
142+
return err
143+
}
144+
145+
if strings.HasPrefix(rel, "..") {
146+
return fmt.Errorf("path %s, does not belongs to dir %s, rel %s", filePath, dirPath, rel)
147+
}
148+
149+
return nil
150+
}
151+
130152
func extractZip(zipPath string, dst string) error {
131153
zr, err := zip.OpenReader(zipPath)
132154
if err != nil {
133155
return err
134156
}
135157
defer zr.Close()
136158

137-
// Check for conflicts
159+
// Check that we can extract zip
138160
for _, f := range zr.File {
139161
filen, err := util.NormPath(util.SanitizePath(filepath.Join(dst, f.Name)))
140162
if err != nil {
141163
return err
142164
}
165+
166+
if err := ensureFilePathBelongsToDir(dst, filen); err != nil {
167+
return fmt.Errorf("unable to extract zip arhive %s: %w", zipPath, err)
168+
}
169+
143170
stat, err := os.Stat(filen)
144171
if os.IsNotExist(err) {
145172
continue
@@ -151,7 +178,7 @@ func extractZip(zipPath string, dst string) error {
151178
// it's ok if directories already exist
152179
continue
153180
}
154-
return fmt.Errorf("file exists: %s", filen)
181+
return fmt.Errorf("unable to extract zip archive %s: file %s is already exists", zipPath, filen)
155182
}
156183

157184
// Create files.
@@ -240,6 +267,11 @@ func checkForConflicts(tr *tar.Reader, dst string) error {
240267
if err != nil {
241268
return err
242269
}
270+
271+
if err := ensureFilePathBelongsToDir(dst, filen); err != nil {
272+
return err
273+
}
274+
243275
stat, err := os.Stat(filen)
244276
if os.IsNotExist(err) {
245277
continue
@@ -293,6 +325,7 @@ func extractTar(ctx context.Context, tarName string, dst string, archiveType age
293325
if err != nil {
294326
return err
295327
}
328+
296329
filedir := filepath.Dir(filen)
297330

298331
if err := os.MkdirAll(filedir, 0700); err != nil {

policies/recipes/steps_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package recipes
2+
3+
import (
4+
"archive/zip"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"testing"
9+
"time"
10+
11+
"github.com/GoogleCloudPlatform/osconfig/util"
12+
)
13+
14+
type fileEntry struct {
15+
name string
16+
content []byte
17+
}
18+
19+
func Test_extractZip(t *testing.T) {
20+
tests := []struct {
21+
name string
22+
entries []fileEntry
23+
wantErr error
24+
}{
25+
{
26+
name: "base case scenario",
27+
entries: []fileEntry{
28+
{
29+
name: "test1", content: []byte("test1"),
30+
},
31+
{
32+
name: "test2", content: []byte("test2"),
33+
},
34+
},
35+
wantErr: nil,
36+
},
37+
{
38+
name: "base case scenario",
39+
entries: []fileEntry{
40+
{
41+
name: "../test1", content: []byte("test1"),
42+
},
43+
{
44+
name: "test2", content: []byte("test2"),
45+
},
46+
},
47+
wantErr: nil,
48+
},
49+
}
50+
51+
for _, tt := range tests {
52+
tmpDir, tmpFile, err := getTempDirAndFile(t, "extractZip")
53+
if err != nil {
54+
t.Errorf("unable to create tmp file: %s", err)
55+
}
56+
57+
ensureZipArchive(t, tmpFile.Name(), tt.entries)
58+
59+
if err := extractZip(tmpFile.Name(), tmpDir); err != tt.wantErr {
60+
t.Errorf("unexpected error for extract zip, want: %s, got: %s", tt.wantErr, err)
61+
}
62+
}
63+
}
64+
65+
func getTempDirAndFile(t *testing.T, filePrefix string) (dir string, file *os.File, err error) {
66+
tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().UnixNano()))
67+
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
68+
t.Errorf("unable to create tmp dir: %s", err)
69+
return "", nil, err
70+
}
71+
72+
tmpFile, err := util.TempFile(tmpDir, filePrefix, os.ModePerm)
73+
if err != nil {
74+
t.Errorf("unable to create tmp file: %s", err)
75+
return "", nil, err
76+
}
77+
78+
return tmpDir, tmpFile, nil
79+
}
80+
81+
func ensureZipArchive(t testing.T, dst string, entries []fileEntry) {
82+
fd, err := os.OpenFile(dst, os.O_RDWR, os.ModePerm)
83+
if err != nil {
84+
t.Errorf("unable to open file: %s", err)
85+
}
86+
w := zip.NewWriter(fd)
87+
88+
for _, entry := range entries {
89+
f, err := w.Create(entry.name)
90+
if err != nil {
91+
t.Errorf("unable to create file: %s", err)
92+
}
93+
94+
if _, err = f.Write(entry.content); err != nil {
95+
t.Errorf("unable to write content to file: %s", err)
96+
}
97+
}
98+
99+
if err := w.Close(); err != err {
100+
t.Errorf("unable to close file: %s", err)
101+
}
102+
}

0 commit comments

Comments
 (0)