CVE-2025-47933

GitOps to PwnOps: Deconstructing the Argo CD Stored XSS (CVE-2025-47933)

Amit Schendel
Amit Schendel
Senior Security Researcher

Jan 6, 2026·6 min read

Executive Summary (TL;DR)

Improper protocol sanitization in Argo CD's repository management UI allows a malicious actor to inject `javascript:` payloads into repository URLs. When an administrator clicks the link, the payload executes, granting the attacker full control over the Argo CD API and the managed Kubernetes clusters. Patch immediately to 2.13.8+, 2.14.13+, or 3.0.4+.

A critical Stored Cross-Site Scripting (XSS) vulnerability in the Argo CD web interface allows attackers with repository edit permissions to execute arbitrary JavaScript in the context of other users, potentially leading to full Kubernetes cluster compromise via the API.

The Hook: When GitOps Becomes a Backdoor

Argo CD is the darling of the Kubernetes world. It’s the "Source of Truth" engine that ensures what you have in Git is exactly what is running in your cluster. It holds the keys to the kingdom—literally. It stores credentials, manages secrets, and orchestrates deployments. So, when we find a vulnerability in Argo CD, we aren't just talking about a defaced website; we are talking about the potential for a complete infrastructure takeover.

CVE-2025-47933 is a Stored Cross-Site Scripting (XSS) vulnerability that feels almost nostalgic. In an era of complex heap corruptions and race conditions, sometimes the most devastating bugs are the simplest: a failure to sanitize a URL. But don't let the simplicity fool you. This isn't just about popping an alert(1) box. This is about leveraging the trust placed in the Argo CD UI to pivot from a low-privilege developer account to a full cluster administrator.

The vulnerability resides in the repository management page. If you have enough permission to add or edit a git repository source—a common permission for developers—you can plant a landmine that waits for an administrator to step on it. Once they do, their browser becomes your remote control for the entire cluster.

The Flaw: Trusting the Protocol

The root cause of this vulnerability is a classic case of "Trust, but don't verify." The flaw lives in the frontend code responsible for rendering repository links, specifically within ui/src/app/shared/components/urls.ts. The application uses a library called git-url-parse to decompose repository URLs into their constituent parts (protocol, resource, owner, name).

When the UI renders the repository list, it takes these parsed components and reconstructs the URL string to create a clickable anchor tag (<a>). The fatal mistake? The code assumed that if git-url-parse successfully parsed the URL, it was safe to render. It failed to validate the protocol (scheme) of the URL.

This means that while the developers expected https://github.com/org/repo, the parser was perfectly happy accepting javascript:alert(document.domain)//. The application would then obediently reconstruct this into <a href="javascript:...">, turning a data field into an execution context. This is a DOM-based XSS that persists in the database, meaning every time an admin loads the repo list, the trap is armed.

The Code: The Smoking Gun

Let's look at the vulnerable code. The function repoUrl was responsible for generating the link string. Notice how it blindly interpolates the protocol without checking what it actually is.

// Vulnerable Code in ui/src/app/shared/components/urls.ts
export function repoUrl(url: string): string {
    try {
        const parsed = GitUrlParse(url);
        if (!parsed) {
            return null;
        }
        // The vulnerability: Direct interpolation of parsed.protocol
        return `${protocol(parsed.protocol)}://${parsed.resource}/${parsed.owner}/${parsed.name}`;
    } catch {
        return null;
    }
}

The fix, applied in commit a5b4041a79c54bc7b3d090805d070bcdb9a9e4d1, introduces a sanity check. The developers decided to implement a blacklist approach (which is risky, but effective here against standard vectors) by explicitly checking for dangerous protocols.

// Patched Code
import {isValidURL} from '../../shared/utils';
 
export function repoUrl(url: string): string {
    try {
        // ... parsing logic ...
        const parsedUrl = `${protocol(parsed.protocol)}://${parsed.resource}/${parsed.owner}/${parsed.name}`;
        
        // The Fix: Validate the reconstructed URL before returning it
        if (!isValidURL(parsedUrl)) {
            return null;
        }
        return parsedUrl;
    } catch {
        return null;
    }
}
 
