Checking for vulnerable Node.js modules

The best tool I’ve found for validating a project’s packages for known vulerabilities is nsp. It checks the installed Node.js modules against the known vulnerabilities in the nodesecurity.io database; https://nodesecurity.io/advisories/ (RSS feed).

To install the nsp package, type the following command into your Terminal:

$ [sudo] npm install nsp -g

Once the nsp module is installed globally, you can check for vulnerable modules in your project using one of the two following commands:

  • nsp audit-package: Takes an existing package.json file and submits it for validation to nodesecurity.io.
  • nsp audit-shrinkwrap: Takes an existing npm-shrinkwrap.json file and submits it for validation to nodesecurity.io.

Note: To generate an npm-shrinkwrap.json file, type $ npm shrinkwrap in your Terminal.

For example, consider the following the following package.json file:

{
  "dependencies": {
    "crumb": "2.2.0",
    "hapi": "6.5.1",
    "libyaml": "0.2.2",
    "marked": "0.3.0",
    "nsp": "0.2.1",
    "qs": "0.6.6",
    "syntax-error": "1.0.0"
  },
  "devDependencies": {
    "eslint": "0.7.4",
    "request": "2.39.0"
  }
}

Running $ npm shrinkwrap in the project’s root folder gives the following output:

$ npm shrinkwrap
npm WARN shrinkwrap Excluding devDependency: eslint
npm WARN shrinkwrap Excluding devDependency: request
wrote npm-shrinkwrap.json

By default, the devDependencies aren’t included in the npm-shrinkwrap.json file, so if you want to check all your dependencies and devDependencies you need to add the --dev flag:

$ npm shrinkwrap --dev
wrote npm-shrinkwrap.json

To check for vulnerable modules, simply type nsp audit-shrinkwrap, as seen in the following example:

$ nsp audit-shrinkwrap
Name          Installed   Patched  Vulnerable Dependency
crumb           2.2.0     >=3.0.0  sample
libyaml         0.2.2     >=0.2.3  sample
marked          0.3.0     >=0.3.1  sample
qs              0.6.6     >= 1.x   sample
syntax-error    1.0.0    >= 1.1.1  sample

Note: If nsp finds any vulnerable modules, it will exit with an exit code of 1:

$ nsp audit-shrinkwrap
Name          Installed   Patched  Vulnerable Dependency
crumb           2.2.0     >=3.0.0  sample
libyaml         0.2.2     >=0.2.3  sample
marked          0.3.0     >=0.3.1  sample
qs              0.6.6     >= 1.x   sample
syntax-error    1.0.0    >= 1.1.1  sample

$ echo $?
1

You can also check whether a specific module/version is vulnerable by querying the https://nodesecurity.io/validate/ API and specifying a specific name and version. For example, https://nodesecurity.io/validate/qs/0.6.6 returns the following data:

[
  {
    "title": "qs Denial-of-Service Extended Event Loop Blocking",
    "author": "Tom Steele",
    "module_name": "qs",
    "publish_date": "Aug 6 2014 09:10:23 GMT-0800 (PST)",
    "cves": [],
    "vulnerable_versions": "<1.0.0",
    "patched_versions": ">= 1.x",
    "url": "qs_dos_extended_event_loop_blocking"
  },
  {
    "title": "qs Denial-of-Service Memory Exhaustion",
    "author": "Dustin Shiver",
    "module_name": "qs",
    "publish_date": "Aug 6 2014 09:10:22 GMT-0800 (PST)",
    "cves": [],
    "vulnerable_versions": "<1.0.0",
    "patched_versions": ">= 1.x",
    "url": "qs_dos_memory_exhaustion"
  }
]

It found vulnerable modules, now what?

The first step after finding vulnerable modules is to check for updates. Hopefully, updating to the latest version of a module will resolve the security warning.

A great command line tool for this is david ($ [sudo] npm install david -g), or you can use the built-in $ npm outdated command.

david

You can search for outdated modules with david by simply typing:

$ david

This will check your local package.json file for outdated modules and list newer versions.

Typing david in the project’s root directory will give us the following output:

$ david

Outdated Dependencies

crumb (package: 2.2.0, latest: 3.1.0)
libyaml (package: 0.2.2, latest: 0.2.4)
syntax-error (package: 1.0.0, latest: 1.1.1)
marked (package: 0.3.0, latest: 0.3.2)
qs (package: 0.6.6, latest: 1.2.2)

