check = function (options, callback) { if (typeof options === 'function') { callback = options; options = {}; } var proxy = options.proxy || Conf.proxy; proxy = proxy || process.env.https_proxy || process.env.HTTPS_PROXY; delete options.proxy; if (proxy) { Conf.api.agent = new ProxyAgent(proxy); } // Set defaults var wreck = Wreck.defaults(Conf.api); var shrinkwrap; var offline = options.offline; delete options.offline; var advisoriesPath = options.advisoriesPath || Conf.advisoriesPath; delete options.advisoriesPath; if (!options.exceptions) { options.exceptions = Conf.exceptions; } // validate if options are correct var isValid = Joi.validate(options, internals.optionSchema); if (isValid.error) { return callback(isValid.error); } options = isValid.value; if (typeof options.package === 'string') { try { options.package = require(options.package); } catch (e) { return callback(e); } } options.package = SanitizePackage(options.package); if (typeof options.shrinkwrap === 'string') { try { shrinkwrap = Fs.readFileSync(require.resolve(options.shrinkwrap), 'utf8'); options.shrinkwrap = require(options.shrinkwrap); } catch (e) { delete options.shrinkwrap; } } else { shrinkwrap = JSON.stringify(options.shrinkwrap, null, 2); } if (offline) { var advisories; if (!options.shrinkwrap) { return callback(new Error('npm-shrinkwrap.json is required for offline mode')); } try { if (advisoriesPath) { if (!PathIsAbsolute(advisoriesPath)) { advisoriesPath = Path.resolve(process.cwd(), advisoriesPath); } advisories = require(advisoriesPath); } else { advisories = require('../advisories'); } } catch (e) { return callback(new Error('Offline mode requires a local advisories.json')); } advisories = advisories.results; var exceptions = options.exceptions.map(function (exception) { return Number(internals.exceptionRegex.exec(exception)[1]); }); var generateContent = function (advisory) { var markdown = [ '# ' + advisory.title, '## Overview:', advisory.overview ]; if (advisory.recommendation) { markdown.push(''); markdown.push('## Recommendation:'); markdown.push(advisory.recommendation); } if (advisory.references) { markdown.push(''); markdown.push('## References:'); markdown.push(advisory.references); } return markdown.join('\n'); }; NPMUtils.getShrinkwrapDependencies(options.shrinkwrap, function (err, tree) { var keys = Object.keys(tree); var vulns = keys.map(function (key) { var mod = tree[key]; var matches = []; for (var i = 0, il = advisories.length; i < il; ++i) { if (mod.name === advisories[i].module_name && exceptions.indexOf(advisories[i].id) === -1 && Semver.satisfies(mod.version, advisories[i].vulnerable_versions)) { matches.push(advisories[i]); } } return { module: mod.name, version: mod.version, vulnerabilities: matches }; }).filter(function (mod) { return mod.vulnerabilities.length > 0; }); var results = []; for (var i = 0, il = vulns.length; i < il; ++i) { var path = tree[vulns[i].module + '@' + vulns[i].version].paths; var line = internals.findLines(shrinkwrap, vulns[i].module, vulns[i].version); for (var x = 0, xl = vulns[i].vulnerabilities.length; x < xl; ++x) { results.push({ module: vulns[i].module, version: vulns[i].version, vulnerable_versions: vulns[i].vulnerabilities[x].vulnerable_versions, patched_versions: vulns[i].vulnerabilities[x].patched_versions, title: vulns[i].vulnerabilities[x].title, path: path, advisory: 'htt ...
n/a
getFormatter = function (name) { if (Object.keys(Formatters).indexOf(name) !== -1) { return Formatters[name]; } try { return require('nsp-formatter-' + name); } catch (e) {} return Formatters.default; }
n/a
codeclimate = function (err, data, pkgPath) { if (err) { return err.stack; } if (!data.length) { return; } var returnString = ''; for (var i = 0, il = data.length; i < il; ++i) { returnString += JSON.stringify({ type: 'issue', check_name: 'Vulnerable module "' + data[i].module + '" identified', description: '`' + data[i].module + '` ' + data[i].title, categories: ['Security'], remediation_points: 300000, content: { body: data[i].content }, location: { path: 'npm-shrinkwrap.json', lines: { begin: data[i].line.start, end: data[i].line.end } } }) + '\0\n'; } return returnString; }
n/a
default = function (err, data, pkgPath) { var returnString = ''; if (err) { if (data) { returnString += Chalk.red('(+) ') + 'Debug output: ' + JSON.stringify(Buffer.isBuffer(data) ? data.toString() : data) + '\ n'; } return returnString + Chalk.yellow('(+) ') + err; } var width = 80; if (process.stdout.isTTY) { width = process.stdout.getWindowSize()[0] - 10; } if (data.length === 0) { return Chalk.green('(+)') + ' No known vulnerabilities found'; } returnString += Chalk.red('(+) ') + data.length + ' vulnerabilities found\n'; data.sort(function(a, b) { return b.cvss_score - a.cvss_score; }).forEach(function (finding) { var table = new Table({ head: ['', finding.title], colWidths: [15, width - 15] }); table.push(['Name', finding.module]); table.push(['CVSS', finding.cvss_score + ' (' + Cvss.getRating(finding.cvss_score) + ')']); table.push(['Installed', finding.version]); table.push(['Vulnerable', finding.vulnerable_versions === '<=99.999.99999' ? 'All' : finding.vulnerable_versions]); table.push(['Patched', finding.patched_versions === '<0.0.0' ? 'None' : finding.patched_versions]); table.push(['Path', finding.path.join(' > ')]); table.push(['More Info', finding.advisory]); returnString += table.toString() + '\n'; }); return returnString; }
...
};
internals.exceptionRegex = /^https\:\/\/nodesecurity\.io\/advisories\/([0-9]+)$/;
internals.optionSchema = Joi.object({
package: Joi.alternatives().try(Joi.string(), Joi.object()),
shrinkwrap: Joi.alternatives().try(Joi.string(), Joi.object()),
exceptions: Joi.array().items(Joi.string().regex(internals.exceptionRegex)).default([]),
advisoriesPath: Joi.string(),
proxy: Joi.string()
}).or(['package', 'shrinkwrap']);
/*
options should be an object that contains one or more of the keys package, shrinkwrap, offline, advisoriesPath
{
...
json = function (err, data, pkgPath) { if (err) { return 'Debug output: ' + JSON.stringify(Buffer.isBuffer(data) ? data.toString() : data) + '\n' + JSON.stringify(err); } return JSON.stringify(data, null, 2); }
n/a
none = function (err, data, pkgPath) { return ''; }
n/a
summary = function (err, data, pkgPath) { var returnString = ''; if (err) { if (data) { returnString += Chalk.red('(+) ') + 'Debug output: ' + JSON.stringify(Buffer.isBuffer(data) ? data.toString() : data) + '\ n'; } return returnString + Chalk.yellow('(+) ') + err; } if (data.length === 0) { return Chalk.green('(+)') + ' No known vulnerabilities found'; } returnString += Chalk.red('(+) ') + data.length + ' vulnerabilities found\n'; var table = new Table({ head: ['Name', 'Installed', 'Patched', 'Path', 'More Info'], chars: { top: '', 'top-mid': '', 'top-left': '', 'top-right': '', 'bottom': '', 'bottom-mid': '', 'bottom-left': '', 'bottom-right': '', left: '', 'left-mid': '', 'mid': '', 'mid-mid': '', right: '', 'right-mid': '', middle: ' ' } }); data.forEach(function (finding) { table.push([finding.module, finding.version, finding.patched_versions === '<0.0.0' ? 'None' : finding.patched_versions, finding .path.join(' > '), finding.advisory]); }); returnString += table.toString() + '\n'; return returnString; }
n/a