Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: scan gradle projects behind --all-projects #1310

Merged
merged 1 commit into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# test fixture build artifacts
/test/acceptance/workspaces/**/project/
/test/acceptance/workspaces/**/target/
test/acceptance/workspaces/**/.gradle
.vscode
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export function formatMonitorOutput(
): string {
const manageUrl = buildManageUrl(res.id, res.org);
const advertiseGradleSubProjectsCount =
packageManager === 'gradle' && !options['gradle-sub-project'];
packageManager === 'gradle' &&
!options['gradle-sub-project'] &&
!options.allProjects;
const advertiseAllProjectsCount =
packageManager !== 'gradle' && !options.allProjects && foundProjectCount;
const issues = res.licensesPolicy ? 'issues' : 'vulnerabilities';
Expand Down
15 changes: 10 additions & 5 deletions src/cli/commands/monitor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,18 +213,15 @@ async function monitor(...args0: MethodArgs): Promise<any> {

analytics.add('packageManager', extractedPackageManager);

let projectName;

const projectName = getProjectName(projectDeps);
if (projectDeps.depGraph) {
debug(`Processing ${projectDeps.depGraph.rootPkg.name}...`);
debug(`Processing ${projectDeps.depGraph.rootPkg?.name}...`);
maybePrintDepGraph(options, projectDeps.depGraph);
projectName = projectDeps.depGraph.rootPkg.name;
}

if (projectDeps.depTree) {
debug(`Processing ${projectDeps.depTree.name}...`);
maybePrintDepTree(options, projectDeps.depTree);
projectName = projectDeps.depTree.name;
}

const tFile = projectDeps.targetFile || targetFile;
Expand Down Expand Up @@ -319,3 +316,11 @@ function validateMonitorPath(path: string, isDocker?: boolean): void {
throw new Error('"' + path + '" is not a valid path for "snyk monitor"');
}
}

function getProjectName(projectDeps): string {
return (
projectDeps.meta?.gradleProjectName ||
projectDeps.depGraph?.rootPkg?.name ||
projectDeps.depTree?.name
);
}
1 change: 1 addition & 0 deletions src/cli/commands/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ function displayResult(
const advertiseGradleSubProjectsCount =
projectType === 'gradle' &&
!options['gradle-sub-project'] &&
!options.allProjects &&
foundProjectCount;
if (advertiseGradleSubProjectsCount) {
multiProjAdvice = chalk.bold.white(
Expand Down
1 change: 1 addition & 0 deletions src/lib/detect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const AUTO_DETECTABLE_FILES: string[] = [
'Pipfile',
'requirements.txt',
'build.sbt',
'build.gradle',
];

// when file is specified with --file, we look it up here
Expand Down
43 changes: 37 additions & 6 deletions src/lib/find-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ async function findInDirectory(
return Array.prototype.concat.apply([], found);
}

function filterForDefaultManifests(files: string[]) {
function filterForDefaultManifests(files: string[]): string[] {
// take all the files in the same dir & filter out
// based on package Manager
if (files.length <= 1) {
Expand All @@ -129,23 +129,28 @@ function filterForDefaultManifests(files: string[]) {
...pathLib.parse(p),
packageManager: detectProjectTypeFromFile(p),
}))
.sortBy('dir')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

important so we find the folders higher up first

.groupBy('dir')
.value();

for (const directory of Object.keys(foundFiles)) {
const filesInDirectory = foundFiles[directory];
if (filesInDirectory.length <= 1) {
filteredFiles.push(filesInDirectory[0].path);
continue;
}

const groupedFiles = _(filesInDirectory)
.groupBy('packageManager')
.value();

for (const packageManager of Object.keys(groupedFiles)) {
const filesPerPackageManager = groupedFiles[packageManager];

if (filesPerPackageManager.length <= 1) {
const shouldSkip = shouldSkipAddingFile(
packageManager,
filesPerPackageManager[0].path,
filteredFiles,
);
if (shouldSkip) {
continue;
}
filteredFiles.push(filesPerPackageManager[0].path);
continue;
}
Expand All @@ -154,6 +159,14 @@ function filterForDefaultManifests(files: string[]) {
packageManager,
);
if (defaultManifestFileName) {
const shouldSkip = shouldSkipAddingFile(
packageManager,
filesPerPackageManager[0].path,
filteredFiles,
);
if (shouldSkip) {
continue;
}
filteredFiles.push(defaultManifestFileName);
}
}
Expand All @@ -173,6 +186,24 @@ function detectProjectTypeFromFile(file: string): string | null {
}
}

function shouldSkipAddingFile(
packageManager: string,
filePath: string,
filteredFiles: string[],
): boolean {
if (['gradle'].includes(packageManager) && filePath) {
const rootGradleFile = filteredFiles
.filter((targetFile) => targetFile.endsWith('build.gradle'))
.filter((targetFile) => {
const parsedPath = pathLib.parse(targetFile);
const relativePath = pathLib.relative(parsedPath.dir, filePath);
return !relativePath.startsWith(`..${pathLib.sep}`);
});
return !!rootGradleFile.length;
}
return false;
}

function chooseBestManifest(
files: Array<{ base: string; path: string }>,
projectType: string,
Expand Down
2 changes: 2 additions & 0 deletions src/lib/plugins/get-deps-from-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export async function getDepsFromPlugin(
if (targetFiles.length === 0) {
throw NoSupportedManifestsFoundError([root]);
}
// enable full sub-project scan for gradle
options.allSubProjects = true;
inspectRes = await multiProjectProcessors[scanType].handler(
root,
options,
Expand Down
102 changes: 102 additions & 0 deletions test/acceptance/cli-monitor/cli-monitor.all-projects.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as sinon from 'sinon';
import * as _ from '@snyk/lodash';
import * as path from 'path';
import * as depGraphLib from '@snyk/dep-graph';

interface AcceptanceTests {
language: string;
Expand Down Expand Up @@ -740,5 +741,106 @@ export const AllProjectsTests: AcceptanceTests = {
);
});
},
'`monitor gradle-monorepo with --all-projects`': (params, utils) => async (
t,
) => {
utils.chdirWorkspaces();
const simpleGradleGraph = depGraphLib.createFromJSON({
schemaVersion: '1.2.0',
pkgManager: {
name: 'gradle',
},
pkgs: [
{
id: 'gradle-monorepo@0.0.0',
info: {
name: 'gradle-monorepo',
version: '0.0.0',
},
},
],
graph: {
rootNodeId: 'root-node',
nodes: [
{
nodeId: 'root-node',
pkgId: 'gradle-monorepo@0.0.0',
deps: [],
},
],
},
});
const plugin = {
async inspect() {
return {
plugin: {
name: 'bundled:gradle',
runtime: 'unknown',
meta: {},
},
scannedProjects: [
{
meta: {
gradleProjectName: 'root-proj',
versionBuildInfo: {
gradleVersion: '6.5',
},
},
depGraph: simpleGradleGraph,
},
{
meta: {
gradleProjectName: 'root-proj/subproj',
versionBuildInfo: {
gradleVersion: '6.5',
},
},
depGraph: simpleGradleGraph,
},
],
};
},
};
const spyPlugin = sinon.spy(plugin, 'inspect');
const loadPlugin = sinon.stub(params.plugins, 'loadPlugin');
t.teardown(loadPlugin.restore);
loadPlugin.withArgs('gradle').returns(plugin);
loadPlugin.callThrough();
const result = await params.cli.monitor('gradle-monorepo', {
allProjects: true,
detectionDepth: 3,
d: true,
});
t.match(
result,
'gradle/graph/some/project-id',
'gradle project was monitored',
);
t.match(
result,
'npm/graph/some/project-id',
'gradle project was monitored',
);

// Pop one extra call to server and filter out call to `featureFlag` endpoint
const requests = params.server
.popRequests(4)
.filter((req) => req.url.includes('/monitor/'));
t.equal(requests.length, 3, 'correct amount of monitor requests');
requests.forEach((req) => {
t.match(
req.url,
/\/api\/v1\/monitor\/(npm\/graph|gradle\/graph)/,
'puts at correct url',
);
t.notOk(req.body.targetFile, "doesn't send the targetFile");
t.equal(req.method, 'PUT', 'makes PUT request');
t.equal(
req.headers['x-snyk-cli-version'],
params.versionNumber,
'sends version number',
);
});
},
},
};
2 changes: 1 addition & 1 deletion test/acceptance/cli-test/cli-test.acceptance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ if (!isWindows) {
}
});

// @later: try and remove this config stuff
// TODO: try and remove this config stuff
// Was copied straight from ../src/cli-server.js
after('teardown', async (t) => {
t.plan(4);
Expand Down
43 changes: 15 additions & 28 deletions test/acceptance/cli-test/cli-test.all-projects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,34 +547,6 @@ export const AllProjectsTests: AcceptanceTests = {
);
},

'`test large-mono-repo with --all-projects and --detection-depth=2`': (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the test below covers this one too, seems unnessesary

params,
utils,
) => async (t) => {
utils.chdirWorkspaces();
const spyPlugin = sinon.spy(params.plugins, 'loadPlugin');
t.teardown(spyPlugin.restore);
await params.cli.test('large-mono-repo', {
allProjects: true,
detectionDepth: 2,
});
t.equals(
spyPlugin.withArgs('rubygems').callCount,
1,
'calls rubygems plugin once',
);
t.equals(
spyPlugin.withArgs('npm').callCount,
19,
'calls npm plugin 19 times',
);
t.equals(
spyPlugin.withArgs('maven').callCount,
1,
'calls maven plugin once',
);
},

'`test large-mono-repo with --all-projects and --detection-depth=7`': (
params,
utils,
Expand All @@ -596,6 +568,16 @@ export const AllProjectsTests: AcceptanceTests = {
19,
'calls npm plugin 19 times',
);
t.equals(
spyPlugin.withArgs('gradle').callCount,
2,
'calls gradle plugin 2 times',
);
t.equals(
spyPlugin.withArgs('gradle').args[0][1].allSubProjects,
true,
'calls gradle plugin with allSubProjects property',
);
t.equals(
spyPlugin.withArgs('maven').callCount,
6,
Expand Down Expand Up @@ -742,6 +724,11 @@ export const AllProjectsTests: AcceptanceTests = {
19,
'calls npm plugin 19 times',
);
t.equals(
spyPlugin.withArgs('gradle').callCount,
2,
'calls gradle plugin 2 times',
);
t.equals(
spyPlugin.withArgs('maven').callCount,
1,
Expand Down
Empty file.
18 changes: 18 additions & 0 deletions test/acceptance/workspaces/gradle-monorepo/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions test/acceptance/workspaces/gradle-monorepo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "shallow-goof",
"version": "0.0.1",
"description": "A vulnerable demo application",
"homepage": "https://snyk.io/",
"repository": {
"type": "git",
"url": "https://github.com/Snyk/shallow-goof"
},
"dependencies": {
"qs": "0.0.6",
"node-uuid": "1.4.0"
}
}
5 changes: 5 additions & 0 deletions test/acceptance/workspaces/gradle-monorepo/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
rootProject.name = 'root-proj'

include 'subproj'


9 changes: 9 additions & 0 deletions test/acceptance/workspaces/gradle-monorepo/subproj/.snyk
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.14.1
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore:
SNYK-JAVA-ORGBOUNCYCASTLE-32364:
- '*':
reason: None Given
expires: 2020-07-19T11:33:19.028Z
patch: {}
Loading