AppSec POV on Dependency Management


It’s tempting to assume that all packages in the npm registry are safe to use––and, for the vast majority of them, that’s true. The npm security team and the JavaScript community at large exercises a high degree of vigilance over the hygiene of the massive shared code library that has made JavaScript the most popular and powerful software platform.

The vast majority of packages are safe… but, some aren’t. And, some packages that used to be safe can become compromised. Even in a safe neighborhood, it’s best to lock your car.

In the following post, we take an application security expert’s point of view at good practices and behaviors you should be doing whenever you start a project as well as on a [good] cadence throughout the life of your projects.

The Dependency Iceberg

It’s no secret that open source JavaScript modules tend to be highly… modular. It’s not uncommon for a single declared dependency to include hundreds of transitive dependencies. The dependencies you specifically declare are just the tip of the iceberg, so to speak. It’s the 95% of the code that lies invisibly below the water line that can rip through your hull if you’re not careful.

Fig. 1. Iceberg; not to scale

What can be done, then, to steer safely clear of potential danger? The answers depend on where you are in the lifecycle of your project.

At the beginning of your project and any time you add a new dependency, choose your dependencies carefully. As an ongoing part of development throughout the life of your project, update your dependencies responsibly.

Choose Carefully

Just like anything you download from the Internet, use care when choosing your dependencies. npm, Inc. and the JavaScript community at large continuously work to maintain registry hygiene, but it’s not a good practice to assume that everything in the registry is suitable for use. As a developer, it’s your responsibility to take due care in choosing your dependencies.

Use the package rating metrics and social cues available on the npm package page. High scores for popularity, quality, and maintenance are strong signals that a package is suitable for use.

Fig. 2. package quality metrics

Review the version history of the package to ensure a healthy release cadence has been established by the package maintainers. A slowing or stalled release cadence or a shift in project personnel may be a sign of maintainer fatigue. Maintainer fatigue can make it easier for unscrupulous actors to take control of projects through social engineering. When maintainers have poured years of heart, soul, and sweat into a project for no money and little community support, an offer to switch control to a new party may be too tempting to invite thorough vetting before handing over the keys. While not common, custody transitions have resulted in malicious code entering once-trusted libraries and could certainly happen again.

Review npm Advisories and Use npm audit

When you install a package from the public registry, npm will report any security vulnerabilities:

Use npm audit to check the vulnerability status of your candidate dependency set:

If there’s a vulnerability, read the advisory to judge whether or not it’s a show-stopper and if there are patched versions available.

Update Responsibly

Pin Dependencies as Narrowly as Possible

When you install a dependency without specifying a version, npm installs the latest version and sets the dependency package.json to ^x.y.z––which will automatically update the dependency to the latest minor version within the scope of the specified major version whenever you perform a fresh install or npm update.

The minor-version range provided by this default behavior strikes a balance between safety and reproducibility on the one hand and the benefits of bug-fixes and on the other. How wide or narrow to pin dependencies is by no means a settled issue, but from a security perspective, the narrower the better.

Also, commit your package-lock.json file to source control. This will ensure that all transitive dependencies are pinned, as well.

See the npm documentation for more details on package locking.

Update Tempo

As your project matures, establish a steady tempo of updating your dependencies. If you update too slowly, you may miss important security updates and expose yourself to a widening window of vulnerability. If you update too aggressively, you don’t give the ecosystem’s immune system time to react to potential malware. Aim for the goldilocks zone when establishing your update tempo.

Don’t automatically install random stuff from the Internet. Before committing to a dependency update, check to see if there are any security advisories for it. In addition to showing a history of security advisories for all packages,the advisories page on npmjs.com supports searching for advisories on particular packages, e.g.:

https://www.npmjs.com/advisories?search=eslint-utils

You can also pull a package down for further inspection without installing and/or install without running install scripts, then perform npm audit.

To install without running package scripts, run:

npm install --ignore-scripts

You can do this in a test/dummy project to avoid accidentally installing an unexpected package into a working project.

To run an audit without installing anything, run:

npm install --package-lock-only

then:

npm audit

What We Do Behind The Scenes

Ultimately, the practices of choosing, using, and updating open source software responsibly can only be done by the dependent application developer. But, there is much activity happening behind the scenes at npm to keep the registry safe.

We continually analyze incoming packages for potential risks. We watch install scripts, network activity, among other things looking for suspect behavior and perform delta analysis on new versions of packages. We guard against spam or junk packages and screen for typo-squatting attempts to make sure that registry users don’t install unintended software. We have the world’s largest (and growing) corpus of historical javascript malware to research against. This continuous practice of defense against the dark arts not only maintains registry hygiene, but spins out new and better tools for the entire community to use to improve security.

Perhaps the most important thing we do, though, is to respond quickly to vulnerability reports from the community and alert the maintainers when there’s an issue so a patch can be issued. Like a neighborhood watch program, the vigilance of the entire community is critical to the continued safety and usability of our shared resources.

Open source software contributions do not come only in the form of code commits. If you find a vulnerability in a published package, you can do your part by reporting it to us so others can benefit from your research. We take immediate action on such reports to investigate the nature and extent of the vulnerability as well as to notify maintainers to give them time to fix the issue before disclosing it to the public and removing any vulnerable packages from the registry as necessary.

You can report vulnerabilities here:

https://www.npmjs.com/advisories/report

or by emailing security@npmjs.com

Do You Have a Headache Yet?

npm Enterprise can help. Learn how npm Enterprise automatically ensures security and compliance ›