Using snyk with gulp

At node summit, I had a chance to talk with Guy Podjarny about why I’d make the switch from nsp to snyk. For me, the biggest frustration of using nsp was the list of exceptions that I had to maintain on all of my projects that used nsp. In theory, vulnerabilities aren’t released until there are patches available for the vulnerability; in practice, vulnerable packages are still present as transitive dependencies for weeks, even if you’re using Greenkeeper to keep your dependencies up-to-date. The nsprc file felt like a list of vulnerabilities that attackers could use to break my apps and libraries, and like I was covering instances of a vulnerability I could fix because there was one that I couldn’t. Snyk ships with a protect command to run as part of npm’s package lifecycle to apply patches that keep your module protected while the patch makes its way through your dependency closure.

Snyk wizard does a great job of retrofitting your existing npm scripts to run their tasks at the right part of the npm lifecycle

"scripts": {
"prepublish": "npm run snyk-protect; gulp prepublish",
"test": "snyk test && gulp",
"clean": "gulp clean",
"snyk-protect": "snyk protect"
},

However, you might want to have Snyk run as part of your development workflow using gulp, rather than using npm directly. The nsp ecosystem was the easy integration with gulp with gulp-nsp.

const nsp = require('gulp-nsp');
const path = require('path');
gulp.task('nsp', (cb) => {
nsp({package: path.join(__dirname, 'package.json')}, cb);
});

It turns out, though, that snyk has a great promise-based programmatic api that doesn’t seem to be documented, as far as I can tell.

const snyk = require('snyk');
snyk.test('.').then((data) => { 
if (data.vulnerabilities.length) {
console.error(data.vulnerabilities);
throw new Error(`Snyk found ${data.vulnerabilities.length} vulnernabilities`);
}
});

Which makes it pretty easy to write up a simple gulp task that uses snyk and fails the build when vulnerabilities are found

const snyk = require('snyk');
const gulp = require('gulp');
gulp.task('snyk', function () {
return snyk.test('.').then((data) => {
this.emit('error', new Error(`Snyk found vulnerabilities ${JSON.stringify(data.vulnerabilities)}`));
});
});

Which makes it easy to consolidate to a single task pretty simple by updating the existing test task (like `gulp.task(‘test’, [‘xo’, ‘ava’, ‘coveralls’, ‘snyk’]);`

"scripts": {
"prepublish": "npm run snyk-protect; gulp prepublish",
"test": "gulp test",
"clean": "gulp clean",
"snyk-protect": "snyk protect"
},

But there’s still the snyk step that isn’t being run on prepublish. When you install new dependencies, you need to patch the dependency closure; prepublish runs when you install your dependencies (and not when your module is installed as a dependency, like the install lifecycle hooks). If you use your prepublish script as your generic build script, you may not want to have developers run snyk protect on every build, but if you’ve separated them and want to make prepublish work the same locally as on CI, you might want to run protect at part of gulp prepublish. There doesn’t seem to be a protect method on the programmatic api, but you can use child_process.exec instead

const gulp = require('gulp');
const cp = require('child_process');
gulp.task('protect', function(cb) {
cp.exec('./node_modules/.bin/snyk protect', (err, data) => {
gutil.log(data);
cb();
});
})

But that’s kind of a lot to remember, and if you have to install gulp-util, you’re already adding an import and a dependency, so if you’d prefer, you can grab gulp-snyk off npm instead

const gulp = require('gulp');
const snyk = require('gulp-snyk');
gulp.task('protect', function(cb) {
return snyk({command: 'protect', debug: true}, cb);
});
gulp.task('test', function (cb) {
return snyk({command: 'test', debug: true}, cb);
});
gulp.task('prepublish', ['protect']);

And then your package.json is a simple passthrough to your gulp file

"scripts": {
"prepublish": "gulp prepublish",
"test": "gulp test"
},

Happy hacking!