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

  1. Do not install the archive; treat it as malicious or corrupt
  2. Rebuild the zip with relative paths only (zip from inside the package directory)
  3. Inspect entry names (unzip -l) for leading '/' or '..' before re-uploading
  4. 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

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


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)