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

View File

@ -1,10 +1,11 @@
package archives package archives
import ( import (
"fmt"
"io" "io"
fs2 "io/fs" fs2 "io/fs"
"os" "os"
stdpath "path" "path/filepath"
"strings" "strings"
"github.com/OpenListTeam/OpenList/v4/internal/errs" "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 { if err != nil {
return err 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 { if err != nil {
return err return err
} }

View File

@ -1,9 +1,11 @@
package iso9660 package iso9660
import ( import (
"fmt"
"io" "io"
"os" "os"
stdpath "path" "path/filepath"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/archive/tool" "github.com/OpenListTeam/OpenList/v4/internal/archive/tool"
"github.com/OpenListTeam/OpenList/v4/internal/errs" "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 obj.IsDir() {
if args.InnerPath != "/" { 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 { if err = os.MkdirAll(outputPath, 0700); err != nil {
return err return err
} }

View File

@ -1,8 +1,9 @@
package iso9660 package iso9660
import ( import (
"fmt"
"os" "os"
stdpath "path" "path/filepath"
"strings" "strings"
"github.com/OpenListTeam/OpenList/v4/internal/errs" "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 { 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 { if err != nil {
return err return err
} }
@ -84,7 +89,10 @@ func decompressAll(children []*iso9660.File, path string) error {
if err != nil { if err != nil {
return err 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 { if err = os.MkdirAll(nextPath, 0700); err != nil {
return err return err
} }

View File

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

View File

@ -5,7 +5,7 @@ import (
"io" "io"
"io/fs" "io/fs"
"os" "os"
stdpath "path" "path/filepath"
"sort" "sort"
"strings" "strings"
"time" "time"
@ -124,7 +124,7 @@ type WrapFileInfo struct {
} }
func (f *WrapFileInfo) Name() string { func (f *WrapFileInfo) Name() string {
return stdpath.Base(f.File.Name) return filepath.Base(f.File.Name)
} }
func (f *WrapFileInfo) Size() int64 { func (f *WrapFileInfo) Size() int64 {
@ -183,13 +183,17 @@ func getReader(ss []*stream.SeekableStream, password string) (*rardecode.Reader,
func decompress(reader *rardecode.Reader, header *rardecode.FileHeader, filePath, outputPath string) error { func decompress(reader *rardecode.Reader, header *rardecode.FileHeader, filePath, outputPath string) error {
targetPath := outputPath targetPath := outputPath
dir, base := stdpath.Split(filePath) dir, base := filepath.Split(filePath)
if dir != "" { if dir != "" {
targetPath = stdpath.Join(targetPath, dir) targetPath = filepath.Join(targetPath, dir)
if strings.HasPrefix(targetPath, outputPath+string(os.PathSeparator)) {
err := os.MkdirAll(targetPath, 0700) err := os.MkdirAll(targetPath, 0700)
if err != nil { if err != nil {
return err return err
} }
} else {
targetPath = outputPath
}
} }
if base != "" { if base != "" {
err := _decompress(reader, header, targetPath, func(_ float64) {}) err := _decompress(reader, header, targetPath, func(_ float64) {})
@ -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 { 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 { if err != nil {
return err return err
} }

View File

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