NPM Audit Fix: The Complete Guide to How It Works - Part 2
09 June, 2020 - 4 min read
In my earlier post, I dived into how NPM Audit works. In this concluding post, I talk specifically about how the fix flag works in npm audit fix
.
To do a quick re-cap if you haven't read the earlier article, NPM will use your package.json
and package-lock.json
files to create an object consisting of your dependencies. These dependencies are then sent over to your registry(typically NPM) to check for any reported vulnerabilities. Once it receives a response from the registry, NPM begins the process of fixing those vulnerabilities.
The response from NPM outlines the actions to be taken regarding vulnerabilities in your dependencies. The response body looks something like this:
"actions": [
{
"isMajor": false,
"action": "install",
"resolves": [
{
"id": 1486,
"path": "http-proxy",
"dev": false,
"optional": false,
"bundled": false
}
],
"module": "http-proxy",
"target": "1.18.1"
}
],
There may (and probably will) be multiple objects in this actions object returned from the registry.
The first job for NPM is to segregate these individual objects based on their importance and type. It uses a reducer for this, and begins with the object below as the original value. If you aren't very familiar with reducers, check out this Mozilla Developer Network(MDN) link on how the reduce function works in Javascript.
{
install: new Set(),
installFixes: new Set(),
update: new Set(),
updateFixes: new Set(),
major: new Set(),
majorFixes: new Set(),
review: new Set()
}
Alright, so how exactly does audit.js act on this actions array?
To begin with, the actions array seen in the previous snippet is run through a reducer.
In this reducer, each action is passed to a filter function called filterEnv. This function returns a new object with only the relevant dependencies. The relevant dependencies are identified basis the environment you're building in - essentially dev and production.
There are four buckets a fix may fall into
- install
- update
- major
- review
Using the isMajor
field and the action
field seen in each of the objects from the sample response body above, the sets seen earlier are populated. Depending on the set, information regarding the module, target, id and path of the vulnerability fix is added. You can see this in the npm audit.js file here.
Since the sample response is of action type install, the install
Set would be populated with the module and target. It would look something like:
(http-proxy::1.18.1, ... )
Meanwhile, the installFixes
Set would be populated with the id and path. It would look something like:
(1486::http-proxy, ... )
Similarly, the other sets are also filled depending on the type of vulnerability you're dealing with.
Finally, NPM will install these dependencies by passing the contents of these sets to an instance of the Auditor Class which extends the Installer Class. The run()
method is called on this instance which actually runs the installation.
Does NPM Audit Fix handle every vulnerability?
Nope. If a particular object has the action key with value review
, it is added to the review set. These items require manual review by the developer and are not updated automatically.
But, I heard about a --force flag?
Correct! But that's primarily for what are considered as "major changes".
You can identify if a change is a major one through the isMajor
flag. These are essentially actions that that involve potentially breaking changes. If the --force
flag is provided, NPM will go ahead and fix the vulnerability - albeit with the inherent risk that something may break.
For other cases where the flag is not specified, NPM will produce a log asking you to fix these manually or use npm audit fix --force
.
I referred to a couple of resources while writing this article. Here they are:
Did you like this article? Let me know on Twitter at @SatejSawant. If you spot something incorrect, please write to me at satejs93[at]gmail[dot]com. You can also find me on LinkedIn.