I recently received an email from Github, informing me of a certain security vulnerability with one of my repos.

Subject of Github email

This is not the the first time I’ve received such an email. In the past, on receiving this email, I’d immediately get down to eliminating the detected vulnerabilities. But on this one occasion, I thought it would be a good idea to share about it, and probably explain how I usually go about resolving it.

The above reported repo, ssr-react-app, was a test repo that I was using to play around with Next.js and its implementation of Server-Side rendering. This was a while back. Since this article is not about any of those, I will not delve into the details. We will stick strictly to the topic at hand, the issue below.

Github security vulnerability email body

A know high severity security vulnerability has been detected in installed version of js-yaml.

Before we begin discussing various ways on how we can go about fixing this, we need to understand how something like this happens. In an attempt to contribute to the open-source community, many developers go out to identify gaps in the available solutions, or challenges faced by other developers that do not have a published solution yet.

Most of us use a lot of code built by other developers, in the form of libraries. The reasons for using said libraries vary - time-constraints to build a custom made solution, the feeling that there’s no need to reinvent the wheel, simple appreciation of certain library authors, et cetera, et cetera. Whatever the reason, in the course of software development, more often than not, we find ourselves pulling in libraries into our codebase. In a Javascript environment, as is my case, the packages are pulled from NPM (Node Package Manager).

NPM is a registry for thousands of packages published by different authors. In as much as these packages solve a specific problem, sometimes they may introduce vulnerabilities in the code. This may be from the package itself, or dependencies it uses — we call this cascading dependencies. For instance, installing Express, a Node.js web application framework, installs an additional 40+ other packages that it depends on! Security is a great concern, especially when we’re talking about shipping software products to production. The repercussions of turning a blind eye are high.

Examples of vulnerabilities can range from Cross-Site Scripting issues (which account for over 80% of all security issues), to Denial of Service issues such as Event Loop Blocking, Memory Exhaustion and Regex security Attacks. These security vulnerabilities could result in crashed servers and stolen data.

Online services like Snyk can be used to test Github repos, npm packages and docker images for known vulnerabilities. But in our case, we’ll jump straight to the terminal and explore npm audit and npm audit fix commands.

NPM Audit

NPM Audit is used to track down and give a report of the security vulnerabilities of npm modules that you have in your environment. Each vulnerability has a severity level (Low, Moderate, High, Critical). It goes without saying, but issues tagged as High or Critical need to be addressed immediately. Having detected the issues, NPM Audit then gives you an option to fix them by upgrading the packages to a published version that fixed the identified issue.

Back to the ssr-react-app. Below is the package.json file. Take note of the dependencies property.

ssr-react-app package.json

The detected package that Github had reported, js-yaml, is missing missing in the dependencies specified in our package.json! Odd? We’ll get back to that shortly.

The next step is to navigate to the root of my application folder and run the npm audit command. Below are the results.

Results of npm audit

Let’s dissect this. We have three detected vulnerabilities (2 of a moderate severity and 1 of a high severity).

Each vulnerability has a Severity level and Type of Vulnerability, Name of the Package, Dependency of (which indicates if that package is a dependency of another package that is installed) and a More info column which provides a link that has more details on the nature of the vulnerability. Following the More info link is something I highly recommend. If we aim to solve a problem, then we must first understand it.

The second reported vulnerability from the above output is js-yaml. But if you look closely, you’ll see that it’s a dependency of zeit/next-css. This is as a result of cascading dependencies — we mentioned this earlier on.

The npm audit command has a json flag that outputs the same info in a JSON format. This output can optionally be written to a file.

Export npm audit to a JSON file

The JSON output can be fed into a visualizer or a parser that pulls out the total number of issues in it during a Continuous Integration (CI) process.

Running the npm audit fix command attempts to fix these vulnerabilities.

Results of npm audit fix command

There we go. 3 out of 3 issues resolved. The axios package has been upgraded from 0.18.0 to 0.19.0. In addition 3 packages from 4 contributors have been added and 3 have been updated. That seems to have fixed the vulnerability. But there’s no mention of js-yaml anywhere. Was there really an upgrade for it? Well, it might not seem that way, but a quick git diff package-lock.json will prove otherwise.

Snippet of git diff package-lock.json command

This proves without a doubt that the js-yaml package was indeed upgraded from version 3.12.2 to version 3.13.1 which has an implementation of the vulnerability fix.

Semantic versioning is something to take note of when it comes to running the npm audit fix command. If the upgrade is a patch or a minor version, then the upgrade happens automatically. However, if the upgrade requires shifting to a major version, then a force flag has to be passed. Be careful with this flag. Unlike minor and patch versions which have backward compatibility, major versions usually have breaking changes. Hence, refer to the package docs before running the command.

If you’d just like to see what will potentially be upgraded upon running npm audit fix, without necessarily effecting the changes, then the dry-run flag is what you need.

npm audit fix — dry-run command

The fix command also offers an only flag to target the environment packages i.e dev or prod. The prod argument will target the packages in the dependencies property in the package.json file, while the dev argument will target the devDependencies.

npm audit fix — only=prod

What if there’s no published fix?

It is possible at times to have certain issues reported but no published fix is available. In such a case:

Help out the package maintainers by discovering the root of the reported risk and reporting it by creating an issue. You could even go ahead and make a Pull Request with your implemented solution. Use a forked version of the package and make a fix to it. You could then reference your forked forked version in your application.

Benefits of NPM Audit

Leverages the work of other contributors in the open-source community Problems are clearly identified, and labeled with level of severity Offers an out-of-the-box option to fix the detected vulnerabilities if a solution has been published

Wrapping up

Manually going through all the dependencies and their cascading dependencies to find security holes is a tedious endeavour. We have to rely on the community to find security problems, report/fix them. And that’s what npm audit is all about — leveraging this vast community that’s not only writing the code that we use but also identifying and fixing vulnerabilities in all of this code.

It is important to note that the likelihood of the scenarios under which these vulnerabilities might be triggered can be also be mitigated by doing proper sanitization of input data.

Eliminating vulnerabilities by using npm audit does not remove vulnerabilities that exist in your code itself i.e. code written by the application developer. Proper coding standards and design patterns also go a long way in helping with that.