fix(restore): reject zip entries with path traversal sequences

Validate all zip entry names during restore to reject entries
containing directory traversal sequences (e.g. ../../../pwned.txt).
This prevents a Zip Slip attack where a malicious archive could
write files outside the intended extraction directory.
This commit is contained in:
kolaente
2026-02-24 20:07:18 +01:00
parent b6155d525c
commit 12dca5f0b0

View File

@@ -26,6 +26,7 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
@@ -71,6 +72,10 @@ func Restore(filename string, overrideConfig bool) error {
dbfiles := make(map[string]*zip.File)
filesFiles := make(map[string]*zip.File)
for _, file := range r.File {
if containsPathTraversal(file.Name) {
return fmt.Errorf("unsafe path in zip archive: %q", file.Name)
}
if strings.HasPrefix(file.Name, "config") {
configFile = file
continue
@@ -427,6 +432,17 @@ func restoreConfig(configFile, dotEnvFile *zip.File) error {
return nil
}
// containsPathTraversal checks if a zip entry name contains directory traversal
// sequences that could be used to write files outside the intended directory.
func containsPathTraversal(name string) bool {
// Clean the path and check for traversal
cleanPath := filepath.ToSlash(filepath.Clean(name))
return strings.HasPrefix(cleanPath, "../") ||
strings.Contains(cleanPath, "/../") ||
cleanPath == ".." ||
strings.HasPrefix(name, "/")
}
func checkVikunjaVersion(versionFile *zip.File) error {
if versionFile == nil {
return fmt.Errorf("dump does not contain VERSION file, refusing to continue")