← Back to CVE List
CVE-2026-77596NVD
Vulnerability Summary
`uploadPipeline` and `uploadPipelineVersion` both build a `ResourceAttributes{Verb: create}` and delegate the access decision to `canUploadVersionedPipeline`: ```go // backend/src/apiserver/server/pipeline_upload_server.go:387-421 func (s *PipelineUploadServer) canUploadVersionedPipeline(r *http.Request, pipelineID string, resourceAttributes *authorizationv1.ResourceAttributes) error { if !common.IsMultiUserMode() { return nil } if len(pipelineID) > 0 { namespace, err := s.resourceManager.FetchNamespaceFromPipelineId(pipelineID) if err != nil { return util.Wrap(err, "Failed to authorize with the Pipeline ID") } if len(resourceAttributes.Namespace) == 0 { resourceAttributes.Namespace = namespace } } if resourceAttributes.Namespace == "" { return nil // empty namespace = no SubjectAccessReview } // ... only here does IsAuthorized() run ... } ``` When the namespace is empty (a shared/cluster-wide pipeline, or simply an upload with no `namespace` parameter), the function returns at line 401-403 and `IsAuthorized` is never reached. Reachability is the default. `ValidateNamespaceRequired` only rejects an empty namespace when `IsNamespaceRequiredForPipelines()` is true: ```go // backend/src/apiserver/validation/namespace.go:25-30 func ValidateNamespaceRequired(namespace string) error { if common.IsMultiUserMode() && common.IsNamespaceRequiredForPipelines() && namespace == "" { return util.NewInvalidInputError("Namespace is required for pipeline operations") } return nil } // backend/src/apiserver/common/config.go:73-74 -> default FALSE func IsNamespaceRequiredForPipelines() bool { return GetBoolConfigWithDefault(RequireNamespaceForPipelines, false) } ``` `REQUIRE_NAMESPACE_FOR_PIPELINES` (`config.go:41`) defaults to `false` and is not set in any manifest under `manifests/`. So in a default multi-user install, empty namespaces pass validation and flow into the unguarded branch. The gRPC sibling proves the intended behavior is to authorize empty-namespace writes: ```go // backend/src/apiserver/server/pipeline_server.go:1290-1306 (canAccessPipeline) // Skip authorization for read-only operations on shared pipelines in multi-user mode. // Write operations (create, update, delete) on shared pipelines must still be authorized // against the KFP system namespace, since shared pipelines have no namespace of their own. if s.resourceManager.IsEmptyNamespace(resourceAttributes.Namespace) { if resourceAttributes.Verb == common.RbacResourceVerbGet || resourceAttributes.Verb == common.RbacResourceVerbList { return nil } resourceAttributes.Namespace = common.GetPodNamespace() // ... IsAuthorized(...) ... return nil } ``` Reads of shared pipelines intentionally skip authorization; writes must be authorized against the system namespace. The upload endpoints only ever perform writes, yet `canUploadVersionedPipeline` applies the read behavior to them.
CVSS v3.1 Base Metrics — Score 9.6
Attack VectorNetwork
Attack ComplexityLow
Privileges RequiredLow
User InteractionNone
ScopeChanged
ConfidentialityHigh
IntegrityHigh
AvailabilityNone
Affected & Patched Versions
- ghcr.io/kubeflow/kfp-api-server <= 2.17.3
- ghcr.io/kubeflow/kfp-api-server 2.18.0