fix: sanitize archive entry paths before extraction to prevent path traversal

Move path sanitization before directory handling in extractFile so
directory entries with "../" prefixes are rejected the same way file
entries already are. Add the same sanitization to extractSevenZipMultiPart
and both zip extraction loops in the Windows updater, which had no path
validation at all.
This commit is contained in:
sysy committed 2026-09-16 23:31:35 -04:00
1 parent 7c1447f659
commit 022fbf633b
3 files changed
+20 -14

No files matched your search

+10 -4
View File
@@ -37,15 +37,17 @@ func installByInstaller(killSignalChan chan<- any, packagePath, destDir string)
} }
defer reader.Close() defer reader.Close()
// Find the installer file
var installerPath string var installerPath string
for _, file := range reader.File { for _, file := range reader.File {
cleanName := filepath.Clean(file.Name)
if strings.HasPrefix(cleanName, "..") || filepath.IsAbs(cleanName) {
continue
}
if file.FileInfo().IsDir() { if file.FileInfo().IsDir() {
continue continue
} }
// Extract file path := filepath.Join(tempDir, cleanName)
path := filepath.Join(tempDir, file.Name)
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
return err return err
@@ -101,7 +103,11 @@ func installByPortable(killSignalChan chan<- any, packagePath, destDir string) e
defer reader.Close() defer reader.Close()
for _, file := range reader.File { for _, file := range reader.File {
path := filepath.Join(destDir, file.Name) cleanName := filepath.Clean(file.Name)
if strings.HasPrefix(cleanName, "..") || filepath.IsAbs(cleanName) {
continue
}
path := filepath.Join(destDir, cleanName)
if file.FileInfo().IsDir() { if file.FileInfo().IsDir() {
os.MkdirAll(path, file.Mode()) os.MkdirAll(path, file.Mode())
+4 -8
View File
@@ -282,19 +282,15 @@ func countArchiveFiles(archivePath string, password string) (int, error) {
// extractFile handles extracting a single file from an archive // extractFile handles extracting a single file from an archive
func extractFile(ctx context.Context, fileInfo archives.FileInfo, destDir string) error { func extractFile(ctx context.Context, fileInfo archives.FileInfo, destDir string) error {
// Skip directories, they will be created when extracting files
if fileInfo.IsDir() {
destPath := filepath.Join(destDir, fileInfo.NameInArchive)
return os.MkdirAll(destPath, fileInfo.Mode())
}
// Sanitize the path to prevent path traversal attacks
cleanPath := filepath.Clean(fileInfo.NameInArchive) cleanPath := filepath.Clean(fileInfo.NameInArchive)
if strings.HasPrefix(cleanPath, "..") || filepath.IsAbs(cleanPath) { if strings.HasPrefix(cleanPath, "..") || filepath.IsAbs(cleanPath) {
// Skip files with suspicious paths
return nil return nil
} }
if fileInfo.IsDir() {
return os.MkdirAll(filepath.Join(destDir, cleanPath), fileInfo.Mode())
}
destPath := filepath.Join(destDir, cleanPath) destPath := filepath.Join(destDir, cleanPath)
// Create parent directories // Create parent directories
+6 -2
View File
@@ -4,6 +4,7 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/bodgit/sevenzip" "github.com/bodgit/sevenzip"
) )
@@ -43,7 +44,11 @@ func extractSevenZipMultiPart(firstPartPath string, destDir string, password str
// Extract files with progress tracking // Extract files with progress tracking
extractedFiles := 0 extractedFiles := 0
for _, f := range reader.File { for _, f := range reader.File {
destPath := filepath.Join(destDir, f.Name) cleanName := filepath.Clean(f.Name)
if strings.HasPrefix(cleanName, "..") || filepath.IsAbs(cleanName) {
continue
}
destPath := filepath.Join(destDir, cleanName)
if f.FileInfo().IsDir() { if f.FileInfo().IsDir() {
if err := os.MkdirAll(destPath, f.Mode()); err != nil { if err := os.MkdirAll(destPath, f.Mode()); err != nil {
@@ -52,7 +57,6 @@ func extractSevenZipMultiPart(firstPartPath string, destDir string, password str
continue continue
} }
// Ensure parent directory exists
if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil { if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
return err return err
} }