---
id: 2026-09-simple-git-unsafe-operations-guard-bypass-cve-batch
title: "simple-git (the Node.js git wrapper, 15.1M weekly downloads): four vendor advisories on 2026-09-26 — the default blockUnsafeOperationsPlugin missed git's `include.path`/`includeIf` config includes (CVE-2026-102826, 8.1), long-option abbreviations like `--receive-p` (CVE-2026-102827, 8.1), `trailer.<token>.cmd` (CVE-2026-102828, 9.2) and the `VISUAL` editor variable (CVE-2026-102829, 9.2) — each is OS command execution when an application lets untrusted data reach git's arguments, config or environment; fixed in 4.0.0 / 4.0.1 and @simple-git/argv-parser 2.0.1, and 4.0 now blocks ambient environment variables by default"
date_disclosed: 2026-09-26
last_updated: 2026-10-06
severity: critical
status: patched
ecosystems: [npm, javascript, git, ai-agents]
tools_affected: ["simple-git ≤ 3.36.0 (CVE-2026-102826, -102827), 3.15.0 – 4.0.0 (CVE-2026-102828)", "@simple-git/argv-parser < 2.0.1 (CVE-2026-102829)", "any Node.js service, bot, CI tool or AI coding agent that calls simple-git with user- or model-influenced arguments, config values or environment"]
tags: [cve-batch, command-injection, git, config-injection, denylist-bypass, incomplete-fix, npm, agent-tooling, vendor-ghsa]
---

## TL;DR

**simple-git** is the library most Node.js code uses to run git — **15,136,117 downloads in the week ending 2026-10-04** — including a lot of the bots, CI helpers and coding agents that clone repositories or commit on a user's behalf. Its `blockUnsafeOperationsPlugin` is a denylist of git options and config keys that can execute commands (`--upload-pack`, `core.sshCommand`, `protocol.allow`, `ext::` …), on by default since 3.15.0. On **2026-09-26** the maintainer published **four advisories** for things the denylist did not know about, and GitHub (as CNA) issued CVEs on 09-29; the database's reviewed `npm` critical listing surfaced them on 10-05:

