{"id":"CVE-2026-63395","aliases":["EUVD-2026-116274","GHSA-9qw7-j9xw-fv9c"],"status":"published","title":"isomorphic-git: Arbitrary file write outside the working tree via a directory symlink during checkout (potential RCE)","description":"isomorphic-git is a pure JavaScript implementation of Git for Node.js and browsers. Prior to 1.38.6, isomorphic-git's git.checkout writes new child paths through symlinked leading directory components without checking them. A malicious repository can first place a directory symlink and later replace the tree state with a child file beneath that path; the usual trigger is a clone followed by a later pull, while a single clone can trigger the issue when a symlink already exists or the consuming application accesses the tree. With Node fs on Linux or macOS, the operating system can resolve the write outside the working tree, permitting arbitrary host file writes and possible code execution. Node fs on Windows usually blocks the symlink plant without additional privilege, and browser or in-memory providers limit impact to their virtual filesystem and are affected only when they implement symlink creation and following. This issue is fixed in version 1.38.6.","severity":{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00716,"percentile":0.54263,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22","CWE-59"],"published":"2026-11-03T19:48:11.232Z","modified":"2026-11-09T20:00:04Z","fixes":[{"ecosystem":"npm","package":"isomorphic-git","fixed_in":["1.38.6"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"isomorphic-git","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"1.38.5"},{"fixed":"1.38.6"}]}]},{"source":"osv","ecosystem":"npm","package":"isomorphic-git","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.38.6"}]}]}],"products":[{"vendor":"isomorphic-git","product":"isomorphic-git"}],"references":[{"url":"https://github.com/isomorphic-git/isomorphic-git/security/advisories/GHSA-9qw7-j9xw-fv9c","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/isomorphic-git/isomorphic-git/pull/2370","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/isomorphic-git/isomorphic-git/commit/823843d1cd19182df79bc907c767f5bcecb39a0b","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/isomorphic-git/isomorphic-git/releases/tag/v1.38.6","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-63395","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-9qw7-j9xw-fv9c"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/63xxx/CVE-2026-63395.json","tags":["advisory"]},{"url":"https://github.com/isomorphic-git/isomorphic-git","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-63395","title":"isomorphic-git: Arbitrary file write outside the working tree via a directory symlink during checkout (potential RCE)","description":"isomorphic-git is a pure JavaScript implementation of Git for Node.js and browsers. Prior to 1.38.6, isomorphic-git's git.checkout writes new child paths through symlinked leading directory components without checking them. A malicious repository can first place a directory symlink and later replace the tree state with a child file beneath that path; the usual trigger is a clone followed by a later pull, while a single clone can trigger the issue when a symlink already exists or the consuming application accesses the tree. With Node fs on Linux or macOS, the operating system can resolve the write outside the working tree, permitting arbitrary host file writes and possible code execution. Node fs on Windows usually blocks the symlink plant without additional privilege, and browser or in-memory providers limit impact to their virtual filesystem and are affected only when they implement symlink creation and following. This issue is fixed in version 1.38.6.","status":"published","published":"2026-11-03T19:48:11.232Z","modified":"2026-11-03T21:30:11.615Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-22","CWE-59"],"updated_at":"2026-11-03T22:58:38.387176Z"}],"euvd":[{"id":"EUVD-2026-116274","description":"isomorphic-git: Arbitrary file write outside of working tree via directory symlink during checkout (potential RCE)","status":"published","published":"2026-11-09T19:53:44Z","modified":"2026-11-09T19:53:44Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-09T21:18:10.039091Z"}],"ghsa":[{"id":"GHSA-9qw7-j9xw-fv9c","title":"isomorphic-git: Arbitrary file write outside of working tree via directory symlink during checkout (potential RCE)","description":"### Summary\n`git.checkout` writes working-tree files through a directory **symlink** without checking the leading path, so a malicious repository can write **outside the intended working directory**. The defect is in isomorphic-git's checkout logic itself and is therefore **independent of the `fs` provider**. The *impact* depends on the configured filesystem; worst case is arbitrary file write on the host, e.g. injecting an attacker key into `~/.ssh/authorized_keys` → remote login / code execution.\n\n### Details\nA single git tree can't hold a name as both a symlink and a directory, so the attack uses two states:\n\n```\nCommit N:    config            (mode 120000, symlink → ../.ssh)\nCommit N+1:  config/           (mode 040000, tree)\n               authorized_keys (mode 100644, attacker public key)\n```\n\nChecking out commit N writes `config` as a directory symlink. Checking out commit N+1 sees `config/authorized_keys` as a new path: isomorphic-git `mkdir`s the parent (`config`, already a symlink → `EEXIST`, ignored) then `writeFile`s the file, which the OS resolves through the symlink to outside the working directory. A *file*-level symlink swap is caught (the `120000`→`100644` mode change triggers an `rm`), but the **directory**-level variant is not — the payload is always a new child path, a plain `create`, so the parent symlink is never removed.\n\nThe symlink target is attacker-chosen. Besides `~/.ssh`, pointing it at the repo's own control directory — `hooks → .git/hooks` then `hooks/pre-commit`, or `x → .git` then overwriting `.git/config` (`core.hooksPath` / alias / `core.fsmonitor`) — yields RCE on the next native `git` command **without** depending on `~/.ssh` existing, since `.git/` is always present in a fresh clone.\n\n#### Canonical git behavior (divergence)\ngit refuses to do this: `has_symlink_leading_path()` (symlinks.c) checks each leading path component during checkout and will not follow a symlink — the hardening behind CVE-2021-21300 and CVE-2024-32002. isomorphic-git has no equivalent check.\n\n### PoC\n`https://github.com/spiceoverflow/av1-symlink-hook-rce` — open in a Codespace; it builds the malicious repo, serves it locally, and runs a victim. Output on Linux:\n\n```\n[AV-1] Directory symlink on disk: …/config → ../.ssh  (→ /home/node/.ssh)\n[AV-1] *** SSH KEY INJECTION CONFIRMED ***\n```\n\n**Trigger:** minimal realistic path is `clone` (commit N) + a later `pull` (commit N+1) — ordinary sync of an already-trusted repo. It also collapses to a single `clone` when the consuming app reads or writes into the cloned tree, or when a symlink already exists on disk.\n\n\n### Impact\n\nisomorphic-git is filesystem-agnostic, so exploitability and blast radius depend on the active `fs` provider — specifically whether it creates a symlink and follows it on a later write:\n\n- **Node `fs` on Linux/macOS** (servers, CI, CLIs, Electron/desktop tooling — the common server-side use): real OS symlinks → the write lands anywhere the process can reach → **arbitrary file write on the host → RCE** (the PoC above). Highest impact.\n- **Node `fs` on Windows:** `fs.symlink` typically requires privilege and throws `EPERM`, so the symlink-plant step usually fails — not exploitable by default. (A property of the OS/provider, not of isomorphic-git.)\n- **In-memory / browser providers** (memfs, ZenFS, lightning-fs, custom `fs`): there is no host filesystem to escape to, so the worst case is writing **outside the intended clone directory but within that virtual filesystem** — i.e. corrupting other repos/app data the same provider holds. Bounded by the sandbox, and only if the provider implements symlink creation + following.\n\nThe CVSS above reflects the worst-case (Node `fs` on a POSIX host). Browser/in-memory-only deployments are lower severity (sandboxed integrity impact).\n\n### Suggested fix\nBefore each working-tree write, `lstat` every leading path component and refuse to traverse a\nsymlink (mirrors git's `has_symlink_leading_path()`); optionally honor `core.symlinks=false`.","status":"published","published":"2026-11-09T19:53:44Z","modified":"2026-11-09T19:53:46Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-22","CWE-59"],"updated_at":"2026-11-09T20:19:39.584745Z"}],"nvd":[{"id":"CVE-2026-63395","description":"isomorphic-git is a pure JavaScript implementation of Git for Node.js and browsers. Prior to 1.38.6, isomorphic-git's git.checkout writes new child paths through symlinked leading directory components without checking them. A malicious repository can first place a directory symlink and later replace the tree state with a child file beneath that path; the usual trigger is a clone followed by a later pull, while a single clone can trigger the issue when a symlink already exists or the consuming application accesses the tree. With Node fs on Linux or macOS, the operating system can resolve the write outside the working tree, permitting arbitrary host file writes and possible code execution. Node fs on Windows usually blocks the symlink plant without additional privilege, and browser or in-memory providers limit impact to their virtual filesystem and are affected only when they implement symlink creation and following. This issue is fixed in version 1.38.6.","status":"published","published":"2026-11-03T20:18:14.917Z","modified":"2026-11-03T22:19:47.677Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-22","CWE-59"],"updated_at":"2026-11-04T00:48:10.032782Z"}],"osv":[{"id":"CVE-2026-63395","title":"isomorphic-git: Arbitrary file write outside the working tree via a directory symlink during checkout (potential RCE)","description":"isomorphic-git is a pure JavaScript implementation of Git for Node.js and browsers. Prior to 1.38.6, isomorphic-git's git.checkout writes new child paths through symlinked leading directory components without checking them. A malicious repository can first place a directory symlink and later replace the tree state with a child file beneath that path; the usual trigger is a clone followed by a later pull, while a single clone can trigger the issue when a symlink already exists or the consuming application accesses the tree. With Node fs on Linux or macOS, the operating system can resolve the write outside the working tree, permitting arbitrary host file writes and possible code execution. Node fs on Windows usually blocks the symlink plant without additional privilege, and browser or in-memory providers limit impact to their virtual filesystem and are affected only when they implement symlink creation and following. This issue is fixed in version 1.38.6.","status":"published","published":"2026-11-03T19:48:11.232Z","modified":"2026-11-05T03:30:32.466392186Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-22","CWE-59"],"updated_at":"2026-11-05T07:12:46.161235Z"},{"id":"GHSA-9qw7-j9xw-fv9c","title":"isomorphic-git: Arbitrary file write outside of working tree via directory symlink during checkout (potential RCE)","description":"### Summary\n`git.checkout` writes working-tree files through a directory **symlink** without checking the leading path, so a malicious repository can write **outside the intended working directory**. The defect is in isomorphic-git's checkout logic itself and is therefore **independent of the `fs` provider**. The *impact* depends on the configured filesystem; worst case is arbitrary file write on the host, e.g. injecting an attacker key into `~/.ssh/authorized_keys` → remote login / code execution.\n\n### Details\nA single git tree can't hold a name as both a symlink and a directory, so the attack uses two states:\n\n```\nCommit N:    config            (mode 120000, symlink → ../.ssh)\nCommit N+1:  config/           (mode 040000, tree)\n               authorized_keys (mode 100644, attacker public key)\n```\n\nChecking out commit N writes `config` as a directory symlink. Checking out commit N+1 sees `config/authorized_keys` as a new path: isomorphic-git `mkdir`s the parent (`config`, already a symlink → `EEXIST`, ignored) then `writeFile`s the file, which the OS resolves through the symlink to outside the working directory. A *file*-level symlink swap is caught (the `120000`→`100644` mode change triggers an `rm`), but the **directory**-level variant is not — the payload is always a new child path, a plain `create`, so the parent symlink is never removed.\n\nThe symlink target is attacker-chosen. Besides `~/.ssh`, pointing it at the repo's own control directory — `hooks → .git/hooks` then `hooks/pre-commit`, or `x → .git` then overwriting `.git/config` (`core.hooksPath` / alias / `core.fsmonitor`) — yields RCE on the next native `git` command **without** depending on `~/.ssh` existing, since `.git/` is always present in a fresh clone.\n\n#### Canonical git behavior (divergence)\ngit refuses to do this: `has_symlink_leading_path()` (symlinks.c) checks each leading path component during checkout and will not follow a symlink — the hardening behind CVE-2021-21300 and CVE-2024-32002. isomorphic-git has no equivalent check.\n\n### PoC\n`https://github.com/spiceoverflow/av1-symlink-hook-rce` — open in a Codespace; it builds the malicious repo, serves it locally, and runs a victim. Output on Linux:\n\n```\n[AV-1] Directory symlink on disk: …/config → ../.ssh  (→ /home/node/.ssh)\n[AV-1] *** SSH KEY INJECTION CONFIRMED ***\n```\n\n**Trigger:** minimal realistic path is `clone` (commit N) + a later `pull` (commit N+1) — ordinary sync of an already-trusted repo. It also collapses to a single `clone` when the consuming app reads or writes into the cloned tree, or when a symlink already exists on disk.\n\n\n### Impact\n\nisomorphic-git is filesystem-agnostic, so exploitability and blast radius depend on the active `fs` provider — specifically whether it creates a symlink and follows it on a later write:\n\n- **Node `fs` on Linux/macOS** (servers, CI, CLIs, Electron/desktop tooling — the common server-side use): real OS symlinks → the write lands anywhere the process can reach → **arbitrary file write on the host → RCE** (the PoC above). Highest impact.\n- **Node `fs` on Windows:** `fs.symlink` typically requires privilege and throws `EPERM`, so the symlink-plant step usually fails — not exploitable by default. (A property of the OS/provider, not of isomorphic-git.)\n- **In-memory / browser providers** (memfs, ZenFS, lightning-fs, custom `fs`): there is no host filesystem to escape to, so the worst case is writing **outside the intended clone directory but within that virtual filesystem** — i.e. corrupting other repos/app data the same provider holds. Bounded by the sandbox, and only if the provider implements symlink creation + following.\n\nThe CVSS above reflects the worst-case (Node `fs` on a POSIX host). Browser/in-memory-only deployments are lower severity (sandboxed integrity impact).\n\n### Suggested fix\nBefore each working-tree write, `lstat` every leading path component and refuse to traverse a\nsymlink (mirrors git's `has_symlink_leading_path()`); optionally honor `core.symlinks=false`.","status":"published","published":"2026-11-09T19:53:44Z","modified":"2026-11-09T20:00:04.157950335Z","cvss":[{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-22","CWE-59"],"updated_at":"2026-11-09T20:08:38.95245Z"}]}}
