From ba9ad07d4426a2b8483d515812edccf307cfa968 Mon Sep 17 00:00:00 2001 From: Shaun Smith Date: Wed, 20 Jun 2018 15:25:14 +0100 Subject: [PATCH] feat: monitor support for docker (beta) --- cli/commands/monitor.js | 43 ++++++--- lib/monitor.js | 12 ++- test/acceptance/cli.acceptance.test.js | 127 +++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 16 deletions(-) diff --git a/cli/commands/monitor.js b/cli/commands/monitor.js index 80d867cfc2..9fb84edb2f 100644 --- a/cli/commands/monitor.js +++ b/cli/commands/monitor.js @@ -38,28 +38,32 @@ function monitor() { return args.reduce(function (acc, path) { return acc.then(function () { return fs.exists(path).then(function (exists) { - if (!exists) { + if (!exists && !options.docker) { throw new Error( 'snyk monitor should be pointed at an existing project'); } + var packageManager = detect.detectPackageManager(path, options); - var targetFile = options.file || detect.detectPackageFile(path); - var meta = { - method: 'cli', - packageManager: packageManager, - 'policy-path': options['policy-path'], - 'project-name': options['project-name'] || config['PROJECT_NAME'], - }; - var plugin = plugins.loadPlugin(packageManager); + + var targetFile = options.docker + ? undefined + : (options.file || detect.detectPackageFile(path)); + + + var plugin = plugins.loadPlugin(packageManager, options); + var moduleInfo = ModuleInfo(plugin, options.policy); - var relativeTargetPath = - pathUtil.relative('.', pathUtil.join(path, targetFile)); + var displayPath = pathUtil.relative( + '.', pathUtil.join(path, targetFile || '')); + + var analysisType = options.docker ? 'docker' : packageManager; + var analyzingDepsSpinnerLabel = - 'Analyzing ' + packageManager + ' dependencies for ' + - relativeTargetPath; + 'Analyzing ' + analysisType + ' dependencies for ' + displayPath; + var postingMonitorSpinnerLabel = - 'Posting monitor snapshot for ' + relativeTargetPath + ' ...'; + 'Posting monitor snapshot for ' + displayPath + ' ...'; return spinner(analyzingDepsSpinnerLabel) .then(function () { @@ -71,6 +75,17 @@ function monitor() { .then(function () { return info }); }) .then(function (info) { + if (_.get(info, 'plugin.packageManager')) { + packageManager = info.plugin.packageManager; + } + var meta = { + method: 'cli', + packageManager: packageManager, + 'policy-path': options['policy-path'], + 'project-name': + options['project-name'] || config['PROJECT_NAME'], + isDocker: !!options.docker, + }; return snyk.monitor(path, meta, info); }) .then(spinner.clear(postingMonitorSpinnerLabel)) diff --git a/lib/monitor.js b/lib/monitor.js index 60bfb5a91b..6202645663 100644 --- a/lib/monitor.js +++ b/lib/monitor.js @@ -12,12 +12,19 @@ var analytics = require('./analytics'); function monitor(root, meta, info) { var pkg = info.package; var pluginMeta = info.plugin; - var policyPath = meta['policy-path'] || root; - var policyLocations = [policyPath].concat(pluckPolicies(pkg)); + var policyPath = meta['policy-path']; + if (!meta.isDocker) { + policyPath = policyPath || root; + } + var policyLocations = [policyPath].concat(pluckPolicies(pkg)) + .filter(Boolean); var opts = { loose: true }; var packageManager = meta.packageManager || 'npm'; return apiTokenExists('snyk monitor') .then(function () { + if (policyLocations.length == 0) { + return snyk.policy.create(); + } return snyk.policy.load(policyLocations, opts); }).then(function (policy) { analytics.add('packageManager', packageManager); @@ -39,6 +46,7 @@ function monitor(root, meta, info) { org: config.org ? decodeURIComponent(config.org) : undefined, pluginName: pluginMeta.name, pluginRuntime: pluginMeta.runtime, + dockerImageId: pluginMeta.dockerImageId, projectName: meta['project-name'], }, policy: policy.toString(), diff --git a/test/acceptance/cli.acceptance.test.js b/test/acceptance/cli.acceptance.test.js index 84f7c2610c..20bf6931be 100644 --- a/test/acceptance/cli.acceptance.test.js +++ b/test/acceptance/cli.acceptance.test.js @@ -1446,6 +1446,133 @@ test('`monitor composer-app ruby-app` works on multiple params', function (t) { }); }); +test('`monitor foo:latest --docker`', +function (t) { + var dockerImageId = 'sha256:' + + '578c3e61a98cb5720e7c8fc152017be1dff373ebd72a32bbe6e328234efc8d1a'; + var plugin = { + inspect: function () { + return Promise.resolve({ + plugin: { + packageManager: 'rpm', + dockerImageId: dockerImageId, + }, + package: {}, + }); + }, + }; + sinon.spy(plugin, 'inspect'); + + sinon.stub(plugins, 'loadPlugin') + .withArgs(sinon.match.any, sinon.match({docker: true})) + .returns(plugin); + t.teardown(plugins.loadPlugin.restore); + + return cli.monitor('foo:latest', { + docker: true, + org: 'explicit-org', + }) + .then(function () { + var req = server.popRequest(); + t.equal(req.method, 'PUT', 'makes PUT request'); + t.match(req.url, '/monitor/rpm', + 'puts at correct url (uses package manager from plugin response)'); + t.equal(req.body.meta.dockerImageId, dockerImageId, 'sends dockerImageId'); + t.same(plugin.inspect.getCall(0).args, + ['foo:latest', null, { + args: null, + docker: true, + org: 'explicit-org', + }], 'calls docker plugin with expected arguments'); + }); +}); + +test('`monitor foo:latest --docker` doesnt send policy from cwd', +function (t) { + chdirWorkspaces('npm-package-policy'); + var plugin = { + inspect: function () { + return Promise.resolve({ + plugin: { + packageManager: 'rpm', + }, + package: {}, + }); + }, + }; + sinon.spy(plugin, 'inspect'); + + sinon.stub(plugins, 'loadPlugin') + .withArgs(sinon.match.any, sinon.match({docker: true})) + .returns(plugin); + t.teardown(plugins.loadPlugin.restore); + + return cli.monitor('foo:latest', { + docker: true, + org: 'explicit-org', + }) + .then(function () { + var req = server.popRequest(); + t.equal(req.method, 'PUT', 'makes PUT request'); + t.match(req.url, '/monitor/rpm', + 'puts at correct url (uses package manager from plugin response)'); + t.same(plugin.inspect.getCall(0).args, + ['foo:latest', null, { + args: null, + docker: true, + org: 'explicit-org', + }], 'calls docker plugin with expected arguments'); + + return snykPolicy.create().then(function (emptyPolicy) { + t.same(req.body.policy, emptyPolicy.toString(), 'empty policy is sent'); + }); + }); +}); + +test('`monitor foo:latest --docker` with custom policy path', +function (t) { + chdirWorkspaces('npm-package-policy'); + var plugin = { + inspect: function () { + return Promise.resolve({ + plugin: { + packageManager: 'rpm', + }, + package: {}, + }); + }, + }; + sinon.spy(plugin, 'inspect'); + + sinon.stub(plugins, 'loadPlugin') + .withArgs(sinon.match.any, sinon.match({docker: true})) + .returns(plugin); + t.teardown(plugins.loadPlugin.restore); + + return cli.monitor('foo:latest', { + docker: true, + org: 'explicit-org', + 'policy-path': 'custom-location', + }) + .then(function () { + var req = server.popRequest(); + t.equal(req.method, 'PUT', 'makes PUT request'); + t.match(req.url, '/monitor/rpm', + 'puts at correct url (uses package manager from plugin response)'); + t.same(plugin.inspect.getCall(0).args, + ['foo:latest', null, { + args: null, + docker: true, + org: 'explicit-org', + 'policy-path': 'custom-location', + }], 'calls docker plugin with expected arguments'); + var expected = fs.readFileSync( + path.join('custom-location', '.snyk'), + 'utf8'); + var policyString = req.body.policy; + t.equal(policyString, expected, 'sends correct policy'); + }); +}); test('`wizard` for unsupported package managers', function (t) { chdirWorkspaces();