npm install --save crumb@3.1.0 libyaml@0.2.4 syntax-error@1.1.1 marked@0.3.2 qs@1.2.2


Outdated Dev Dependencies

request (package: 2.39.0, latest: 2.40.0)

npm install --save-dev request@2.40.0

To check for outdated global modules, you can use david -g, as seen in the following example:

$ david -g

Outdated Global Dependencies

express (package: 3.16.0, latest: 4.8.4)
npm (package: 1.4.21, latest: 1.4.23)

npm install --global express@4.8.4 npm@1.4.23

npm outdated

Another option for checking for outdated modules is npm’s very own npm outdated command.

Using the same package.json example above, we get the following output:

$ npm outdated
Package              Current  Wanted  Latest  Location
async                 0.2.10  0.2.10   0.9.0  nsp > async
celeri                 0.3.2     git     git  nsp > celeri
chalk                  0.4.0   0.4.0   0.5.1  eslint > chalk
cli-color              0.2.3   0.2.3   0.3.2  nsp > cli-color
crumb                  2.2.0   2.2.0   3.1.0  crumb
debug                  0.8.1   0.8.1   1.0.4  eslint > debug
estraverse             1.3.2   1.3.2   1.5.1  eslint > estraverse
estraverse             1.3.2   1.5.1   1.5.1  eslint > escope > estraverse
js-yaml                3.0.2   3.0.2   3.1.0  eslint > js-yaml
libyaml                0.2.2   0.2.2   0.2.4  libyaml
marked                 0.3.0   0.3.0   0.3.2  marked
minimatch              0.3.0   0.3.0   1.0.0  eslint > minimatch
minimist               0.0.8   0.0.8   1.1.0  eslint > mkdirp > minimist
minimist               0.0.8   0.0.8   1.1.0  nsp > minimist
minimist               0.0.8  0.0.10   1.1.0  nsp > optimist > minimist
minimist              0.0.10  0.0.10   1.1.0  hapi > optimist > minimist
multiparty            3.2.10  3.2.10   3.3.2  hapi > multiparty
npm-registry-client   0.4.12  0.4.12   3.1.1  nsp > npm-registry-client
npmconf               0.1.16  0.1.16   1.1.5  nsp > npmconf
object-assign          0.3.1   0.3.1   1.0.0  eslint > object-assign
osenv                  0.0.3   0.0.3   0.1.0  nsp > osenv
qs                     0.6.6   0.6.6   1.2.2  qs
request               2.34.0  2.34.0  2.40.0  nsp > request
request               2.39.0  2.39.0  2.40.0  request
semver                 2.3.2   2.3.2   3.0.1  hapi > semver
semver                 2.3.2   2.3.2   3.0.1  nsp > semver
strip-json-comments    0.1.3   0.1.3   1.0.1  eslint > strip-json-comments
syntax-error           1.0.0   1.0.0   1.1.1  syntax-error

The npm outdated command has an optional depth flag which allows you to control how many levels deep to check for outdated dependencies. To only check the top level dependencies and devDependencies, you can specify a depth of 0:

$ npm outdated --depth 0
Package       Current  Wanted  Latest  Location
crumb           2.2.0   2.2.0   3.1.0  crumb
libyaml         0.2.2   0.2.2   0.2.4  libyaml
marked          0.3.0   0.3.0   0.3.2  marked
qs              0.6.6   0.6.6   1.2.2  qs
request        2.39.0  2.39.0  2.40.0  request
syntax-error    1.0.0   1.0.0   1.1.1  syntax-error

Similarly, to check for outdated global modules, you can pass the --global or -g flag:

$ npm outdated -g --depth 0
Package    Current    Wanted    Latest  Location
express     3.16.0     4.8.4     4.8.4  /usr/local/lib > express
npm         1.4.21    1.4.23    1.4.23  /usr/local/lib > npm

Creating shortcuts using package.json scripts

By adding some commands to the scripts section in your package.json file, you can make it easier to remember flags or make sure everybody on the team uses the same commands to check outdated files or create shrinkwrap files.

{
  "name": "sample",
  "version": "1.0.0",
  "scripts": {
    "audit": "nsp audit-shrinkwrap",
    "outdated": "npm outdated --depth 0",
    "shrinkwrap-dev": "npm shrinkwrap --dev",
    "start": "node index"
  },
  "dependencies": {
    "crumb": "2.2.0",
    "hapi": "6.5.1",
    "libyaml": "0.2.2",
    "marked": "0.3.0",
    "nsp": "0.2.1",
    "qs": "0.6.6",
    "syntax-error": "1.0.0"
  },
  "devDependencies": {
    "eslint": "0.7.4",
    "request": "2.39.0"
  }
}

