helm/helm · error

path contains '..', which is illegal

Error message

path contains '..', which is illegal

What it means

Thrown by cleanJoin (internal/plugin/installer/extractor.go:106) when a tar member name, after backslashes are normalized to '/', contains '..' as a path segment. Parent-directory traversal is the classic tar-slip attack, and Helm's policy is to reject (not clean) any path that looks like an escape attempt from the extraction directory.

Source

Thrown at internal/plugin/installer/extractor.go:106

//   - The path component `..` is considered suspicious, and therefore illegal
//   - The character \ (backslash) is treated as a path separator and is converted to /.
//   - Beginning a path with a path separator is illegal
//   - Rudimentary symlink protections are offered by SecureJoin.
func cleanJoin(root, dest string) (string, error) {
	// On Windows, this is a drive separator. On UNIX-like, this is the path list separator.
	// In neither case do we want to trust a TAR that contains these.
	if strings.Contains(dest, ":") {
		return "", errors.New("path contains ':', which is illegal")
	}

	// The Go tar library does not convert separators for us.
	// We assume here, as we do elsewhere, that `\\` means a Windows path.
	dest = strings.ReplaceAll(dest, "\\", "/")

	// We want to alert the user that something bad was attempted. Cleaning it
	// is not a good practice.
	if slices.Contains(strings.Split(dest, "/"), "..") {
		return "", errors.New("path contains '..', which is illegal")
	}

	// If a path is absolute, the creator of the TAR is doing something shady.
	if path.IsAbs(dest) {
		return "", errors.New("path is absolute, which is illegal")
	}

	// SecureJoin will do some cleaning, as well as some rudimentary checking of symlinks.
	// The directory needs to be cleaned prior to passing to SecureJoin or the location may end up
	// being wrong or returning an error. This was introduced in v0.4.0.
	root = filepath.Clean(root)
	newpath, err := securejoin.SecureJoin(root, dest)
	if err != nil {
		return "", err
	}

	return filepath.ToSlash(newpath), nil
}

View on GitHub (pinned to 2a29f1770b)

Solutions

  1. Repack from inside the plugin directory so member names never traverse upward: 'cd myplugin && tar -czf ../myplugin-1.0.0.tgz .'
  2. Inspect entries first with 'tar -tzf file.tgz' and remove/relocate any entry containing '..' segments
  3. If the archive came from a third party, do not attempt to bypass the check - the tarball is presumed malicious; obtain a clean artifact

Example fix

// before: entry '../../../plugin.yaml' or 'myplugin/../../x' in the archive
tar -tzf bad.tgz   # shows ../.. entries

// after: rebuild with only downward-relative paths
cd myplugin && tar -czf ../myplugin-1.0.0.tgz .
Defensive patterns

Strategy: validation

Validate before calling

func tarHasTraversal(path string) (bool, error) {
	f, err := os.Open(path)
	if err != nil {
		return false, err
	}
	defer f.Close()
	gz, err := gzip.NewReader(f)
	if err != nil {
		return false, err
	}
	tr := tar.NewReader(gz)
	for {
		h, err := tr.Next()
		if err == io.EOF {
			return false, nil
		}
		if err != nil {
			return false, err
		}
		norm := strings.ReplaceAll(h.Name, "\\", "/")
		if slices.Contains(strings.Split(norm, "/"), "..") {
			return true, nil
		}
	}
}

Try / catch

if err := installer.Install(i); err != nil {
	if strings.Contains(err.Error(), "path contains '..'") {
		// tar-slip attempt or bad packaging: reject the artifact entirely
		return err
	}
	return err
}

Prevention

When it happens

Trigger: Installing a plugin archive (HTTP tarball via TarGzExtractor.Extract, local tarball, or OCI extractTar) whose member name contains a '..' segment, e.g. '../../etc/passwd' or 'pkg/../../escape'. A single header triggers the error and aborts the entire install.

Common situations: Hand-packed tarballs that include relative parent references; archives produced by tools that preserve '../../' fragments; malicious third-party plugin archives. Occasionally a repackaging script that walks above its root dir creates such entries accidentally.

Related errors


AI-assisted analysis of helm/helm@2a29f1770b (2026-08-15). Data as JSON: /api/errors/f89adcbf0b66c378. Report an issue: GitHub.