Argo CD CVE-2026-42880: When Read-Only Means Read-Everything-Including-Secrets
A week ago, on May 7, 2026, the Argo CD project published GHSA-3v3m-wc6v-x4x3. The summary is short: any authenticated Argo CD user, including everyone on the default role:readonly, can pull plaintext Kubernetes Secret values out of any Application that uses ServerSideDiff with the mutation-webhook annotation. CVSS 9.6, scope-changed because the leak crosses the Argo CD trust boundary into etcd.
If you maintain an Argo CD instance shared by more than one team, you almost certainly have read-only users. If you maintain one Application with the IncludeMutationWebhook=true compare option set, that Application's rendered Secrets are visible to every one of those users. Service account tokens, TLS private keys, database credentials, the lot, sitting one API call away.
This post covers the patch matrix, how to find at-risk Applications in your cluster today, what to do if you cannot upgrade immediately, and why this is the second authorization-bypass of this exact shape inside twelve months.
TL;DR
- CVE-2026-42880, CVSS 9.6, disclosed 2026-05-07. Affects Argo CD 3.2.0 to 3.2.10 and 3.3.0 to 3.3.8. Fixed in v3.2.11 and v3.3.9. v2.x is not affected.
- Any authenticated user with
applications, get(the defaultrole:readonlygrants this) can call ServerSideDiff and receive unmasked Kubernetes Secret values for any Application that has the annotationargocd.argoproj.io/compare-optionscontainingIncludeMutationWebhook=true. - Detection is a single
jqquery againstkubectl get applications.argoproj.io -A -o json. See below. - If you cannot upgrade today, the practical mitigation is removing
IncludeMutationWebhook=truefrom those annotations. It is safe to remove. Diffs simply revert to filtering out fields injected by mutating webhooks, which is the default ServerSideDiff behavior anyway. - This is the second authorization-bypass disclosure in Argo CD in twelve months. Both leaked through endpoints that forgot to call a redaction helper. Treat
role:readonlyas "read-everything-including-secrets" until proven otherwise.
Prerequisites
- An Argo CD installation on 3.2.x or 3.3.x. Run
argocd version(server) orkubectl -n argocd get deploy argocd-server -o jsonpath='{.spec.template.spec.containers[0].image}'to confirm. kubectlaccess to the namespace your Applications live in (usuallyargocd, but the CR can live anywhere).jqfor the one-liner.yqworks too if you prefer.
What the bug actually is
ServerSideDiff is the Argo CD feature that asks the Kubernetes API server to do a Server-Side Apply dry-run and then diffs the resulting object against the desired state. It was added in 3.x because it produces more accurate diffs than the older client-side approach, especially when controllers or mutating webhooks add fields to the live object.
The vulnerable code path is the gRPC method application.ApplicationService/ServerSideDiff, exposed over REST as /api/v1/applications/{appName}/resource-tree/diff. The handler at server/application/application.go:3051-3062 constructs its response from the raw PredictedLive and NormalizedLive objects returned by the dry-run, without ever calling hideSecretData().
That helper is what every other diff and state endpoint in Argo CD calls before returning a Secret-shaped object to a client. GetManifests, GetManifestsWithFiles, GetResource, PatchResource, all of them route through it. ServerSideDiff was the one handler that missed it.
The vulnerability needs two conditions:
- The caller has
applications, getpermission. In the shippedrole:readonlypolicy this is wildcard, so every authenticated user has it. - The target Application has the compare option
IncludeMutationWebhook=trueset on theargocd.argoproj.io/compare-optionsannotation. Without that flag, a secondary filter calledremoveWebhookMutation()runs over the response and strips fields injected by mutating webhooks, which incidentally catches the leak. The dangerous combination isServerSideDiff=true,IncludeMutationWebhook=truein the same annotation value.
What leaks is the rendered Kubernetes Secret as it would appear in etcd. The advisory specifically calls out service account tokens, TLS private keys, database credentials, and API keys. SealedSecrets and ExternalSecrets are not decrypted by Argo CD itself, but the bug leaks the Secret object their controllers produce, which is materially the same outcome from the attacker's perspective.
One nuance worth knowing: the CVSS vector is AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N. PR:L means a valid authenticated session is required. The advisory is correctly framed as "every authenticated user", not "every unauthenticated attacker". For most Argo CD deployments this is a distinction without a difference, since SSO is typically wired up to the whole engineering org.
Find at-risk Applications in your cluster
The fastest way to know whether you are affected is to list every Application whose compare-options annotation contains IncludeMutationWebhook=true:
kubectl get applications.argoproj.io -A -o json \
| jq -r '.items[]
| select(.metadata.annotations["argocd.argoproj.io/compare-options"]
| tostring
| contains("IncludeMutationWebhook=true"))
| "\(.metadata.namespace)/\(.metadata.name)"'
This returns one namespace/name per affected Application. Empty output means no Application in the cluster carries the dangerous annotation, but that does not mean you can skip the upgrade. The annotation can also be set globally via the resource.compareoptions field in the argocd-cm ConfigMap:
kubectl -n argocd get cm argocd-cm -o jsonpath='{.data.resource\.compareoptions}'
If that output contains IncludeMutationWebhook=true, every Application in the cluster inherits the dangerous setting, even Applications without their own annotation. The upgrade becomes urgent rather than important.
If you want to know whether the bug has actually been exploited against you, the bad news is there is no dedicated audit field. The Argo CD access log records the gRPC method and the subject from the JWT, so the best you can do is grep historical logs for non-admin subjects calling ServerSideDiff:
kubectl -n argocd logs deploy/argocd-server --tail=1000000 \
| grep ServerSideDiff \
| grep -v 'sub=admin'
That gives a noisy but reviewable list. If your subjects are organisation emails or group claims, swap the second grep for the pattern that matches your admins.
The upgrade
The patch is a pure bugfix. The diff adds the existing hideSecretData() call to the ServerSideDiff response builder. There are no new flags, no new defaults, no behavior change for legitimate users beyond the obvious one of no longer seeing plaintext Secret values in diffs. Most teams using ServerSideDiff for legitimate reasons (catching drift introduced by mutating webhooks) get back the same masked diff they already get from every other endpoint.
The version mapping is straightforward:
Argo CD 3.3.x -> upgrade to v3.3.9
Argo CD 3.2.x -> upgrade to v3.2.11
Argo CD 2.x -> not affected, no action needed
If you install via the official Helm chart, the 9.5.x line tracks 3.3.x and pins appVersion v3.3.9 from chart 9.5.11 onward. The 3.2.x line is still served by the older chart majors (8.x). Verify the appVersion mapping on Artifact Hub before pinning a chart version, since the Argo team does not always cut chart releases on the same day as the controller release.
Once the new image is running, the on-the-wire fix is verifiable. Authenticated as a read-only user, call the ServerSideDiff endpoint against an Application that carries IncludeMutationWebhook=true and confirm the response no longer contains data fields populated for Secret resources.
If you cannot upgrade today
Two options buy time. The first is annotation removal:
kubectl -n argocd annotate application <name> \
argocd.argoproj.io/compare-options-
Removing the annotation is safe. It reverts to default ServerSideDiff behavior, which filters mutation-webhook-injected fields. The only consequence is that diffs no longer include those fields. If you were depending on seeing them, you were also leaking Secrets to read-only users, so this is the correct fix regardless. To remove the global setting from argocd-cm, edit the ConfigMap and drop IncludeMutationWebhook=true from the resource.compareoptions value.
The second option is RBAC scope-down. The Argo CD argocd-rbac-cm ConfigMap controls who can call which endpoint. The minimum effective change is to stop defaulting users to role:readonly. Edit the policy.default line:
# argocd-rbac-cm
policy.default: "" # was: role:readonly
policy.csv: |
# Existing admin role still gets everything
g, your-admin-group, role:admin
# New explicit team scope, no wildcard get on applications
p, role:dev-readonly, applications, list, */*, allow
p, role:dev-readonly, repositories, get, *, allow
p, role:dev-readonly, projects, get, *, allow
g, your-dev-group, role:dev-readonly
This is more disruptive than annotation removal because it changes what the UI shows to unprivileged users. List works, individual application detail does not, which is what stops the ServerSideDiff endpoint cold. Best to combine annotation removal with the RBAC change rather than rely on either alone.
A Kyverno policy can also block new at-risk Applications at admission time:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: block-include-mutation-webhook
spec:
validationFailureAction: enforce
rules:
- name: deny-include-mutation-webhook
match:
any:
- resources:
kinds: ["argoproj.io/v1alpha1/Application"]
validate:
message: "IncludeMutationWebhook=true leaks Kubernetes Secrets to read-only users (CVE-2026-42880). Remove it or upgrade Argo CD to 3.2.11 / 3.3.9 first."
pattern:
metadata:
=(annotations):
=(argocd.argoproj.io/compare-options): "!*IncludeMutationWebhook=true*"
The same shape works as a Gatekeeper constraint if you are on OPA. Both are admission-time defences, so they prevent new bad Applications but do not retroactively fix existing ones. Pair with the jq query above to clean up what is already in the cluster.
The pattern: redaction-by-handler is fragile
CVE-2026-42880 is the second authorization-bypass of this exact shape in Argo CD inside twelve months. The previous one was CVE-2025-55190 on September 4, 2025, CVSS 9.9. That bug lived in server/project/project.go and leaked repository credentials through the GetDetailedProject endpoint. Same default RBAC (any authenticated user with projects, get), same missing redaction call.
Both bugs share a structural property worth flagging. Argo CD's redaction is per-handler, not middleware. Every endpoint that returns an object is responsible for calling hideSecretData() or its equivalent before serialising. Adding a new endpoint without that call ships a CVE. Adding a new field that holds a secret to an existing response ships a CVE.
For operators, the practical lesson is to stop treating role:readonly as if read-only is meaningful in a security sense. It grants get against everything, and "get returns Secret values" turns out to be true twice in a row. The realistic default for shared Argo CD instances is:
policy.default: ""(no implicit role)- Explicit per-team roles with only the verbs and resources the team needs
- An explicit admin role for the platform team
- Kyverno or Gatekeeper guards on the annotations and ConfigMap fields known to be dangerous
Treat the next "low-severity read-only information disclosure" advisory from the project the same way you would treat a privilege-escalation one, because the read-only/privilege-escalation distinction has so far been a coin flip.
Sources
- GHSA: github.com/argoproj/argo-cd/security/advisories/GHSA-3v3m-wc6v-x4x3
- NVD: nvd.nist.gov/vuln/detail/CVE-2026-42880
- Argo CD v3.3.9: github.com/argoproj/argo-cd/releases/tag/v3.3.9
- Argo CD v3.2.11: github.com/argoproj/argo-cd/releases/tag/v3.2.11
- Diff strategies docs (annotation syntax): argo-cd.readthedocs.io/en/stable/user-guide/diff-strategies
- Built-in RBAC policy source: github.com/argoproj/argo-cd/blob/master/assets/builtin-policy.csv
- Prior pattern, CVE-2025-55190: github.com/argoproj/argo-cd/security/advisories/GHSA-786q-9hcg-v9ff
Patch, then audit your RBAC. The annotation is the smoking gun. The RBAC default is the loaded weapon.
We earn commissions when you shop through the links below.
DigitalOcean
Cloud infrastructure for developers
Simple, reliable cloud computing designed for developers
DevDojo
Developer community & tools
Join a community of developers sharing knowledge and tools
SMTPfast
Developer-first email API
Send transactional and marketing email through a clean REST API. Detailed logs, webhooks, and embeddable signup forms in one dashboard.
QuizAPI
Developer-first quiz platform
Build, generate, and embed quizzes with a powerful REST API. AI-powered question generation and live multiplayer.
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorFound an issue?
Related Posts
Also worth your time on this topic
At Least One Invalid Signature Was Encountered
Understand the causes of invalid signatures in Kubernetes and learn how to troubleshoot and resolve them.
Argo CD Multi-Environment Repository Structure Checklist
How to organize your Git repositories when running Argo CD across dev, staging, and production. Covers folder layout, app-of-apps, ApplicationSets, secrets, RBAC, and promotion flow.
60-90 minutes
GitOps with ArgoCD - Automated Kubernetes Deployments
Implement GitOps workflows using ArgoCD for automated, declarative, and auditable Kubernetes application deployments.
120 minutes