AlistGo/alist · critical · ErrArchiveIllegalPath
archive entry has illegal path
Error message
archive entry has illegal path
What it means
ErrArchiveIllegalPath is returned by SecureJoin when an archive entry name is unsafe to extract: it contains NUL bytes, starts with '//' (UNC path), is absolute, escapes the base directory via '..' traversal, or uses Windows drive letters. The offending entry name is attached via fmt.Errorf('%w: %s'). This is the zip-slip / path-traversal defense for extraction.
Source
Thrown at internal/archive/tool/securepath.go:13
package tool
import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
)
// ErrArchiveIllegalPath indicates an archive entry path is unsafe for extraction.
var ErrArchiveIllegalPath = errors.New("archive entry has illegal path")
// SecureJoin returns a safe extraction path for an archive entry.
// It rejects absolute paths, traversal, Windows drive/UNC paths, and NUL bytes.
func SecureJoin(baseDir, entryName string) (string, error) {
if strings.Contains(entryName, "\x00") {
return "", fmt.Errorf("%w: %s", ErrArchiveIllegalPath, entryName)
}
normalized := strings.ReplaceAll(entryName, "\\", "/")
if strings.HasPrefix(normalized, "//") {
return "", fmt.Errorf("%w: %s", ErrArchiveIllegalPath, entryName)
}
cleaned := path.Clean(normalized)
if cleaned == "." || cleaned == ".." || strings.HasPrefix(cleaned, "../") {
return "", fmt.Errorf("%w: %s", ErrArchiveIllegalPath, entryName)
}
if strings.HasPrefix(cleaned, "/") {View on GitHub (pinned to 843d9dc814)
Solutions
- Reject the archive if it comes from an untrusted source — illegal paths are a strong maliciousness signal
- If the archive is trusted and paths are merely absolute, re-pack it with relative paths before extraction (e.g. on the machine that created it)
- Never bypass SecureJoin or strip the check to 'make it work'
- Scan archives (e.g. unzip -l, zipinfo) before extracting user-supplied content
Example fix
// before: unsafe join allows escape
full := filepath.Join(destDir, entry.Name)
// after: guarded join
full, err := tool.SecureJoin(destDir, entry.Name)
if err != nil { return err } Defensive patterns
Strategy: type-guard
Validate before calling
for _, name := range entryNames {
if strings.Contains(name, "\x00") || strings.HasPrefix(name, "/") || strings.HasPrefix(name, "\\\\") { return tool.ErrArchiveIllegalPath }
} Type guard
func isIllegalPath(err error) bool { return errors.Is(err, tool.ErrArchiveIllegalPath) } Try / catch
dest, err := tool.SecureJoin(baseDir, entry.Name)
if errors.Is(err, tool.ErrArchiveIllegalPath) {
log.Warnf("skipping hostile entry %q", entry.Name)
continue // skip entry; extraction proceeds
} Prevention
- Never join archive entry names with plain filepath.Join
- Quarantine archives that contain traversal entries
- Skip-and-log hostile entries for availability, but alert on frequency
When it happens
Trigger: Extracting an archive containing entries like '../../etc/passwd', 'C:\Windows\system32\x', '\\server\share\x', '/absolute/path', or names with embedded \x00. Any code path that calls SecureJoin per entry during extraction will refuse these immediately.
Common situations: Malicious uploads crafted for zip-slip; archives created by tools that store absolute paths (some Windows archivers); entries with backslash-separated names on Linux extraction; symlink-heavy archives from unknown sources.
Related errors
- ErrArchiveIllegalPath
- total size of decompressed files exceeds the limit
- ErrArchiveIllegalPath
- ErrArchiveIllegalPath
- ErrArchiveIllegalPath
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/f12692c33bcdb297.
Report an issue: GitHub.