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>
This commit is contained in:
hshpy
2025-09-15 13:25:21 +08:00
committed by GitHub
parent 61a8ed515f
commit c1d03c5bcc
7 changed files with 77 additions and 37 deletions

View File

@ -1,10 +1,11 @@
package archives
import (
"fmt"
"io"
"io/fs"
"os"
stdpath "path"
"path/filepath"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/archive/tool"
@ -107,7 +108,7 @@ func (Archives) Decompress(ss []*stream.SeekableStream, outputPath string, args
}
if stat.IsDir() {
isDir = true
outputPath = stdpath.Join(outputPath, stat.Name())
outputPath = filepath.Join(outputPath, stat.Name())
err = os.Mkdir(outputPath, 0700)
if err != nil {
return filterPassword(err)
@ -120,11 +121,14 @@ func (Archives) Decompress(ss []*stream.SeekableStream, outputPath string, args
return err
}
relPath := strings.TrimPrefix(p, path+"/")
dstPath := stdpath.Join(outputPath, relPath)
dstPath := filepath.Join(outputPath, relPath)
if !strings.HasPrefix(dstPath, outputPath+string(os.PathSeparator)) {
return fmt.Errorf("illegal file path: %s", relPath)
}
if d.IsDir() {
err = os.MkdirAll(dstPath, 0700)
} else {
dir := stdpath.Dir(dstPath)
dir := filepath.Dir(dstPath)
err = decompress(fsys, p, dir, func(_ float64) {})
}
return err

View File

@ -1,10 +1,11 @@
package archives
import (
"fmt"
"io"
fs2 "io/fs"
"os"
stdpath "path"
"path/filepath"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
@ -69,7 +70,11 @@ func decompress(fsys fs2.FS, filePath, targetPath string, up model.UpdateProgres
if err != nil {
return err
}
f, err := os.OpenFile(stdpath.Join(targetPath, stat.Name()), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
destPath := filepath.Join(targetPath, stat.Name())
if !strings.HasPrefix(destPath, targetPath+string(os.PathSeparator)) {
return fmt.Errorf("illegal file path: %s", stat.Name())
}
f, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
if err != nil {
return err
}

View File

@ -1,9 +1,11 @@
package iso9660
import (
"fmt"
"io"
"os"
stdpath "path"
"path/filepath"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/archive/tool"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
@ -79,7 +81,11 @@ func (ISO9660) Decompress(ss []*stream.SeekableStream, outputPath string, args m
}
if obj.IsDir() {
if args.InnerPath != "/" {
outputPath = stdpath.Join(outputPath, obj.Name())
rootpath := outputPath
outputPath = filepath.Join(outputPath, obj.Name())
if !strings.HasPrefix(outputPath, rootpath+string(os.PathSeparator)) {
return fmt.Errorf("illegal file path: %s", obj.Name())
}
if err = os.MkdirAll(outputPath, 0700); err != nil {
return err
}

View File

@ -1,8 +1,9 @@
package iso9660
import (
"fmt"
"os"
stdpath "path"
"path/filepath"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
@ -62,7 +63,11 @@ func toModelObj(file *iso9660.File) model.Obj {
}
func decompress(f *iso9660.File, path string, up model.UpdateProgress) error {
file, err := os.OpenFile(stdpath.Join(path, f.Name()), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
destPath := filepath.Join(path, f.Name())
if !strings.HasPrefix(destPath, path+string(os.PathSeparator)) {
return fmt.Errorf("illegal file path: %s", f.Name())
}
file, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
if err != nil {
return err
}
@ -84,7 +89,10 @@ func decompressAll(children []*iso9660.File, path string) error {
if err != nil {
return err
}
nextPath := stdpath.Join(path, child.Name())
nextPath := filepath.Join(path, child.Name())
if !strings.HasPrefix(nextPath, path+string(os.PathSeparator)) {
return fmt.Errorf("illegal file path: %s", child.Name())
}
if err = os.MkdirAll(nextPath, 0700); err != nil {
return err
}

View File

@ -3,7 +3,7 @@ package rardecode
import (
"io"
"os"
stdpath "path"
"path/filepath"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/archive/tool"
@ -93,7 +93,7 @@ func (RarDecoder) Decompress(ss []*stream.SeekableStream, outputPath string, arg
}
} else {
innerPath := strings.TrimPrefix(args.InnerPath, "/")
innerBase := stdpath.Base(innerPath)
innerBase := filepath.Base(innerPath)
createdBaseDir := false
for {
var header *rardecode.FileHeader
@ -115,7 +115,7 @@ func (RarDecoder) Decompress(ss []*stream.SeekableStream, outputPath string, arg
}
break
} else if strings.HasPrefix(name, innerPath+"/") {
targetPath := stdpath.Join(outputPath, innerBase)
targetPath := filepath.Join(outputPath, innerBase)
if !createdBaseDir {
err = os.Mkdir(targetPath, 0700)
if err != nil {

View File

@ -5,7 +5,7 @@ import (
"io"
"io/fs"
"os"
stdpath "path"
"path/filepath"
"sort"
"strings"
"time"
@ -124,7 +124,7 @@ type WrapFileInfo struct {
}
func (f *WrapFileInfo) Name() string {
return stdpath.Base(f.File.Name)
return filepath.Base(f.File.Name)
}
func (f *WrapFileInfo) Size() int64 {
@ -183,12 +183,16 @@ func getReader(ss []*stream.SeekableStream, password string) (*rardecode.Reader,
func decompress(reader *rardecode.Reader, header *rardecode.FileHeader, filePath, outputPath string) error {
targetPath := outputPath
dir, base := stdpath.Split(filePath)
dir, base := filepath.Split(filePath)
if dir != "" {
targetPath = stdpath.Join(targetPath, dir)
err := os.MkdirAll(targetPath, 0700)
if err != nil {
return err
targetPath = filepath.Join(targetPath, dir)
if strings.HasPrefix(targetPath, outputPath+string(os.PathSeparator)) {
err := os.MkdirAll(targetPath, 0700)
if err != nil {
return err
}
} else {
targetPath = outputPath
}
}
if base != "" {
@ -201,7 +205,11 @@ func decompress(reader *rardecode.Reader, header *rardecode.FileHeader, filePath
}
func _decompress(reader *rardecode.Reader, header *rardecode.FileHeader, targetPath string, up model.UpdateProgress) error {
f, err := os.OpenFile(stdpath.Join(targetPath, stdpath.Base(header.Name)), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
destPath := filepath.Join(targetPath, filepath.Base(header.Name))
if !strings.HasPrefix(destPath, targetPath+string(os.PathSeparator)) {
return fmt.Errorf("illegal file path: %s", filepath.Base(header.Name))
}
f, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
if err != nil {
return err
}

View File

@ -1,10 +1,11 @@
package tool
import (
"fmt"
"io"
"io/fs"
"os"
stdpath "path"
"path/filepath"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/model"
@ -40,13 +41,13 @@ func GenerateMetaTreeFromFolderTraversal(r ArchiveReader) (bool, []model.ObjTree
isNewFolder := false
if !file.FileInfo().IsDir() {
// 先将 文件 添加到 所在的文件夹
dir = stdpath.Dir(name)
dir = filepath.Dir(name)
dirObj = dirMap[dir]
if dirObj == nil {
isNewFolder = dir != "."
dirObj = &model.ObjectTree{}
dirObj.IsFolder = true
dirObj.Name = stdpath.Base(dir)
dirObj.Name = filepath.Base(dir)
dirObj.Modified = file.FileInfo().ModTime()
dirMap[dir] = dirObj
}
@ -64,28 +65,28 @@ func GenerateMetaTreeFromFolderTraversal(r ArchiveReader) (bool, []model.ObjTree
dirMap[dir] = dirObj
}
dirObj.IsFolder = true
dirObj.Name = stdpath.Base(dir)
dirObj.Name = filepath.Base(dir)
dirObj.Modified = file.FileInfo().ModTime()
}
if isNewFolder {
// 将 文件夹 添加到 父文件夹
// 考虑压缩包仅记录文件的路径,不记录文件夹
// 循环创建所有父文件夹
parentDir := stdpath.Dir(dir)
parentDir := filepath.Dir(dir)
for {
parentDirObj := dirMap[parentDir]
if parentDirObj == nil {
parentDirObj = &model.ObjectTree{}
if parentDir != "." {
parentDirObj.IsFolder = true
parentDirObj.Name = stdpath.Base(parentDir)
parentDirObj.Name = filepath.Base(parentDir)
parentDirObj.Modified = file.FileInfo().ModTime()
}
dirMap[parentDir] = parentDirObj
}
parentDirObj.Children = append(parentDirObj.Children, dirObj)
parentDir = stdpath.Dir(parentDir)
parentDir = filepath.Dir(parentDir)
if dirMap[parentDir] != nil {
break
}
@ -127,7 +128,7 @@ func DecompressFromFolderTraversal(r ArchiveReader, outputPath string, args mode
}
} else {
innerPath := strings.TrimPrefix(args.InnerPath, "/")
innerBase := stdpath.Base(innerPath)
innerBase := filepath.Base(innerPath)
createdBaseDir := false
for _, file := range files {
name := file.Name()
@ -138,7 +139,7 @@ func DecompressFromFolderTraversal(r ArchiveReader, outputPath string, args mode
}
break
} else if strings.HasPrefix(name, innerPath+"/") {
targetPath := stdpath.Join(outputPath, innerBase)
targetPath := filepath.Join(outputPath, innerBase)
if !createdBaseDir {
err = os.Mkdir(targetPath, 0700)
if err != nil {
@ -159,12 +160,16 @@ func DecompressFromFolderTraversal(r ArchiveReader, outputPath string, args mode
func decompress(file SubFile, filePath, outputPath, password string) error {
targetPath := outputPath
dir, base := stdpath.Split(filePath)
dir, base := filepath.Split(filePath)
if dir != "" {
targetPath = stdpath.Join(targetPath, dir)
err := os.MkdirAll(targetPath, 0700)
if err != nil {
return err
targetPath = filepath.Join(targetPath, dir)
if strings.HasPrefix(targetPath, outputPath+string(os.PathSeparator)) {
err := os.MkdirAll(targetPath, 0700)
if err != nil {
return err
}
} else {
targetPath = outputPath
}
}
if base != "" {
@ -185,7 +190,11 @@ func _decompress(file SubFile, targetPath, password string, up model.UpdateProgr
return err
}
defer func() { _ = rc.Close() }()
f, err := os.OpenFile(stdpath.Join(targetPath, file.FileInfo().Name()), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
destPath := filepath.Join(targetPath, file.FileInfo().Name())
if !strings.HasPrefix(destPath, targetPath+string(os.PathSeparator)) {
return fmt.Errorf("illegal file path: %s", file.FileInfo().Name())
}
f, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
if err != nil {
return err
}