siyuan-note/siyuan · critical
marketplace package contains an invalid path
Error message
marketplace package contains an invalid path
What it means
During extraction, each zip entry name is normalized and validated: it must be non-empty, must not be absolute (leading '/'), and the joined destination path must remain a sub-path of the extraction directory (blocks '../' escapes and absolute or drive-qualified targets). Any violation throws this error — this is the Zip Slip defense.
Solutions
- Do not install the archive; treat it as malicious or corrupt
- Rebuild the zip with relative paths only (zip from inside the package directory)
- Inspect entry names (unzip -l) for leading '/' or '..' before re-uploading
- Repackage using a standard tool from the package root
Example fix
// before: entry name "/etc/passwd" or "../../evil.sh" // after: cd my-plugin && zip -r ../my-plugin.zip . # yields relative entries like "plugin.json"
Defensive patterns
Strategy: validation
Validate before calling
const names = execSync(`zipinfo -1 ${zipPath}`).toString().split("\n").filter(Boolean);
const evil = names.some(n => n.startsWith("/") || n.includes("..") || /^[A-Za-z]:/.test(n));
if (evil) throw new Error("archive contains unsafe entry paths"); Try / catch
try { await installLocalPackage(zipPath); } catch (e) { if (String(e).includes("invalid path")) { /* reject the package; do not retry */ } else throw e; } Prevention
- Zip with relative paths from inside the package directory
- Scan entry names for leading '/', '..', or drive letters before installing
- Never attempt to bypass this check — it protects the filesystem (Zip Slip)
When it happens
Trigger: extractLocalPackageItem encounters an entry named "", one starting with "/", or one whose resolved path (via ../ or absolute name) escapes the destination directory.
Common situations: Malicious package crafted to overwrite files outside the temp dir (Zip Slip); zip built on Windows with absolute paths; zip built by a buggy tool that includes leading slashes; symlink-style path tricks in entry names.
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
- archive entry resolves outside destination
- archive entry escapes destination
- asset escapes its directory
- asset path escapes data directory
- asset path escapes data directory
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/b6533392a2ebc9a1.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/bazaar/local.go:132
}
}
if err = os.MkdirAll(destination, 0755); err != nil {
return err
}
var extractedTotal uint64
for _, item := range reader.File {
if err = extractLocalPackageItem(item, destination, &extractedTotal); err != nil {
return err
}
}
return nil
}
func extractLocalPackageItem(item *zip.File, destination string, extractedTotal *uint64) error {
name := strings.ReplaceAll(item.Name, "\\", "/")
if name == "" || strings.HasPrefix(name, "/") {
return errors.New("marketplace package contains an invalid path")
}
destinationPath := filepath.Join(destination, filepath.FromSlash(name))
if !gulu.File.IsSubPath(destination, destinationPath) {
return errors.New("marketplace package contains an invalid path")
}
mode := item.Mode()
if mode&os.ModeSymlink != 0 || (!mode.IsRegular() && !mode.IsDir()) {
return errors.New("marketplace package contains an unsupported file")
}
if mode.IsDir() {
return os.MkdirAll(destinationPath, 0755)
}
if err := os.MkdirAll(filepath.Dir(destinationPath), 0755); err != nil {
return err
}
source, err := item.Open()View on GitHub (pinned to 9f775e8a12)