{"record":{"id":"8fe56e15bbde11d8","repo":"siyuan-note/siyuan","slug":"archive-entry-resolves-outside-destination-s","errorCode":null,"errorMessage":"archive entry resolves outside destination [%s]","messagePattern":"archive entry resolves outside destination \\[(.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/api/archive.go","lineNumber":287,"sourceCode":"\treturn nil\n}\n\nfunc validateArchiveEntryPath(destination, entryPath string) error {\n\trel, err := filepath.Rel(destination, entryPath)\n\tif err != nil || !filepath.IsLocal(rel) {\n\t\treturn fmt.Errorf(\"invalid archive entry path [%s]\", entryPath)\n\t}\n\tresolved, err := resolveArchivePath(entryPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tresolvedDestination, err := resolveArchivePath(destination)\n\tif err != nil {\n\t\treturn err\n\t}\n\trel, err = filepath.Rel(resolvedDestination, resolved)\n\tif err != nil || !filepath.IsLocal(rel) {\n\t\treturn fmt.Errorf(\"archive entry resolves outside destination [%s]\", entryPath)\n\t}\n\treturn rejectEncryptedArchivePath(entryPath)\n}\n\nfunc extractWorkspaceArchiveEntry(entry *archivezip.File, destination string) error {\n\tif entry.FileInfo().IsDir() {\n\t\treturn os.MkdirAll(destination, 0755)\n\t}\n\tif err := os.MkdirAll(filepath.Dir(destination), 0755); err != nil {\n\t\treturn err\n\t}\n\tsource, err := entry.Open()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer source.Close()\n\ttarget, err := os.Create(destination)\n\tif err != nil {","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/api/archive.go#L269-L305","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmkdir dest && ln -s /tmp/leak dest/out  // archive entry \"out/x.txt\" resolves outside dest\n\n// after\nmkdir dest && mkdir dest/out  # real directory, no symlink","handlingStrategy":"validation","validationCode":"resolvedEntry, _ := filepath.EvalSymlinks(entryPath)\nresolvedDest, _ := filepath.EvalSymlinks(destination)\nrel, err := filepath.Rel(resolvedDest, resolvedEntry)\nif err != nil || !filepath.IsLocal(rel) {\n    return fmt.Errorf(\"entry would resolve outside destination\")\n}","typeGuard":null,"tryCatchPattern":"if err := unzipArchive(f, dest); err != nil && strings.Contains(err.Error(), \"resolves outside destination\") {\n    log.Warn(\"symlink component redirected an entry outside the destination; strip symlinks and retry\")\n}","preventionTips":["Keep destination directories free of symlinks","Create fresh temp directories for extraction","Re-zip archives containing only regular files and directories"],"tags":["archive","security","symlink","path-validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}