CVE-2021-39135
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. It uses an event-driven, non-blocking I/O model and is widely used for web applications and server-side development. Every official Node.js distribution ships with npm, the default package manager, so the npm version a developer runs is usually the one bundled with their Node.js release. Starting with npm 7, npm builds and installs the dependency tree through @npmcli/arborist, the library that calculates dependency trees and manages the node_modules folder hierarchy.
A vulnerability (CVE-2021-39135) has been identified in @npmcli/arborist, as bundled with npm in Node.js. Arborist is meant to extract package contents only into a project's node_modules folder. However, if the node_modules folder of the root project or of any dependency has been replaced with a symbolic link, arborist follows that link and writes package contents wherever it points, which can be any location on the file system the installing user can write to.
This flaw maps to CWE-59 (Improper Link Resolution Before File Access, 'Link Following'), and specifically to its child CWE-61 (UNIX Symbolic Link Following), where software opening a file or folder does not account for a link that resolves outside the area it is meant to control. In arborist, the extraction target is trusted to be a real directory without being checked. Because npm runs with the developer's or build system's permissions, writing through that link can lead to arbitrary file creation, file overwrite and code execution.
Symbolic links inside package tarballs are filtered out, so an attacker needs another way to put the link in place. A dependency's preinstall script can replace node_modules with a symlink, although running npm with --ignore-scripts prevents that. Alternatively, an attacker can hand the victim a git repository that already contains a node_modules symlink and ask them to run npm install --ignore-scripts, an action normally assumed not to touch anything outside the project. The vulnerable arborist was bundled with npm 7 up to 7.20.6, which shipped with Node.js 15.x and Node.js 16.x before 16.8.0. Node.js also addressed this CVE in its 12.22.6 and 14.17.6 security releases, which listed the 12.x and 14.x release lines as vulnerable.
Details
Module Info
- Product: Node.js
- Affected packages: @npmcli/arborist (bundled with npm in Node.js)
- Affected versions: >=12.0.0 <12.22.6 >=14.0.0 <14.17.6 >=15.0.0 <=15.14.0 >=16.0.0 <16.8.0
- GitHub repository: https://github.com/nodejs/node
- Published packages: https://nodejs.org/en/download
- Package manager: Not applicable; Node.js is distributed as runtime builds from nodejs.org rather than as a published npm package
- Fixed in: Node.js 16.8.0 (August 25, 2021), which bundled npm 7.21.0 with @npmcli/arborist 2.8.2, and Node.js 12.22.6 and 14.17.6 (August 31, 2021), whose security releases listed this CVE as addressed; Node.js NES v14.21.4 (14.x line, shipped August 24, 2024) and v12.22.13 (12.x line, shipped November 10, 2025), which also list this CVE as fixed. Node.js 15.x never received a fix
Vulnerability Info
This High-severity vulnerability is found in @npmcli/arborist 2.8.1 and earlier, the dependency-tree library bundled with npm and therefore with Node.js. NVD assigns a CVSS v3.1 score of 7.8.
Arborist is supposed to extract every package into a node_modules folder inside the project. In the vulnerable versions, it did not confirm that the node_modules folder it was writing into was a real directory, so a symbolic link in its place redirected the extraction to the link's target. Arborist 2.8.2 checks, before extracting any package, that the destination node_modules is a real directory and removes it if it is not. See the arborist advisory for the details of the change.
Exploitation requires the victim to run npm install on a codebase or dependency the attacker controls. No privileges on the target system are needed beyond getting that install to run, and the impact covers the confidentiality, integrity and availability of anything the installing account can write. The --ignore-scripts flag blocks the preinstall-script route but not the pre-placed symlink in a supplied repository.
Note: Until the fix is applied, do not run npm install on an untrusted codebase without first confirming that its node_modules folder is not a symbolic link. Projects that deliberately symlink node_modules to share dependencies will stop working after the fix; npm link, explicit file: dependencies or workspaces are the supported alternatives.
Mitigation
Users of the affected components should apply one of the following mitigations:
- Upgrade to a currently supported Node.js LTS release (22.x or 24.x), both of which bundle an npm version that includes this fix.
- Migrate affected applications away from the End-of-Life Node.js release lines.
- Leverage a commercial support partner like HeroDevs for post-EOL security support, through Node.js NES.
Credits
- JarLob (finder)