Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

CVE-2026-63475: CVE-2026-63475: Persistent Denial of Service via Circular Symbolic References in isomorphic-git

CVE-2026-63475: Persistent Denial of Service via Circular Symbolic References in isomorphic-git Vulnerability ID: CVE-2026-63475 CVSS Score: 4.3 Published: 2026-11-09 CVE-2026-63475 (GHSA-96wc-2m84-2cj7) is an uncon

CVE-2026-63475: Persistent Denial of Service via Circular Symbolic References in isomorphic-git

Vulnerability ID: CVE-2026-63475
CVSS Score: 4.3
Published: 2026-11-09

CVE-2026-63475 (GHSA-96wc-2m84-2cj7) is an uncontrolled recursion and infinite loop vulnerability in isomorphic-git prior to version 1.38.6. When processing Git protocol responses from a malicious or man-in-the-middle (MitM) server, isomorphic-git writes server-advertised symbolic reference (symref) capabilities directly to the local repository filesystem without validating for circular dependencies. Because the reference resolution logic in GitRefManager.resolve() lacks both cycle detection and a default recursion depth limit, any subsequent operation that resolves the circular references enters a non-terminating asynchronous recursion loop. Because the malicious references are persisted on disk, this condition induces a persistent denial of service (DoS) against the affected repository.

TL;DR

Versions of isomorphic-git prior to 1.38.6 fail to detect circular symbolic references during ref resolution in GitRefManager.resolve(). Cloning or fetching from a malicious Git remote that advertises mutually referential symref capabilities writes circular reference files to disk, causing subsequent Git operations (checkout, log, status, branch) to hang indefinitely.

⚠️ Exploit Status: POC

Technical Details

  • CVE / GHSA ID: CVE-2026-63475 / GHSA-96wc-2m84-2cj7
  • Weakness Class: CWE-674 (Uncontrolled Recursion) / CWE-835 (Infinite Loop)
  • CVSS v3.1 Score: 4.3 Medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L)
  • EPSS Score: 0.40% (34.33rd percentile)
  • Affected Component: GitRefManager.resolve() & updateRemoteRefs in isomorphic-git < 1.38.6
  • Primary Impact: Persistent Denial of Service (non-terminating async recursion on repository access)
  • Exploit & KEV Status: PoC Available (Unit Test) / Not Listed in CISA KEV

Affected Systems

  • isomorphic-git (Node.js and browser environments)
  • isomorphic-git: < 1.38.6 (Fixed in: 1.38.6)

Code Analysis

Commit: 823843d

fix: fixing git internals (PR #2370) - Add cycle detection via visited Set in GitRefManager.resolve()

diff --git a/src/managers/GitRefManager.js b/src/managers/GitRefManager.js
@@ -3,6 +3,7 @@ import '../typedefs.js'
 // This is a convenience wrapper for reading and writing files in the 'refs' directory.
 import AsyncLock from 'async-lock'

+import { InternalError } from '../errors/InternalError.js'
 import { InvalidOidError } from '../errors/InvalidOidError.js'
 import { NoRefspecError } from '../errors/NoRefspecError.js'
 import { NotFoundError } from '../errors/NotFoundError.js'
@@ -255,7 +256,13 @@ export class GitRefManager {
    * @param {number} [args.depth = undefined] - The maximum depth to resolve symbolic refs.
    * @returns {Promise<string>} - The resolved object ID.
    */
-  static async resolve({ fs, gitdir, ref, depth = undefined }) {
+  static async resolve({
+    fs,
+    gitdir,
+    ref,
+    depth = undefined,
+    visited = new Set(),
+  }) {
     if (depth !== undefined) {
       depth--
       if (depth === -1) {
@@ -266,7 +273,7 @@ export class GitRefManager {
     // Is it a ref pointer?
     if (ref.startsWith('ref: ')) {
       ref = ref.slice('ref: '.length)
-      return GitRefManager.resolve({ fs, gitdir, ref, depth })
+      return GitRefManager.resolve({ fs, gitdir, ref, depth, visited })
     }
     // Is it a complete and valid SHA?
     if (ref.length === 40 && /[0-9a-f]{40}/.test(ref)) {
@@ -285,7 +292,21 @@ export class GitRefManager {
           packedMap.get(ref)
       )
       if (sha) {
-        return GitRefManager.resolve({ fs, gitdir, ref: sha.trim(), depth })
+        // Guard against circular symbolic references (e.g. a -> b -> a). Without
+        // tracking visited refs this recursion would not terminate.
+        if (visited.has(ref)) {
+          throw new InternalError(
+            `Circular reference detected while resolving ref "${ref}"`
+          )
+        }
+        visited.add(ref)
+        return GitRefManager.resolve({
+          fs,
+          gitdir,
+          ref: sha.trim(),
+          depth,
+          visited,
+        })
       }
     }

Exploit Details

  • GitHub Pull Request #2370 Regression Test: Official Jest regression test (tests/test-GitRefManager-symref-cycle.js) demonstrating non-terminating resolution on circular symbolic references (a -> b -> a).

Mitigation Strategies

  • Upgrade isomorphic-git to version 1.38.6 or later to enable automatic cycle detection during symbolic reference resolution.
  • Restrict git.clone and git.fetch operations to trusted, authenticated remote repositories over encrypted transports (HTTPS/SSH) to prevent rogue server or MitM exploitation.
  • Audit and sanitize local .git/refs/remotes/ directories for mutually referential 'ref: ' pointers if a workspace becomes unresponsive or throws Circular reference InternalErrors.
  • Supply an explicit numeric 'depth' parameter (such as depth: 5) whenever directly calling the internal GitRefManager.resolve() API.

Remediation Steps:

  1. Update package.json to require 'isomorphic-git': '^1.38.6' and run 'npm install isomorphic-git@^1.38.6' (or the equivalent yarn/pnpm command).
  2. Verify the installed version using 'npm list isomorphic-git' to ensure no transitive dependencies resolve to versions below 1.38.6.
  3. For any existing local repositories that fail to open or resolve references, check '.git/refs/remotes/' for files containing 'ref: ' that point to each other in a loop, and delete the offending files.

References

Read the full report for CVE-2026-63475 on our website for more details including interactive diagrams and full exploit analysis.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.