{"id":"CVE-2026-118773","aliases":["GHSA-3g3m-2fjr-6pw4"],"title":"Glob: AbortSignal cancellation bypass allows post-timeout filesystem traversal","summary":"Glob: AbortSignal cancellation bypass allows post-timeout filesystem traversal","severity":"medium","cwe":["CWE-400"],"vendor":"glob","product":"glob","ecosystem":"npm","affected":["glob >= 9.0.0, <= 14.0.1"],"patched":["glob 14.0.2"],"published":"2026-10-30","updated":"2026-10-30","sourceUpdated":"2026-10-30T23:09:15Z","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-3g3m-2fjr-6pw4","references":[{"url":"https://github.com/isaacs/node-glob/security/advisories/GHSA-3g3m-2fjr-6pw4"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-118773"},{"url":"https://github.com/isaacs/node-glob/commit/4682e8c9babee1b385202ede36389634e12076d7"},{"url":"https://github.com/isaacs/node-glob/releases/tag/v14.0.2"},{"url":"https://github.com/advisories/GHSA-3g3m-2fjr-6pw4"}],"tags":["ghsa","npm"],"epss":0.0035,"epssPercentile":0.28686,"ingestedAt":"2026-10-30T23:11:25.768Z","slug":"CVE-2026-118773","body":"## Overview\n\n### Summary\n\nIn glob 13.0.6, an aborted async walk rejects or emits an error but continues recursively reading directories in the background.\n\nThe root cause is that abort branches in src/walker.ts call cb() without returning. As a result, callers that rely on signal or AbortSignal.timeout() to bound glob traversal cost can still incur filesystem I/O and CPU work after the request has already been rejected.\n\n### Details\n\nThis was reproduced on glob v13.0.6 (commit e80cb38ae60d6cbff9e75f39032a994858994d35).\n\nThe README documents that the signal option cancels the glob walk. However, in src/walker.ts, the following methods call cb() when this.signal?.aborted is true but do not return:\n\n- GlobUtil.walkCB()\n- GlobUtil.walkCB2()\n- GlobUtil.walkCBSync()\n- GlobUtil.walkCB2Sync()\n\nExample vulnerable pattern:\n\nif (this.signal?.aborted) cb()\n\nBecause execution continues after cb(), the walker still reaches processor.processPatterns(), readdirCB(), and recursive walkCB2() calls.\n\nThe issue was directly demonstrated against v13.0.6. A source-history review shows the same missing-return pattern is present in tagged releases from v9.0.0 through v13.0.6.\n\nThe issue affects both glob() and globStream().\n\nRecommended fix:\nChange each abort branch to return immediately:\n\nif (this.signal?.aborted) return cb()\n\n### Proof of concept\n\nEnvironment:\n- Linux x86_64\n- Node.js v22.23.3\n- npm 10.9.9\n- glob 13.0.6\n\nReproduction steps:\n\n1. Install the affected package:\n   npm init -y\n   npm install glob@13.0.6\n\n2. Run the attached PoC:\n   node poc/abort-continues-work.mjs\n\nThe PoC aborts after the first readdir() call, waits briefly, and counts how many more directory reads happen after the promise has already rejected.\n\nObserved output on the vulnerable version:\n\n{\n  \"rejection\": \"Error: probe-abort\",\n  \"readdirCalls\": 1365,\n  \"readdirCallsAtReject\": 5,\n  \"readdirCallsAfterReject\": 1360\n}\n\nExpected behavior:\nreaddirCallsAfterReject should be 0.\n\nA second PoC using AbortSignal.timeout() also reproduces the issue:\n\n{\n  \"rejection\": \"TimeoutError: The operation was aborted due to timeout\",\n  \"readdirCalls\": 133,\n  \"readdirCallsAtReject\": 1,\n  \"readdirCallsAfterReject\": 132\n}\n\nThe same test against a patched build with return cb() in the four abort branches stops immediately:\n\n{\n  \"readdirCalls\": 1,\n  \"readdirCallsAtReject\": 1,\n  \"readdirCallsAfterReject\": 0\n}\n\n### Impact\n\nThis is an availability issue.\n\nApplications that expose globbing over attacker-influenced patterns or attacker-controlled directory trees and use AbortSignal / AbortSignal.timeout() as a traversal budget can still perform significant filesystem I/O and CPU work after a request has already timed out or been canceled.\n\nRepeated timed-out requests can stack background glob walks and degrade worker throughput or service availability.\n\nNo confidentiality or integrity impact was observed.\n\n## Affected packages\n\n- `glob >= 9.0.0, <= 14.0.1`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `glob 14.0.2`","depth":"sunlit","depthScore":28,"depthScoreParts":{"impact":27.5,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}