mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-08 01:20:27 +00:00
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.
141 lines
2.9 KiB
Go
141 lines
2.9 KiB
Go
//go:build windows
|
|
|
|
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func install(killSignalChan chan<- any, updateChannel, packagePath, destDir string) (bool, error) {
|
|
switch updateChannel {
|
|
case "windowsInstaller":
|
|
return false, installByInstaller(killSignalChan, packagePath, destDir)
|
|
default:
|
|
return true, installByPortable(killSignalChan, packagePath, destDir)
|
|
}
|
|
}
|
|
|
|
// installByInstaller extracts the installer from the zip file and runs it
|
|
func installByInstaller(killSignalChan chan<- any, packagePath, destDir string) error {
|
|
// Create a temp directory for extraction
|
|
tempDir, err := os.MkdirTemp("", "gopeed_update")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
// Extract the zip file
|
|
reader, err := zip.OpenReader(packagePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer reader.Close()
|
|
|
|
var installerPath string
|
|
for _, file := range reader.File {
|
|
cleanName := filepath.Clean(file.Name)
|
|
if strings.HasPrefix(cleanName, "..") || filepath.IsAbs(cleanName) {
|
|
continue
|
|
}
|
|
if file.FileInfo().IsDir() {
|
|
continue
|
|
}
|
|
|
|
path := filepath.Join(tempDir, cleanName)
|
|
|
|
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
|
return err
|
|
}
|
|
|
|
dstFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
srcFile, err := file.Open()
|
|
if err != nil {
|
|
dstFile.Close()
|
|
return err
|
|
}
|
|
|
|
_, err = io.Copy(dstFile, srcFile)
|
|
srcFile.Close()
|
|
dstFile.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If this is likely an installer (.exe, .msi), save its path
|
|
ext := strings.ToLower(filepath.Ext(file.Name))
|
|
if ext == ".exe" || ext == ".msi" {
|
|
installerPath = path
|
|
}
|
|
}
|
|
|
|
if installerPath == "" {
|
|
return fmt.Errorf("no installer found in the update package")
|
|
}
|
|
|
|
// Run the installer
|
|
cmd := exec.Command(installerPath)
|
|
if err := cmd.Start(); err != nil {
|
|
return err
|
|
}
|
|
|
|
killSignalChan <- nil
|
|
return nil
|
|
}
|
|
|
|
// installByPortable extracts the portable version to the destination directory
|
|
func installByPortable(killSignalChan chan<- any, packagePath, destDir string) error {
|
|
killSignalChan <- nil
|
|
|
|
reader, err := zip.OpenReader(packagePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer reader.Close()
|
|
|
|
for _, file := range reader.File {
|
|
cleanName := filepath.Clean(file.Name)
|
|
if strings.HasPrefix(cleanName, "..") || filepath.IsAbs(cleanName) {
|
|
continue
|
|
}
|
|
path := filepath.Join(destDir, cleanName)
|
|
|
|
if file.FileInfo().IsDir() {
|
|
os.MkdirAll(path, file.Mode())
|
|
continue
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
|
return err
|
|
}
|
|
|
|
dstFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
srcFile, err := file.Open()
|
|
if err != nil {
|
|
dstFile.Close()
|
|
return err
|
|
}
|
|
|
|
_, err = io.Copy(dstFile, srcFile)
|
|
srcFile.Close()
|
|
dstFile.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|