---
id: "CVE-2026-102827"
kind: "vulnerability"
aliases: ["CVE-2026-102827", "GHSA-858h-whjf-mvg5"]
title: "simple-git: unsafe-operations plugin bypass via git long-option abbreviation (--receive-p/--exe) -> command execution (residual of CVE-2026-28291)"
severity: "critical"
cvss: 9.8
epss: 0.00576
exploited: false
status: "open"
category: "package"
products: [{"name": "simple-git", "fixed": ["4.0.0"], "affected": "<= 3.36.0", "ecosystem": "npm"}, {"name": "simple-git project simple-git", "fixed": ["4.0.0"], "affected": "< 4.0.0", "ecosystem": "vendor"}]
ecosystems: ["npm", "vendor"]
tags: ["cwe-77", "cwe-88", "git", "node", "npm", "package", "simple-git"]
not_affected_if: ["default-config-overridden", "no-user-input-to-push"]
published_at: "2026-09-29T19:17:25+00:00"
first_seen_at: "2026-10-31T16:25:54+00:00"
updated_at: "2026-10-31T16:36:24+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-858h-whjf-mvg5", "first_seen_at": "2026-10-31T16:25:54+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102827", "first_seen_at": "2026-10-31T16:36:24+00:00"}]
---

# simple-git: unsafe-operations plugin bypass via git long-option abbreviation (--receive-p/--exe) -> command execution (residual of CVE-2026-28291)

simple-git's default protection against dangerous git options misses abbreviated spellings such as --receive-p and --exe on git push. An attacker who influences push options or arguments can make git execute an arbitrary shell command on a local or file-path remote.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:simple-git affected <= 3.36.0 fixed 4.0.0
- vendor:simple-git project simple-git affected < 4.0.0 fixed 4.0.0

## How to check

- Check the installed version with `npm ls simple-git` and `npm ls @simple-git/argv-parser`; versions at or below `3.36.0` are affected.
- Search application code for `.push(` calls that pass user-influenced options or arguments to simple-git.
- Check whether the app pushes to local or file-path remotes.

## Fix

- `simple-git` fixed in `4.0.0` (affected up to `3.36.0`).

## Timeline

- 2026-10-31T16:25:54+00:00 ghsa https://github.com/advisories/GHSA-858h-whjf-mvg5
- 2026-10-31T16:36:24+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-102827

## Documents

- [advisory] Maintainer's advisory: api.github.com https://api.github.com/repos/steveukx/git-js/security-advisories/GHSA-858h-whjf-mvg5
- [advisory] Maintainer's advisory: github.com https://github.com/steveukx/git-js/security/advisories/GHSA-858h-whjf-mvg5
- [fix] Patch: github.com https://github.com/steveukx/git-js/pull/1193
- [fix] Patch: github.com https://github.com/steveukx/git-js/commit/98864c678444d9336357c844efa4fd5a7984c0d7
- [fix] Release: github.com https://github.com/steveukx/git-js/releases/tag/simple-git@4.0.0

## From the sources

simple-git's blockUnsafeOperationsPlugin blocks dangerous git options (--upload-pack/--receive-pack/--exec/...) unless the consumer opts in via unsafe:{allowUnsafePack:true}. Detection (@simple-git/argv-parser detectVulnerableFlags) matches the parsed flag NAME against literal-spelling patterns: /--(upload|receive)-pack/ (requires the literal '-pack') and the string '--exec'. But git accepts unambiguous prefix abbreviations of long options, and expandToken (token-expander.ts) returns the LITERAL token as flag.name (it uses the spec only for needsNext, never to canonicalize). So `git push --receive-p=<cmd>` parses with flag.name='--receive-p', which /--(upload|receive)-pack/ does NOT match, yet git expands --receive-p -> --receive-pack and runs <cmd> (on a local/file remote, locally). The clone side is robust (its '--u' substring rule catches every --upload* abbreviation); the push --receive-* and --exe* abbreviations have no equivalent rule and slip through.

Proof of concept (latest: simple-git 3.36.0, @simple-git/argv-parser 1.1.1, default unsafe plugin ON):
Gate vulnerabilityCheck: 'push --receive-pack=touch...' -> BLOCKED; 'push --receive-pa=touch...' and 'push --receive-p=touch...' -> BYPASS (empty vulns); '--exec=' BLOCKED, '--exe=' BYPASS.
End-to-end via simpleGit().push():
  CONTROL git.push(['../bare','HEAD:refs/heads/c','--receive-pack=touch /tmp/pwned_ctrl;']) -> throws GitPluginError 'Use of --upload-pack or --receive-pack is not permitted...'; no command runs.
  BYPASS  git.push(['../bare','HEAD:refs/heads/e2e','--receive-p=touch /tmp/pwned_e2e;']) -> NO GitPluginError; /tmp/pwned_e2e CREATED (git executed the injected command); only a later GitError surfaces.
Direct git confirms `git push ../bare --receive-p='touch X;' HEAD:refs/heads/m` and `--exe='touch Y;'` both execute on a local/path remote.

Impact: any app relying on simple-git's default unsafe-operations protection while passing attacker-influenced options/args into a git push (local/file remote, or attacker-influenced receive-pack target) can be made to execute arbitrary commands -- the exact protection CVE-2026-28291 provided, defeated by an abbreviated spelling. Same class/impact as GHSA-jcxm-m3jx-f287, on the push path.

Remediation: canonicalize git option abbreviations before matching (resolve to the canonical long name via the per-task flag spec in expandToken), or match on the option stem/prefix down to the shortest unambiguous form for push receive-pack/exec (mirroring the clone-side '--u' approach). Also audit --template and -c/config-write detection for the same abbreviation gap.

Credit: anir0y (independent security research).

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
