Skip to content

Commit c1d03c5

Browse files
hshpyILoveScratch2
andauthored
fix(security): zip slip (#1228)
* fix(security): Zip Slip * chore:remove repeat clean * fix: archives,iso9660 and rardecode module --------- Co-authored-by: ILoveScratch <ilovescratch@foxmail.com>
1 parent 61a8ed5 commit c1d03c5

File tree

7 files changed

+77
-37
lines changed

7 files changed

+77
-37
lines changed

internal/archive/archives/archives.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package archives
22

33
import (
4+
"fmt"
45
"io"
56
"io/fs"
67
"os"
7-
stdpath "path"
8+
"path/filepath"
89
"strings"
910

1011
"github.com/OpenListTeam/OpenList/v4/internal/archive/tool"
@@ -107,7 +108,7 @@ func (Archives) Decompress(ss []*stream.SeekableStream, outputPath string, args
107108
}
108109
if stat.IsDir() {
109110
isDir = true
110-
outputPath = stdpath.Join(outputPath, stat.Name())
111+
outputPath = filepath.Join(outputPath, stat.Name())
111112
err = os.Mkdir(outputPath, 0700)
112113
if err != nil {
113114
return filterPassword(err)
@@ -120,11 +121,14 @@ func (Archives) Decompress(ss []*stream.SeekableStream, outputPath string, args
120121
return err
121122
}
122123
relPath := strings.TrimPrefix(p, path+"/")
123-
dstPath := stdpath.Join(outputPath, relPath)
124+
dstPath := filepath.Join(outputPath, relPath)
125+
if !strings.HasPrefix(dstPath, outputPath+string(os.PathSeparator)) {
126+
return fmt.Errorf("illegal file path: %s", relPath)
127+
}
124128
if d.IsDir() {
125129
err = os.MkdirAll(dstPath, 0700)
126130
} else {
127-
dir := stdpath.Dir(dstPath)
131+
dir := filepath.Dir(dstPath)
128132
err = decompress(fsys, p, dir, func(_ float64) {})
129133
}
130134
return err

internal/archive/archives/utils.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package archives
22

33
import (
4+
"fmt"
45
"io"
56
fs2 "io/fs"
67
"os"
7-
stdpath "path"
8+
"path/filepath"
89
"strings"
910

1011
"github.com/OpenListTeam/OpenList/v4/internal/errs"
@@ -69,7 +70,11 @@ func decompress(fsys fs2.FS, filePath, targetPath string, up model.UpdateProgres
6970
if err != nil {
7071
return err
7172
}
72-
f, err := os.OpenFile(stdpath.Join(targetPath, stat.Name()), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
73+
destPath := filepath.Join(targetPath, stat.Name())
74+
if !strings.HasPrefix(destPath, targetPath+string(os.PathSeparator)) {
75+
return fmt.Errorf("illegal file path: %s", stat.Name())
76+
}
77+
f, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
7378
if err != nil {
7479
return err
7580
}

internal/archive/iso9660/iso9660.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package iso9660
22

33
import (
4+
"fmt"
45
"io"
56
"os"
6-
stdpath "path"
7+
"path/filepath"
8+
"strings"
79

810
"github.com/OpenListTeam/OpenList/v4/internal/archive/tool"
911
"github.com/OpenListTeam/OpenList/v4/internal/errs"
@@ -79,7 +81,11 @@ func (ISO9660) Decompress(ss []*stream.SeekableStream, outputPath string, args m
7981
}
8082
if obj.IsDir() {
8183
if args.InnerPath != "/" {
82-
outputPath = stdpath.Join(outputPath, obj.Name())
84+
rootpath := outputPath
85+
outputPath = filepath.Join(outputPath, obj.Name())
86+
if !strings.HasPrefix(outputPath, rootpath+string(os.PathSeparator)) {
87+
return fmt.Errorf("illegal file path: %s", obj.Name())
88+
}
8389
if err = os.MkdirAll(outputPath, 0700); err != nil {
8490
return err
8591
}

internal/archive/iso9660/utils.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package iso9660
22

33
import (
4+
"fmt"
45
"os"
5-
stdpath "path"
6+
"path/filepath"
67
"strings"
78

89
"github.com/OpenListTeam/OpenList/v4/internal/errs"
@@ -62,7 +63,11 @@ func toModelObj(file *iso9660.File) model.Obj {
6263
}
6364

6465
func decompress(f *iso9660.File, path string, up model.UpdateProgress) error {
65-
file, err := os.OpenFile(stdpath.Join(path, f.Name()), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
66+
destPath := filepath.Join(path, f.Name())
67+
if !strings.HasPrefix(destPath, path+string(os.PathSeparator)) {
68+
return fmt.Errorf("illegal file path: %s", f.Name())
69+
}
70+
file, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
6671
if err != nil {
6772
return err
6873
}
@@ -84,7 +89,10 @@ func decompressAll(children []*iso9660.File, path string) error {
8489
if err != nil {
8590
return err
8691
}
87-
nextPath := stdpath.Join(path, child.Name())
92+
nextPath := filepath.Join(path, child.Name())
93+
if !strings.HasPrefix(nextPath, path+string(os.PathSeparator)) {
94+
return fmt.Errorf("illegal file path: %s", child.Name())
95+
}
8896
if err = os.MkdirAll(nextPath, 0700); err != nil {
8997
return err
9098
}

internal/archive/rardecode/rardecode.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package rardecode
33
import (
44
"io"
55
"os"
6-
stdpath "path"
6+
"path/filepath"
77
"strings"
88

99
"github.com/OpenListTeam/OpenList/v4/internal/archive/tool"
@@ -93,7 +93,7 @@ func (RarDecoder) Decompress(ss []*stream.SeekableStream, outputPath string, arg
9393
}
9494
} else {
9595
innerPath := strings.TrimPrefix(args.InnerPath, "/")
96-
innerBase := stdpath.Base(innerPath)
96+
innerBase := filepath.Base(innerPath)
9797
createdBaseDir := false
9898
for {
9999
var header *rardecode.FileHeader
@@ -115,7 +115,7 @@ func (RarDecoder) Decompress(ss []*stream.SeekableStream, outputPath string, arg
115115
}
116116
break
117117
} else if strings.HasPrefix(name, innerPath+"/") {
118-
targetPath := stdpath.Join(outputPath, innerBase)
118+
targetPath := filepath.Join(outputPath, innerBase)
119119
if !createdBaseDir {
120120
err = os.Mkdir(targetPath, 0700)
121121
if err != nil {

internal/archive/rardecode/utils.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"io"
66
"io/fs"
77
"os"
8-
stdpath "path"
8+
"path/filepath"
99
"sort"
1010
"strings"
1111
"time"
@@ -124,7 +124,7 @@ type WrapFileInfo struct {
124124
}
125125

126126
func (f *WrapFileInfo) Name() string {
127-
return stdpath.Base(f.File.Name)
127+
return filepath.Base(f.File.Name)
128128
}
129129

130130
func (f *WrapFileInfo) Size() int64 {
@@ -183,12 +183,16 @@ func getReader(ss []*stream.SeekableStream, password string) (*rardecode.Reader,
183183

184184
func decompress(reader *rardecode.Reader, header *rardecode.FileHeader, filePath, outputPath string) error {
185185
targetPath := outputPath
186-
dir, base := stdpath.Split(filePath)
186+
dir, base := filepath.Split(filePath)
187187
if dir != "" {
188-
targetPath = stdpath.Join(targetPath, dir)
189-
err := os.MkdirAll(targetPath, 0700)
190-
if err != nil {
191-
return err
188+
targetPath = filepath.Join(targetPath, dir)
189+
if strings.HasPrefix(targetPath, outputPath+string(os.PathSeparator)) {
190+
err := os.MkdirAll(targetPath, 0700)
191+
if err != nil {
192+
return err
193+
}
194+
} else {
195+
targetPath = outputPath
192196
}
193197
}
194198
if base != "" {
@@ -201,7 +205,11 @@ func decompress(reader *rardecode.Reader, header *rardecode.FileHeader, filePath
201205
}
202206

203207
func _decompress(reader *rardecode.Reader, header *rardecode.FileHeader, targetPath string, up model.UpdateProgress) error {
204-
f, err := os.OpenFile(stdpath.Join(targetPath, stdpath.Base(header.Name)), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
208+
destPath := filepath.Join(targetPath, filepath.Base(header.Name))
209+
if !strings.HasPrefix(destPath, targetPath+string(os.PathSeparator)) {
210+
return fmt.Errorf("illegal file path: %s", filepath.Base(header.Name))
211+
}
212+
f, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
205213
if err != nil {
206214
return err
207215
}

internal/archive/tool/helper.go

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package tool
22

33
import (
4+
"fmt"
45
"io"
56
"io/fs"
67
"os"
7-
stdpath "path"
8+
"path/filepath"
89
"strings"
910

1011
"github.com/OpenListTeam/OpenList/v4/internal/model"
@@ -40,13 +41,13 @@ func GenerateMetaTreeFromFolderTraversal(r ArchiveReader) (bool, []model.ObjTree
4041
isNewFolder := false
4142
if !file.FileInfo().IsDir() {
4243
// 先将 文件 添加到 所在的文件夹
43-
dir = stdpath.Dir(name)
44+
dir = filepath.Dir(name)
4445
dirObj = dirMap[dir]
4546
if dirObj == nil {
4647
isNewFolder = dir != "."
4748
dirObj = &model.ObjectTree{}
4849
dirObj.IsFolder = true
49-
dirObj.Name = stdpath.Base(dir)
50+
dirObj.Name = filepath.Base(dir)
5051
dirObj.Modified = file.FileInfo().ModTime()
5152
dirMap[dir] = dirObj
5253
}
@@ -64,28 +65,28 @@ func GenerateMetaTreeFromFolderTraversal(r ArchiveReader) (bool, []model.ObjTree
6465
dirMap[dir] = dirObj
6566
}
6667
dirObj.IsFolder = true
67-
dirObj.Name = stdpath.Base(dir)
68+
dirObj.Name = filepath.Base(dir)
6869
dirObj.Modified = file.FileInfo().ModTime()
6970
}
7071
if isNewFolder {
7172
// 将 文件夹 添加到 父文件夹
7273
// 考虑压缩包仅记录文件的路径,不记录文件夹
7374
// 循环创建所有父文件夹
74-
parentDir := stdpath.Dir(dir)
75+
parentDir := filepath.Dir(dir)
7576
for {
7677
parentDirObj := dirMap[parentDir]
7778
if parentDirObj == nil {
7879
parentDirObj = &model.ObjectTree{}
7980
if parentDir != "." {
8081
parentDirObj.IsFolder = true
81-
parentDirObj.Name = stdpath.Base(parentDir)
82+
parentDirObj.Name = filepath.Base(parentDir)
8283
parentDirObj.Modified = file.FileInfo().ModTime()
8384
}
8485
dirMap[parentDir] = parentDirObj
8586
}
8687
parentDirObj.Children = append(parentDirObj.Children, dirObj)
8788

88-
parentDir = stdpath.Dir(parentDir)
89+
parentDir = filepath.Dir(parentDir)
8990
if dirMap[parentDir] != nil {
9091
break
9192
}
@@ -127,7 +128,7 @@ func DecompressFromFolderTraversal(r ArchiveReader, outputPath string, args mode
127128
}
128129
} else {
129130
innerPath := strings.TrimPrefix(args.InnerPath, "/")
130-
innerBase := stdpath.Base(innerPath)
131+
innerBase := filepath.Base(innerPath)
131132
createdBaseDir := false
132133
for _, file := range files {
133134
name := file.Name()
@@ -138,7 +139,7 @@ func DecompressFromFolderTraversal(r ArchiveReader, outputPath string, args mode
138139
}
139140
break
140141
} else if strings.HasPrefix(name, innerPath+"/") {
141-
targetPath := stdpath.Join(outputPath, innerBase)
142+
targetPath := filepath.Join(outputPath, innerBase)
142143
if !createdBaseDir {
143144
err = os.Mkdir(targetPath, 0700)
144145
if err != nil {
@@ -159,12 +160,16 @@ func DecompressFromFolderTraversal(r ArchiveReader, outputPath string, args mode
159160

160161
func decompress(file SubFile, filePath, outputPath, password string) error {
161162
targetPath := outputPath
162-
dir, base := stdpath.Split(filePath)
163+
dir, base := filepath.Split(filePath)
163164
if dir != "" {
164-
targetPath = stdpath.Join(targetPath, dir)
165-
err := os.MkdirAll(targetPath, 0700)
166-
if err != nil {
167-
return err
165+
targetPath = filepath.Join(targetPath, dir)
166+
if strings.HasPrefix(targetPath, outputPath+string(os.PathSeparator)) {
167+
err := os.MkdirAll(targetPath, 0700)
168+
if err != nil {
169+
return err
170+
}
171+
} else {
172+
targetPath = outputPath
168173
}
169174
}
170175
if base != "" {
@@ -185,7 +190,11 @@ func _decompress(file SubFile, targetPath, password string, up model.UpdateProgr
185190
return err
186191
}
187192
defer func() { _ = rc.Close() }()
188-
f, err := os.OpenFile(stdpath.Join(targetPath, file.FileInfo().Name()), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
193+
destPath := filepath.Join(targetPath, file.FileInfo().Name())
194+
if !strings.HasPrefix(destPath, targetPath+string(os.PathSeparator)) {
195+
return fmt.Errorf("illegal file path: %s", file.FileInfo().Name())
196+
}
197+
f, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
189198
if err != nil {
190199
return err
191200
}

0 commit comments

Comments
 (0)