CVE-2026-102826
simple-git allows command execution through unblocked Git configuration includes
simple-git, an interface for running git commands in any node.js application, enables applications to execute Git operations from JavaScript. Prior to 4.0.0, the default blockUnsafeOperationsPlugin does not completely reject configuration includes supplied through customArgs to git.clone(). The missing include.path classification permits Git to load an attacker-controlled configuration file, and the initial remediation does not cover includeIf.<condition>.path, allowing the same file-loading primitive through a conditional include. A loaded configuration can set an executable Git option such as core.sshCommand, which Git invokes during the clone operation with the privileges of the Node.js process. Exploitation requires the application to pass attacker-influenced custom arguments and requires an attacker-controlled file that the process can read. This issue is fixed in 4.0.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.1
simple-git allows command execution through unblocked Git configuration includes
simple-git, an interface for running git commands in any node.js application, enables applications to execute Git operations from JavaScript. Prior to 4.0.0, the default blockUnsafeOperationsPlugin does not completely reject configuration includes supplied through customArgs to git.clone(). The missing include.path classification permits Git to load an attacker-controlled configuration file, and the initial remediation does not cover includeIf.<condition>.path, allowing the same file-loading primitive through a conditional include. A loaded configuration can set an executable Git option such as core.sshCommand, which Git invokes during the clone operation with the privileges of the Node.js process. Exploitation requires the application to pass attacker-influenced custom arguments and requires an attacker-controlled file that the process can read. This issue is fixed in 4.0.0.
- Record
CVE-2026-102826- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-77, CWE-78
- Updated
- 2026-11-02
EUVDEUVD-2026-89332
8.1
simple-git, an interface for running git commands in any node.js application, enables applications to execute Git operations from JavaScript. Prior to 4.0.0, the default blockUnsafeOperationsPlugin does not completely reject configuration includes supplied through customArgs to git.clone(). The missing include.path classification permits Git to load an attacker-controlled configuration file, and the initial remediation does not cover includeIf.<condition>.path, allowing the same file-loading primitive through a conditional include. A loaded configuration can set an executable Git option such as core.sshCommand, which Git invokes during the clone operation with the privileges of the Node.js process. Exploitation requires the application to pass attacker-influenced custom arguments and requires an attacker-controlled file that the process can read. This issue is fixed in 4.0.0.
- Record
EUVD-2026-89332- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-g4wm-2vf7-vfgr
8.1
simple-git allows command execution through unblocked Git configuration includes
Summary
An OS command injection vulnerability in git.clone() allows any application that flows attacker-influenced data into customArgs to execute arbitrary code. simple-git 3.36.0 (current latest on npm) ships without any include.path entry in the blockUnsafeOperationsPlugin denylist. Passing -c include.path=<file> via customArgs loads any local file as a gitconfig. The loaded file can set core.sshCommand (or any otherwise-denied key), and the next remote operation in the same clone executes the attacker's command.
PR #1167 (merged to main 2026-05-10, not yet released to npm) adds preventConfigBuilder('include.path', 'allowUnsafeInclude') to the denylist. The generated regex /\s*include.path/ closes the plain spelling but does not match the conditional form includeIf.<cond>.path. The variant therefore survives the upcoming release if the regex is not tightened in the same cycle.
This sits in the same denylist class as the prior incomplete-fix chain (CVE-2022-24433, CVE-2022-24066, CVE-2022-25912, CVE-2022-25860, CVE-2026-28291, CVE-2026-28292). include and includeIf are not referenced in any published advisory, in any commit prior to PR #1167, or anywhere in the 3.36.0 source.
Details
Two sinks share the same root cause: the denylist is incomplete.
Sink A: published 3.36.0 has no include.path entry
packages/argv-parser/src/vulnerabilities/detect-vulnerable-config-writes.ts in the v3.36.0 tag contains no entry for include.path or includeIf.*.path. The argv parser recognises -c include.path=<file> and -c includeIf.<cond>.path=<file> as config writes, but detectVulnerableConfigWrites iterates a denylist that does not include either key. The plugin returns no vulnerability and the operation proceeds.
Sink B: pending PR #1167 regex misses includeIf
PR #1167 adds:
const preventUnsafeConfig = [
// ...
preventConfigBuilder('include.path', 'allowUnsafeInclude'),
// ...
];
preventConfigBuilder constructs a non-anchored regex from the string:
function preventConfigBuilder(config, category, message) {
const regex = typeof config === 'string'
? new RegExp(`\\s*${config.toLowerCase()}`)
: config;
return function preventCommand(key) {
if (regex.test(key)) { /* throw */ }
};
}
For 'include.path', the generated regex is /\s*include.path/. The . between include and path is a regex wildcard. The engine matches include plus exactly one arbitrary character plus path. Conditional include keys have the form includeIf.<condition>.path (includeIf.gitdir:.path, includeIf.onbranch:main.path, includeIf.hasconfig:r.u:**.path, etc.). The substring between include and path is if.<condition>:, always longer than one character. The 11-character match window cannot align and the test returns false.
/\s*include.path/.test('include.path') // true
/\s*include.path/.test('includeif.gitdir:.path') // false
/\s*include.path/.test('includeif.onbranch:main.path') // false
The argv parser at packages/argv-parser/src/argv/analyse-config.ts correctly recognises both include.path=... and includeIf.gitdir:.path=... as config writes; both yield a ConfigWrite with the lowercased key. The defect is purely in the denylist regex (after PR #1167) and in the entry being absent (before PR #1167).
Exploitation chain
-
Attacker writes a gitconfig to any path the simple-git process can read. Realistic write primitives: file upload (avatar, attachment, CI artifact, S3-mounted bucket), shared
/tmpin multi-tenant runners, log poisoning that lands[core]headers in a log path, predictable artifact paths, container volume mounts the attacker controls.[core] sshCommand = "/bin/sh -c 'id > /tmp/pwned; touch /tmp/RCE'" -
Attacker triggers
git.clone()with craftedcustomArgs. Either the URL or the customArgs flow from attacker-influenced input. This is…
- Record
GHSA-g4wm-2vf7-vfgr- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-77, CWE-78
- Updated
- 2026-11-02
NVD
8.1
simple-git, an interface for running git commands in any node.js application, enables applications to execute Git operations from JavaScript. Prior to 4.0.0, the default blockUnsafeOperationsPlugin does not completely reject configuration includes supplied through customArgs to git.clone(). The missing include.path classification permits Git to load an attacker-controlled configuration file, and the initial remediation does not cover includeIf.<condition>.path, allowing the same file-loading primitive through a conditional include. A loaded configuration can set an executable Git option such as core.sshCommand, which Git invokes during the clone operation with the privileges of the Node.js process. Exploitation requires the application to pass attacker-influenced custom arguments and requires an attacker-controlled file that the process can read. This issue is fixed in 4.0.0.
- Record
CVE-2026-102826- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-77, CWE-78
- Updated
- 2026-11-02
OSV
8.1
simple-git allows command execution through unblocked Git configuration includes
simple-git, an interface for running git commands in any node.js application, enables applications to execute Git operations from JavaScript. Prior to 4.0.0, the default blockUnsafeOperationsPlugin does not completely reject configuration includes supplied through customArgs to git.clone(). The missing include.path classification permits Git to load an attacker-controlled configuration file, and the initial remediation does not cover includeIf.<condition>.path, allowing the same file-loading primitive through a conditional include. A loaded configuration can set an executable Git option such as core.sshCommand, which Git invokes during the clone operation with the privileges of the Node.js process. Exploitation requires the application to pass attacker-influenced custom arguments and requires an attacker-controlled file that the process can read. This issue is fixed in 4.0.0.
- Record
CVE-2026-102826- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-77, CWE-78
- Updated
- 2026-11-02
OSVGHSA-g4wm-2vf7-vfgr
8.1
simple-git allows command execution through unblocked Git configuration includes
Summary
An OS command injection vulnerability in git.clone() allows any application that flows attacker-influenced data into customArgs to execute arbitrary code. simple-git 3.36.0 (current latest on npm) ships without any include.path entry in the blockUnsafeOperationsPlugin denylist. Passing -c include.path=<file> via customArgs loads any local file as a gitconfig. The loaded file can set core.sshCommand (or any otherwise-denied key), and the next remote operation in the same clone executes the attacker's command.
PR #1167 (merged to main 2026-05-10, not yet released to npm) adds preventConfigBuilder('include.path', 'allowUnsafeInclude') to the denylist. The generated regex /\s*include.path/ closes the plain spelling but does not match the conditional form includeIf.<cond>.path. The variant therefore survives the upcoming release if the regex is not tightened in the same cycle.
This sits in the same denylist class as the prior incomplete-fix chain (CVE-2022-24433, CVE-2022-24066, CVE-2022-25912, CVE-2022-25860, CVE-2026-28291, CVE-2026-28292). include and includeIf are not referenced in any published advisory, in any commit prior to PR #1167, or anywhere in the 3.36.0 source.
Details
Two sinks share the same root cause: the denylist is incomplete.
Sink A: published 3.36.0 has no include.path entry
packages/argv-parser/src/vulnerabilities/detect-vulnerable-config-writes.ts in the v3.36.0 tag contains no entry for include.path or includeIf.*.path. The argv parser recognises -c include.path=<file> and -c includeIf.<cond>.path=<file> as config writes, but detectVulnerableConfigWrites iterates a denylist that does not include either key. The plugin returns no vulnerability and the operation proceeds.
Sink B: pending PR #1167 regex misses includeIf
PR #1167 adds:
const preventUnsafeConfig = [
// ...
preventConfigBuilder('include.path', 'allowUnsafeInclude'),
// ...
];
preventConfigBuilder constructs a non-anchored regex from the string:
function preventConfigBuilder(config, category, message) {
const regex = typeof config === 'string'
? new RegExp(`\\s*${config.toLowerCase()}`)
: config;
return function preventCommand(key) {
if (regex.test(key)) { /* throw */ }
};
}
For 'include.path', the generated regex is /\s*include.path/. The . between include and path is a regex wildcard. The engine matches include plus exactly one arbitrary character plus path. Conditional include keys have the form includeIf.<condition>.path (includeIf.gitdir:.path, includeIf.onbranch:main.path, includeIf.hasconfig:r.u:**.path, etc.). The substring between include and path is if.<condition>:, always longer than one character. The 11-character match window cannot align and the test returns false.
/\s*include.path/.test('include.path') // true
/\s*include.path/.test('includeif.gitdir:.path') // false
/\s*include.path/.test('includeif.onbranch:main.path') // false
The argv parser at packages/argv-parser/src/argv/analyse-config.ts correctly recognises both include.path=... and includeIf.gitdir:.path=... as config writes; both yield a ConfigWrite with the lowercased key. The defect is purely in the denylist regex (after PR #1167) and in the entry being absent (before PR #1167).
Exploitation chain
-
Attacker writes a gitconfig to any path the simple-git process can read. Realistic write primitives: file upload (avatar, attachment, CI artifact, S3-mounted bucket), shared
/tmpin multi-tenant runners, log poisoning that lands[core]headers in a log path, predictable artifact paths, container volume mounts the attacker controls.[core] sshCommand = "/bin/sh -c 'id > /tmp/pwned; touch /tmp/RCE'" -
Attacker triggers
git.clone()with craftedcustomArgs. Either the URL or the customArgs flow from attacker-influenced input. This is…
- Record
GHSA-g4wm-2vf7-vfgr- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-77, CWE-78
- Updated
- 2026-11-02
OSVCLSA-2026-1791293888 No score
TuxCare security update for simple-git (2 CVEs)
TuxCare rebuilt simple-git to address 2 CVEs. Fixed in 3.36.0-tuxcare.1.
- Record
CLSA-2026-1791293888- Updated
- 2026-11-02
OSVCLSA-2026-1792758525 No score
TuxCare security update for @simple-git/argv-parser (3 CVEs)
TuxCare rebuilt @simple-git/argv-parser to address 3 CVEs. Fixed in 1.1.1-tuxcare.2.
- Record
CLSA-2026-1792758525- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
simple-gitnpm |
through 3.36.0 | 4.0.0 |
simple-gitTuxCare:npm From an advisory covering 2 CVEs |
3.36.0-tuxcare.0 up to 3.36.0-tuxcare.1, plus listed versions 3.36.0 |
3.36.0-tuxcare.1 |
@simple-git/argv-parserTuxCare:npm From an advisory covering 3 CVEs |
1.1.1-tuxcare.0 up to 1.1.1-tuxcare.2, plus listed versions 1.1.11.1.1-tuxcare.1 |
1.1.1-tuxcare.2 |
simple-gitnpm |
before 4.0.0 | 4.0.0 |
Public exploits
Links only. Every CVE stores no exploit code.
| Source | Link | Date |
|---|---|---|
| PoC in GitHub | CompleteTech-Security/simple-git-rce-check | 2026-10-23 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, aliases | ||
| affected | ||
| affected, facts | OSV | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV, PoC in GitHub |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-102826 is a high vulnerability (CVSS 8.1) in Simple-git Project Simple-git, published September 29, 2026. Public exploit code exists. EPSS puts the chance of exploitation in the next 30 days at 0.46%, the 40th percentile. Fixed in simple-git 4.0.0.
References
github.com/steveukx/git-js/security/advisories/GHSA-g4wm-2vf7-vfgr
github.com/steveukx/git-js/pull/1193
github.com/steveukx/git-js/commit/98864c678444d9336357c844efa4fd5a7984c0d7
github.com/steveukx/git-js/releases/tag/[email protected]
nvd.nist.gov/vuln/detail/CVE-2026-102826
github.com/advisories/GHSA-g4wm-2vf7-vfgr
github.com/CVEProject/cvelistV5/tree/main/cves/2026/102xxx/CVE-2026-102826.json
github.com/steveukx/git-js
Left out here: the rest of 2 source descriptions. The JSON record has them.