argoproj/argo-workflows · error
illegal file path after symlink resolution: %s resolves outs
Error message
illegal file path after symlink resolution: %s resolves outside destination
What it means
For regular files/dirs in the tar, the executor evaluates any existing symlinked parent directories (filepath.EvalSymlinks on the parent path) and confirms the resolved location is still inside the destination. If the physical parent resolves outside dest — meaning a previously extracted symlink redirects the write — extraction aborts with this error.
Source
Thrown at workflow/executor/executor.go:1186
// Before writing the file, check if the parent directory resolves outside dest
parentDir := filepath.Dir(target)
// Resolve the destination directory
resolvedDest, err := filepath.EvalSymlinks(dest)
if err != nil {
return err
}
// Check if parent exists and if so, verify it doesn't resolve outside dest
if _, lstatErr := os.Lstat(parentDir); lstatErr == nil {
// Parent exists, resolve it to check for symlink traversal
resolvedParent, evalErr := filepath.EvalSymlinks(parentDir)
if evalErr != nil {
return evalErr
}
// Check if resolved parent is outside dest
if !strings.HasPrefix(resolvedParent+string(os.PathSeparator), resolvedDest+string(os.PathSeparator)) && resolvedParent != resolvedDest {
return fmt.Errorf("illegal file path after symlink resolution: %s resolves outside destination", header.Name)
}
} else if !os.IsNotExist(lstatErr) {
return lstatErr
} else {
// Parent doesn't exist, create it
if mkdirErr := os.MkdirAll(parentDir, 0o755); mkdirErr != nil {
return mkdirErr
}
}
f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
if err != nil {
return err
}
if _, err := io.Copy(f, tr); err != nil {
return err
}
if err := f.Close(); err != nil {View on GitHub (pinned to 35bff19146)
Solutions
- Repackage the artifact without symlinks in directory positions (use real directories)
- Inspect the archive (tar -tvf) for symlink members before loading it
- Extract to a fresh, empty destination so no pre-existing symlinks can redirect paths
- Treat artifacts from untrusted producers as hostile and re-verify contents
Example fix
# before (archive: 'sub' -> /tmp symlink, then 'sub/file') # after (archive uses real directory) mkdir sub && cp file sub/ && tar -C root -cf out.tar .
Defensive patterns
Strategy: validation
Validate before calling
// Detect archives where directory-position symlinks could redirect writes:
// tar -tvf artifact.tar | awk '$1 ~ /^l/ {print}' # review all symlinks
// Prefer archives with no symlinks in directory positions Try / catch
if err := loadArtifacts(ctx); err != nil {
if strings.Contains(err.Error(), "resolves outside destination") {
// symlinked parent redirects writes: reject/repackage the archive
}
} Prevention
- Avoid symlinks in directory positions inside distributed tarballs
- Extract to a fresh empty directory to avoid pre-existing symlinks
- Review symlink members of untrusted archives before loading
- Keep executor updated — this EvalSymlinks check is a recent hardening
When it happens
Trigger: Writing a tar member under a path whose parent directory chain traverses a symlink created earlier in the same archive (or pre-existing in dest) that points outside the extraction root, so the final write would land outside dest even though the joined name looked safe.
Common situations: Multi-stage attacks where an archive first extracts a symlink like 'sub -> /tmp' and then writes 'sub/file' (path 'dest/sub/file' passes prefix check but resolves to /tmp/file); archives recombining paths from multiple sources.
Related errors
- illegal symlink target: %s -> %s
- illegal file path: %s
- artifact key %q must not contain '..'
- %s: Illegal file path
- failed to stat artifact path %q at %s: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/a79efd995acec9f9.
Report an issue: GitHub.