Skip to content

Commit 45794ba

Browse files
committed
chore: unpack ui in a separate temporary directory to avoid malicious compressed packages from polluting workdir
1 parent a934791 commit 45794ba

File tree

1 file changed

+62
-82
lines changed

1 file changed

+62
-82
lines changed

component/updater/update_ui.go

Lines changed: 62 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package updater
33
import (
44
"archive/tar"
55
"archive/zip"
6+
"bytes"
67
"compress/gzip"
78
"fmt"
89
"io"
@@ -32,6 +33,17 @@ const (
3233
typeTarGzip
3334
)
3435

36+
func (t compressionType) String() string {
37+
switch t {
38+
case typeZip:
39+
return "zip"
40+
case typeTarGzip:
41+
return "tar.gz"
42+
default:
43+
return "unknown"
44+
}
45+
}
46+
3547
var DefaultUiUpdater = &UIUpdater{}
3648

3749
func NewUiUpdater(externalUI, externalUIURL, externalUIName string) *UIUpdater {
@@ -99,48 +111,35 @@ func detectFileType(data []byte) compressionType {
99111
}
100112

101113
func (u *UIUpdater) downloadUI() error {
102-
err := u.prepareUIPath()
103-
if err != nil {
104-
return fmt.Errorf("prepare UI path failed: %w", err)
105-
}
106-
107114
data, err := downloadForBytes(u.externalUIURL)
108115
if err != nil {
109116
return fmt.Errorf("can't download file: %w", err)
110117
}
111118

112-
fileType := detectFileType(data)
113-
if fileType == typeUnknown {
114-
return fmt.Errorf("unknown or unsupported file type")
115-
}
116-
117-
ext := ".zip"
118-
if fileType == typeTarGzip {
119-
ext = ".tgz"
120-
}
121-
122-
saved := path.Join(C.Path.HomeDir(), "download"+ext)
123-
log.Debugln("compression Type: %s", ext)
124-
if err = saveFile(data, saved); err != nil {
125-
return fmt.Errorf("can't save compressed file: %w", err)
119+
tmpDir := C.Path.Resolve("downloadUI.tmp")
120+
defer os.RemoveAll(tmpDir)
121+
extractedFolder, err := extract(data, tmpDir)
122+
if err != nil {
123+
return fmt.Errorf("can't extract compressed file: %w", err)
126124
}
127-
defer os.Remove(saved)
128125

129-
err = cleanup(u.externalUIPath)
126+
log.Debugln("cleanupFolder: %s", u.externalUIPath)
127+
err = cleanup(u.externalUIPath) // cleanup files in dir don't remove dir itself
130128
if err != nil {
131129
if !os.IsNotExist(err) {
132130
return fmt.Errorf("cleanup exist file error: %w", err)
133131
}
134132
}
135133

136-
extractedFolder, err := extract(saved, C.Path.HomeDir())
134+
err = u.prepareUIPath()
137135
if err != nil {
138-
return fmt.Errorf("can't extract compressed file: %w", err)
136+
return fmt.Errorf("prepare UI path failed: %w", err)
139137
}
140138

141-
err = os.Rename(extractedFolder, u.externalUIPath)
139+
log.Debugln("moveFolder from %s to %s", extractedFolder, u.externalUIPath)
140+
err = moveDir(extractedFolder, u.externalUIPath) // move files from tmp to target
142141
if err != nil {
143-
return fmt.Errorf("rename UI folder failed: %w", err)
142+
return fmt.Errorf("move UI folder failed: %w", err)
144143
}
145144
return nil
146145
}
@@ -155,12 +154,11 @@ func (u *UIUpdater) prepareUIPath() error {
155154
return nil
156155
}
157156

158-
func unzip(src, dest string) (string, error) {
159-
r, err := zip.OpenReader(src)
157+
func unzip(data []byte, dest string) (string, error) {
158+
r, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
160159
if err != nil {
161160
return "", err
162161
}
163-
defer r.Close()
164162

165163
// check whether or not only exists singleRoot dir
166164
rootDir := ""
@@ -199,17 +197,7 @@ func unzip(src, dest string) (string, error) {
199197
log.Debugln("extractedFolder: %s", extractedFolder)
200198
} else {
201199
log.Debugln("Match the multiRoot")
202-
// or put the files/dirs into new dir
203-
baseName := filepath.Base(src)
204-
baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))
205-
extractedFolder = filepath.Join(dest, baseName)
206-
207-
for i := 1; ; i++ {
208-
if _, err := os.Stat(extractedFolder); os.IsNotExist(err) {
209-
break
210-
}
211-
extractedFolder = filepath.Join(dest, fmt.Sprintf("%s_%d", baseName, i))
212-
}
200+
extractedFolder = dest
213201
log.Debugln("extractedFolder: %s", extractedFolder)
214202
}
215203

@@ -253,14 +241,8 @@ func unzip(src, dest string) (string, error) {
253241
return extractedFolder, nil
254242
}
255243

256-
func untgz(src, dest string) (string, error) {
257-
file, err := os.Open(src)
258-
if err != nil {
259-
return "", err
260-
}
261-
defer file.Close()
262-
263-
gzr, err := gzip.NewReader(file)
244+
func untgz(data []byte, dest string) (string, error) {
245+
gzr, err := gzip.NewReader(bytes.NewReader(data))
264246
if err != nil {
265247
return "", err
266248
}
@@ -303,8 +285,7 @@ func untgz(src, dest string) (string, error) {
303285
isSingleRoot = false
304286
}
305287

306-
file.Seek(0, 0)
307-
gzr, _ = gzip.NewReader(file)
288+
_ = gzr.Reset(bytes.NewReader(data))
308289
tr = tar.NewReader(gzr)
309290

310291
var extractedFolder string
@@ -314,17 +295,7 @@ func untgz(src, dest string) (string, error) {
314295
log.Debugln("extractedFolder: %s", extractedFolder)
315296
} else {
316297
log.Debugln("Match the multiRoot")
317-
baseName := filepath.Base(src)
318-
baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))
319-
baseName = strings.TrimSuffix(baseName, ".tar")
320-
extractedFolder = filepath.Join(dest, baseName)
321-
322-
for i := 1; ; i++ {
323-
if _, err := os.Stat(extractedFolder); os.IsNotExist(err) {
324-
break
325-
}
326-
extractedFolder = filepath.Join(dest, fmt.Sprintf("%s_%d", baseName, i))
327-
}
298+
extractedFolder = dest
328299
log.Debugln("extractedFolder: %s", extractedFolder)
329300
}
330301

@@ -371,16 +342,16 @@ func untgz(src, dest string) (string, error) {
371342
return extractedFolder, nil
372343
}
373344

374-
func extract(src, dest string) (string, error) {
375-
srcLower := strings.ToLower(src)
376-
switch {
377-
case strings.HasSuffix(srcLower, ".tar.gz") ||
378-
strings.HasSuffix(srcLower, ".tgz"):
379-
return untgz(src, dest)
380-
case strings.HasSuffix(srcLower, ".zip"):
381-
return unzip(src, dest)
345+
func extract(data []byte, dest string) (string, error) {
346+
fileType := detectFileType(data)
347+
log.Debugln("compression Type: %s", fileType)
348+
switch fileType {
349+
case typeZip:
350+
return unzip(data, dest)
351+
case typeTarGzip:
352+
return untgz(data, dest)
382353
default:
383-
return "", fmt.Errorf("unsupported file format: %s", src)
354+
return "", fmt.Errorf("unknown or unsupported file type")
384355
}
385356
}
386357

@@ -402,24 +373,33 @@ func cleanTarPath(path string) string {
402373
}
403374

404375
func cleanup(root string) error {
405-
if _, err := os.Stat(root); os.IsNotExist(err) {
406-
return nil
376+
dirEntryList, err := os.ReadDir(root)
377+
if err != nil {
378+
return err
407379
}
408-
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
380+
381+
for _, dirEntry := range dirEntryList {
382+
err = os.RemoveAll(filepath.Join(root, dirEntry.Name()))
409383
if err != nil {
410384
return err
411385
}
412-
if info.IsDir() {
413-
if err := os.RemoveAll(path); err != nil {
414-
return err
415-
}
416-
} else {
417-
if err := os.Remove(path); err != nil {
418-
return err
419-
}
386+
}
387+
return nil
388+
}
389+
390+
func moveDir(src string, dst string) error {
391+
dirEntryList, err := os.ReadDir(src)
392+
if err != nil {
393+
return err
394+
}
395+
396+
for _, dirEntry := range dirEntryList {
397+
err = os.Rename(filepath.Join(src, dirEntry.Name()), filepath.Join(dst, dirEntry.Name()))
398+
if err != nil {
399+
return err
420400
}
421-
return nil
422-
})
401+
}
402+
return nil
423403
}
424404

425405
func inDest(fpath, dest string) bool {

0 commit comments

Comments
 (0)