Skip to content

Commit 4c73aa6

Browse files
use SafeJoin function inside SavefileonTempDir
1 parent 716e7aa commit 4c73aa6

File tree

2 files changed

+72
-3
lines changed

2 files changed

+72
-3
lines changed

utilities/utilities.go

+24-3
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ import (
1919
"archive/zip"
2020
"bytes"
2121
"errors"
22+
"fmt"
2223
"io"
2324
"os"
2425
"os/exec"
2526
"path"
2627
"path/filepath"
28+
"strings"
2729
)
2830

2931
// SaveFileonTempDir creates a temp directory and saves the file data as the
@@ -32,15 +34,21 @@ import (
3234
// Returns an error if the filename doesn't form a valid path.
3335
//
3436
// Note that path could be defined and still there could be an error.
35-
func SaveFileonTempDir(filename string, data io.Reader) (path string, err error) {
36-
// Create Temp Directory
37+
func SaveFileonTempDir(filename string, data io.Reader) (string, error) {
3738
tmpdir, err := os.MkdirTemp("", "arduino-create-agent")
3839
if err != nil {
3940
return "", errors.New("Could not create temp directory to store downloaded file. Do you have permissions?")
4041
}
42+
return saveFileonTempDir(tmpdir, filename, data)
43+
}
4144

45+
func saveFileonTempDir(tmpDir, filename string, data io.Reader) (string, error) {
46+
path, err := SafeJoin(tmpDir, filename)
47+
if err != nil {
48+
return "", err
49+
}
4250
// Determine filename
43-
filename, err = filepath.Abs(tmpdir + "/" + filename)
51+
filename, err = filepath.Abs(path)
4452
if err != nil {
4553
return "", err
4654
}
@@ -141,3 +149,16 @@ func Unzip(zippath string, destination string) (err error) {
141149
}
142150
return
143151
}
152+
153+
// SafeJoin performs a filepath.Join of 'parent' and 'subdir' but returns an error
154+
// if the resulting path points outside of 'parent'.
155+
func SafeJoin(parent, subdir string) (string, error) {
156+
res := filepath.Join(parent, subdir)
157+
if !strings.HasSuffix(parent, string(os.PathSeparator)) {
158+
parent += string(os.PathSeparator)
159+
}
160+
if !strings.HasPrefix(res, parent) {
161+
return res, fmt.Errorf("unsafe path join: '%s' with '%s'", parent, subdir)
162+
}
163+
return res, nil
164+
}

utilities/utilities_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package utilities
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"path/filepath"
7+
"runtime"
8+
"testing"
9+
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestSaveFileonTemp(t *testing.T) {
14+
filename := "file"
15+
tmpDir := t.TempDir()
16+
17+
path, err := saveFileonTempDir(tmpDir, filename, bytes.NewBufferString("TEST"))
18+
require.NoError(t, err)
19+
require.Equal(t, filepath.Join(tmpDir, filename), path)
20+
}
21+
22+
func TestSaveFileonTempDirWithEvilName(t *testing.T) {
23+
evilFileNames := []string{
24+
"/",
25+
"..",
26+
"../",
27+
"../evil.txt",
28+
"../../../../../../../../../../../../../../../../../../../../tmp/evil.txt",
29+
"some/path/../../../../../../../../../../../../../../../../../../../../tmp/evil.txt",
30+
"/../../../../../../../../../../../../../../../../../../../../tmp/evil.txt",
31+
"/some/path/../../../../../../../../../../../../../../../../../../../../tmp/evil.txt",
32+
}
33+
if runtime.GOOS == "windows" {
34+
evilFileNames = []string{
35+
"..\\",
36+
"..\\evil.txt",
37+
"..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\tmp\\evil.txt",
38+
"some\\path\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\tmp\\evil.txt",
39+
"\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\tmp\\evil.txt",
40+
"\\some\\path\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\tmp\\evil.txt",
41+
}
42+
}
43+
for _, evilFileName := range evilFileNames {
44+
_, err := saveFileonTempDir(t.TempDir(), evilFileName, bytes.NewBufferString("TEST"))
45+
require.Error(t, err, fmt.Sprintf("with filename: '%s'", evilFileName))
46+
require.ErrorContains(t, err, "unsafe path join")
47+
}
48+
}

0 commit comments

Comments
 (0)