To run these scripts, you can use the following commands:

  • npm run audit: Uploads the npm-shrinkwrap.json file to nodesecurity.io for validation.
  • npm run outdated: Checks for newer versions of top level dependencies and devDependencies.
  • npm run shrinkwrap-dev: Creates an npm-shrinkwrap.json file with both the dependencies and devDependencies.
  • npm start: Runs the ./index.js file in your project’s directory.

Now, running a task such as $ npm run outdated will give the following output:

$ npm run outdated

> sample@1.0.0 outdated /Users/pdehaan/dev/nsp-sample
> npm outdated --depth 0

Package       Current  Wanted  Latest  Location
syntax-error    1.0.0   1.0.0   1.1.1  syntax-error
qs              0.6.6   0.6.6   1.2.2  qs
crumb           2.2.0   2.2.0   3.1.0  crumb
marked          0.3.0   0.3.0   0.3.2  marked
request        2.39.0  2.39.0  2.40.0  request
libyaml         0.2.2   0.2.2   0.2.4  libyaml

Running the audit task gives slightly different output, as you can see in the following example:

$ npm run audit

> sample@1.0.0 audit /Users/pdehaan/dev/nsp-sample
> nsp audit-shrinkwrap

Name          Installed   Patched  Vulnerable Dependency
crumb           2.2.0     >=3.0.0
libyaml         0.2.2     >=0.2.3
marked          0.3.0     >=0.3.1
qs              0.6.6     >= 1.x
qs              0.6.6     >= 1.x
syntax-error    1.0.0    >= 1.1.1

npm ERR! sample@1.0.0 audit: `nsp audit-shrinkwrap`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the sample@1.0.0 audit script.
npm ERR! This is most likely a problem with the sample package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     nsp audit-shrinkwrap
npm ERR! You can get their info via:
npm ERR!     npm owner ls sample
npm ERR! There is likely additional logging output above.
npm ERR! System Darwin 12.5.0
npm ERR! command "node" "/usr/local/bin/npm" "run" "audit"
npm ERR! cwd /Users/pdehaan/dev/nsp-sample
npm ERR! node -v v0.10.30
npm ERR! npm -v 1.4.21
npm ERR! code ELIFECYCLE
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR!     /Users/pdehaan/dev/nsp-sample/npm-debug.log
npm ERR! not ok code 0

Since the $ nsp audit-shrinkwrap command exits with an exit code of 1 if it finds any vulnerable modules, the script fails. If you don’t want that behavior, you can add || true to audit script so the exit code will always be 0 (success).

{
  "scripts": {
    "audit": "nsp audit-shrinkwrap || true"
  }
}

Now when we run the npm run audit script, we get the following results:

$ npm run audit

> sample@1.0.0 audit /Users/pdehaan/dev/nsp-sample
> nsp audit-shrinkwrap || true

Name          Installed   Patched  Vulnerable Dependency
crumb           2.2.0     >=3.0.0
libyaml         0.2.2     >=0.2.3
marked          0.3.0     >=0.3.1
qs              0.6.6     >= 1.x
qs              0.6.6     >= 1.x
syntax-error    1.0.0    >= 1.1.1

$ echo $?
0

Forcing the exit code to 0 can be very important if you’re using this as part of a build system (Travis, for example), and don’t want to fail a build due to potentially vulnerable sub-sub-dependencies.

Using GruntJS and grunt-nsp-shrinkwrap

If you use Grunt, then you can simply this process by using the grunt-nsp-shrinkwrap module.

First, install the grunt and grunt-nsp-shrinkwrap modules (and optionally save them as devDependencies using the -D flag):

$ npm i grunt grunt-nsp-shrinkwrap -D

Next, create a Gruntfile.js file with the following contents:

module.exports = function (grunt) {
  grunt.loadNpmTasks("grunt-nsp-shrinkwrap");

  grunt.initConfig({});

  grunt.registerTask("default", ["validate-shrinkwrap"]);
};

Assuming you have the grunt-cli module already installed globally ($ [sudo] npm install grunt-cli -g), you can now run the validate-shrinkwrap task using $ grunt validate-shrinkwrap or $ grunt (since it is registered as our default task).

