A logic flaw in Argo Workflows allows users with Workflow create permissions to bypass templateReferencing: Strict, gaining host network access, SA token mounting, and control-plane scheduling. Patched in 3.7.14 and 4.0.5.
# A Dangerous Shortcut in Popular Workflow Software
Argo Workflows is software that helps companies automate tasks — think of it like a digital assembly line that runs jobs automatically without human intervention. Millions of organizations rely on it to manage everything from data processing to software deployments.
Security researchers discovered a critical flaw in older versions of this software. It's like a lock that's supposed to prevent junior employees from accessing the executive offices, except the lock doesn't actually work.
Here's the problem: The software has a security feature meant to stop workers with limited permissions from doing dangerous things. But hackers can trick it. Someone with basic access can fool the system into letting them do things they shouldn't be able to do — like accessing other companies' networks, stealing database credentials, or running malicious code.
This matters because Argo runs on Kubernetes, which is the infrastructure that hosts applications for banks, healthcare providers, retailers, and tech companies. A breach here cascades quickly. An attacker could grab sensitive data, install ransomware, or launch attacks on other companies sharing the same infrastructure.
Who's at risk? Any organization using Argo Workflows versions older than 3.7.14 or 4.0.5, particularly those where contractors or less-trusted employees have workflow creation access.
What should you do?
First, check if your company uses Argo Workflows and ask your IT team which version you're running. Second, if you're on an older version, update immediately — newer versions patch this hole. Third, if you can't update right away, ask your security team to limit who can create workflows and monitor those activities closely for suspicious behavior.
Want the full technical analysis? Click "Technical" above.
CVE-2026-42296 is a security boundary bypass in Argo Workflows affecting all versions prior to 3.7.14 and 4.0.5. The vulnerability allows any principal with create permission on Workflow resources to circumvent templateReferencing: Strict — a feature explicitly designed to restrict workflows to administrator-approved templates. The bypass is not a privilege escalation in the classical sense; it is a complete nullification of an enforcement boundary that cluster operators may be relying on as their sole pod security control layer.
The practical blast radius includes: enabling hostNetwork on workflow pods, switching service accounts, overriding podSecurityContext, adding tolerations to land pods on control-plane nodes, and forcing SA token auto-mounting. On clusters that use PodSecurity admission or OPA/Gatekeeper, some of these primitives are independently blocked. On clusters that treat Argo's Strict mode as the primary enforcement layer, all of them are fully accessible.
Root cause: The templateReferencing: Strict enforcement path in the workflow admission logic fails to recursively validate inline pod spec overrides embedded within submitted Workflow objects, allowing attacker-controlled fields to survive template substitution and reach the Kubernetes API server unmodified.
Affected Component
The vulnerable subsystem is the workflow validation and template-resolution pipeline, specifically the admission webhook and the controller's wftmpl enforcement logic. The relevant Go packages are:
pkg/apis/workflow/v1alpha1 — Workflow and WorkflowTemplate CRD types
workflow/validate — template reference validation and strict mode enforcement
workflow/controller — pod construction from resolved templates
The feature flag is set in ConfigMap/workflow-controller-configmap under templateReferencing: Strict. When enabled, submitted Workflow specs are supposed to be constrained to referencing pre-approved WorkflowTemplate or ClusterWorkflowTemplate objects. The vulnerability is in how "constrained" is implemented.
Root Cause Analysis
In Strict mode, the controller validates that a submitted Workflow's steps reference approved templates. However, the validation logic checks only the template reference — the name pointer — not the full resolved pod spec that results from merging the Workflow-level podSpecPatch, securityContext, tolerations, and automountServiceAccountToken fields onto the referenced template.
The relevant validation function in Go (pre-patch, reconstructed from advisory and diff context):
// workflow/validate/validate.go (pre-patch pseudocode)
// validateTemplateRef checks that a step references an approved WorkflowTemplate.
// BUG: validates the reference name but does NOT validate the surrounding
// Workflow-level pod spec fields that are merged AFTER this check passes.
func (ctx *ValidateCtx) validateTemplateRef(scope map[string]interface{}, tmplRef *wfv1.TemplateRef) error {
// Resolves the named template — this part works correctly.
tmpl, err := ctx.tmplCtx.GetTemplateByName(tmplRef.Name, tmplRef.Template)
if err != nil {
return err
}
// BUG: validateTemplate is called on the RESOLVED template only.
// The Workflow object's own .spec.podSpecPatch, .spec.securityContext,
// .spec.tolerations, and .spec.volumes are NEVER validated against
// the strict policy. They are merged into the final pod spec later
// in the controller loop, after admission has already approved the object.
return ctx.validateTemplate(scope, tmpl, nil)
// MISSING: validation of wf.Spec.PodSpecPatch
// MISSING: validation of wf.Spec.SecurityContext
// MISSING: validation of wf.Spec.Tolerations
// MISSING: validation of wf.Spec.AutomountServiceAccountToken
// MISSING: validation of wf.Spec.HostNetwork
}
The controller's pod construction path in workflow/controller/operator.go then applies these unvalidated fields unconditionally:
// workflow/controller/operator.go (pre-patch pseudocode)
// createWorkflowPod constructs the final pod spec.
// At this point, strict-mode validation has already passed.
func (woc *wfOperationCtx) createWorkflowPod(nodeName string, tmpl *wfv1.Template, ...) (*apiv1.Pod, error) {
pod := &apiv1.Pod{}
// Template fields — correctly sourced from the approved WorkflowTemplate.
pod.Spec.Containers = tmpl.Container
// BUG: Workflow-level overrides applied WITHOUT re-checking Strict policy.
// These fields come from the user-submitted Workflow object, not the template.
if woc.wf.Spec.HostNetwork != nil {
pod.Spec.HostNetwork = *woc.wf.Spec.HostNetwork // attacker-controlled
}
if woc.wf.Spec.SecurityContext != nil {
pod.Spec.SecurityContext = woc.wf.Spec.SecurityContext // attacker-controlled
}
for _, tol := range woc.wf.Spec.Tolerations {
pod.Spec.Tolerations = append(pod.Spec.Tolerations, tol) // attacker-controlled
}
if woc.wf.Spec.AutomountServiceAccountToken != nil {
pod.Spec.AutomountServiceAccountToken = woc.wf.Spec.AutomountServiceAccountToken
}
// podSpecPatch is applied as a strategic merge patch — also unvalidated.
if woc.wf.Spec.PodSpecPatch != "" {
// BUG: arbitrary JSON patch from user reaches kubectl apply-equivalent.
applyPodSpecPatch(pod, woc.wf.Spec.PodSpecPatch)
}
return woc.submitPodToKubernetes(pod)
}
Exploitation Mechanics
Exploitation requires only create permission on the workflows resource in the target namespace. No additional Kubernetes RBAC is needed beyond what Argo Workflows typically grants to workflow submitters.
EXPLOIT CHAIN:
1. Attacker identifies cluster running Argo Workflows < 3.7.14 or < 4.0.5
with templateReferencing: Strict enabled in workflow-controller-configmap.
2. Attacker enumerates available WorkflowTemplates in the namespace
(requires 'list' on workflowtemplates, or guesses common names).
Any valid template reference is sufficient — the template itself is benign.
3. Attacker crafts a Workflow manifest that:
a. Correctly references a legitimate WorkflowTemplate (passes strict check)
b. Embeds malicious pod-level fields at .spec scope:
- .spec.hostNetwork: true (host network access)
- .spec.tolerations: [{key: node-role.kubernetes.io/control-plane}]
- .spec.securityContext.runAsUser: 0 (root)
- .spec.automountServiceAccountToken: true
- .spec.serviceAccountName: kube-system-sa (SA hijack)
- .spec.podSpecPatch: '{"spec":{"hostPID":true}}'
4. Workflow is submitted via kubectl or Argo CLI:
argo submit --watch malicious-workflow.yaml
5. Admission webhook calls validateTemplateRef() — passes, because the
referenced WorkflowTemplate name is valid and approved.
6. Controller's wfOperationCtx.createWorkflowPod() merges attacker-controlled
.spec fields onto the resolved pod spec without policy re-evaluation.
7. Pod is submitted to Kubernetes API server with attacker-specified fields.
If no external PodSecurity/OPA controls exist, pod is scheduled as-is.
8. On control-plane node (via toleration), with hostNetwork and hostPID,
attacker's container image executes with full host visibility.
From here: node compromise, credential theft from mounted secrets,
lateral movement to other nodes via host network.
A minimal proof-of-concept Workflow manifest demonstrating the bypass:
This is a logic/policy bypass rather than a memory corruption vulnerability. The relevant "state" is the Go struct representing the Workflow spec as it transitions through the validation and pod-construction pipeline:
WORKFLOW OBJECT STATE — THROUGH VALIDATION PIPELINE:
[1] USER SUBMISSION (wf.Spec as received by admission webhook):
wf.Spec.WorkflowTemplateRef.Name = "legitimate-template" <- validated
wf.Spec.HostNetwork = true <- NOT validated
wf.Spec.Tolerations = [{control-plane}] <- NOT validated
wf.Spec.SecurityContext = {runAsUser:0} <- NOT validated
wf.Spec.AutomountSAToken = true <- NOT validated
wf.Spec.PodSpecPatch = '{"spec":{"hostPID":true}}' <- NOT validated
[2] AFTER validateTemplateRef() — STATUS: APPROVED (incorrectly)
Validation checked: WorkflowTemplateRef.Name only.
Attacker fields: still present, untouched, unexamined.
[3] AFTER createWorkflowPod() — FINAL POD SPEC:
pod.Spec.Containers = [from approved template] <- safe
pod.Spec.HostNetwork = true <- attacker
pod.Spec.Tolerations = [{control-plane}] <- attacker
pod.Spec.SecurityContext = {runAsUser:0} <- attacker
pod.Spec.AutomountSAToken = true <- attacker
pod.Spec.HostPID = true (via patch) <- attacker
[4] KUBERNETES API SERVER — pod admitted and scheduled:
Node: control-plane (matched by toleration)
Network: host namespace (HostNetwork=true)
PID ns: host namespace (HostPID=true)
User: root (runAsUser=0, no PSA to block it)
Patch Analysis
The fix, introduced in versions 3.7.14 and 4.0.5, extends the strict-mode validation to cover all Workflow-level fields that influence pod construction. The validation gate is moved to wrap the complete merged spec, not just the template reference lookup.
// BEFORE (vulnerable): workflow/validate/validate.go
func (ctx *ValidateCtx) validateWorkflowTemplateRef(wf *wfv1.Workflow) error {
if ctx.Lint || ctx.WorkflowTemplateRefEnabled {
// Only validates that the referenced template exists and is approved.
// Workflow-level pod spec fields are never examined.
err := ctx.validateTemplateRef(scope, wf.Spec.WorkflowTemplateRef)
return err
// BUG: wf.Spec.HostNetwork, wf.Spec.Tolerations, wf.Spec.SecurityContext,
// wf.Spec.PodSpecPatch, wf.Spec.AutomountServiceAccountToken,
// wf.Spec.ServiceAccountName — all passed through unchecked.
}
}
// AFTER (patched): workflow/validate/validate.go
func (ctx *ValidateCtx) validateWorkflowTemplateRef(wf *wfv1.Workflow) error {
if ctx.Lint || ctx.WorkflowTemplateRefEnabled {
err := ctx.validateTemplateRef(scope, wf.Spec.WorkflowTemplateRef)
if err != nil {
return err
}
// ADDED: reject any workflow-level fields that override pod security
// when operating in Strict template referencing mode.
if ctx.templateReferencingStrict() {
if err := ctx.validateStrictWorkflowFields(wf); err != nil {
return err
}
}
return nil
}
}
// NEW FUNCTION (patch):
func (ctx *ValidateCtx) validateStrictWorkflowFields(wf *wfv1.Workflow) error {
// Reject fields that bypass pod security controls enforced by templates.
if wf.Spec.HostNetwork != nil {
return fmt.Errorf("hostNetwork may not be set in Strict templateReferencing mode")
}
if wf.Spec.SecurityContext != nil {
return fmt.Errorf("securityContext may not be set in Strict templateReferencing mode")
}
if len(wf.Spec.Tolerations) > 0 {
return fmt.Errorf("tolerations may not be set in Strict templateReferencing mode")
}
if wf.Spec.AutomountServiceAccountToken != nil {
return fmt.Errorf("automountServiceAccountToken may not be set in Strict templateReferencing mode")
}
if wf.Spec.PodSpecPatch != "" {
return fmt.Errorf("podSpecPatch may not be set in Strict templateReferencing mode")
}
if wf.Spec.ServiceAccountName != "" {
return fmt.Errorf("serviceAccountName may not be set in Strict templateReferencing mode")
}
return nil
}
Detection and Indicators
Audit submitted Workflow objects for the presence of security-sensitive fields alongside a workflowTemplateRef. The following query identifies potentially malicious submissions in the Kubernetes API server audit log:
# detect_strict_bypass.py — parse k8s audit log for suspicious Workflow submissions
import json, sys
SUSPICIOUS_SPEC_KEYS = {
"hostNetwork", "tolerations", "securityContext",
"podSpecPatch", "automountServiceAccountToken",
"serviceAccountName", "hostPID", "hostIPC"
}
for line in sys.stdin:
event = json.loads(line)
if event.get("objectRef", {}).get("resource") != "workflows":
continue
if event.get("verb") not in ("create", "update"):
continue
spec = event.get("requestObject", {}).get("spec", {})
# Flag: has templateRef AND any pod-override field
has_tmpl_ref = "workflowTemplateRef" in spec
has_override = SUSPICIOUS_SPEC_KEYS & set(spec.keys())
if has_tmpl_ref and has_override:
print(f"[ALERT] Possible Strict bypass:")
print(f" User: {event['user']['username']}")
print(f" Namespace: {event['objectRef']['namespace']}")
print(f" Fields: {has_override}")
print(f" Timestamp: {event['requestReceivedTimestamp']}")
level=info msg="Creating pod" workflow=strict-bypass-xxxxx node=strict-bypass-xxxxx
level=info msg="pod submitted" hostNetwork=true tolerations="[{node-role.kubernetes.io/control-plane...}]"
# Kubernetes event on the node:
Warning FailedScheduling 0s default-scheduler
(absent on clusters without PodSecurity — pod schedules silently)
# On vulnerable clusters, pod runs on control-plane node:
kubectl get pods -A -o wide | grep strict-bypass
argo strict-bypass-xxxxx Running control-plane-node-name
Remediation
Immediate: Upgrade to Argo Workflows 3.7.14 (3.x track) or 4.0.5 (4.x track). These versions add validateStrictWorkflowFields() to the admission path, causing the API server to reject any Workflow object that combines a workflowTemplateRef with pod-security-sensitive fields when Strict mode is enabled.
Defense-in-depth (for clusters that cannot patch immediately):
Deploy PodSecurity admission at restricted or baseline level on namespaces running Argo workflows. This independently blocks hostNetwork, hostPID, and privilege escalation even if Argo's validation is bypassed.
Deploy an OPA/Gatekeeper or Kyverno policy that denies pods with hostNetwork: true, control-plane tolerations, or runAsUser: 0 in Argo-managed namespaces.
Audit existing ClusterRole bindings — restrict create on workflows to only principals that absolutely require it.
Enable Kubernetes API server audit logging with a policy capturing RequestResponse level for all workflows resource operations, and run the detection script above against the log stream.
Verification after patching: Attempt to submit the PoC manifest above against the patched version. The admission webhook should return a validation error referencing the restricted field before the Workflow object is persisted. If it does not, the patch was not applied correctly or the workflow-controller-configmap is not setting templateReferencing: Strict.