New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

snyk

Package Overview
Dependencies
Maintainers
2
Versions
1967
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

snyk - npm Package Compare versions

Comparing version 1.6.0 to 1.6.1-alpha1

coverage/lcov-report/base.css

3

cli/commands/modules.js

@@ -5,2 +5,5 @@ var snyk = require('../../lib');

module.exports = function (path, options) {
if (!options) {
options = {};
}
return snyk.modules(path || process.cwd()).then(function (modules) {

@@ -7,0 +10,0 @@

200

cli/commands/protect/prompts.js

@@ -65,20 +65,7 @@ module.exports = {

}
// if no upgrade, then hopefully a patch
res = sort('publicationTime')(b, a);
}
// // sort by patch date
// if (a.patches.length) {
// // .slice because sort mutates
// var pda = a.patches.slice(0).sort(function (a, b) {
// return a.modificationTime - b.modificationTime;
// }).pop();
// var pdb = (b.patches || []).slice(0).sort(function (a, b) {
// return a.modificationTime - b.modificationTime;
// }).pop();
// if (pda && pdb) {
// return pda.modificationTime < pdb.modificationTime ? 1 :
// pda.modificationTime > pdb.modificationTime ? -1 : 0;
// }
// }
return res;

@@ -126,2 +113,3 @@ }

var from = curr.from[1];
if (!acc[from]) {

@@ -134,15 +122,31 @@ // only copy the biggest change

if (!acc[from].grouped) {
acc[from].grouped = {
affected: moduleToObject(from),
main: true,
id: acc[from].id + '-' + i,
count: 1,
upgrades: [],
};
acc[from].grouped.affected.full = from;
var upgrades = curr.upgradePath.slice(-1).shift();
debug('upgrade available? %s', upgrades && curr.upgradePath[1]);
// otherwise it's a patch and that's hidden for now
if (upgrades && curr.upgradePath[1]) {
if (!acc[from].grouped) {
acc[from].grouped = {
affected: moduleToObject(from),
main: true,
id: acc[from].id + '-' + i,
count: 1,
upgrades: [],
};
acc[from].grouped.affected.full = from;
// splice this vuln into the list again so if the user choses to review
// they'll get this individual vuln and remediation
copy.grouped = {
// splice this vuln into the list again so if the user choses to review
// they'll get this individual vuln and remediation
copy.grouped = {
main: false,
requires: acc[from].grouped.id,
};
res.splice(i + offset, 0, copy);
offset++;
}
debug('vuln found on group');
acc[from].grouped.count++;
curr.grouped = {
main: false,

@@ -152,18 +156,2 @@ requires: acc[from].grouped.id,

res.splice(i + offset, 0, copy);
offset++;
}
debug('vuln found on group');
acc[from].grouped.count++;
curr.grouped = {
main: false,
requires: acc[from].grouped.id,
};
var upgrades = curr.upgradePath.slice(-1).shift();
debug('upgrade available? %s', upgrades && curr.upgradePath[1]);
// otherwise it's a patch and that's hidden for now
if (upgrades && curr.upgradePath[1]) {
var p = moduleToObject(upgrades);

@@ -181,37 +169,40 @@ if (p.name !== acc[from].grouped.affected.name &&

// console.log(commonPatch);
// now filter out any vulns that don't have an upgrade path and only patches
// and have already been grouped
var dropped = [];
res = res.filter(function (vuln) {
if (vuln.grouped) {
if (vuln.grouped.main) {
if (vuln.grouped.upgrades.length === 0) {
debug('dropping %s', vuln.grouped.id);
dropped.push(vuln.grouped.id);
return false;
}
}
// var dropped = [];
// res = res.filter(function (vuln) {
// if (vuln.grouped) {
// if (vuln.grouped.main) {
// debug('ok!!');
// if (vuln.grouped.upgrades.length === 0) {
// debug('dropping %s', vuln.grouped.id);
// // dropped.push(vuln.grouped.id);
// // return false;
// }
// }
// we have to remove the group property on the collective vulns if the
// top grouping has been removed, because otherwise they won't be shown
if (dropped.indexOf(vuln.grouped.requires) !== -1) {
delete vuln.grouped;
}
}
// // we have to remove the group property on the collective vulns if the
// // top grouping has been removed, because otherwise they won't be shown
// if (dropped.indexOf(vuln.grouped.requires) !== -1) {
// delete vuln.grouped;
// }
// }
return true;
});
// return true;
// });
// resort after we made changes
res.sort(function (a) {
if (a.grouped) {
return -1;
}
// resort after we made changes putting upgrades first
// res.sort(function (a) {
// if (a.grouped) {//} && a.upgradePath[1]) { // RS changed to add in the upgradePath
// return -1;
// }
if (a.upgradePath[1]) {
return -1;
}
// if (a.upgradePath[1]) {
// return -1;
// }
return 1;
});
// return 1;
// });

@@ -233,3 +224,3 @@ debug(res.map(function (v) {

// do stuff
// console.log(prompts);

@@ -299,2 +290,3 @@ return prompts;

var messageIntro;
var fromText = false;
var group = vuln.grouped && vuln.grouped.main ? vuln.grouped : false;

@@ -310,6 +302,12 @@

messageIntro = severity + ' severity vulnerability found in ' + vulnIn +
', introduced via ' + from + (from !== vuln.from.slice(1).join(' > ') ?
'\n- from: ' + vuln.from.slice(1).join(' > ') : '');
', introduced via ' + from;
fromText = (from !== vuln.from.slice(1).join(' > ') ?
'- from: ' + vuln.from.slice(1).join(' > ') : '');
}
var note = false;
if (vuln.note) {
note = '- note: ' + vuln.note;
}
var res = {

@@ -337,5 +335,16 @@ when: function (answers) {

// if we've upgraded, then stop asking
var updatedTo = null;
res = groupAnswer.filter(function (answer) {
return answer.choice === 'update';
if (answer.choice === 'update') {
updatedTo = answer;
return true;
}
}).length === 0;
if (!res) {
// echo out what would be upgraded
var via = 'Fixed through previous upgrade instruction to ' + updatedTo.vuln.upgradePath[1];
console.log(['', messageIntro, infoLink, via].join('\n'));
// console.log(updatedTo);
}
}

@@ -350,3 +359,4 @@

type: 'list',
message: [messageIntro, infoLink, ' Remediation options'].join('\n')
message: [messageIntro, fromText, infoLink, note, ' Remediation options']
.filter(Boolean).join('\n')
};

@@ -390,9 +400,9 @@

// No upgrade available (as per no patch)
choices.push({
value: 'skip',
key: 'u',
short: 'Upgrade (none available)',
name: 'Upgrade (no sufficient upgrade available for ' +
from.split('@')[0] + ', we\'ll notify you when there is one)',
});
// choices.push({
// value: 'skip',
// key: 'u',
// short: 'Upgrade (none available)',
// name: 'Upgrade (no sufficient upgrade available for ' +
// from.split('@')[0] + ', we\'ll notify you when there is one)',
// });
}

@@ -402,7 +412,4 @@

if (group && group.upgrades.length) {
review.meta = {
groupId: group.id,
};
choices.push(review);
if (upgradeAvailable && group) {
} else {

@@ -421,6 +428,14 @@ if (vuln.patches && vuln.patches.length) {

res.patches = patches;
if (group) {
patch.name = 'Patch the ' + group.count + ' vulnerabilities';
}
choices.push(patch);
}
}
}
// only show patch option if this is NOT a grouped upgrade
if (upgradeAvailable === false || !group) {
if (patches === null) {

@@ -440,2 +455,9 @@ // add a disabled option saying that patch isn't available

if (group) {
review.meta = {
groupId: group.id,
};
choices.push(review);
}
if (patches === null && !upgradeAvailable) {

@@ -442,0 +464,0 @@ ignore.default = true;

@@ -123,2 +123,6 @@ module.exports = test;

if (vuln.note) {
res += vuln.note + '\n';
}
var upgradeSteps = (vuln.upgradePath || []).filter(Boolean);

@@ -125,0 +129,0 @@

{
"API": "https://app.snyk.io/api/v1",
"API": "https://snyk.io/api/v1",
"devDeps": false
}

@@ -11,2 +11,3 @@ module.exports = loadModules;

var fs = require('then-fs');
var _ = require('lodash');
var Promise = require('es6-promise').Promise; // jshint ignore:line

@@ -54,5 +55,5 @@ var path = require('path');

var modules = {};
var promise = Promise.resolve().then(function () {
// 1. read package.json for written deps
var pkg = tryRequire(path.resolve(root, 'package.json'));
var dir = path.resolve(root, 'package.json');
// 1. read package.json for written deps
var promise = tryRequire(dir).then(function (pkg) {

@@ -77,62 +78,84 @@ // if there's a package found, collect this information too

var res = dirs.map(function (dir) {
// completely ignore `.bin` npm helper dir
if (dir === '.bin') {
return null;
}
// this is a scoped namespace, and we'd expect to find directories
// inside *this* `dir`, so treat differently
if (dir.indexOf('@') === 0) {
dir = path.resolve(root, 'node_modules', dir);
return fs.readdir(dir).then(function (dirs) {
return Promise.all(dirs.map(function (scopedDir) {
dir = path.resolve(dir, scopedDir, 'package.json');
return tryRequire(dir);
}));
});
}
// otherwise try to load a package.json from this node_module dir
dir = path.resolve(root, 'node_modules', dir, 'package.json');
return tryRequire(dir);
})
.filter(Boolean);
});
if (res.length === 0) {
// effectively not a node module
var e = new Error('missing node_modules');
e.code = 'MISSING_NODE_MODULES';
throw e;
}
return Promise.all(res).then(function (res) {
res = _.flatten(res).filter(Boolean);
res.reduce(function (acc, curr) {
var license;
var licenses = curr.license || curr.licenses;
if (res.length === 0) {
// effectively not a node module
var e = new Error('missing node_modules');
e.code = 'MISSING_NODE_MODULES';
throw e;
}
if (Array.isArray(licenses)) {
license = licenses.reduce(function (acc, curr) {
acc.push((curr || {}).type || curr);
res.reduce(function (acc, curr) {
var license;
var licenses = curr.license || curr.licenses;
if (Array.isArray(licenses)) {
license = licenses.reduce(function (acc, curr) {
acc.push((curr || {}).type || curr);
return acc;
}, []).join('/');
} else {
license = (licenses || {}).type || licenses;
}
var depType = rootDepType;
if (depType !== DEP_TYPE_DEV) {
if (pkg.dependencies && pkg.dependencies[curr.name]) {
depType = DEP_TYPE_PROD;
} else if (pkg.devDependencies && pkg.devDependencies[curr.name]) {
depType = DEP_TYPE_DEV;
}
}
// By default include all modules, but optionally skip devDeps
if (depType === DEP_TYPE_DEV && !options.dev) {
return acc;
}, []).join('/');
} else {
license = (licenses || {}).type || licenses;
}
}
var depType = rootDepType;
if (depType !== DEP_TYPE_DEV) {
if (pkg.dependencies && pkg.dependencies[curr.name]) {
depType = DEP_TYPE_PROD;
} else if (pkg.devDependencies && pkg.devDependencies[curr.name]) {
depType = DEP_TYPE_DEV;
var valid = false;
if (pkg.dependencies) {
valid = semver.satisfies(curr.version, pkg.dependencies[curr.name]);
}
}
// By default include all modules, but optionally skip devDeps
if (depType === DEP_TYPE_DEV && !options.dev) {
acc[curr.name] = {
name: curr.name,
version: curr.version || null,
full: curr.name + '@' + (curr.version || '0.0.0'),
valid: valid,
devDependencies: curr.devDependencies,
depType: depType,
snyk: curr.snyk,
license: license || 'none',
dep: pkg.dependencies ? pkg.dependencies[curr.name] || null : null,
};
return acc;
}
}, modules.dependencies);
var valid = false;
if (pkg.dependencies) {
valid = semver.satisfies(curr.version, pkg.dependencies[curr.name]);
}
acc[curr.name] = {
name: curr.name,
version: curr.version || null,
full: curr.name + '@' + (curr.version || '0.0.0'),
valid: valid,
devDependencies: curr.devDependencies,
depType: depType,
license: license || 'none',
dep: pkg.dependencies ? pkg.dependencies[curr.name] || null : null,
};
return acc;
}, modules.dependencies);
return modules;
return modules;
});
}).then(function (modules) {
var deps = Object.keys(modules.dependencies);

@@ -139,0 +162,0 @@

@@ -8,2 +8,3 @@ var yaml = require('js-yaml');

var parse = require('./parser');
var tryRequire = require('../try-require');

@@ -24,3 +25,3 @@ module.exports = {

function load(root, options) {
if (typeof root === 'object') {
if (!Array.isArray(root) && typeof root !== 'string') {
options = root;

@@ -38,2 +39,6 @@ root = null;

if (Array.isArray(root)) {
return mergePolicies(root, options);
}
var filename = root ? path.resolve(root, '.snyk') : defaultFilename();

@@ -57,2 +62,54 @@

function mergePolicies(policyDirs, options) {
return Promise.all(policyDirs.map(function (dir) {
return load(dir, options);
})).then(function (policies) {
// firstly extend the paths in the ignore and patch
var rootPolicy = policies[0];
var others = policies.slice(1);
return Promise.all(others.map(function (policy) {
var filename = path.dirname(policy.__filename) + '/package.json';
return tryRequire(filename).then(function (pkg) {
var full = pkg.name + '@' + pkg.version;
mergePath('ignore', 'suggest', full, rootPolicy, policy);
mergePath('patch', 'patch', full, rootPolicy, policy);
});
})).then(function () {
return rootPolicy;
});
// return policies[0];
});
}
// note: mutates both objects, be warned!
function mergePath(type, into, pathRoot, rootPolicy, policy) {
if (!rootPolicy[into]) {
rootPolicy[into] = {};
}
Object.keys(policy[type]).forEach(function (id) {
// convert the path from `module@version` to `parent > module@version`
policy[type][id] = policy[type][id].map(function (path) {
// this is because our policy file format favours "readable" yaml,
// instead of easy to use object structures.
var key = Object.keys(path).pop();
var newPath = {};
newPath[pathRoot + ' > ' + key] = path[key];
path[key].from = pathRoot;
return newPath;
});
// add the rule if we don't have it in our policy already
if (!rootPolicy[into][id]) {
rootPolicy[into][id] = policy[type][id];
return;
}
// otherwise we need to merge up manually
rootPolicy[into][id] = rootPolicy[type][id].concat(policy[type][id]);
});
}
function save(object, root) {

@@ -59,0 +116,0 @@ var filename = root ?

// eventually we'll have v2 which will point to latestParser, and v1 will
// need to process the old form of data and upgrade it to v2 structure
module.exports = function (d) { return d; };
module.exports = function (policy) {
if (!policy.ignore) {
policy.ignore = {};
}
if (!policy.patch) {
policy.patch = {};
}
return policy;
};

@@ -9,2 +9,3 @@ var protect = module.exports = {

filterPatched: filterPatched,
attachNotes: attachNotes,
applyPatch: applyPatch,

@@ -223,2 +224,48 @@ };

function attachNotes(notes, vuln) {
if (!notes) {
return vuln;
}
debug('attaching notes');
var now = Date.now();
return vuln.map(function (vuln) {
if (!notes[vuln.id]) {
return vuln;
}
debug('%s has rules', vuln.id);
// if rules.some, then add note to the vuln
notes[vuln.id].forEach(function (rule) {
var path = Object.keys(rule)[0]; // this is a string
var expires = rule[path].expires;
// first check if the path is a match on the rule
var pathMatch = snyk.policy.matchToRule(vuln, rule);
if (pathMatch && expires < now) {
debug('%s vuln rule has expired (%s)', vuln.id, expires);
return false;
}
if (pathMatch) {
// strip any control characters in the 3rd party reason file
var reason = rule[path].reason.replace('/[\x00-\x1F\x7F-\x9F]/u', '');
debug('adding note based on path match: %s ~= %s', path,
vuln.from.slice(1).join(' > '));
vuln.note = 'Snyk policy in ' + rule[path].from +
' suggests ignoring this issue, with reason: ' + reason;
}
return false;
});
return vuln;
});
}
// cwd is used for testing

@@ -468,9 +515,10 @@ function filterPatched(patched, vuln, cwd) {

function generatePolicy(policy, tasks, live) {
var promises = [
protect.ignore(tasks.ignore, live),
protect.update(tasks.update, live),
protect.patch(tasks.patch, live),
log(tasks, live),
];
var promises = ['ignore', 'update', 'patch'].filter(function (task) {
return tasks[task].length;
}).map(function (task) {
return protect[task](tasks[task], live);
});
promises.push(log(tasks, live));
return Promise.all(promises).then(function (res) {

@@ -477,0 +525,0 @@ // we're squashing the arrays of arrays into a flat structure

@@ -11,2 +11,3 @@ module.exports = test;

var isCI = require('./is-ci');
var _ = require('lodash');

@@ -42,4 +43,7 @@ // important: this is different from ./config (which is the *user's* config)

var p = Promise.resolve();
var policyLocations = [root];
if (exists) {
p = snyk.modules(root, options).then(function (pkg) {
policyLocations = policyLocations.concat(pluckPolicies(pkg));
debug('policies found', policyLocations);
hasDevDependencies = pkg.hasDevDependencies;

@@ -89,4 +93,4 @@ payload.method = 'POST';

}).then(function (res) {
return snyk.policy.load(root, options).then(function (config) {
if (!config.ignore || res.ok) {
return snyk.policy.load(policyLocations, options).then(function (config) {
if (res.ok) {
return res;

@@ -98,3 +102,4 @@ }

config.ignore,
res.vulnerabilities
res.vulnerabilities,
root
);

@@ -104,5 +109,14 @@

config.patch,
res.vulnerabilities
res.vulnerabilities,
root
);
if (config.suggest) {
res.vulnerabilities = protect.attachNotes(
config.suggest,
res.vulnerabilities,
root
);
}
// if there's no vulns after the ignore process, let's reset the `ok`

@@ -134,1 +148,20 @@ // state and remove the vulns entirely.

}
function pluckPolicies(pkg) {
if (!pkg) {
return null;
}
if (pkg.snyk) {
return pkg.snyk;
}
if (!pkg.dependencies) {
return null;
}
return _.flatten(Object.keys(pkg.dependencies).map(function (name) {
return pluckPolicies(pkg.dependencies[name]);
}).filter(Boolean));
}
module.exports = tryRequire;
var fs = require('fs');
var fs = require('then-fs');
var path = require('path');
var debug = require('debug')('snyk');
var Promise = require('es6-promise').Promise; // jshint ignore:line
function tryRequire(path) {
try {
return JSON.parse(fs.readFileSync(path, 'utf8'));
} catch (e) {
return null;
}
function tryRequire(filename) {
return fs.readFile(filename, 'utf8')
.then(JSON.parse)
.catch(function (e) {
debug('tryRequire silently failing on %s', e.message);
return null;
})
.then(function (pkg) {
if (!pkg) {
return pkg;
}
// also try to find a .snyk policy file whilst we're at it
var dir = path.dirname(filename);
if (!pkg.snyk) {
pkg.snyk = fs.existsSync(path.resolve(dir, '.snyk'));
}
if (pkg.snyk) {
pkg.snyk = dir;
}
return pkg;
});
}

@@ -5,2 +5,3 @@ {

"main": "lib/index.js",
"version": "1.6.1-alpha1",
"directories": {

@@ -61,4 +62,3 @@ "test": "test"

"url": "https://github.com/Snyk/snyk.git"
},
"version": "1.6.0"
}
}
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc