argoproj/argo-workflows · error

%s: Illegal file path

Error message

%s: Illegal file path

What it means

Thrown by the executor while extracting a zip archive (artifact unpack): a zip entry's joined destination path does not stay under the extraction directory. This is a Zip-Slip (path traversal) guard — an entry name like `../../etc/passwd` would otherwise escape `dest` and write outside it. The executor intentionally rejects such archives instead of following the malicious path.

Source

Thrown at workflow/executor/executor.go:1245

				panic(err)
			}
		}()

		// Closure to address file descriptors issue with all the deferred .Close() methods
		extractAndWriteFile := func(f *zip.File) error {
			rc, err := f.Open()
			if err != nil {
				return err
			}
			defer func() {
				if closeErr := rc.Close(); closeErr != nil {
					panic(closeErr)
				}
			}()

			path := filepath.Join(dest, f.Name)
			if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) {
				return fmt.Errorf("%s: Illegal file path", path)
			}

			if f.FileInfo().IsDir() {
				if err = os.MkdirAll(path, f.Mode()); err != nil {
					return err
				}
			} else {
				if err = os.MkdirAll(filepath.Dir(path), f.Mode()); err != nil {
					return err
				}
				outFile, openErr := os.OpenFile(filepath.Clean(path), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
				if openErr != nil {
					return openErr
				}
				defer func() {
					if closeErr := outFile.Close(); closeErr != nil {
						panic(closeErr)
					}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Inspect the zip with `unzip -l` and remove/rename entries containing `..` or leading absolute paths
  2. Rebuild the artifact with relative entry paths (e.g. `zip -r artifact.zip ./dir` from the archive root, not `zip artifact.zip /abs/paths`)
  3. Verify the artifact source — the archive likely comes from an untrusted or compromised producer
  4. If you control extraction, extract into a directory whose prefix the entries genuinely belong under

Example fix

// before: packer used absolute path
zip.Add("/etc/hosts", data) // entry name "/etc/hosts"
// after: use paths relative to the archive root
zip.Add("etc/hosts", data)
Defensive patterns

Strategy: validation

Validate before calling

cleanDest := filepath.Clean(dest)
for _, f := range zipReader.File {
    path := filepath.Join(dest, f.Name)
    if !strings.HasPrefix(path, cleanDest+string(os.PathSeparator)) {
        return fmt.Errorf("unsafe zip entry: %s", f.Name)
    }
}

Type guard

func isSafeZipEntry(dest, name string) bool {
    return strings.HasPrefix(filepath.Join(dest, name), filepath.Clean(dest)+string(os.PathSeparator))
}

Prevention

When it happens

Trigger: Unpacking a zip artifact whose central directory contains an entry with a name using `..` segments or an absolute path, so that filepath.Join(dest, f.Name) does not have filepath.Clean(dest)+os.PathSeparator as prefix.

Common situations: Artifact was produced by a malicious/buggy packer that injects traversal entry names; archives created on Windows or with absolute paths like `/abs/file`; hand-crafted zip files submitted via git/http/raw artifacts; upgrading to a version that hardened against CVE-class zip-slip and previously-'working' malicious archives now fail.

Related errors


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