High severity7.5NVD Advisory· Published Mar 11, 2026· Updated Mar 11, 2026
Argo Workflows has unauthorized access to Argo Workflows Template
CVE-2026-28229
Description
Argo Workflows is an open source container-native workflow engine for orchestrating parallel jobs on Kubernetes. Prior to 4.0.2 and 3.7.11, Workflow templates endpoints allow any client to retrieve WorkflowTemplates (and ClusterWorkflowTemplates). Any request with a Authorization: Bearer nothing token can leak sensitive template content, including embedded Secret manifests. This vulnerability is fixed in 4.0.2 and 3.7.11.
Affected products
1- Range: >= 4.0.0, < 4.0.2
Patches
134afaf9c0c36Merge commit from fork
4 files changed · +75 −0
server/clusterworkflowtemplate/cluster_workflow_template_server.go+8 −0 modified@@ -6,6 +6,7 @@ import ( "sort" "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" clusterwftmplpkg "github.com/argoproj/argo-workflows/v4/pkg/apiclient/clusterworkflowtemplate" @@ -52,6 +53,13 @@ func (cwts *Server) CreateClusterWorkflowTemplate(ctx context.Context, req *clus } func (cwts *Server) GetClusterWorkflowTemplate(ctx context.Context, req *clusterwftmplpkg.ClusterWorkflowTemplateGetRequest) (*v1alpha1.ClusterWorkflowTemplate, error) { + allowed, err := auth.CanI(ctx, "get", "clusterworkflowtemplates", "", req.Name) + if err != nil { + return nil, serverutils.ToStatusError(err, codes.Internal) + } + if !allowed { + return nil, status.Error(codes.PermissionDenied, "permission denied") + } wfTmpl, err := cwts.getTemplateAndValidate(ctx, req.Name) if err != nil { return nil, serverutils.ToStatusError(err, codes.Internal)
server/clusterworkflowtemplate/cluster_workflow_template_server_test.go+27 −0 modified@@ -7,7 +7,10 @@ import ( "github.com/go-jose/go-jose/v3/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + authorizationv1 "k8s.io/api/authorization/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/fake" + k8stesting "k8s.io/client-go/testing" clusterwftmplpkg "github.com/argoproj/argo-workflows/v4/pkg/apiclient/clusterworkflowtemplate" "github.com/argoproj/argo-workflows/v4/pkg/apis/workflow/v1alpha1" @@ -153,6 +156,11 @@ const userEmailLabel = "my-sub.at.your.org" func getClusterWorkflowTemplateServer(t *testing.T) (clusterwftmplpkg.ClusterWorkflowTemplateServiceServer, context.Context) { t.Helper() kubeClientSet := fake.NewSimpleClientset() + kubeClientSet.PrependReactor("create", "selfsubjectaccessreviews", func(action k8stesting.Action) (bool, runtime.Object, error) { + return true, &authorizationv1.SelfSubjectAccessReview{ + Status: authorizationv1.SubjectAccessReviewStatus{Allowed: true}, + }, nil + }) wfClientset := wftFake.NewSimpleClientset(&unlabelled, &cwftObj2, &cwftObj3) ctx := context.WithValue(logging.TestContext(t.Context()), auth.WfKey, wfClientset) ctx = context.WithValue(ctx, auth.KubeKey, kubeClientSet) @@ -207,6 +215,25 @@ func TestWorkflowTemplateServer_GetClusterWorkflowTemplate(t *testing.T) { }) require.Error(t, err) }) + t.Run("Unauthorized", func(t *testing.T) { + kubeClientSet := fake.NewSimpleClientset() + kubeClientSet.PrependReactor("create", "selfsubjectaccessreviews", func(action k8stesting.Action) (bool, runtime.Object, error) { + return true, &authorizationv1.SelfSubjectAccessReview{ + Status: authorizationv1.SubjectAccessReviewStatus{Allowed: false}, + }, nil + }) + wfClientset := wftFake.NewSimpleClientset(&cwftObj2) + ctx := context.WithValue(context.WithValue(context.WithValue(logging.TestContext(t.Context()), + auth.WfKey, wfClientset), + auth.KubeKey, kubeClientSet), + auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: "my-sub"}}) + server := NewClusterWorkflowTemplateServer(instanceid.NewService("my-instanceid"), nil, nil) + _, err := server.GetClusterWorkflowTemplate(ctx, &clusterwftmplpkg.ClusterWorkflowTemplateGetRequest{ + Name: "cluster-workflow-template-whalesay-template2", + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "PermissionDenied") + }) } func TestWorkflowTemplateServer_ListClusterWorkflowTemplates(t *testing.T) {
server/workflowtemplate/workflow_template_server.go+8 −0 modified@@ -8,6 +8,7 @@ import ( "strings" "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" workflowtemplatepkg "github.com/argoproj/argo-workflows/v4/pkg/apiclient/workflowtemplate" @@ -58,6 +59,13 @@ func (wts *Server) CreateWorkflowTemplate(ctx context.Context, req *workflowtemp } func (wts *Server) GetWorkflowTemplate(ctx context.Context, req *workflowtemplatepkg.WorkflowTemplateGetRequest) (*v1alpha1.WorkflowTemplate, error) { + allowed, err := auth.CanI(ctx, "get", "workflowtemplates", req.Namespace, req.Name) + if err != nil { + return nil, sutils.ToStatusError(err, codes.Internal) + } + if !allowed { + return nil, status.Error(codes.PermissionDenied, "permission denied") + } wfTmpl, err := wts.getTemplateAndValidate(ctx, req.Namespace, req.Name) if err != nil { return nil, sutils.ToStatusError(err, codes.Internal)
server/workflowtemplate/workflow_template_server_test.go+32 −0 modified@@ -9,8 +9,11 @@ import ( "github.com/go-jose/go-jose/v3/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + authorizationv1 "k8s.io/api/authorization/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/fake" + k8stesting "k8s.io/client-go/testing" workflowtemplatepkg "github.com/argoproj/argo-workflows/v4/pkg/apiclient/workflowtemplate" "github.com/argoproj/argo-workflows/v4/pkg/apis/workflow/v1alpha1" @@ -174,6 +177,11 @@ func getWorkflowTemplateServer(t *testing.T) (workflowtemplatepkg.WorkflowTempla v1alpha1.MustUnmarshal(wftStr2, &wftObj1) v1alpha1.MustUnmarshal(wftStr3, &wftObj2) kubeClientSet := fake.NewSimpleClientset() + kubeClientSet.PrependReactor("create", "selfsubjectaccessreviews", func(action k8stesting.Action) (bool, runtime.Object, error) { + return true, &authorizationv1.SelfSubjectAccessReview{ + Status: authorizationv1.SubjectAccessReviewStatus{Allowed: true}, + }, nil + }) wfClientset := wftFake.NewSimpleClientset(&unlabelledObj, &wftObj1, &wftObj2) ctx := context.WithValue(context.WithValue(context.WithValue(logging.TestContext(t.Context()), auth.WfKey, wfClientset), auth.KubeKey, kubeClientSet), auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: "my-sub"}, Email: "my-sub@your.org"}) wftmplStore := NewClientStore() @@ -229,6 +237,30 @@ func TestWorkflowTemplateServer_GetWorkflowTemplate(t *testing.T) { _, err := server.GetWorkflowTemplate(ctx, &workflowtemplatepkg.WorkflowTemplateGetRequest{Name: "unlabelled", Namespace: "default"}) require.Error(t, err) }) + t.Run("Unauthorized", func(t *testing.T) { + kubeClientSet := fake.NewSimpleClientset() + kubeClientSet.PrependReactor("create", "selfsubjectaccessreviews", func(action k8stesting.Action) (bool, runtime.Object, error) { + return true, &authorizationv1.SelfSubjectAccessReview{ + Status: authorizationv1.SubjectAccessReviewStatus{Allowed: false}, + }, nil + }) + var wftObj1 v1alpha1.WorkflowTemplate + v1alpha1.MustUnmarshal(wftStr2, &wftObj1) + wfClientset := wftFake.NewSimpleClientset(&wftObj1) + ctx := context.WithValue(context.WithValue(context.WithValue(logging.TestContext(t.Context()), + auth.WfKey, wfClientset), + auth.KubeKey, kubeClientSet), + auth.ClaimsKey, &types.Claims{Claims: jwt.Claims{Subject: "my-sub"}}) + wftmplStore := NewWorkflowTemplateClientStore() + cwftmplStore := clusterworkflowtemplate.NewClusterWorkflowTemplateClientStore() + server := NewWorkflowTemplateServer(instanceid.NewService("my-instanceid"), wftmplStore, cwftmplStore) + _, err := server.GetWorkflowTemplate(ctx, &workflowtemplatepkg.WorkflowTemplateGetRequest{ + Name: "workflow-template-whalesay-template2", + Namespace: "default", + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "PermissionDenied") + }) } func TestWorkflowTemplateServer_ListWorkflowTemplates(t *testing.T) {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-56px-hm34-xqj5ghsaADVISORY
- github.com/argoproj/argo-workflows/security/advisories/GHSA-56px-hm34-xqj5ghsax_refsource_CONFIRM
- github.com/argoproj/argo-workflows/commit/34afaf9c0c36f1ba8645d483ea4752cfc4a391e8ghsa
- github.com/argoproj/argo-workflows/releases/tag/v3.7.11ghsa
- github.com/argoproj/argo-workflows/releases/tag/v4.0.2ghsa
- nvd.nist.gov/vuln/detail/CVE-2026-28229ghsa
News mentions
0No linked articles in our index yet.