Eliminating Known Vulnerabilities With Snyk
The way we consume open source software (OSS) dramatically changed over the past decade or two. Flash back to the early 2000s, we mostly used large OSS projects from a small number of providers, such as Apache, MySQL, Linux and OpenSSL. These projects came from well–known software shops that maintained good development and quality practices. It wasn’t our code, but it felt trustworthy, and it was safe to assume it didn’t hold more bugs than our own code.
Fast–forward to today and OSS has turned into crowd–sourced marketplaces. Node’s npm carries over 210,000 packages from over 60,000 contributors; RubyGems holds over 110,000 gems, and Maven’s central repository indexes nearly 130,000 artifacts. Packages can be written by anybody, and range from small utilities that convert milliseconds to full–blown web servers. Packages often use other packages in turn, ending with a typical application holding hundreds if not thousands of OSS packages.
This wealth of open source functionality is awesome, but it also carries risk. You’re running a stranger’s code inside your applications. Do you know which packages you’re running? Do you know if their authors understand or care about security? Do you know if they have vulnerabilities?
The web community needs multiple new tools and practices to help us track and scrutinize these components — and Snyk takes on the security part of the story.
Security Risks And Known Vulnerabilities
Running untrusted code inside your system poses a broad security risk. Tackling it isn’t simple, but a good place to start is by addressing known vulnerabilities. These are publicly disclosed security bugs, typically found and logged by users, or intentionally found and reported by security researchers. Being public makes these issues the easiest ones for attackers to find and exploit, and so very important to address.
Right now, roughly 14% of the top npm packages carry known vulnerabilities. The stats are far worse for full applications: over 80% of Snyk users find vulnerabilities when testing their applications. Some of these issues are minor in nature, and some are very severe. You can see a live demo of a hacker exploiting one such vulnerability in this video (roughly four minutes in).
Fortunately, there’s a new free tool that can help you find and fix vulnerabilities in Node.js — Snyk. This article will walk you through how you can use Snyk to make your applications more secure.
Getting Started
First off, you’ll need a project to test. If you don’t have one handy, you can use the demo application we’ll review in this article, snyk-demo-app. Simply run these lines to clone it and install its dependencies:
git clone https://github.com/Snyk/snyk-demo-app.git
cd snyk-demo-app
npm install
Now that you have a project to test, you need to install Snyk from npm, change directory to your project’s folder and run Snyk’s Wizard. We’ll install Snyk as a global tool for now; later we’ll touch on using it as a local dependency of your automated tests. Run the following in your project’s folder:
npm install –g snyk
snyk wizard
As its name implies, the wizard is a guided walkthrough. It walks you through finding and fixing the issues found through upgrades and patches, and creates a Snyk policy (a .snyk file) with your decisions. The wizard leverages four other Snyk commands — auth
, test
, protect
and monitor
- which we’ll explain as we advance.
Authentication
If this is the first time you use Snyk, the wizard will first ask you to register using your GitHub account. Note that Snyk does not require access to your repositories. It only requests access to your email, using GitHub as an authentication system.
Once authenticated, the wizard will get an API key to store locally and get on with the testing. The same authentication process can be done by running snyk auth
, or running snyk auth <api-key>
(especially useful when integrating Snyk into your build/continuous integration (CI) system).
Testing
The next step is to find the vulnerabilities. The wizard will traverse the local project and collect the packages it uses (note this means you should only run it after you run npm install
). It then posts this list to the Snyk service, where they’re matched against Snyk’s open source vulnerability database. This test also can be performed by running snyk test
, which is useful when integrating Snyk into your CI (more on that later).
Once the vulnerabilities are determined, the wizard will go through them and guide you through the remediation steps needed. It remembers the answers you give, and when the questions end it makes the changes you asked for.
Let’s go through the findings on the snyk demo app.
You can see the wizard found 11 vulnerabilities in the 314 dependencies used. The first one is a high severity issue in a direct dependency called bassmaster. There’s only so much detail we can share in a terminal, but you can use the info link to get more information about the vulnerability itself.
In many cases, disclosed vulnerabilities are fixed shortly after they’re discovered, and all you need to do is upgrade to the relevant version. When that’s possible, upgrade is the cleanest and best way to address such a security bug. In the case of bassmaster, all we need to apply is a ‘fix’ upgrade, making the decision to upgrade a no brainer.
Next, the wizard reports three vulnerabilities in the hapi@10.5.0 dependency. This typically happens when you are several versions behind the latest version, and in the meantime multiple vulnerabilities have been found and fixed. In this case, clicking the info link will show three vulnerabilities fixed in version 11.0.0, 11.1.3 and 11.1.4 of hapi.
The wizard’s default suggestion is to upgrade to the latest version, addressing all three issues, but you can also choose to review and act on each vulnerability separately.
Next, we see that a direct dependency, falcor-router-demo@1.0.3, introduced three vulnerabilities. The vulnerabilities in this case aren’t in the falcor-router-demo
code, but rather in the dependencies it pulls in. This is a very common scenario, as most of the packages used by your application are actually pulled in indirectly.
The info link, which brings you to a test page on the direct dependency, further shows two different vulnerabilities in qs
and one in semver
.
Unfortunately, you can’t upgrade a deep dependency, both for technical reasons and for fear of breaking functionality. Your remediation step therefore is to upgrade the direct dependency, triggering the deep dependency upgrade. In this case, upgrading falcor-router-demo
to version 1.0.5 (a ‘fix’ upgrade) will trigger the qs
and semver
upgrades you need to fix the vulnerabilities.
Patch And Protect
The next issue the wizard reports on our demo app is different.
Snyk found a vulnerability in handlebars, pulled in via the direct dependency snyk-demo-child
. Although the vulnerability was fixed in Handlebars 4.0.0, snyk-demo-child
has not upgraded to that version — so you can’t upgrade the vulnerability away.
This scenario is especially common in recently disclosed vulnerabilities, as it takes a while for the dependency chain to catch up. In addition, sometimes an upgrade is available, but it’s a major upgrade with breaking changes, and you can’t handle it right now. In cases when you have no upgrade option, instead of simply remaining vulnerable, Snyk suggests you patch the vulnerability.
Patching means changing the locally installed package files to fix the vulnerability. The patches are created and maintained by Snyk, and typically start with the code changes the package owner made to fix the issue, having removed any cosmetic or unrelated changes. The Snyk security team then verifies them, back–ports them to older versions, and tests they’ve not broken functionality. The patches are a part of Snyk’s open source vulnerability database.
After we patch Handlebars, the wizard prompts about two instances of uglify–js vulnerabilities, suggesting you patch them all.
As projects expand, it’s common to find the same package repeated in the dependency tree, and it’s not that rare for one package to have multiple vulnerabilities. When the wizard sees multiple instances of a vulnerable package, it offers a shortcut to patch them all to save time. You can still choose to review and patch each issue separately, and if an instance was sufficiently upgraded by previously chosen upgrades it won’t be touched.
Note the wizard only patches the locally installed files. This means you need to reapply this patch every time dependencies are reinstalled, which you can do by running snyk protect
. The wizard stores the patches you chose in the Snyk policy (.snyk), and snyk protect
will apply those patches, and those patches alone — it never unilaterally applies a patch. Each time you reinstall your dependencies, you should run snyk protect
to close the vulnerabilities. The wizard can do this for you, as we’ll see later on.
Ignore
The next issue the wizard shows is not as easily solved.
A vulnerability is found in a deep validator
dependency, which has neither an upgrade nor a patch available. There are many combinations of vulnerability and module versions, and not all of them can be patched. Snyk’s security team is constantly adding more patches to the open source VulnDB and would welcome pull requests, but some issues still have no patch.
There’s no easy fix for these issues. You’ll need to better understand the risk this issue presents to your system, and weigh this risk against the effort of fixing the issue — for instance, by removing the dependency. While you consider your actions, you can ‘snooze’ the issue with Snyk, telling it to ignore the issue for 30 days. Snyk will prompt you to provide a reason for ignoring, to help you remember why you did it later on.
If you assess the vulnerability and decide it’s not an issue (for instance, because a component is not really deployed to production), you can manually edit the Snyk policy (.snyk) file to use a far–future expiry date for this instance. Note that Snyk does not test devDependencies by default, avoiding most such red herrings.
In addition to any action you take, Snyk will let you know when a patch or upgrade become available for this scenario, so you can apply a better solution.
Applying Your Choices
That’s it, we’ve tackled all the issues — hurray!
Before the wizard applies the requested changes, it suggests adding two steps to your Package.json workflow to keep you vulnerability free.
First, the wizard suggests adding Snyk’s test to your regular npm test
action. If a vulnerable package was added, the test would fail, keeping you safe. The wizard will also add snyk
as devDependency, as you’ll need it in your test or CI environment. You can use the same logic to run this test in any favourite CI/test platform.
If you’ve chosen to patch an issue, the wizard will also suggest adding snyk protect
to the postinstall
step. The postinstall
hook runs every time you install this package’s dependencies, ensuring those dependencies are always properly patched. Note that such a hook requires adding snyk
as a dependency (not devDependency).
With all the questions answered, the wizard proceeds to apply the changes. It modifies the Package.json file with any upgrade requests or hooks, runs npm update
to apply the changes, and stores the Snyk policy in the .snyk file (you can pretty–print it by running snyk policy
). Make sure to add this .snyk file to your source control for patch and ignore instructions to apply.
Lastly, the wizard takes a snapshot of your dependencies, so it can monitor them over time.
Monitor
Now that you’re free of known vulnerabilities, there are two ways that can change. The first is adding vulnerable packages to your code, which we handle by adding snyk test
to your test/CI system. The second is through newly disclosed vulnerabilities. These are new disclosures of vulnerabilities in old code — the code you’re running in production!
This is addressed by Snyk’s last step — monitor. The snapshot the wizard takes is saved on Snyk’s servers, remembering the dependencies used on this application. If a new disclosed vulnerability affects your application, you’ll get an email alerting you to it. You can then run the wizard again to upgrade or patch as needed, and deploy the secure code.
Below is an example of an email you may get. As we mentioned above, you’ll also be notified about new remediation options; for instance, a patch or upgrade path that didn’t exist before.
To keep Snyk’s understanding of your application up to date, you can run snyk monitor
at the end of your deployment process. Doing so will take a fresh snapshot of your application, just like the wizard does, and will ensure Snyk’s alerts apply to your most recent code.
Summary
Open Source packages are awesome, but they carry with them a substantial security risk. A good way to start acknowledging and handling this risk is to address the known vulnerabilities in your dependencies, as those are the ones attackers are most likely to exploit.
Snyk makes it easy for you to find, fix and monitor for known vulnerabilities in Node.js. I recommend you:
- Run
snyk wizard
on your application and fix current issues. Upgrade when possible, patch when you can’t upgrade, and manually assess the rest of the issues. - Add
snyk test
to your tests and CI, to avoid adding vulnerable packages. - If you’ve patched issues, run
snyk protect
whenever you reinstall your dependencies; for instance, by adding it to yourpostinstall
script. - Add
snyk monitor
to your deployment process, to ensure you’re monitoring for vulnerabilities in the most up–to–date dependencies. - Stay alert for email notifications from Snyk about new vulnerabilities, and act quickly when they arrive.
If you own a public GitHub project, add a badge to show you’re vulnerability free, and remind your users security matters.
If you’re not using Node.js, register to get notified when we add new languages, or let us know which platforms you’d like us to support next!
Further Reading
- “I Contributed To An Open-Source Editor, And So Can You,” Christian Heilmann
- “A Short Guide To Open-Source And Similar Licenses,” Cameron Chapman
- “So You’ve Decided To Open-Source A Project At Work. What Now?,” Nicholas C. Zakas
- “Front-End Performance Checklist 2017,” Vitaly Friedman