$ grunt validate-shrinkwrap
Running "validate-shrinkwrap" task
/Users/pdehaan/dev/nsp-sample/npm-shrinkwrap.json
>> Name          Installed   Patched  Vulnerable Dependency
>> crumb           2.2.0     >=3.0.0
>> libyaml         0.2.2     >=0.2.3
>> marked          0.3.0     >=0.3.1
>> qs              0.6.6     >= 1.x
>> qs              0.6.6     >= 1.x
>> syntax-error    1.0.0    >= 1.1.1
Warning: known vulnerable modules found Use --force to continue.

Aborted due to warnings.

$ echo $?
6

To force the validate-shrinkwrap task to exit with an exit code of zero (similar to our npm run audit script we defined earlier), you can specify a --force flag, as seen in the following example:

$ grunt validate-shrinkwrap --force
Running "validate-shrinkwrap" task
/Users/pdehaan/dev/nsp-sample/npm-shrinkwrap.json
>> Name          Installed   Patched  Vulnerable Dependency
>> crumb           2.2.0     >=3.0.0
>> libyaml         0.2.2     >=0.2.3
>> marked          0.3.0     >=0.3.1
>> qs              0.6.6     >= 1.x
>> qs              0.6.6     >= 1.x
>> syntax-error    1.0.0    >= 1.1.1
Warning: known vulnerable modules found Used --force, continuing.

Done, but with warnings.

$ echo $?
0

For more information on Node.js security, check out the nodesecurity.io Resources page.

Using the dba module in a #GruntJS task

The following example shows how you can create a simple Grunt task wrapper for the Node.js dba module.

DBA is an experimental tool which is trying to determine a complex JS functions with very low comments / logical lines of code ratio.

Install the required dev dependencies and save them to our package.json file:

$ npm i grunt dba lodash.template -D

Next, enter the following task into your Grunt tasks folder:

#!/usr/bin/env node

var path = require('path');

var dba = require('dba');
var _ = {
  template: require('lodash.template')
};

module.exports = function (grunt) {
  'use strict';

  grunt.config('dba', {
    app: {
      options: {
        effort: 2000,
        ratio: 0
      },
      src: [
        '**/*.js',
        '!node_modules/**'
      ]
    }
  });

  grunt.registerMultiTask('dba', 'Seriously, just comment your code.', function () {
    var options = this.options() || {};
    options.format = options.format || '- [${file}:${line}] ${name}() -> ${effort}';
    var _tmpl = _.template(options.format);

    var files = this.filesSrc.filter(function (file) {
      // Make sure its actually a file and not a folder named `foo.js`.
      return grunt.file.isFile(file);
    });

    var results = [];

    files.forEach(function (file) {
      var content = grunt.file.read(file, 'utf8');
      var arr;
      try {
        arr = dba(content, options);
        // If we got results from `dba` log the to the results array and set the
        // `success` flag to fail the build.
        if (arr.length > 0) {
          arr = arr.map(function (item) {
            // Remap some data to make it easier to template.
            item.file = path.basename(file);
            item.effort = parseInt(item.effort, 10);
            return item;
          });
          results.push({'file': file, 'messages': arr});
        }
      } catch (err) {
        grunt.verbose.writeln('Unable to parse %s', file);
      }
    });

    if (results.length > 0) {
      results.forEach(function (result) {
        grunt.log.subhead(result.file);
        result.messages.forEach(function (message) {
          grunt.log.writeln(_tmpl(message));
        });
      });
      return false;
    }
  });
};

This Week in #GruntJS (2013-11-29)

@NodeJS Updates

@npmjs Updates

@GruntJS Updates

Continue reading

This Week in #GruntJS (2013-11-22)

@NodeJS Updates

@npmjs Updates

@gruntjs Updates

Continue reading

This Week in #GruntJS (2013-11-15)

@Nodejs Updates

@npmjs Updates

  • Hi! I’m the @npmjs mirror for Europe. http://npmjs.eu Come check me out! (src)
  • @npmjs tip: You can now use the GitHub shorthand to define repository in your package.json: “repository”: “sindresorhus/chalk” (src)

@GruntJS Updates

Continue reading

This Week in #GruntJS (2013-11-08)

#GruntJS Articles

Continue reading

This Week in #GruntJS (2013-11-01)

@Nodejs Updates

@GruntJS Updates

Continue reading