siyuan-note/siyuan · error
archive entry resolves outside destination [%s]
Error message
archive entry resolves outside destination [%s]
What it means
The final stage of validateArchiveEntryPath resolves both destination and entry path to their real filesystem locations (following symlinks and Windows junctions on the longest existing parent) and requires the resolved entry to stay local to the resolved destination. If a symlink component redirects the entry outside the destination, extraction is blocked. Afterwards the encrypted-notebook check still runs on the entry path.
Source
Thrown at kernel/api/archive.go:287
return nil
}
func validateArchiveEntryPath(destination, entryPath string) error {
rel, err := filepath.Rel(destination, entryPath)
if err != nil || !filepath.IsLocal(rel) {
return fmt.Errorf("invalid archive entry path [%s]", entryPath)
}
resolved, err := resolveArchivePath(entryPath)
if err != nil {
return err
}
resolvedDestination, err := resolveArchivePath(destination)
if err != nil {
return err
}
rel, err = filepath.Rel(resolvedDestination, resolved)
if err != nil || !filepath.IsLocal(rel) {
return fmt.Errorf("archive entry resolves outside destination [%s]", entryPath)
}
return rejectEncryptedArchivePath(entryPath)
}
func extractWorkspaceArchiveEntry(entry *archivezip.File, destination string) error {
if entry.FileInfo().IsDir() {
return os.MkdirAll(destination, 0755)
}
if err := os.MkdirAll(filepath.Dir(destination), 0755); err != nil {
return err
}
source, err := entry.Open()
if err != nil {
return err
}
defer source.Close()
target, err := os.Create(destination)
if err != nil {View on GitHub (pinned to 8641553a1f)
Solutions
- Remove symlinks inside the destination directory before extraction.
- Extract into a fresh, link-free directory.
- Recreate the archive with only regular files and directories.
Example fix
// before mkdir dest && ln -s /tmp/leak dest/out // archive entry "out/x.txt" resolves outside dest // after mkdir dest && mkdir dest/out # real directory, no symlink
Defensive patterns
Strategy: validation
Validate before calling
resolvedEntry, _ := filepath.EvalSymlinks(entryPath)
resolvedDest, _ := filepath.EvalSymlinks(destination)
rel, err := filepath.Rel(resolvedDest, resolvedEntry)
if err != nil || !filepath.IsLocal(rel) {
return fmt.Errorf("entry would resolve outside destination")
} Try / catch
if err := unzipArchive(f, dest); err != nil && strings.Contains(err.Error(), "resolves outside destination") {
log.Warn("symlink component redirected an entry outside the destination; strip symlinks and retry")
} Prevention
- Keep destination directories free of symlinks
- Create fresh temp directories for extraction
- Re-zip archives containing only regular files and directories
When it happens
Trigger: unzip API on an archive where an entry path contains a symlink directory pointing outside the destination, so resolveArchivePath(entryPath) resolves outside resolveArchivePath(destination). Called for every entry during unzipWorkspaceArchive.
Common situations: Archives paired with pre-planted symlinks in the destination; destination directory itself reached via a link while entries reference sibling links escaping it; extraction tools that resolve parent dirs differently than the kernel.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- symlink resolves into encrypted notebook [%s]
- invalid archive entry path [%s]
- symlink [%s] resolves outside data/assets: [%s]
- Obsidian Vault path is unsafe
- path belongs to encrypted notebook [%s]
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/8fe56e15bbde11d8.
Report an issue: GitHub.