{"record":{"id":"88c4f51f7e94a59c","repo":"argoproj/argo-workflows","slug":"artifact-key-q-must-have-a-bare-filename-segment","errorCode":null,"errorMessage":"artifact key %q must have a bare filename segment","messagePattern":"artifact key %q must have a bare filename segment","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/utils/artifactkey.go","lineNumber":50,"sourceCode":"\t\treturn fmt.Errorf(\"artifact key %q is not in canonical form\", key)\n\t}\n\n\tparts := strings.Split(key, \"/\")\n\tif len(parts) != 4 {\n\t\treturn fmt.Errorf(\"artifact key %q must have exactly 4 segments: uploads/{namespace}/{uuid}/{filename}\", key)\n\t}\n\tif slices.Contains(parts, \"\") {\n\t\treturn fmt.Errorf(\"artifact key %q must not contain empty segments\", key)\n\t}\n\n\tuuidSegment := parts[2]\n\tif _, err := uuid.Parse(uuidSegment); err != nil {\n\t\treturn fmt.Errorf(\"artifact key %q must have a valid UUID segment: %w\", key, err)\n\t}\n\n\tfilename := parts[3]\n\tif path.Base(filename) != filename {\n\t\treturn fmt.Errorf(\"artifact key %q must have a bare filename segment\", key)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":32,"sourceCodeEnd":55,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/server/utils/artifactkey.go#L32-L55","documentation":"ValidateUploadedArtifactKey requires the final segment of an upload key to be a bare filename: path.Base(filename) must equal filename. Any embedded '/' (nested path) or traversal-like name is rejected, because the key is applied to the artifact location unchecked and could otherwise smuggle a subdirectory path.","triggerScenarios":"Calling ValidateUploadedArtifactKey with a 4th segment containing '/': e.g. 'uploads/myns/<uuid>/sub/dir/file.txt' — a nested relative path — or a filename that is itself a path like './file.txt' where Base differs from the input.","commonSituations":"Clients that preserve the local directory structure of uploaded files (e.g. tarball-style relative paths); building keys from file paths instead of file names; mistakenly treating the upload key as a general-purpose object path with folders.","solutions":["Flatten the upload target to only its base name (filepath.Base) before composing the key.","Upload nested content as a single archive (tgz/zip) so the key holds one bare filename.","If directory structure matters, encode it inside the archive rather than in the artifact key."],"exampleFix":"// before\nkey := \"uploads/myns/\" + uuidStr + \"/assets/css/style.css\"\n// after\nkey := \"uploads/myns/\" + uuidStr + \"/style.css\"","handlingStrategy":"validation","validationCode":"func isBareFilename(name string) bool {\n    return name != \"\" && name != \".\" && name != string(filepath.Separator) && filepath.Base(name) == name\n}","typeGuard":null,"tryCatchPattern":"if err := utils.ValidateUploadedArtifactKey(ns, key); err != nil {\n    if strings.Contains(err.Error(), \"bare filename\") {\n        key = path.Base(key) // or reject the upload\n    }\n}","preventionTips":["Apply filepath.Base to user-supplied filenames before composing keys.","Upload directory trees as a single archive rather than one key per file.","Reject keys containing '/' in tests for anything constructing upload keys."],"tags":["go","validation","path-traversal","artifacts"],"backgroundTag":"invalid-artifact-key","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}