{"record":{"id":"b6533392a2ebc9a1","repo":"siyuan-note/siyuan","slug":"marketplace-package-contains-an-invalid-path","errorCode":null,"errorMessage":"marketplace package contains an invalid path","messagePattern":"marketplace package contains an invalid path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/bazaar/local.go","lineNumber":132,"sourceCode":"\t\t}\n\t}\n\n\tif err = os.MkdirAll(destination, 0755); err != nil {\n\t\treturn err\n\t}\n\tvar extractedTotal uint64\n\tfor _, item := range reader.File {\n\t\tif err = extractLocalPackageItem(item, destination, &extractedTotal); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc extractLocalPackageItem(item *zip.File, destination string, extractedTotal *uint64) error {\n\tname := strings.ReplaceAll(item.Name, \"\\\\\", \"/\")\n\tif name == \"\" || strings.HasPrefix(name, \"/\") {\n\t\treturn errors.New(\"marketplace package contains an invalid path\")\n\t}\n\tdestinationPath := filepath.Join(destination, filepath.FromSlash(name))\n\tif !gulu.File.IsSubPath(destination, destinationPath) {\n\t\treturn errors.New(\"marketplace package contains an invalid path\")\n\t}\n\n\tmode := item.Mode()\n\tif mode&os.ModeSymlink != 0 || (!mode.IsRegular() && !mode.IsDir()) {\n\t\treturn errors.New(\"marketplace package contains an unsupported file\")\n\t}\n\tif mode.IsDir() {\n\t\treturn os.MkdirAll(destinationPath, 0755)\n\t}\n\tif err := os.MkdirAll(filepath.Dir(destinationPath), 0755); err != nil {\n\t\treturn err\n\t}\n\n\tsource, err := item.Open()","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/bazaar/local.go#L114-L150","documentation":"A zip entry's name is empty or begins with a forward slash (absolute path). Thrown by extractLocalPackageItem (kernel/bazaar/local.go:130-132) after normalising backslashes to slashes. Absolute or empty entry names are rejected outright because they cannot map cleanly into the destination tree.","triggerScenarios":"ExtractLocalPackage decompresses an archive that contains an entry whose Name is \"\" or starts with \"/\". This typically comes from zipping with absolute paths or from a corrupt/crafted central directory.","commonSituations":"The author zipped files with absolute paths (e.g. `zip -r pkg.zip /home/user/...`), producing leading-slash entry names; a tool emitted a malformed entry.","solutions":["Re-create the archive using relative paths (cd into the package dir before zipping)","Verify entry names with `unzip -l <path>` — none should start with /","If generating the zip programmatically, set zip.FileHeader.Name to a relative path"],"exampleFix":"# before: absolute paths leak into entry names\nzip -r pkg.zip /home/user/myplugin\n\n# after: zip from inside the package directory so names are relative\ncd /home/user/myplugin && zip -r ../pkg.zip .","handlingStrategy":"validation","validationCode":"func assertEntryNamesRelative(path string) error {\n    r, err := zip.OpenReader(path)\n    if err != nil { return err }\n    defer r.Close()\n    for _, f := range r.File {\n        n := strings.ReplaceAll(f.Name, \"\\\\\", \"/\")\n        if n == \"\" || strings.HasPrefix(n, \"/\") {\n            return fmt.Errorf(\"entry name invalid: %q\", f.Name)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err != nil { return fmt.Errorf(\"package layout invalid: %w\", err) }","preventionTips":["Always zip from inside the package directory so entry names are relative","Inspect `unzip -l` output and reject any entry starting with /","Set zip.FileHeader.Name to a relative path when generating archives in code"],"tags":["zip","archive","bazaar","path-traversal","security"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}