29 Sep 2018

It's Dependencies All the Way Down

NodeJS has experienced a lot of growth over the years. Like any platform, programming or otherwise, much of that growth and user adoption depends not only on the quality of the platform itself, but also the ecosystem that builds up around it.

In this respect, NodeJS has possibly one of the largest ecosystems available. With over 700,000 packages published to npm, the NodeJS platform offers developers a lot of choice when it comes to 3rd-party modules. This choice lets developers choose and compose modules from many different authors when writing software, saving time, consolidating work, and reducing bugs instead of constantly reinventing the wheel.

The NodeJS ecosystem by and large follows the Unix philosophy, in particular the primary tenant to

Write programs [and libraries] that do one thing and do it well

— Peter H. Salus, A Quarter-Century of Unix (1994)

Keeping a module’s focus small is an important aspect of many programming design patterns such as the single-responsibility principle, as well as improving code reuse and composability. However, including these modules doesn’t come for free. Every project dependency is an added burden that the maintainers need to keep up to date and have to modify their code as the API of the module changes. Sometimes serious bugs are found in these modules that can expose application secrets, user data, or unauthorized access, among other classes of vulnerabilities, that the module authors need to fix before dependent projects can update to fix the bug.

Staying on top of vulnerability disclosures and keeping track of all of the changes from version to version in a project’s dependencies is a challenging and resource-draining task. Even more challenging, authors must not only be concerned with the vulnerabilities in modules they depend on directly, but also those in all of the transitive dependencies that NodeJS is (in)famous for.

npm audit

In the April 2018 announcement of npm@6, the headlining feature the npm team showcased was npm audit, a powerful tool designed to provide automatic security warnings when using code with a known security issue. The npm audit tool allows developers to completely analyze a project’s dependency tree - including all transitive dependencies - to identify known vulnerable versions of dependencies based on data from the Node Security Platform database that npm acquired in April 2018.

The npm team made two really important decisions when designing npm audit. First, all install requests issued through npm install are automatically subjected to the same analysis that the npm audit command performs, analyzing the package to be installed as well as any additional dependencies that package would pull into the project. Second, npm audit suggests resolutions for vulnerabilities it identifies thereby giving clear actionable feedback to developers on the next steps they can take to resolve issues identified in the audit. These two decisions are critical because not only does npm take a security-conscious approach by default, but it tries to make resolving issues as painless as possible. Developers don’t want to spend hours combing through changelogs or reviewing vulnerability reports just to find the package version that includes the vulnerability fix, they want to resolve the problem and get on with writing code.

As of [email protected], npm includes the npm audit fix command to automatically run all the package installations recommended by npm audit, lowering the pain of updating vulnerable packages even further.

The npm team shares their vision with the inclusion of tools like npm audit and a security-conscious approach to ecosystem packages:

By alerting the entire community to security vulnerabilities within a tool you already use, we can make JavaScript development safer for everyone.

npm, Inc.

Auditing filer

The first thing to do after making a fresh clone of any project is to install its dependencies. After grabbing the filer git url and cloning the repository, a quick npm install pulls in all the dependencies we should need to get started.

added 1079 packages from 1531 contributors and audited 9518 packages in 36.358s
found 6 vulnerabilities (1 low, 5 moderate)
  run `npm audit fix` to fix them, or `npm audit` for details

It looks like filer@353290a has some vulnerable packages, which is a perfect opportunity to dig into the auditing process.

npm@6 is pretty in-your-face about this information, which is important. It lists the total number of vulnerabilities, as well as breaks down the number of vulnerabilities of varying severity, and provides an actionable next step: running a full audit.

