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

  1. Rebuild the zip with relative entry paths from a working directory (cd into the folder, then zip)
  2. Inspect offenders first: unzip -l artifact.zip | grep '^/'
  3. 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

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


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/6dc26bfb0d7221e8. Report an issue: GitHub.