router-for-me/CLIProxyAPI · error
zip entry %s 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.
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)
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 %s escapes archive root
- zip entry has empty name
- zip entry %s uses backslash path separators
- invalid name
- invalid auth file name
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/6dc26bfb0d7221e8.
Report an issue: GitHub.