Mar 10, 2026·6 min read·8 visits
A case-sensitivity flaw in simple-git's security regex allows attackers to pass dangerous git configuration options, leading to arbitrary Remote Code Execution via the ext:: protocol.
The simple-git Node.js library versions 3.15.0 through 3.32.2 are vulnerable to unauthenticated Remote Code Execution (RCE). A flaw in the blockUnsafeOperationsPlugin allows attackers to bypass security controls by supplying configuration keys with mixed or uppercase characters. This configuration bypass permits the use of the ext:: protocol, which Git executes as an OS command, leading to complete system compromise.
The simple-git library is a widely used Node.js interface designed for executing Git commands within Javascript applications. Applications frequently use this library to automate repository management, handle clones, and process user-supplied branch names or repository URLs. When user input is insufficiently sanitized before reaching the simple-git execution context, attackers can inject malicious arguments into the underlying Git process.
CVE-2026-28292 is an OS Command Injection vulnerability (CWE-78) stemming from the Improper Handling of Case Sensitivity (CWE-178). The flaw exists in versions 3.15.0 through 3.32.2 of the package. It allows remote, unauthenticated attackers to execute arbitrary code on the host system running the Node.js application.
This vulnerability serves as a direct bypass of previous security measures implemented in simple-git, specifically the patches for CVE-2022-25860 and CVE-2022-25912. Those initial fixes introduced the blockUnsafeOperationsPlugin to explicitly deny known dangerous Git configuration overrides. The failure to account for case normalization in the underlying Git binary renders the previous protections entirely ineffective.
The vulnerability originates within the blockUnsafeOperationsPlugin, an internal mechanism of simple-git designed to validate arguments before they are passed to the child_process.spawn function. A specific function, preventProtocolOverride, is tasked with identifying and blocking attempts to set the protocol.allow Git configuration. This configuration key controls whether Git is permitted to execute helper programs via specific protocols.
The validation logic relied on a regular expression to detect the dangerous configuration flag. The original pattern was /^\s*protocol(.[a-z]+)?.allow/. This pattern checks for strings starting with optional whitespace, followed by "protocol", an optional submodule specification, and ".allow". This regular expression lacks the case-insensitive i flag. It only matches lowercase representations of the target string.
A discrepancy between the Node.js validation logic and the underlying Git binary execution creates the bypass condition. Git processes configuration keys in a strictly case-insensitive manner. The Git binary parses protocol.allow, PROTOCOL.ALLOW, and ProToCol.AlLoW identically, normalizing them to lowercase internally before applying the configuration changes.
By providing an uppercase or mixed-case string such as PROTOCOL.ALLOW=always, an attacker evades the simple-git regular expression filter. The plugin categorizes the input as benign and passes the arguments to the Git process. Git subsequently interprets the configuration, overriding the default security boundaries and enabling all protocols for the duration of the command execution.
The flaw resides in the src/lib/plugins/block-unsafe-operations-plugin.ts file within the simple-git source code. The preventProtocolOverride function evaluates each command-line argument passed to the library. If the argument matches the targeted configuration override, the function throws an error, halting the execution chain.
// Vulnerable implementation
function preventProtocolOverride(next) {
if (!/^\s*protocol(.[a-z]+)?.allow/.test(next)) {
return;
}
throw new Error('Unsafe operation blocked');
}The vulnerable implementation explicitly fails to match strings that contain uppercase characters. An attacker invoking git.raw(['-c', 'PROTOCOL.ALLOW=always', 'clone', 'ext::sh -c "id"']) bypasses the check because PROTOCOL.ALLOW=always does not test true against the lowercase-only regex.
The official patch, introduced in commit f7042088aa2dac59e3c49a84d7a2f4b26048a257, modifies a single character in the codebase. The maintainers appended the i flag to the regular expression, instructing the JavaScript engine to perform a case-insensitive match.
// Patched implementation
- if (!/^\s*protocol(.[a-z]+)?.allow/.test(next)) {
+ if (!/^\s*protocol(.[a-z]+)?.allow/i.test(next)) {This fix ensures that all variations of the protocol.allow string, regardless of capitalization, are caught and blocked by the plugin. The patch comprehensively closes the bypass vector by aligning the Node.js validation logic with Git's internal parsing behavior.
Exploitation requires the attacker to control at least a portion of the arguments passed to a simple-git command execution. The most direct vector involves the git.raw() method, which accepts an array of strings representing arguments. If an application constructs this array using unsanitized user input, the attacker can inject the -c flag followed by the case-manipulated configuration string.
Once the configuration bypass is achieved, the attacker must supply a malicious repository URL utilizing the ext:: protocol. The ext:: protocol in Git is designed to invoke an external command to establish a connection. When combined with the -c PROTOCOL.ALLOW=always override, Git executes the command specified in the URL string immediately upon processing the operation.
const simpleGit = require('simple-git');
const git = simpleGit();
const maliciousInput = [
'config',
'-c', 'PROTOCOL.ALLOW=always',
'clone', 'ext::sh -c "whoami > /tmp/pwned"', 'target_dir'
];
git.raw(...maliciousInput).catch(err => console.error(err));This proof-of-concept payload constructs an array that overrides the protocol restriction and instructs Git to clone from an ext:: URL. Git spawns a subshell (sh), executes the whoami command, and redirects the output to a file in the /tmp directory. This execution occurs with the privileges of the Node.js application process.
The successful exploitation of CVE-2026-28292 results in full Remote Code Execution (RCE) on the underlying host operating system. The arbitrary commands execute within the context of the user account running the Node.js application. This level of access grants the attacker the ability to interact with the filesystem, process environment, and local network.
An attacker can leverage this access to exfiltrate sensitive data, such as environment variables containing database credentials, API keys, or private cryptographic material. Read access to application source code and configuration files facilitates further horizontal movement within the infrastructure. The attacker can also establish persistence mechanisms by modifying local files or spawning background processes.
The CVSS v3.1 score of 9.8 reflects the severity of this vulnerability. The attack vector is remote (AV:N), the attack complexity is low (AC:L), and no privileges or user interaction are required (PR:N/UI:N). The impact on confidentiality, integrity, and availability is high across all three metrics (C:H/I:H/A:H). Applications exposing Git operations to untrusted network input are at immediate risk of complete compromise.
The primary remediation strategy requires upgrading the simple-git package to version 3.32.3 or later. This version incorporates the corrected regular expression, preventing the case-sensitivity bypass. Development teams must ensure all package lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml) reflect the patched version to guarantee the secure dependency is deployed to production environments.
For systems where an immediate upgrade is structurally impossible, development teams must implement rigorous input validation. Applications must never pass raw, unsanitized user input into the argument arrays of simple-git commands. Any user-supplied data intended for use as a repository URL, branch name, or configuration parameter must be strictly validated against an explicit allowlist of expected characters.
Security teams should review application codebases for instances of git.raw(), git.clone(), and git.fetch(). Static analysis tools can assist in identifying data flow paths from external inputs (such as HTTP request bodies or URL parameters) to these sinks. Blocking all instances of the -c flag and the ext:: protocol string at the application perimeter via a Web Application Firewall (WAF) provides a temporary defense-in-depth measure.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
simple-git steveukx | >= 3.15.0, <= 3.32.2 | 3.32.3 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | OS Command Injection |
| Attack Vector | Network |
| Authentication | None Required |
| CVSS v3.1 Score | 9.8 (Critical) |
| Exploit Status | Proof of Concept (PoC) Available |
| CWE IDs | CWE-78, CWE-178 |
| Fixed Version | 3.32.3 |
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.