Running npm audit produces the following report:

                        === npm audit security report ===

    # Run  npm install --save-dev [email protected]  to resolve 6 vulnerabilities
    SEMVER WARNING: Recommended action is a potentially breaking change
    ┌───────────────┬──────────────────────────────────────────────────────────────┐
    │ Moderate      │ Memory Exposure                                              │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Package       │ tunnel-agent                                                 │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Dependency of │ karma [dev]                                                  │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Path          │ karma > log4js > loggly > request > tunnel-agent             │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ More info     │ https://nodesecurity.io/advisories/598                       │
    └───────────────┴──────────────────────────────────────────────────────────────┘

    ┌───────────────┬──────────────────────────────────────────────────────────────┐
    │ Moderate      │ Prototype pollution                                          │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Package       │ hoek                                                         │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Dependency of │ karma [dev]                                                  │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Path          │ karma > log4js > loggly > request > hawk > boom > hoek       │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ More info     │ https://nodesecurity.io/advisories/566                       │
    └───────────────┴──────────────────────────────────────────────────────────────┘

    ┌───────────────┬──────────────────────────────────────────────────────────────┐
    │ Moderate      │ Prototype pollution                                          │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Package       │ hoek                                                         │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Dependency of │ karma [dev]                                                  │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Path          │ karma > log4js > loggly > request > hawk > cryptiles > boom  │
    │               │ > hoek                                                       │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ More info     │ https://nodesecurity.io/advisories/566                       │
    └───────────────┴──────────────────────────────────────────────────────────────┘

    ┌───────────────┬──────────────────────────────────────────────────────────────┐
    │ Moderate      │ Prototype pollution                                          │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Package       │ hoek                                                         │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Dependency of │ karma [dev]                                                  │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Path          │ karma > log4js > loggly > request > hawk > hoek              │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ More info     │ https://nodesecurity.io/advisories/566                       │
    └───────────────┴──────────────────────────────────────────────────────────────┘

    ┌───────────────┬──────────────────────────────────────────────────────────────┐
    │ Moderate      │ Prototype pollution                                          │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Package       │ hoek                                                         │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Dependency of │ karma [dev]                                                  │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Path          │ karma > log4js > loggly > request > hawk > sntp > hoek       │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ More info     │ https://nodesecurity.io/advisories/566                       │
    └───────────────┴──────────────────────────────────────────────────────────────┘

    ┌───────────────┬──────────────────────────────────────────────────────────────┐
    │ Low           │ Regular Expression Denial of Service                         │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Package       │ timespan                                                     │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Dependency of │ karma [dev]                                                  │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ Path          │ karma > log4js > loggly > timespan                           │
    ├───────────────┼──────────────────────────────────────────────────────────────┤
    │ More info     │ https://nodesecurity.io/advisories/533                       │
    └───────────────┴──────────────────────────────────────────────────────────────┘

    found 6 vulnerabilities (1 low, 5 moderate) in 9518 scanned packages
    6 vulnerabilities require semver-major dependency updates.

The audit provides a lot of information, but it’s all broken down for the developer in a consistent format so even those who aren’t familiar with vulnerability mitigation or announcement procedures can understand what’s going on.

One of the most important parts of the audit output is the path. This shows how the project and the vulnerable package are related, and is a subset of the total dependency graph for the project. This is really useful if there is no clear update path for the package your project depends on directly and you need to report (or ideally, submit a pull request) that a dependency in one of your direct dependencies should update one (or more) of its packages. Without this information clearly displayed, you would spend a lot of time chasing dependencies down by manually reviewing package.json and package-lock.json files trying to figure out who needed to update their dependencies so you can update yours.

In the case of Filer we’re pretty lucky: not only are all of the vulnerable packages related to a single direct dependency, but that dependency is one that is only used during development; resolving the problems is a simple as updating to [email protected]. Since this is a simple resolution, we could either run npm audit fix and have npm take care of the version updates for us, or we can update the karma version in our package.json and install the updated package.

After updating karma, re-running npm audit to ensure we haven’t introduced any new vulnerabilities, and running the project’s tests to ensure we haven’t accidentally broken any functionality, we should take the time to contribute this fix back to the main project. The fix was small and simple, but security is everyone’s responsibility; if you notice a project using outdated or vulnerable packages, take some time to help the maintainers out by submitting a pull request. Also, projects should consider looking into automating the process of keeping project dependencies up to date through various tools like Greenkeeper and Snyk, which is a conversation you can start in your project today!