router-for-me/CLIProxyAPI · error
zip entry is absolute
Error message
zip entry %s is absolute
What it means
Returned by cleanZipName (install.go:382-384) when an entry name is an absolute path — it starts with '/' (on any OS) or a Windows drive letter that path.IsAbs recognizes. Absolute entry names would let an archive claim an arbitrary location outside the install directory, so they are rejected before path.Clean runs.
Solutions
- Rebuild the zip with relative entry paths from a working directory (cd into the folder, then zip)
- Inspect offenders first: unzip -l artifact.zip | grep '^/'
- Treat repeated occurrences from a third-party source as a red flag about that artifact's provenance
Example fix
# before: zipped from / zip plugin.zip /build/out/myplugin.so # entry '/build/out/myplugin.so' # after: zipped from the output dir cd build/out && zip plugin.zip myplugin.so
Defensive patterns
Strategy: validation
Validate before calling
func zipEntriesRelative(archiveData []byte) error {
r, err := zip.NewReader(bytes.NewReader(archiveData), int64(len(archiveData)))
if err != nil { return err }
for _, f := range r.File {
if path.IsAbs(f.Name) || strings.Contains(f.Name, `\`) {
return fmt.Errorf("entry %q is not a safe relative path", f.Name)
}
}
return nil
} Prevention
- Always zip from inside the directory containing the artifact (cd build/out && zip ...)
- Sanitize names when producing archives programmatically
- Reject absolute-entry archives at any ingestion boundary — they are malformed at best, hostile at worst
When it happens
Trigger: InstallArchive on a zip containing entries like '/usr/lib/myplugin.so' or 'C:\\Windows\\myplugin.dll'. Any single such entry fails the entire install during the scan loop at install.go:322-326.
Common situations: Zipping with absolute paths from the command line (zip stores what it is given), hostile archives crafted for zip-slip, or naive archivers that preserve the source path root.
Related errors
- zip entry escapes archive root
- zip entry has empty name
- zip entry uses backslash path separators
- artifacts[ ]: pinned artifact url must not contain…
- artifacts[ ]: pinned artifact url must not contain query or…
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/6dc26bfb0d7221e8.
Report an issue: GitHub.
Appendix: source
Thrown at internal/pluginstore/install.go:383
if mode == 0 {
mode = 0o755
}
return data, mode, nil
}
func versionedPluginFileName(id string, version string, goos string) string {
return strings.TrimSpace(id) + "-v" + normalizeVersion(version) + pluginExtension(goos)
}
func cleanZipName(name string) (string, error) {
if strings.TrimSpace(name) == "" {
return "", fmt.Errorf("zip entry has empty name")
}
if strings.Contains(name, `\`) {
return "", fmt.Errorf("zip entry %s uses backslash path separators", name)
}
if path.IsAbs(name) {
return "", fmt.Errorf("zip entry %s is absolute", name)
}
cleaned := path.Clean(name)
if cleaned == "." || cleaned == ".." || strings.HasPrefix(cleaned, "../") {
return "", fmt.Errorf("zip entry %s escapes archive root", name)
}
return cleaned, nil
}
func regularZipFile(file *zip.File) bool {
mode := file.FileInfo().Mode()
return mode.IsRegular() || mode.Type() == 0
}
func hasDynamicLibraryExtension(name string) bool {
lowerName := strings.ToLower(name)
return strings.HasSuffix(lowerName, ".dylib") || strings.HasSuffix(lowerName, ".so") || strings.HasSuffix(lowerName, ".dll")
}
View on GitHub (pinned to 78f0c4079e)