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 / 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 / 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 / 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 / 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, 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, 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 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?

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 covers the agent case, 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.

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.
  • 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.

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