Feb 26, 2026·5 min read·106 visits
Argo CD trusted user input when rendering repository URLs. Attackers with 'repo edit' permissions can inject `javascript:` payloads. When an admin views the settings page, the payload executes, allowing the attacker to ride the admin's session to delete apps, steal secrets, or deploy malware.
A critical Stored Cross-Site Scripting (XSS) vulnerability has been discovered in Argo CD, the industry-standard GitOps continuous delivery tool. By injecting a malicious URL protocol into the repository configuration, an attacker can execute arbitrary JavaScript in the browser of any user viewing the repository list—typically a high-privileged administrator. This effectively bridges the gap between low-level configuration access and full Kubernetes cluster compromise.
Argo CD is the darling of the Cloud Native world. It’s the bouncer, the gatekeeper, and the janitor for your Kubernetes clusters. It holds the keys to the castle—literally—managing secrets, deployments, and infrastructure state. If you compromise Argo CD, you don't just get a shell; you get kubectl apply -f ownage.yaml on every cluster it manages.
Usually, we think of XSS as a "medium" severity annoyance—alert boxes and maybe some cookie theft. But context is king. In an administrative dashboard like Argo CD, XSS is the digital equivalent of a Jedi mind trick on the sysadmin. You aren't hacking the server; you are convincing the server that the admin wants to delete the production database. This vulnerability, CVE-2025-47933, turns a simple UI bug into a catastrophic infrastructure collapse.
The road to hell is paved with helpful utility libraries. In this case, the vulnerability lies in how Argo CD's frontend handles Git URLs. To make the UI look pretty, the developers used a library called git-url-parse to break down repository URLs into their component parts (protocol, owner, resource, etc.) and then reassemble them for display.
Here is the logic flaw: The code assumed that if a URL could be parsed, it was safe to display. It took the parsed components and blindly concatenated them back into a string to be used in an href attribute. It failed to ask the most important question in web security: "Is this protocol actually safe?"
By supplying a URL that technically satisfies the parser but utilizes the javascript: pseudo-protocol, an attacker can turn a clickable link into a stored execution trigger. It’s a classic case of sanitization happening in the wrong place—or in this case, not happening at all.
Let's look at the crime scene in ui/src/app/shared/components/urls.ts. The vulnerable function repoUrl takes a string and tries to return a formatted URL.
The Vulnerable Code:
export function repoUrl(url: string): string {
try {
const parsed = GitUrlParse(url);
// The fatal flaw: Blind concatenation without validation
return `${protocol(parsed.protocol)}://${parsed.resource}/${parsed.owner}/${parsed.name}`;
} catch {
return null;
}
}See that return statement? It constructs a string using the protocol derived from the input. If I pass in javascript:alert(1), and git-url-parse is lenient enough to treat javascript as the protocol, the function happily returns a valid JS URI.
The Fix:
The patch is simple: validation. The maintainers introduced an isValidURL check that likely whitelists protocols like http, https, ssh, and git.
import {isValidURL} from '../../shared/utils';
export function repoUrl(url: string): string {
try {
const parsed = GitUrlParse(url);
const parsedUrl = `${protocol(parsed.protocol)}://${parsed.resource}/${parsed.owner}/${parsed.name}`;
// The Guard Rail
if (!isValidURL(parsedUrl)) {
return null;
}
return parsedUrl;
}
}It’s a three-line fix for a 9.1 CVSS vulnerability. Security is often boring like that.
So, how do we burn it down? We need two things: access to create/edit a repository, and an admin victim.
Step 1: The Setup
We authenticate as a low-privileged user (perhaps a developer with access to a specific project). We navigate to Settings -> Repositories or use the CLI to register a new repo. Instead of a valid GitHub URL, we provide our payload. The parser might be tricky, so we craft it to look like a valid Git URL structure but with a malicious protocol.
Step 2: The Payload
We inject something nasty. We don't want an alert box; we want persistence.
javascript:fetch('/api/v1/account/password', {method: 'PUT', body: JSON.stringify({currentPassword: '...', newPassword: 'owned'})})Or perhaps we simply delete the production application:
javascript:fetch('/api/v1/applications/prod-app?cascade=true', {method: 'DELETE'})Step 3: The Trap
The URL is saved to Kubernetes secrets. Now we wait. An Argo CD administrator logs in to check why a sync failed or to audit the repositories. They navigate to the Repositories page. The UI renders our malicious link. Depending on the browser behavior and the exact rendering, this might trigger on a stray click, or we could style the link to cover the whole screen using CSS injection if the inputs allow it.
Step 4: Game Over
The script executes in the context of the Admin. The browser sends the API request with the Admin's cookies/tokens. The API sees a valid request from a Super Admin. The cluster is yours.
If you are running a vulnerable version, you are one disgruntled employee or one compromised dev account away from total disaster.
Because Argo CD is declarative, this XSS allows an attacker to rewrite the "truth" of your infrastructure. They could inject a sidecar container into your payment processing pods to skim credit cards. They could modify the OIDC configuration to lock everyone out. They could simply wipe the cluster.
This isn't just a UI bug; it's a privilege escalation tunnel from "I can edit git repos" to "I am Root on the Cluster".
Don't try to get clever with WAF rules here; the payload is stored encrypted in your secrets and rendered by the client. You need to patch the binary.
Update Immediately:
If you cannot patch immediately, you must restrict RBAC permissions. Remove repositories, update and repositories, create from all non-admin roles. And for the love of Ops, audit your current repositories:
kubectl get secrets -n argocd -l argocd.argoproj.io/secret-type=repository -o jsonpath="{.items[*].data.url}" | base64 -dIf you see javascript: in there, pull the fire alarm.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Argo CD Argo Project | >= 1.2.0-rc1, < 2.13.8 | 2.13.8 |
Argo CD Argo Project | >= 2.14.0-rc1, < 2.14.13 | 2.14.13 |
Argo CD Argo Project | >= 3.0.0-rc1, < 3.0.4 | 3.0.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 (Cross-Site Scripting) |
| CVSS v3.1 | 9.1 (Critical) |
| Attack Vector | Network (Stored in Config) |
| Privileges Required | Low (Repo Edit) |
| Impact | Remote Code Execution (via API) |
| Patch Status | Available |
A local security vulnerability in the Nuxt development server (nuxt dev) allows local unprivileged users to access sensitive configuration files and source code. On Linux environments running Node.js 20+, Nuxt bound its internal vite-node IPC server to an abstract-namespace Unix socket without any peer authentication, enabling co-resident local users to connect and request module code directly.
Mozilla Bleach is an open-source HTML sanitizing library for Python. Versions up to and including 6.3.0 contain an incomplete filtering implementation in the URI validation logic ('sanitize_uri_value'). This logic fails to detect disallowed protocols, such as 'javascript:', if they contain Unicode invisible characters, whitespace characters, or characters with a code point greater than U+00A0. While standard-compliant web browsers do not directly execute invalid URI schemes containing these non-standard characters, downstream systems that normalize Unicode text by stripping invisible or non-ASCII characters can unintentionally reactivate the 'javascript:' prefix, causing Cross-Site Scripting (XSS). Additionally, this behavior violates Bleach's core sanitization contract by outputting URIs that bypass protocol allowlists configured by the caller.
An uncontrolled resource consumption vulnerability exists in the Python package Bleach when parsing text to linkify email addresses. When `parse_email=True` is enabled, the regular expression engine is forced into a quadratic-time complexity scan on specially crafted payloads lacking an '@' symbol. This causes immediate CPU exhaustion and blocks application server worker processes.
A path traversal and sandbox escape vulnerability in LangChain and LangChain-Anthropic Python packages allows unauthenticated local attackers to access files outside the restricted directory via crafted input, symbolic links, or prefix bypasses.
The PHP Secure Communications Library (phpseclib) contains a Server-Side Request Forgery (SSRF) vulnerability due to an insecure default implementation of Authority Information Access (AIA) certificate chasing. This flaw allows remote, unauthenticated attackers to coerce applications validating user-supplied X.509 certificates into generating arbitrary outbound HTTP requests to internal networks or local interfaces.
A directory traversal vulnerability exists in the Microsoft .NET System.Formats.Tar library during archive extraction. When extracting a TAR archive using the TarFile.ExtractToDirectory API, the extraction engine improperly resolves symbolic links prior to file creation, allowing local unauthorized attackers to write or overwrite arbitrary files outside the target directory. This can lead to local tampering, privilege escalation, or arbitrary code execution.