Today we’re taking another deep dive into one of Socket’s alerts, examining npm shrinkwrap
, including its legitimate use cases and some of the potential supply chain risks.
npm shrinkwrap
was introduced more than a decade ago, as a primitive attempt at package locking when project reproducibility was still a distant dream. The primary purpose of was to create a way to lock down the versions of dependencies in an npm project, ensuring that every install would produce the same results, with the exact same versions of dependencies, regardless of when or where it was installed.
This command locks down the dependencies based on what's currently installed in node_modules. The resulting npm-shrinkwrap.json
is a publishable lockfile.
npm shrinkwrap
emerged as an important tool in the early days of Node.js development to address the challenge of dependency management, but it had some significant drawbacks that were later addressed with the release of package-lock.json
in npm 5. The two locking mechanisms have several distinctions:
npm-shrinkwrap.json
is useful if you want your module's dependencies to be locked down for anyone who installs it. This file ensures that everyone who installs your module gets the exact same versions of the dependencies.package-lock.json
is used to lock down dependencies for your own development environment and is meant to be checked into your source control, but it doesn't get published with your module.
The package-lock.json
file is automatically generated and maintained by npm, whereas npm shrinkwrap
requires a manual command to create or update the shrinkwrap file. As a result, npm shrinkwrap became less commonly used, as package-lock.json
provided an easier and more streamlined approach to locking dependencies.
In most cases, using package-lock.json
is preferable, as it ensures consistent installs in your own development environment without affecting others who use your module.
npm-shrinkwrap.json
is reserved for more specialized cases where strict version control across all environments is necessary, i.e. development, testing, and production. Another potentially legitimate use case is when you publish a library or a package meant for public distribution and want to ensure that everyone who installs your package gets the exact versions of your dependencies (as opposed to any compatible versions).
npm’s docs outline a narrow scope of recommended uses:
The recommended use-case for npm-shrinkwrap.json
is applications deployed through the publishing process on the registry: for example, daemons and command-line tools intended as global installs or devDependencies. It's strongly discouraged for library authors to publish this file, since that would prevent end users from having control over transitive dependency updates.
It’s also important to note that if both package-lock.json
and npm-shrinkwrap.json
are present in a package root, any existing or future package-lock.json
will be ignored in favor of the npm-shrinkwrap.json
file.
Navigating Socket’s npm Shrinkwrap Alert#
Socket flags npm Shrinkwrap as a high-severity supply chain risk. (This alert is specific to the JavaScript ecosystem.) In the context of the supply chain, there are a number of potential issues and security concerns associated with its use.
While the shrinkwrap file wasn’t created with bad intentions, it essentially lets you completely bypass all normal dependency installation rules. It’s also not caught by most SCA tools. Shrinkwrapping allows a package to install any unsigned code from any host and also override any transitive dependency in your tree.
Some developers might not realize that shrinkwrap files can be included in packages, not just within their own applications. If a package is using this tool, it’s going to raise some red flags and should be investigated. Shrinkwrapped packages require just as much manual auditing as any Node package that has a native / binary module or arbitrary C++ code in it.
Due to these issues, there are some who think it should probably be deprecated. There was a time when it had reasonable use, but most modern software projects will opt for using package-lock.json
.
Here are a few of the risks:
Risk of Outdated Dependencies
npm shrinkwrap tightly locks down the versions of dependencies and their sub-dependencies, ensuring consistency across environments. However, if the shrinkwrap file is not regularly updated, this can lead to the use of outdated or potentially insecure versions of packages, increasing the risk of security vulnerabilities.
Manual Maintenance
Shrinkwrap files require manual updates and maintenance. If the file is neglected or not updated properly, it can lock the project into using dependencies with known vulnerabilities, thus exposing the application to security risks.
Complexity in Auditing
Shrinkwrap files can complicate the auditing process. Security tools might have difficulty traversing the locked dependency tree effectively, making it harder to identify and address vulnerabilities. This could potentially hide security issues from being flagged during automated scans.
Security Misconfigurations
In some cases, the use of npm shrinkwrap can introduce misconfigurations or conflicts that might be exploited. This can create openings for attackers if not managed carefully.
In 2016, npm warned that npm shrinkwrap allows remote code execution if it contains a HTTP registry URL. This makes it possible to for a local network attacker (MITM) to execute malicious code on your machine. This is highly unlikely but the risk isn’t zero, and the impact would be severe.
Shrinkwrapped dependencies also have the potential to land you in "dependency hell," where resolving conflicts between versions becomes difficult and error-prone. Developers might assume that because they have locked down their dependencies, they are secure. This false sense of security can lead to complacency in maintaining and updating the project’s dependencies, thereby increasing the risk of exploitation.
What to Do When You Receive the npm Shrinkwrap Alert#
If Socket flags one of your dependencies with the "npm shrinkwrap" alert, here are some mitigation options you can consider:
Review the Shrinkwrap File
Start by reviewing the npm-shrinkwrap.json file to understand why it was created and what dependencies it locks. Check for any outdated or potentially vulnerable packages that might be included.
Consider Using package-lock.json Instead
If you don't need the exact version locking provided by npm shrinkwrap, consider replacing the npm-shrinkwrap.json
file with a package-lock.json file. The package-lock.json
file serves a similar purpose but is generally more suitable for most projects, as it does not get published with the package, reducing the risk of stale dependencies.
Manual Dependency Audit
Perform a manual audit of the locked dependencies to ensure there are no security vulnerabilities. If you find any issues, consider updating or replacing those dependencies with more secure alternatives.
Engage with the Dependency's Maintainers
If the shrinkwrap file is part of a third-party package, consider reaching out to the maintainers to discuss the security implications. They may provide guidance on whether the shrinkwrap file is necessary and how to safely update or remove it.
While npm shrinkwrap can be useful for locking down dependencies with precision, it's important to be aware of the security risks it can introduce, such as potential for stale dependencies and security misconfigurations. By carefully evaluating the necessity of using npm shrinkwrap, considering alternatives like package-lock.json
, and regularly auditing your dependencies, you can mitigate these risks and maintain a more secure software supply chain.
Install Socket for GitHub to Detect if Your Dependencies Use npm Shrinkwrap
If you're not already using Socket, the fastest way to check for npm shrinkwrap files in your dependencies is to install our free GitHub app. It's a two-click install and will detect shrinkwrapped packages, as well as 60+ other supply chain risks.