GHSA-CW2G-2VWQ-88W6
Vulnerability from github – Published: 2026-11-09 20:13 – Updated: 2026-11-09 20:13Summary
isomorphic-git's tree parser (src/models/GitTree.js) validates each entry name only against the path separators / and \. It does not reject the standalone reserved name .., so a malicious repository can, on a single clone/checkout, write files outside the working
directory (unbounded depth). Works on any host-backed fs provider.
Details
The guard is:
// src/models/GitTree.js
if (path.includes('\\') || path.includes('/')) {
throw new UnsafeFilepathError(path)
}
The standalone string .. passes. isomorphic-git assembles the full path incrementally across tree levels, so nesting .. trees yields ../escaped.txt, ../../escaped.txt, … with unbounded depth (each level is a separate tree object that passes the per-entry guard). On checkout mkdir(workdir/..) hits EEXIST (ignored) and the file is written above the working directory. Depth is chosen to reach the home dir, /etc, /tmp, etc.; overwriting a shell rc / config / startup file can escalate to code execution.
Canonical git behavior (divergence)
git rejects this via verify_path(): git read-tree fails with error: invalid path '../escaped.txt' and git fsck flags warning … hasDotdot: contains '..'. (git's verify_path also rejects . and .git, the latter flagged by fsck as hasDotgit — see the fix note below.) GitHub runs fsck on push, so such a tree can't be hosted there; reach is limited to a self-hosted/Smart-HTTP remote or a MitM.
PoC
https://github.com/spiceoverflow/av2-dotdot-traversal — open in a Codespace; a single git.clone() from the bundled server writes escaped.txt one directory above the clone target (DEPTH=N escapes N levels):
[AV-2] *** PATH TRAVERSAL CONFIRMED ***
[AV-2] File written outside workdir: …/escaped.txt
Impact
A path-traversal arbitrary file write outside the working directory (CWE-22), triggered by a single clone/checkout; it can escalate to code execution by overwriting shell rc, config, or startup files.
Any application or user that clones/checks out an untrusted (or compromised / MitM'd) repository with isomorphic-git on a host-backed filesystem (Linux, macOS, or Windows — no symlink or privilege required). Requires a non-GitHub remote (self-hosted / Smart-HTTP) or a MitM, since GitHub's fsck rejects trees containing ...
Suggested fix
In the tree parser, reject any entry name equal to . or ... For full verify_path parity it should also reject .git (case-insensitively, trailing dots/spaces stripped) and the NTFS 8.3 / HFS variants git blocks. Note: a literal .git entry is not independently exploitable via
clone/checkout in current isomorphic-git — the checkout walk skips a .git tree entry (a sibling file is written but the .git entry is not), so rejecting .git is defense-in-depth / git parity rather than a separately demonstrated bug. Additionally, normalize and bounds-check the assembled full path against the working-tree root before any filesystem write.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.38.5"
},
"package": {
"ecosystem": "npm",
"name": "isomorphic-git"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.38.6"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-63478"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2026-11-09T20:13:52Z",
"nvd_published_at": "2026-11-03T20:18:16Z",
"severity": "MODERATE"
},
"details": "### Summary\nisomorphic-git\u0027s tree parser (`src/models/GitTree.js`) validates each entry name only against the path separators `/` and `\\`. It does not reject the standalone reserved name `..`, so a malicious repository can, on a single `clone`/`checkout`, **write files outside the working\ndirectory** (unbounded depth). Works on any host-backed `fs` provider.\n\n### Details\nThe guard is:\n\n```js\n// src/models/GitTree.js\nif (path.includes(\u0027\\\\\u0027) || path.includes(\u0027/\u0027)) {\n throw new UnsafeFilepathError(path)\n}\n```\n\nThe standalone string `..` passes. isomorphic-git assembles the full path incrementally across tree levels, so nesting `..` trees yields `../escaped.txt`, `../../escaped.txt`, \u2026 with unbounded depth (each level is a separate tree object that passes the per-entry guard). On checkout `mkdir(workdir/..)` hits `EEXIST` (ignored) and the file is written above the working directory. Depth is chosen to reach the home dir, `/etc`, `/tmp`, etc.; overwriting a shell rc / config / startup file can escalate to code execution.\n\n#### Canonical git behavior (divergence)\ngit rejects this via `verify_path()`: `git read-tree` fails with `error: invalid path \u0027../escaped.txt\u0027` and `git fsck` flags `warning \u2026 hasDotdot: contains \u0027..\u0027`. (git\u0027s `verify_path` also rejects `.` and `.git`, the latter flagged by fsck as `hasDotgit` \u2014 see the fix note below.) GitHub runs fsck on push, so such a tree can\u0027t be hosted there; reach is limited to a self-hosted/Smart-HTTP remote or a MitM.\n\n\n### PoC\n`https://github.com/spiceoverflow/av2-dotdot-traversal` \u2014 open in a Codespace; a single `git.clone()` from the bundled server writes `escaped.txt` one directory above the clone target (`DEPTH=N` escapes N levels):\n\n```\n[AV-2] *** PATH TRAVERSAL CONFIRMED ***\n[AV-2] File written outside workdir: \u2026/escaped.txt\n```\n\n### Impact\nA path-traversal arbitrary file write outside the working directory (CWE-22), triggered by a single clone/checkout; it can escalate to code execution by overwriting shell rc, config, or startup files.\n\nAny application or user that clones/checks out an untrusted (or compromised / MitM\u0027d) repository with isomorphic-git on a host-backed filesystem (**Linux, macOS, or Windows** \u2014 no symlink or privilege required). Requires a non-GitHub remote (self-hosted / Smart-HTTP) or a MitM, since GitHub\u0027s fsck rejects trees containing `..`.\n\n### Suggested fix\nIn the tree parser, reject any entry name equal to `.` or `..`. For full `verify_path` parity it should **also** reject `.git` (case-insensitively, trailing dots/spaces stripped) and the NTFS 8.3 / HFS variants git blocks. Note: a literal `.git` entry is *not* independently exploitable via\n`clone`/`checkout` in current isomorphic-git \u2014 the checkout walk skips a `.git` tree entry (a sibling file is written but the `.git` entry is not), so rejecting `.git` is defense-in-depth / git parity rather than a separately demonstrated bug. Additionally, normalize and bounds-check the assembled full path against the working-tree root before any filesystem write.",
"id": "GHSA-cw2g-2vwq-88w6",
"modified": "2026-11-09T20:13:52Z",
"published": "2026-11-09T20:13:52Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/isomorphic-git/isomorphic-git/security/advisories/GHSA-cw2g-2vwq-88w6"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-63478"
},
{
"type": "WEB",
"url": "https://github.com/isomorphic-git/isomorphic-git/pull/2370"
},
{
"type": "WEB",
"url": "https://github.com/isomorphic-git/isomorphic-git/commit/823843d1cd19182df79bc907c767f5bcecb39a0b"
},
{
"type": "PACKAGE",
"url": "https://github.com/isomorphic-git/isomorphic-git"
},
{
"type": "WEB",
"url": "https://github.com/isomorphic-git/isomorphic-git/releases/tag/v1.38.6"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "isomorphic-git: Path traversal via a \"..\" tree-entry name writes files outside the working directory on clone/checkout"
}
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
How durations are calculated
Following the VTEM framework (Black Hat USA 2015), time to patch starts when the vendor or a coordinator was informed, total time to patch at discovery, and disclosure to patch at public disclosure; vendor response time runs from the report to its acknowledgement. Time to exploit and time unpatched start at the earliest discovery or report, falling back to public disclosure. Unpatched time ends at the first recorded patch, or the calculation time when no patch is recorded. Exploitation to patch shows the interval between those two events; a published proof of concept is a public exploit, not exploitation.
Each result shows its source dates. A negative interval means the end event came first; when date-only inputs leave the order open, the result says so. Date-only inputs are approximate. The earliest evidence wins: KEV report dates and sightings are labelled as such. Records describing another vulnerability are listed but never calculated from, and a PoC alone does not establish exploitation. Missing patch evidence does not prove that no patch exists. Filters below change the event list only.
Loading timeline…
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.