mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2025-09-19 12:16:24 +08:00
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:
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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,12 +183,16 @@ 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)
|
||||||
err := os.MkdirAll(targetPath, 0700)
|
if strings.HasPrefix(targetPath, outputPath+string(os.PathSeparator)) {
|
||||||
if err != nil {
|
err := os.MkdirAll(targetPath, 0700)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetPath = outputPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if base != "" {
|
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 {
|
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
|
||||||
}
|
}
|
||||||
|
@ -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,12 +160,16 @@ 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)
|
||||||
err := os.MkdirAll(targetPath, 0700)
|
if strings.HasPrefix(targetPath, outputPath+string(os.PathSeparator)) {
|
||||||
if err != nil {
|
err := os.MkdirAll(targetPath, 0700)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetPath = outputPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if base != "" {
|
if base != "" {
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user