Skip to content

Commit 5c56cd9

Browse files
redbeamopenshift-merge-bot[bot]
authored andcommitted
use safe functions to join file paths and open files
Use safe functions to join file paths and open files in places reported by Snyk. `BuildPathChecked` checks for the ZipSlip and path traversal vulnerabilities, while files are now opened using `os.OpenInRoot`.
1 parent 154ea6e commit 5c56cd9

File tree

5 files changed

+45
-21
lines changed

5 files changed

+45
-21
lines changed

pkg/crc/machine/bundle/repository.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"io"
78
"os"
89
"path/filepath"
910
"runtime"
@@ -36,10 +37,14 @@ func (repo *Repository) Get(bundleName string) (*CrcBundleInfo, error) {
3637
if _, err := os.Stat(path); err != nil {
3738
return nil, errors.Wrapf(err, "could not find cached bundle info in %s", path)
3839
}
39-
jsonFilepath := filepath.Join(path, metadataFilename)
40-
content, err := os.ReadFile(filepath.Clean(jsonFilepath))
40+
metadataFile, err := os.OpenInRoot(path, metadataFilename)
4141
if err != nil {
42-
return nil, errors.Wrapf(err, "error reading %s file", jsonFilepath)
42+
return nil, errors.Wrapf(err, "error opening metadata file")
43+
}
44+
defer metadataFile.Close()
45+
content, err := io.ReadAll(metadataFile)
46+
if err != nil {
47+
return nil, errors.Wrapf(err, "error reading %s file", filepath.Join(path, metadataFilename))
4348
}
4449
var bundleInfo CrcBundleInfo
4550
if err := json.Unmarshal(content, &bundleInfo); err != nil {

pkg/extract/extract.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func untar(ctx context.Context, reader io.Reader, targetDir string, fileFilter f
107107
}
108108

109109
// the target location where the dir/file should be created
110-
path, err := buildPath(targetDir, header.Name)
110+
path, err := BuildPathChecked(targetDir, header.Name)
111111
if err != nil {
112112
return nil, err
113113
}
@@ -120,13 +120,13 @@ func untar(ctx context.Context, reader io.Reader, targetDir string, fileFilter f
120120
// check the file type
121121
switch header.Typeflag {
122122

123-
// if its a dir and it doesn't exist create it
123+
// if it's a dir, and it doesn't exist, create it
124124
case tar.TypeDir:
125125
if err := os.MkdirAll(path, header.FileInfo().Mode()); err != nil { // nolint:gosec // G703: paths from header.FileInfo()
126126
return nil, err
127127
}
128128

129-
// if it's a file create it
129+
// if it's a file, create it
130130
case tar.TypeReg, tar.TypeGNUSparse:
131131
// tar.Next() will externally only iterate files, so we might have to create intermediate directories here
132132
if err := uncompressFile(ctx, tarReader, header.FileInfo(), path, showProgress); err != nil {
@@ -159,17 +159,19 @@ func uncompressFile(ctx context.Context, tarReader io.Reader, fileInfo os.FileIn
159159
return file.Close()
160160
}
161161

162-
func buildPath(baseDir, filename string) (string, error) {
162+
// BuildPathChecked joins filename to baseDir and checks for ZipSlip and path traversal vulnerabilities.
163+
// More info: https://snyk.io/research/zip-slip-vulnerability, https://learn.snyk.io/lesson/directory-traversal/?ecosystem=golang
164+
func BuildPathChecked(baseDir, filename string) (string, error) {
163165
path := filepath.Join(baseDir, filename) // #nosec G305
164-
165-
// Check for ZipSlip. More Info: https://snyk.io/research/zip-slip-vulnerability
166166
baseDir = filepath.Clean(baseDir)
167-
if path != baseDir && !strings.HasPrefix(path, baseDir+string(os.PathSeparator)) {
168-
return "", fmt.Errorf("%s: illegal file path (expected prefix: %s)", path, baseDir+string(os.PathSeparator))
167+
// clean prefix to ensure it isn't "//" when baseDir is "/"
168+
prefix := filepath.Clean(baseDir + string(os.PathSeparator))
169+
if path != baseDir && !strings.HasPrefix(path, prefix) {
170+
return "", fmt.Errorf("%s: illegal file path (expected prefix: %s)", path, prefix)
169171
}
170-
171172
return path, nil
172173
}
174+
173175
func unzip(ctx context.Context, archive, target string, fileFilter func(string) bool, showProgress bool) ([]string, error) {
174176
var extractedFiles []string
175177
reader, err := zip.OpenReader(archive)
@@ -182,7 +184,7 @@ func unzip(ctx context.Context, archive, target string, fileFilter func(string)
182184
}
183185

184186
for _, file := range reader.File {
185-
path, err := buildPath(target, file.Name)
187+
path, err := BuildPathChecked(target, file.Name)
186188
if err != nil {
187189
return nil, err
188190
}

pkg/extract/extract_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ func TestZipSlip(t *testing.T) {
6565
assert.ErrorContains(t, err, "illegal file path")
6666
}
6767

68+
func TestBuildPathChecked(t *testing.T) {
69+
baseDir := "/tmp/testDir"
70+
validFilename := "file.txt"
71+
invalidFilename := "../../etc/passwd"
72+
73+
joinedValid, err := BuildPathChecked(baseDir, validFilename)
74+
assert.NoError(t, err)
75+
assert.Equal(t, filepath.Join(baseDir, validFilename), joinedValid)
76+
77+
_, err = BuildPathChecked(baseDir, invalidFilename)
78+
assert.ErrorContains(t, err, "illegal file path")
79+
}
80+
6881
func copyFileMap(orig fileMap) fileMap {
6982
copiedMap := fileMap{}
7083
for key, value := range orig {

test/extended/util/prepare.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,14 @@ func CleanTestRunDir() error {
9696
return err
9797
}
9898

99+
rootDir, err := os.OpenRoot(TestRunDir)
100+
if err != nil {
101+
return err
102+
}
103+
defer rootDir.Close()
104+
99105
for _, file := range files {
100-
err := os.RemoveAll(filepath.Clean(filepath.Join(TestRunDir, file.Name())))
106+
err = rootDir.RemoveAll(file.Name())
101107
if err != nil {
102108
return err
103109
}

test/extended/util/util.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,9 @@ func CopyResourcesFromPath(resourcesPath string) error {
6868
}
6969
destLoc, _ := os.Getwd()
7070
for _, file := range files {
71+
fmt.Printf("Copying %s to %s\n", filepath.Join(resourcesPath, file.Name()), destLoc)
7172

72-
sFileName := filepath.Join(resourcesPath, file.Name())
73-
fmt.Printf("Copying %s to %s\n", sFileName, destLoc)
74-
75-
sFile, err := os.Open(filepath.Clean(sFileName))
73+
sFile, err := os.OpenInRoot(resourcesPath, file.Name())
7674
if err != nil {
7775
fmt.Printf("Error occurred opening file: %s", err)
7876
return err
@@ -112,7 +110,7 @@ func DownloadBundle(bundleLocation string, bundleDestination string, bundleName
112110

113111
if bundleLocation[:4] != "http" {
114112

115-
// copy the file locall
113+
// copy the file locally
116114

117115
if bundleDestination == "." {
118116
bundleDestination, _ = os.Getwd()
@@ -242,11 +240,11 @@ func SendCommandToVM(cmd string) (string, error) {
242240
if err != nil {
243241
return "", err
244242
}
245-
ssh, err := ssh.NewClient(connectionDetails.SSHUsername, connectionDetails.IP, connectionDetails.SSHPort, connectionDetails.SSHKeys...)
243+
sshClient, err := ssh.NewClient(connectionDetails.SSHUsername, connectionDetails.IP, connectionDetails.SSHPort, connectionDetails.SSHKeys...)
246244
if err != nil {
247245
return "", err
248246
}
249-
out, _, err := ssh.Run(cmd)
247+
out, _, err := sshClient.Run(cmd)
250248
if err != nil {
251249
return "", err
252250
}

0 commit comments

Comments
 (0)