// And the validation logic added to utils.ts
export function isValidURL(url: string): boolean {
    try {
        const parsedUrl = new URL(url);
        // Explicitly blocking the "Big Three" of XSS vectors
        return parsedUrl.protocol !== 'javascript:' && 
               parsedUrl.protocol !== 'data:' && 
               parsedUrl.protocol !== 'vbscript:';
    } catch (TypeError) {
        // Fallback for relative URLs
        // ...
    }
}

By ensuring the final URL is parsed by the browser's native URL constructor and checking the protocol property, they effectively neutralize the injection.

The Exploit: Escalation to Admin

To exploit this, an attacker needs repository:update or repository:create permissions. In many organizations, developers are allowed to add their own application repositories. This is the entry point.

The Attack Chain

  1. Injection: The attacker uses the Argo CD CLI or API to register a new repository. Instead of a valid Git URL, they supply the malicious payload.

    argocd repo add "javascript:fetch('/api/v1/cluster', {method:'DELETE'})//"
  2. The Trap: The malicious entry is saved to the database. It sits there, waiting.

  3. The Trigger: A cluster administrator logs into the Argo CD UI to check on the status of repositories or debug a connection issue. They navigate to Settings -> Repositories.

  4. The Execution: The administrator sees the list. They might click the link thinking it goes to GitHub, or the script might execute if the attacker gets clever with how the DOM handles the link (though this specific vector requires a click). Upon clicking, the JavaScript executes in the administrator's session.

  5. The Payload: Since Argo CD is a Single Page Application (SPA), the XSS payload shares the same origin. The attacker's script can make authenticated calls to the Argo CD API using the victim's cookies/tokens. They could create a new admin user, dump secrets, or delete entire clusters.

The Impact: Why You Should Panic

Often, XSS is dismissed as a "medium" severity issue. In the context of a GitOps tool like Argo CD, it is undeniably Critical. Argo CD is often configured with high privileges in the Kubernetes cluster (ClusterAdmin) to apply changes to any namespace.

If an attacker hijacks an administrator's session via this XSS:

  • Lateral Movement: They can pivot from the UI to the underlying Kubernetes API.
  • Secret Exfiltration: They can read K8s Secrets managed by Argo CD.
  • Denial of Service: They can delete all Applications and unexpected resources.
  • Supply Chain Poisoning: They could modify the sync settings of other applications to point to malicious container images.

The CVSS score of 9.1 reflects this reality. The scope is changed (S:C), meaning the vulnerability affects resources beyond the vulnerable component itself (i.e., the managed clusters).

The Fix: Remediation

The remediation is straightforward: patch your Argo CD instances. The vulnerability has been fixed in the following versions:

  • 2.13.8
  • 2.14.13
  • 3.0.4

If you cannot upgrade immediately, you must restrict permissions. Ensure that only trusted administrators have the create or update permission on the repositories resource in your RBAC configuration. Additionally, you should audit your current repositories for any URLs starting with javascript:, data:, or vbscript:.

[!NOTE] This vulnerability demonstrates why "Input Validation" is not enough. You need "Output Encoding" and rigorous protocol validation, especially when handling URLs that turn into clickable elements.

Fix Analysis (1)

Technical Appendix

CVSS Score
9.1/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H
EPSS Probability
0.01%
Top 99% most exploited

Affected Systems

Argo CD API ServerArgo CD Web UI

Affected Versions Detail

Product
Affected Versions
Fixed Version
Argo CD
Argo Project
>= 1.2.0-rc1, <= 1.8.7N/A (End of Life)
Argo CD
Argo Project
>= 2.0.0-rc3, < 2.13.82.13.8
Argo CD
Argo Project
>= 2.14.0-rc1, < 2.14.132.14.13
Argo CD
Argo Project
>= 3.0.0-rc1, < 3.0.43.0.4
AttributeDetail
CVE IDCVE-2025-47933
CVSS v3.19.1 (Critical)
Attack VectorNetwork (Stored XSS)
CWECWE-79 (Improper Neutralization of Input)
ImpactRCE / Cluster Compromise
AuthenticationRequired (Low Privilege)
EPSS Score0.00011 (Low probability)
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.

Vulnerability Timeline

Patch committed to Argo CD repository
2025-05-28
Public disclosure and GHSA published
2025-05-29
Included in CISA Weekly Summary
2025-06-02

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.