argoproj/argo-workflows · error
artifact key %q must not contain '..'
Error message
artifact key %q must not contain '..'
What it means
As part of the key format check, ValidateUploadedArtifactKey rejects any key containing '..' to block path traversal. Even before reaching the prefix check's siblings, any '..' anywhere in the key fails validation because client-supplied keys are used in storage paths unescaped.
Source
Thrown at server/utils/artifactkey.go:26
"github.com/google/uuid"
)
// ValidateUploadedArtifactKey checks that key is exactly the format the upload
// endpoint generates for namespace: uploads/{namespace}/{uuid}/{filename}. It
// rejects path traversal, absolute paths, empty segments, and any key outside
// the upload prefix, since a client-supplied key is otherwise applied to the
// artifact location without further checks.
//
// This is defense-in-depth, not a proof of ownership: a valid-looking key
// naming another user's upload under the same namespace still passes.
func ValidateUploadedArtifactKey(namespace, key string) error {
prefix := "uploads/" + namespace + "/"
if !strings.HasPrefix(key, prefix) {
return fmt.Errorf("artifact key %q must start with %q", key, prefix)
}
if strings.Contains(key, "..") {
return fmt.Errorf("artifact key %q must not contain '..'", key)
}
if strings.HasPrefix(key, "/") {
return fmt.Errorf("artifact key %q must not be an absolute path", key)
}
if path.Clean(key) != key {
return fmt.Errorf("artifact key %q is not in canonical form", key)
}
parts := strings.Split(key, "/")
if len(parts) != 4 {
return fmt.Errorf("artifact key %q must have exactly 4 segments: uploads/{namespace}/{uuid}/{filename}", key)
}
if slices.Contains(parts, "") {
return fmt.Errorf("artifact key %q must not contain empty segments", key)
}
uuidSegment := parts[2]
if _, err := uuid.Parse(uuidSegment); err != nil {View on GitHub (pinned to 35bff19146)
Solutions
- Remove '..' segments — use only a bare filename for the final segment.
- Sanitize/validate any user-supplied filename before composing the key.
- Use path.Join with the fixed prefix and a cleaned, base-name-only filename.
Example fix
// before key := "uploads/my-ns/" + id + "/../../etc/passwd" // after key := "uploads/my-ns/" + id + "/" + path.Base(userFilename)
Defensive patterns
Strategy: validation
Validate before calling
if strings.Contains(key, "..") {
return fmt.Errorf("key %q must not contain '..'", key)
} Type guard
func traversalFree(key string) bool { return !strings.Contains(key, "..") } Try / catch
if err := utils.ValidateUploadedArtifactKey(ns, key); err != nil {
if strings.Contains(err.Error(), "must not contain '..'") {
key = path.Join("uploads", ns, uuid.NewString(), path.Base(userFilename))
}
} Prevention
- Always pass filenames through path.Base before composing keys.
- Treat any user-supplied filename as untrusted and strip path separators.
- Never concatenate relative paths into artifact keys.
When it happens
Trigger: Keys like 'uploads/ns/uuid/../../secret', keys built by joining user input, or copying keys containing relative-path segments.
Common situations: Path traversal attempts or accidental concatenation of relative paths; template interpolation injecting '..'; naive filepath joining of untrusted filenames.
Related errors
- artifact key %q must start with %q
- artifact key %q must have a bare filename segment
- illegal file path: %s
- illegal symlink target: %s -> %s
- illegal file path after symlink resolution: %s resolves outs
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/89e088922a16c82a.
Report an issue: GitHub.