Modern software projects are mostly composed of open source code. The question of who really controls this code, and is responsible for detecting and fixing software supply chain security issues, became a significant source of concern after the discovery of the Log4Shell vulnerability.
In a more recent development, the highly popular colors and faker npm packages were purposefully corrupted by their maintainer. The developer, Marak Squires, introduced changes which rendered these packages useless, and caused hanging of applications using one of those packages. While the introduced fault was cosmetic in nature, and the inflicted damage is in principle limited to the urgent need to fix numerous tools which became inoperable, this scenario demonstrates a deeper and more concerning problem. Just as easily, the developer could have introduced malicious code, which would be executed on hundreds of thousands of machines which were known to download the faulty version of the package.
The deeply troubling fact is that in many circumstances, new code may be pulled from a repository, used in a project and executed on the machine, in a way that is almost invisible to the developer. The trust in the code obtained from public repositories is deeply rooted in many package managers, but this trust might be misplaced. This incident shows us how trust, built and established for years, can be broken one day and as such even new versions of a robust package cannot be trusted.
The potential risk in npm package installation
To illustrate the risk that may be introduced in modern development methodologies, let’s examine the process of installing npm packages and see how this can lead to applications inadvertently pulling malicious packages from the npm registry.
The common method for enforcing the use of specific versions of the npm dependencies in a project is using package-lock.json file, which specifies the allowed versions of the libraries. We highly recommend using package-lock.json and specifying exact dependency versions whenever possible. It is a little known fact, however, that the current npm installer – when installing a package globally (
npm run with
–global) – does not honor the package-lock.json file and will happily download the latest available version of any package dependency, according to the dependencies specified in the package.json file. This is why users found their applications were using hijacked versions of the colors package, even though they were certain they were “protected” by package-lock.json.
Installing the latest version of any npm package (a dependency) without first validating it can be dangerous not only because it may be malicious (e.g. if the package was hijacked in its latest version as in the case of faker and colors), but also because it can break the code that uses the dependency (e.g. due to API changes in the latest version of the dependency).
Newly-released OSS tools for npm packages
In response to these cases, the JFrog Security Research team has published three OSS tools aimed at detecting and preventing of the installation of possibly faulty npm packages:
- package_checker: a tool providing an indication of whether a specific version of a given package can be trusted. The tool looks for tell-tale signs of packages used in supply-chain attacks and can be used to identify potential risks with newly released versions. Among the checked conditions are:
- A significant gap in version numbers (i.e., jumping from 5.5.3 to 6.6.6, like in the case of the faker npm package)
- An update for a package which was not maintained for a long time
- Discrepancy between the versions appearing in npm and the linked GitHub repository, which may indicate someone hiding the fact that specific code was released
- How recently the version was posted, since a very new version has not been vetted yet, and may contain malicious code.
This tool can be used by developers to actively test new versions of used packages before deciding to update the dependency, or by organizations to monitor all new versions of packages used internally.
- npm-secure-installer: a secure wrapper for
npm install, which will refuse to globally install packages that do not contain an npm-shrinkwrap lock file.
One of the key requirements of Google’s SLSA is dependency hermeticity – meaning that dependencies are locked down to an immutable reference. npm achieves this goal by using package-lock.json which is automatically created on the first
npm installrun. However, when using npm to install global tools, npm does not run from any project and therefore does not use any package-lock.json file, even if one exists. Thankfully, npm provides the shrinkwrap approach which is specifically designed for this scenario.
npm-secure-installerautomates the secure approach – verifying that the package has an npm-shrinkwrap manifest before performing the installation.
This tool can be used as a drop-in replacement for
npm installin order to secure the developer’s workflow.
- package_issues_history: an experimental tool aiming to monitor for problematic package updates, in order to find them even before it is discovered that a certain package version introduced a breaking change. Breaking/malicious changes can happen in different ways, and except for extreme (and probably rare) cases, it is hard to define a rule for such changes which will not be triggered by benign changes as well. It seems like the only certain outcome from a breaking or malicious change in a package will be problems in packages which depend on it. The proposed method implemented in the experimental code is tracking of GitHub issues created in projects that depend on a given package in the days following a version update. For a popular enough library, the number of dependent projects might be large enough so that the surplus issues resulting from a breaking change will be significant with respect to the “background” issues which are unrelated to the change. In the graph below, the rate of issue creation for the colors package and its dependencies definitely tells something important about which version updates were more problematic than others.
We believe these tools are a small step in the right direction towards highlighting the issues the modern software supply chain faces, as well as highlighting some directions to solving it. In addition to supporting the developer community with information and open-source tools to deal with the latest software security threats, JFrog provides developers and security teams advanced software supply chain security capabilities with JFrog Xray. Keep following us for Xray product updates including advanced detection of malicious packages.