diff --git a/src/cli/index.ts b/src/cli/index.ts index 75d382b802c..87f5b3de5ee 100755 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -250,7 +250,7 @@ async function main() { } if (args.options.file && args.options.yarnWorkspaces) { - throw new UnsupportedOptionCombinationError(['file', 'all-projects']); + throw new UnsupportedOptionCombinationError(['file', 'yarn-workspaces']); } if (args.options.file && args.options.allProjects) { @@ -282,6 +282,13 @@ async function main() { throw new UnsupportedOptionCombinationError(['docker', 'all-projects']); } + if (args.options.docker && args.options.yarnWorkspaces) { + throw new UnsupportedOptionCombinationError([ + 'docker', + 'yarn-workspaces', + ]); + } + if (args.options.allSubProjects && args.options.yarnWorkspaces) { throw new UnsupportedOptionCombinationError([ 'all-sub-projects', diff --git a/src/lib/snyk-test/run-test.ts b/src/lib/snyk-test/run-test.ts index cea2a91f1dc..40810158c9d 100644 --- a/src/lib/snyk-test/run-test.ts +++ b/src/lib/snyk-test/run-test.ts @@ -193,7 +193,9 @@ async function parseRes( // For Node.js: inject additional information (for remediation etc.) into the response. if (payload.modules) { - res.dependencyCount = payload.modules.numDependencies; + res.dependencyCount = + payload.modules.numDependencies || + depGraph.countPathsToRoot(depGraph.rootPkg); if (res.vulnerabilities) { res.vulnerabilities.forEach((vuln) => { if (payload.modules && payload.modules.pluck) { diff --git a/test/acceptance/cli-args.test.ts b/test/acceptance/cli-args.test.ts index 23781bdf71a..5619838a4c2 100644 --- a/test/acceptance/cli-args.test.ts +++ b/test/acceptance/cli-args.test.ts @@ -120,7 +120,6 @@ const argsNotAllowedWithYarnWorkspaces = [ 'project-name', 'docker', 'all-sub-projects', - 'all-projects', ]; argsNotAllowedWithYarnWorkspaces.forEach((arg) => { @@ -132,7 +131,7 @@ argsNotAllowedWithYarnWorkspaces.forEach((arg) => { } t.match( stdout.trim(), - `The following option combination is not currently supported: ${arg} + all-projects`, + `The following option combination is not currently supported: ${arg} + yarn-workspaces`, 'when using test', ); }); @@ -142,7 +141,7 @@ argsNotAllowedWithYarnWorkspaces.forEach((arg) => { } t.match( stdout.trim(), - `The following option combination is not currently supported: ${arg} + all-projects`, + `The following option combination is not currently supported: ${arg} + yarn-workspaces`, 'when using monitor', ); }); diff --git a/test/acceptance/cli-test/cli-test.yarn-workspaces.spec.ts b/test/acceptance/cli-test/cli-test.yarn-workspaces.spec.ts index 1e08b3faadb..4be46eca4c5 100644 --- a/test/acceptance/cli-test/cli-test.yarn-workspaces.spec.ts +++ b/test/acceptance/cli-test/cli-test.yarn-workspaces.spec.ts @@ -135,6 +135,14 @@ export const YarnWorkspacesTests: AcceptanceTests = { const loadPlugin = sinon.spy(params.plugins, 'loadPlugin'); // the parser is used directly t.ok(loadPlugin.withArgs('yarn').notCalled, 'skips load plugin'); + t.teardown(() => { + loadPlugin.restore(); + }); + t.match( + result.getDisplayResults(), + '✓ Tested 1 dependencies for known vulnerabilities, no vulnerable paths found.', + 'correctly showing dep number', + ); t.match(result.getDisplayResults(), 'Package manager: yarn\n'); t.match( result.getDisplayResults(), @@ -156,6 +164,7 @@ export const YarnWorkspacesTests: AcceptanceTests = { 'Tested 3 projects, no vulnerable paths were found.', 'no vulnerable paths found as both policies detected and applied.', ); + let policyCount = 0; params.server.popRequests(3).forEach((req) => { t.equal(req.method, 'POST', 'makes POST request'); @@ -166,19 +175,126 @@ export const YarnWorkspacesTests: AcceptanceTests = { ); t.match(req.url, '/api/v1/test-dep-graph', 'posts to correct url'); t.ok(req.body.depGraph, 'body contains depGraph'); - t.ok(req.body.policy, 'body contains policy'); + + if (req.body.targetFileRelativePath.endsWith('apples/package.json')) { + t.match( + req.body.policy, + 'npm:node-uuid:20160328', + 'policy is as expected', + ); + t.ok(req.body.policy, 'body contains policy'); + policyCount += 1; + } else if ( + req.body.targetFileRelativePath.endsWith('tomatoes/package.json') + ) { + t.notOk(req.body.policy, 'body does not contain policy'); + } else if ( + req.body.targetFileRelativePath.endsWith( + 'yarn-workspaces/package.json', + ) + ) { + t.match( + req.body.policy, + 'npm:node-uuid:20111130', + 'policy is as expected', + ); + t.ok(req.body.policy, 'body contains policy'); + policyCount += 1; + } + t.equal( + req.body.depGraph.pkgManager.name, + 'yarn', + 'depGraph has package manager', + ); + }); + t.equal(policyCount, 2, '2 policies found in a workspace'); + }, + 'test --yarn-workspaces --detection-depth=5 multiple workspaces found': ( + params, + utils, + ) => async (t) => { + utils.chdirWorkspaces(); + const result = await params.cli.test({ + yarnWorkspaces: true, + detectionDepth: 5, + strictOutOfSync: false, + }); + const loadPlugin = sinon.spy(params.plugins, 'loadPlugin'); + // the parser is used directly + t.ok(loadPlugin.withArgs('yarn').notCalled, 'skips load plugin'); + t.teardown(() => { + loadPlugin.restore(); + }); + t.match( + result.getDisplayResults(), + '✓ Tested 1 dependencies for known vulnerabilities, no vulnerable paths found.', + 'correctly showing dep number', + ); + t.match(result.getDisplayResults(), 'Package manager: yarn\n'); + t.match( + result.getDisplayResults(), + 'Project name: package.json', + 'yarn project in output', + ); + t.match( + result.getDisplayResults(), + 'Project name: tomatoes', + 'yarn project in output', + ); + t.match( + result.getDisplayResults(), + 'Project name: apples', + 'yarn project in output', + ); + t.match( + result.getDisplayResults(), + 'Tested 6 projects, no vulnerable paths were found.', + 'Tested 6 projects', + ); + let policyCount = 0; + + params.server.popRequests(3).forEach((req) => { + t.equal(req.method, 'POST', 'makes POST request'); t.equal( - req.body.policyLocations, - ['yarn-workspaces', 'yarn-workspaces/apples'], - 'policy locations', + req.headers['x-snyk-cli-version'], + params.versionNumber, + 'sends version number', ); + t.match(req.url, '/api/v1/test-dep-graph', 'posts to correct url'); + t.ok(req.body.depGraph, 'body contains depGraph'); + if (req.body.targetFileRelativePath.endsWith('apples/package.json')) { + t.match( + req.body.policy, + 'npm:node-uuid:20160328', + 'policy is as expected', + ); + t.ok(req.body.policy, 'body contains policy'); + policyCount += 1; + } else if ( + req.body.targetFileRelativePath.endsWith('tomatoes/package.json') + ) { + t.notOk(req.body.policy, 'body does not contain policy'); + } else if ( + req.body.targetFileRelativePath.endsWith( + 'yarn-workspaces/package.json', + ) + ) { + t.match( + req.body.policy, + 'npm:node-uuid:20111130', + 'policy is as expected', + ); + t.ok(req.body.policy, 'body contains policy'); + policyCount += 1; + } t.equal( req.body.depGraph.pkgManager.name, 'yarn', 'depGraph has package manager', ); }); + t.equal(policyCount, 2, '2 policies found in a workspace'); }, }, };