henrygd/beszel · warning

invalid file path: %q

Error message

invalid file path: %q

What it means

archivePath validates the entry name (here used for the downloaded asset filename) with filepath.IsLocal before joining it into destDir, rejecting absolute paths and anything containing traversal elements like "..". This is a path-traversal defense; if the release metadata contains a non-local name, the library refuses to build a destination path.

Source

Thrown at internal/ghupdate/extract.go:83

		outFile, err := os.Create(path)
		if err != nil {
			return err
		}

		if _, err := io.Copy(outFile, tr); err != nil {
			outFile.Close()
			return err
		}
		outFile.Close()
	}

	return nil
}

// archivePath returns a path within destDir, rejecting path traversal entries.
func archivePath(destDir, name string) (string, error) {
	if !filepath.IsLocal(name) {
		return "", fmt.Errorf("invalid file path: %q", name)
	}
	return filepath.Join(destDir, name), nil
}

// extractZip extracts the zip archive at "src" to "dest".
//
// Note that only dirs and regular files will be extracted.
// Symbolic links, named pipes, sockets, or any other irregular files
// are skipped because they come with too many edge cases and ambiguities.
func extractZip(src, dest string) error {
	zr, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer zr.Close()

	for _, f := range zr.File {
		err := extractFile(f, dest)

View on GitHub (pinned to b38fb7dafa)

Solutions

  1. Ensure release assets are named with plain filenames (no slashes, no leading separators).
  2. Only fetch releases from the trusted owner/repo (henrygd/beszel) or a mirror you control; treat this error as a red flag for a compromised source.
  3. If this fires unexpectedly, inspect the release JSON's asset names at the API URL shown in the updater output.

Example fix

// before (malicious asset name in release JSON)
"name": "../../etc/beszel"
// after (valid asset name)
"name": "beszel-agent_0.12.0_linux_amd64.tar.gz"
Defensive patterns

Strategy: validation

Validate before calling

if !filepath.IsLocal(asset.Name) {
    return fmt.Errorf("refusing release with non-local asset name %q — source may be compromised", asset.Name)
}

Try / catch

updated, err := ghupdate.Update(cfg)
if err != nil && strings.Contains(err.Error(), "invalid file path") {
    log.Printf("release contains a path-traversal-looking name (%v); inspect release JSON and consider the source compromised", err)
}

Prevention

When it happens

Trigger: ghupdate.Update -> update -> archivePath when the asset Name from the release JSON is absolute ("/tmp/x"), contains "../", is reserved (".", volume names on Windows), or is otherwise not a local relative path — typically from a hostile or malformed release response.

Common situations: A malicious or spoofed GitHub API response (or compromised mirror) publishing assets with traversal names; test fixtures with unusual names; a fork whose asset names contain directory components.

Related errors


AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31). Data as JSON: /api/errors/0a73b1528d9cb8a9. Report an issue: GitHub.