| Gap in the denylist | Id | Score | Affected → fixed |
|---|---|---|---|
| **Git config includes.** `-c include.path=<file>` loaded an arbitrary gitconfig, which can carry `core.sshCommand`; a pending fix's regex `/\s*include.path/` also missed the conditional `includeIf.gitdir:…path` form | [GHSA-g4wm-2vf7-vfgr](https://github.com/advisories/GHSA-g4wm-2vf7-vfgr) / **CVE-2026-102826** | 8.1 (3.1, AC:H) | `≤ 3.36.0` → **4.0.0** |
| **Long-option abbreviation.** Git accepts any unambiguous prefix, so `--receive-p` and `--exe` passed a filter matching `--receive-pack` and `--exec`; "a residual" of CVE-2026-28291 | [GHSA-858h-whjf-mvg5](https://github.com/advisories/GHSA-858h-whjf-mvg5) / **CVE-2026-102827** | 8.1 (3.1, AC:H) | `≤ 3.36.0` → **4.0.0** |
| **`trailer.<token>.cmd`.** Documented by git as "a shell command invoked by `git interpret-trailers`"; not classified as unsafe config | [GHSA-x6jw-m9v5-85vh](https://github.com/advisories/GHSA-x6jw-m9v5-85vh) / **CVE-2026-102828** | **9.2** (4.0, AT:P) | `≥ 3.15.0, < 4.0.1` → **4.0.1** |
| **`VISUAL`.** The env parser classified `EDITOR`, `GIT_EDITOR` and `GIT_SEQUENCE_EDITOR` as unsafe but dropped `VISUAL` before classification, so a forwarded `VISUAL=<binary>` ran on `commit --amend` or `rebase -i` | [GHSA-v5rq-49vh-5v5c](https://github.com/advisories/GHSA-v5rq-49vh-5v5c) / **CVE-2026-102829** | **9.2** (4.0, AT:P) | `@simple-git/argv-parser < 2.0.1` → **2.0.1** |

The registry shows `simple-git@4.0.0` published **2026-09-25 10:38 UTC**, `4.0.1` at 11:03, `4.0.2` on 09-26, and `@simple-git/argv-parser@2.0.1` on 09-26; `latest` is **4.0.2**. The 4.0 release is a **breaking major**: abbreviated long options now throw `GitConfigurationError`, ambient environment variables are **no longer passed to the git child process** unless listed in a new allow-list, and the unsafe ones need a second opt-in. That is the right design — it closes the whole class the four advisories sample — and it means upgrading is a code change, not a version bump.

## What happened

**The precondition is the same in all four**: the application passes something an attacker influenced into simple-git — `customArgs`, `SimpleGitOptions.config`, inline `-c` values, or the process environment — and relies on the plugin to stop the dangerous ones. The vendor's own impact statements are careful about this: CVE-2026-102828 says risk "is limited to applications passing untrusted data into simple-git configuration or command arguments," CVE-2026-102826's chain also needs attacker-writable file storage the process can read, and CVE-2026-102829 needs the application to forward untrusted environment values. Each CVSS vector carries `AC:H` or `AT:P` for that reason. Where the precondition holds, the result is a shell command running "with the operating-system identity and permissions of the Node.js process."

**This is the third round.** The denylist was already bypassed twice this year: **CVE-2026-28291** ([GHSA-jcxm-m3jx-f287](https://github.com/steveukx/git-js/security/advisories/GHSA-jcxm-m3jx-f287), 8.1, `< 3.32.0`, 2026-04-12) found three-character option variants (`--u`, `-4u`, `-lu` …) that reached `--upload-pack`, itself an incomplete patch for CVE-2022-25860; **CVE-2026-28292** ([GHSA-r275-fr43-pm7q](https://github.com/advisories/GHSA-r275-fr43-pm7q), 9.8, `3.15.0 – < 3.32.3`, same day, CodeAnt AI) found the `protocol.allow` regex was case-sensitive while git lower-cases config keys, so `PROTOCOL.ALLOW=always` re-enabled `ext::`. September's four are the same shape — git has more spellings than the list — and the 4.0 redesign (block the environment by default, reject abbreviations outright) is the maintainer declining to keep playing it.

**Why the agent angle.** A coding agent that commits, pushes, clones a repository a user named, or applies a `.gitconfig`-shaped suggestion is exactly an application that passes model- or user-influenced strings to git. This repo's [GitSpawn cluster](2026-09-gitspawn-git-config-agent-rce-cluster.md) is the same family one layer up: git config values that execute, reached through an agent. simple-git sits underneath many of those tools in Node; whether a given tool is exposed depends on which of its inputs reach `git()` arguments, which no advisory here enumerates and this sweep did not attempt to.

## Am I affected?

```bash
npm ls simple-git @simple-git/argv-parser --all 2>/dev/null
grep -n '"simple-git"\|"@simple-git/argv-parser"' package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null | head
# Where do arguments, config or env reach git?
grep -rn "customArgs\|\.raw(\|\.env(\|config:\s*\[" --include='*.ts' --include='*.js' src/ 2>/dev/null | head -40
grep -rn "allowUnsafe\|unsafe:\s*{" --include='*.ts' --include='*.js' src/ 2>/dev/null
```

- `simple-git` **≤ 3.36.0** → CVE-2026-102826 and -102827; **3.15.0 through 4.0.0** → CVE-2026-102828; `@simple-git/argv-parser` **< 2.0.1** (the transitive dependency `4.0.0` still pulled `2.0.0`) → CVE-2026-102829.
- Any `unsafe: { … }` allowance you set deliberately turns the corresponding guard off; the advisories describe default-configuration bypasses, so an explicit allowance is already a wider exposure.
- Not affected if no untrusted value can reach arguments, config or the environment of the git child process — a CLI you run by hand on your own inputs qualifies; a web service, bot or agent generally does not.

## If you are affected

1. **Upgrade to `simple-git@4.0.2`** (which brings `@simple-git/argv-parser@2.0.1`). Read the 4.0.0 release notes first: the default export and `gitP` are gone (`import { simpleGit } from "simple-git"`), abbreviated long options throw, and **environment variables you relied on reaching git (`GIT_SSH_COMMAND`, `GIT_CONFIG_*`, proxies, credential helpers) must now be allow-listed** — the vendor spells the option `allowEnvoronment` in the notes.
2. If you cannot take the major yet, **stop passing untrusted strings into `customArgs`, `config` and the environment** — that removes the precondition for all six CVEs in this file's history, not just the four new ones.
3. **Audit for exploitation** where untrusted input did reach git: look for `-c include.path`, `includeIf`, `trailer.`, `--receive-p`, `--exe`, `protocol.allow` or a `VISUAL=` assignment in request logs, job parameters or agent transcripts, and for unexpected child processes of your Node service — [if-your-local-ai-agent-was-exploited.md](../playbooks/if-your-local-ai-agent-was-exploited.md) covers the agent case, [if-your-webapp-was-compromised.md](../playbooks/if-your-webapp-was-compromised.md) the service case.
4. Treat credentials the Node process could read (deploy keys, tokens in env) as exposed if the audit is inconclusive — [rotating-cloud-credentials.md](../playbooks/rotating-cloud-credentials.md).

## Prevention

- **A denylist in front of git is not a security boundary.** Git accepts option prefixes, case-insensitive config keys, includes, trailers and a dozen `*.cmd`/`*Command` keys; the fixes here went from patching the list to removing the inputs. Do the same in your own wrappers: fixed argument vectors, no `-c` from untrusted data, a minimal explicit environment — [prevention/agent-sandboxing.md](../prevention/agent-sandboxing.md).
- Run git-driving services and agents as a low-privilege user in a container with no credentials beyond the one repository they need, so the "privileges of the Node.js process" clause is worth little.
- Keep `simple-git` on the current major and watch its advisory tab: six command-execution advisories in six months, each an incomplete fix of the last, is a dependency whose releases need reading — [prevention/supply-chain-attack-surface.md](../prevention/supply-chain-attack-surface.md).

## Why this matters for vibe coders

The generated "GitHub bot" or "auto-commit agent" almost always imports simple-git, almost always builds a `clone`/`commit` call from a string the user or the model supplied, and almost always runs with the token that can push. The library's safety plugin was the thing standing between that string and a shell, and it has now been bypassed six ways this year. The 4.0 upgrade is worth the breaking change precisely because it stops depending on the list.

## Sources

- [GHSA-g4wm-2vf7-vfgr — simple-git allows command execution through unblocked Git configuration includes (CVE-2026-102826)](https://github.com/advisories/GHSA-g4wm-2vf7-vfgr) — fetched 2026-10-06: 8.1 `AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H`, `≤ 3.36.0` → 4.0.0, Sink A (`include.path` absent from the denylist) and Sink B (the `/\s*include.path/` regex missing `includeIf`), the `core.sshCommand` chain and its attacker-writable-storage precondition, PR #1193, finders bhaswanthc and NotAFlightRisk, published 2026-09-26.
- [GHSA-858h-whjf-mvg5 — unsafe-operations plugin bypass via git long-option abbreviation (CVE-2026-102827)](https://github.com/advisories/GHSA-858h-whjf-mvg5) — fetched 2026-10-06: 8.1, `≤ 3.36.0` → 4.0.0, `--receive-p` / `--exe`, "a residual" of CVE-2026-28291, finder anir0y plus nine additional reporters, published 2026-09-26.
- [GHSA-x6jw-m9v5-85vh — unsafe-operation guard does not block trailer command configuration (CVE-2026-102828)](https://github.com/advisories/GHSA-x6jw-m9v5-85vh) — fetched 2026-10-06: 9.2 `CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H`, `≥ 3.15.0, < 4.0.1` → 4.0.1, the git documentation quote, the "limited to applications passing untrusted data" impact statement, reporter sec-reex, published 2026-09-26.
- [GHSA-v5rq-49vh-5v5c — `VISUAL` editor environment variable omitted from unsafe editor detection (CVE-2026-102829)](https://github.com/advisories/GHSA-v5rq-49vh-5v5c) — fetched 2026-10-06: 9.2 (4.0), `@simple-git/argv-parser < 2.0.1` → 2.0.1, the `GitEnvKeys` mapping in `parse-env.ts`, `commit --amend` / `rebase -i`, PR #1201, published 2026-09-26.
- [NVD API — CVE-2026-102826, CVE-2026-102828, CVE-2026-102829](https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-102828) — fetched 2026-10-06: all three published 2026-09-29 by `security-advisories@github.com`, scores as above, references to the fix commits (98864c6, d762810c, 68874c23), PRs #1193 / #1198 / #1201 and the release tags.
- [steveukx/git-js — security advisories tab](https://github.com/steveukx/git-js/security/advisories) — fetched 2026-10-06: the four September entries plus the two April ones.
- [GHSA-jcxm-m3jx-f287 — Command Execution via Option-Parsing Bypass in simple-git (CVE-2026-28291)](https://github.com/steveukx/git-js/security/advisories/GHSA-jcxm-m3jx-f287) — fetched 2026-10-06: 8.1, `< 3.32.0`, the three-character option variants, incomplete fix of CVE-2022-25860, reporter JuHwiSang, published 2026-04-12.
- [GHSA-r275-fr43-pm7q — blockUnsafeOperationsPlugin bypass via case-insensitive protocol.allow (CVE-2026-28292)](https://github.com/advisories/GHSA-r275-fr43-pm7q) — fetched 2026-10-06: 9.8, `≥ 3.15.0, < 3.32.3` → 3.32.3, the missing `/i` flag, `PROTOCOL.ALLOW=always` → `ext::`, reporter CodeAnt-AI-Security, published 2026-04-12.
- [steveukx/git-js — release simple-git@4.0.0](https://github.com/steveukx/git-js/releases/tag/simple-git@4.0.0) — fetched 2026-10-06: the breaking changes (default export removed, abbreviated long options throw `GitConfigurationError`, ambient environment blocked by default with an `allowEnvoronment` allow-list, dual opt-in for unsafe variables, `allowUnsafeExec` detection for `rebase -x`/`--exec`).
- [npm registry — `simple-git` and `@simple-git/argv-parser` `time`, `dist-tags`](https://registry.npmjs.org/simple-git) — queried 2026-10-06: 3.32.3 2026-02-26, 3.36.0 2026-04-12, 4.0.0 2026-09-25 10:38 UTC, 4.0.1 11:03 UTC, 4.0.2 2026-09-26; argv-parser 2.0.0 2026-09-25, 2.0.1 2026-09-26; `latest` 4.0.2.
- [npm downloads API — `simple-git` (week ending 2026-10-04)](https://api.npmjs.org/downloads/point/last-week/simple-git) — 15,136,117.
- [GitHub Advisory Database — reviewed `npm` critical listing](https://github.com/advisories?query=type%3Areviewed+ecosystem%3Anpm+severity%3Acritical) — fetched 2026-10-06: CVE-2026-102828 and -102829 listed Oct 5 (database date).
- Related in this repo: [GitSpawn git-config agent RCE cluster](2026-09-gitspawn-git-config-agent-rce-cluster.md), [GuardFall shell-injection guard bypasses](2026-06-guardfall-shell-injection-agents.md).
