diff --git a/cli/commands/protect/wizard.js b/cli/commands/protect/wizard.js index 0001656d83..f3a3700ef6 100644 --- a/cli/commands/protect/wizard.js +++ b/cli/commands/protect/wizard.js @@ -35,6 +35,10 @@ function wizard(options) { options = {}; } + if (config.org) { + options.org = config.org; + } + try { if (detectPackageManager(cwd, options) === 'rubygems') { throw new Error( diff --git a/cli/commands/test.js b/cli/commands/test.js index ad1ddb2434..21b39a890e 100644 --- a/cli/commands/test.js +++ b/cli/commands/test.js @@ -21,6 +21,10 @@ function test(path, options) { args.push({}); } + if (config.org) { + options.org = config.org; + } + return apiTokenExists('snyk test') .then(function () { // if we have more than path, options, we're going to assume that we've diff --git a/help/help.txt b/help/help.txt index 6b71c63bea..118a3f2664 100644 --- a/help/help.txt +++ b/help/help.txt @@ -25,8 +25,8 @@ Options: --dev .............. Include devDependencies (defaults to production only). --file= ...... Sets package file. For more help run `snyk help file`. - --org= ... Associate a snapshot (or wizard snapshot) with a specific - organisation. For more help run `snyk help orgs`. + --org= ... Run snyk with a specific organisation. For more help + run `snyk help orgs`. --ignore-policy .... Ignores and resets the state of your policy file. --trust-policies ... Applies and uses ignore rules from your dependencies's Snyk policies, otherwise ignore policies are only diff --git a/help/orgs.txt b/help/orgs.txt index c4940d8f31..f541c13cd3 100644 --- a/help/orgs.txt +++ b/help/orgs.txt @@ -1,8 +1,8 @@ Organisations: -To monitor your project for a specific org, you can add an optional flag to -both the `monitor` and `wizard` commands: +To test or monitor your project for a specific org, you can add an optional +flag to the `test`, `monitor` and `wizard` commands: $ snyk monitor --org= Captured a snapshot of this project's dependencies. diff --git a/lib/snyk-test/index.js b/lib/snyk-test/index.js index 97e73445c4..62ee4c1a99 100644 --- a/lib/snyk-test/index.js +++ b/lib/snyk-test/index.js @@ -2,6 +2,8 @@ module.exports = test; var Promise = require('es6-promise').Promise; // jshint ignore:line var detect = require('../detect'); +var runTest = require('./run-test'); + function test(root, options, callback) { if (typeof options === 'function') { @@ -24,8 +26,8 @@ function test(root, options, callback) { function executeTest(root, options) { try { var packageManager = detect.detectPackageManager(root, options); - var runner = loadTestRunner(packageManager); - return runner(root, options) + options.packageManager = packageManager; + return run(root, options) .then(function (res) { res.packageManager = packageManager; return res; @@ -35,19 +37,13 @@ function executeTest(root, options) { } } -function loadTestRunner(packageManager) { - switch (packageManager) { - case 'npm': { - return require('./npm'); - } - case 'rubygems': { - return require('./rubygems'); - } - case 'maven': { - return require('./maven'); - } - default: { - throw new Error('Unsupported package manager: ' + packageManager); - } +function run(root, options) { + var packageManager = options.packageManager; + if (packageManager === 'npm') { // legacy special case (not really different) + return require('./npm')(root, options); + } + if (!new Set(['rubygems', 'maven']).has(packageManager)) { + throw new Error('Unsupported package manager: ' + packageManager); } + return runTest(packageManager, root, options); } diff --git a/lib/snyk-test/npm/index.js b/lib/snyk-test/npm/index.js index 49cbe7a462..4ff6c46cab 100644 --- a/lib/snyk-test/npm/index.js +++ b/lib/snyk-test/npm/index.js @@ -62,6 +62,9 @@ function test(root, options) { hasDevDependencies = pkg.hasDevDependencies; payload.method = 'POST'; payload.body = pkg; + if (options.org) { + payload.qs = {org: options.org}; + } if (options.policy) { payload.body.policy = options.policy.toString(); } @@ -77,6 +80,9 @@ function test(root, options) { payload.method = 'GET'; payload.url += '/' + encodeURIComponent(module.name + '@' + module.version); + if (options.org) { + payload.qs = {org: options.org}; + } return { package: module, payload: payload, diff --git a/lib/snyk-test/rubygems/index.js b/lib/snyk-test/rubygems/index.js deleted file mode 100644 index c43431c923..0000000000 --- a/lib/snyk-test/rubygems/index.js +++ /dev/null @@ -1,137 +0,0 @@ -module.exports = test; - -var debug = require('debug')('snyk'); -var Promise = require('es6-promise').Promise; // jshint ignore:line -var request = require('../../request'); -var fs = require('then-fs'); -var snyk = require('../..'); -var spinner = require('../../spinner'); -var moduleToOjbect = require('snyk-module'); -var isCI = require('../../is-ci'); -var analytics = require('../../analytics'); -var config = require('../../config'); -var getModuleInfo = require('../../module-info'); - -function test(root, options) { - var promise = Promise.resolve().then(function () { - var policyLocations = [root]; - var hasDevDependencies = false; - var lbl = 'Querying vulnerabilities database...'; - return assemblePayload(root, options, policyLocations) - .then(spinner(lbl)) - .then(function (payload) { - return new Promise(function (resolve, reject) { - request(payload, function (error, res, body) { - if (error) { - return reject(error); - } - - if (res.statusCode !== 200) { - var err = new Error(body && body.error ? - body.error : - res.statusCode); - - // this is the case where a local module has been tested, but - // doesn't have any production deps, but we've noted that they - // have dep deps, so we'll error with a more useful message - if (res.statusCode === 404 && hasDevDependencies) { - err.code = 'NOT_FOUND_HAS_DEV_DEPS'; - } else { - err.code = res.statusCode; - } - - if (res.statusCode === 500) { - debug('Server error', body.stack); - } - - return reject(err); - } - - resolve(body); - }); - }); - }).then(function (res) { - analytics.add('vulns-pre-policy', res.vulnerabilities.length); - options.loose = true; // allows merge without root policy - return snyk.policy.load(policyLocations, options) - .then(function (policy) { - return policy.filter(res, root); - }, function (error) { // note: inline catch, to handle error from .load - // the .snyk file wasn't found, which is fine, so we'll return the vulns - if (error.code === 'ENOENT') { - return res; - } - throw error; - }).then(function (res) { - analytics.add('vulns', res.vulnerabilities.length); - - // add the unique count of vulnerabilities found - res.uniqueCount = 0; - var seen = {}; - res.uniqueCount = res.vulnerabilities.reduce(function (acc, curr) { - if (!seen[curr.id]) { - seen[curr.id] = true; - acc++; - } - return acc; - }, 0); - - return res; - }); - }).then(spinner.clear(lbl)); - }); - - return promise; -} - -function assemblePayload(root, options, policyLocations) { - var local = fs.existsSync(root); - analytics.add('local', local); - analytics.add('packageManager', 'rubygems'); - return local ? assembleLocalPayload(root, options, policyLocations) - : assembleRemotePayload(root); -} - -function assembleLocalPayload(root, options, policyLocations) { - return getModuleInfo('rubygems', root, options) - .then(function (module) { - analytics.add('policies', policyLocations.length); - analytics.add('packageName', module.name); - analytics.add('packageVersion', module.version); - analytics.add('package', module.name + '@' + module.version); - var payload = { - method: 'POST', - url: vulnUrl(), - json: true, - headers: { - 'x-is-ci': isCI, - authorization: 'token ' + snyk.api, - }, - body: module, - }; - return payload; - }); -} - -function assembleRemotePayload(root) { - var module = moduleToOjbect(root); - debug('testing remote: %s', module.name + '@' + module.version); - var encodedName = encodeURIComponent(module.name + '@' + module.version); - analytics.add('packageName', module.name); - analytics.add('packageVersion', module.version); - analytics.add('package', module.name + '@' + module.version); - var payload = { - method: 'GET', - url: vulnUrl() + '/' + encodedName, - json: true, - headers: { - 'x-is-ci': isCI, - authorization: 'token ' + snyk.api, - }, - }; - return Promise.resolve(payload); -} - -function vulnUrl() { - return config.API + '/vuln/rubygems'; -} diff --git a/lib/snyk-test/maven/index.js b/lib/snyk-test/run-test.js similarity index 79% rename from lib/snyk-test/maven/index.js rename to lib/snyk-test/run-test.js index 8583562b5c..b1fb73e3da 100644 --- a/lib/snyk-test/maven/index.js +++ b/lib/snyk-test/run-test.js @@ -1,19 +1,21 @@ -module.exports = test; +module.exports = runTest; var debug = require('debug')('snyk'); -var Promise = require('es6-promise').Promise; // jshint ignore:line -var request = require('../../request'); var fs = require('then-fs'); -var snyk = require('../..'); -var spinner = require('../../spinner'); var moduleToOjbect = require('snyk-module'); -var isCI = require('../../is-ci'); -var analytics = require('../../analytics'); -var config = require('../../config'); -var getModuleInfo = require('../../module-info'); +var Promise = require('es6-promise').Promise; // jshint ignore:line + +var analytics = require('../analytics'); +var config = require('../config'); +var getModuleInfo = require('../module-info'); +var isCI = require('../is-ci'); +var request = require('../request'); +var snyk = require('../'); +var spinner = require('../spinner'); -function test(root, options) { - var promise = Promise.resolve().then(function () { + +function runTest(packageManager, root, options) { + return Promise.resolve().then(function () { var policyLocations = [root]; var hasDevDependencies = false; var lbl = 'Querying vulnerabilities database...'; @@ -80,20 +82,18 @@ function test(root, options) { }); }).then(spinner.clear(lbl)); }); - - return promise; } function assemblePayload(root, options, policyLocations) { var local = fs.existsSync(root); analytics.add('local', local); - analytics.add('packageManager', 'maven'); + analytics.add('packageManager', options.packageManager); return local ? assembleLocalPayload(root, options, policyLocations) - : assembleRemotePayload(root); + : assembleRemotePayload(root, options); } function assembleLocalPayload(root, options, policyLocations) { - return getModuleInfo('maven', root, options) + return getModuleInfo(options.packageManager, root, options) .then(function (module) { analytics.add('policies', policyLocations.length); analytics.add('packageName', module.name); @@ -101,7 +101,7 @@ function assembleLocalPayload(root, options, policyLocations) { analytics.add('package', module.name + '@' + module.version); var payload = { method: 'POST', - url: vulnUrl(), + url: vulnUrl(options.packageManager), json: true, headers: { 'x-is-ci': isCI, @@ -109,11 +109,14 @@ function assembleLocalPayload(root, options, policyLocations) { }, body: module, }; + if (options.org) { + payload.qs = {org: options.org}; + } return payload; }); } -function assembleRemotePayload(root) { +function assembleRemotePayload(root, options) { var module = moduleToOjbect(root); debug('testing remote: %s', module.name + '@' + module.version); var encodedName = encodeURIComponent(module.name + '@' + module.version); @@ -122,16 +125,19 @@ function assembleRemotePayload(root) { analytics.add('package', module.name + '@' + module.version); var payload = { method: 'GET', - url: vulnUrl() + '/' + encodedName, + url: vulnUrl(options.packageManager) + '/' + encodedName, json: true, headers: { 'x-is-ci': isCI, authorization: 'token ' + snyk.api, }, }; + if (options.org) { + payload.qs = {org: options.org}; + } return Promise.resolve(payload); } -function vulnUrl() { - return config.API + '/vuln/maven'; +function vulnUrl(packageManager) { + return config.API + '/vuln/' + packageManager; } diff --git a/test/acceptance/cli.acceptance.test.js b/test/acceptance/cli.acceptance.test.js index 004a80c5c4..c89c7371de 100644 --- a/test/acceptance/cli.acceptance.test.js +++ b/test/acceptance/cli.acceptance.test.js @@ -56,24 +56,26 @@ before('prime config', function (t) { */ test('`test semver` sends remote NPM request:', function(t) { - t.plan(2); + t.plan(3); // We care about the request here, not the response - return cli.test('semver', {registry: 'npm'}) + return cli.test('semver', {registry: 'npm', org: 'EFF'}) .then(function() { var req = server.popRequest(); t.equal(req.method, 'GET', 'makes GET request'); t.match(req.url, '/vuln/npm/semver', 'gets from correct url'); + t.equal(req.query.org, 'EFF', 'org sent as a query in request'); }); }); test('`test sinatra --registry=rubygems` sends remote Rubygems request:', function(t) { - t.plan(2); - return cli.test('sinatra', {registry: 'rubygems'}) + t.plan(3); + return cli.test('sinatra', {registry: 'rubygems', org: 'ACME'}) .then(function() { var req = server.popRequest(); t.equal(req.method, 'GET', 'makes GET request'); t.match(req.url, '/vuln/rubygems/sinatra', 'gets from correct url'); + t.equal(req.query.org, 'ACME', 'org sent as a query in request'); }); }); @@ -181,10 +183,10 @@ test('`test monorepo --file=sub-ruby-app/Gemfile`', function(t) { test('`test maven-app --file=pom.xml` sends package info', function(t) { - t.plan(5); + t.plan(6); chdirWorkspaces(); var stub = stubExec('maven-app/mvn-dep-tree-stdout.txt'); - return cli.test('maven-app', {file: 'pom.xml'}) + return cli.test('maven-app', {file: 'pom.xml', org: 'nobelprize.org'}) .then(function() { var req = server.popRequest(); var pkg = req.body; @@ -193,7 +195,9 @@ function(t) { t.equal(pkg.artifactId, 'maven-app', 'specifies artifactId'); t.ok(pkg.dependencies['junit:junit'], 'specifies dependency'); t.equal(pkg.dependencies['junit:junit'].artifactId, 'junit', - 'specifies dependency artifactId'); + 'specifies dependency artifactId'); + t.equal(req.query.org, 'nobelprize.org', 'org sent as a query in request'); + }) .catch(function(err) { t.error(err);