argoproj/argo-workflows · error

illegal symlink target: %s -> %s

Error message

illegal symlink target: %s -> %s

What it means

When a tar entry is a symlink, the executor resolves its target (relative to the link's directory) and verifies the cleaned target stays inside the destination directory. A symlink pointing outside dest would let extraction redirect writes, so it is rejected with 'illegal symlink target'.

Source

Thrown at workflow/executor/executor.go:1153

				return nil
			case err != nil:
				return err
			case header == nil:
				continue
			}
			target := filepath.Join(dest, filepath.Clean(header.Name))
			if !strings.HasPrefix(target, filepath.Clean(dest)+string(os.PathSeparator)) {
				return fmt.Errorf("illegal file path: %s", header.Name)
			}
			switch header.Typeflag {
			case tar.TypeSymlink:
				// Validate symlink target before creating it
				linkTarget := header.Linkname
				if !filepath.IsAbs(linkTarget) {
					linkTarget = filepath.Join(filepath.Dir(target), header.Linkname)
				}
				if !strings.HasPrefix(filepath.Clean(linkTarget), filepath.Clean(dest)+string(os.PathSeparator)) {
					return fmt.Errorf("illegal symlink target: %s -> %s", header.Name, header.Linkname)
				}
				// Create parent directory if needed
				if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
					return err
				}
				err := os.Symlink(header.Linkname, target)
				if err != nil {
					return err
				}
			case tar.TypeDir:
				if err := os.MkdirAll(target, 0o755); err != nil {
					return err
				}
			case tar.TypeReg:
				// Before writing the file, check if the parent directory resolves outside dest
				parentDir := filepath.Dir(target)

				// Resolve the destination directory

View on GitHub (pinned to 35bff19146)

Solutions

  1. Repackage the artifact so symlinks are relative and stay within the archive root
  2. Inspect the tarball (tar -tvf) and remove/fix offending symlinks before uploading
  3. Avoid loading untrusted archives as input artifacts
  4. If the symlink legitimately points elsewhere, copy the real file into the archive instead of linking

Example fix

# before (packaging escape symlink)
ln -s /etc/passwd out.tar member
# after (relative, in-root)
ln -s ./realfile member && tar -C root -cf out.tar .
Defensive patterns

Strategy: try-catch

Validate before calling

// Check archive symlinks before uploading:
// tar -tvf artifact.tar | grep '^l'   # list symlink members
// tar -tvf artifact.tar | grep -E 'l.*/' # links with absolute targets

Try / catch

if err := loadArtifacts(ctx); err != nil {
	if strings.Contains(err.Error(), "illegal symlink target") {
		// archive contains an escaping symlink: repackage with relative links
	}
}

Prevention

When it happens

Trigger: A tarball member of type TypeSymlink whose Linkname points outside the extraction root, e.g. link name 'x' with target '../../etc/passwd' or an absolute path like '/etc/passwd'.

Common situations: Malicious or careless artifact packaging that includes escape symlinks; archives generated on other systems containing absolute symlinks; supply-chain-poisoned third-party artifacts.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/00080764a8b08246. Report an issue: GitHub.