Skip to content

Commit

Permalink
fix: respect default detection depth of 4
Browse files Browse the repository at this point in the history
thisislawatts authored and j-luong committed Sep 30, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 2d2c348 commit 45a74ea
Showing 29 changed files with 1,037 additions and 22 deletions.
3 changes: 3 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -10,3 +10,6 @@ export const CALL_PATH_LEADING_ELEMENTS = 2;

// Number of function names to show in the end of an abbreviated call path
export const CALL_PATH_TRAILING_ELEMENTS = 2;

// Number of subdirectories to search when running monitor or test
export const MAX_DETECTION_DEPTH = 4;
47 changes: 26 additions & 21 deletions src/lib/find-files.ts
Original file line number Diff line number Diff line change
@@ -3,13 +3,14 @@ import * as pathLib from 'path';

import * as sortBy from 'lodash.sortby';
import * as groupBy from 'lodash.groupby';
import * as assign from 'lodash.assign';
import { detectPackageManagerFromFile } from './detect';
import * as debugModule from 'debug';
import {
PNPM_FEATURE_FLAG,
SUPPORTED_MANIFEST_FILES,
} from './package-managers';
import * as merge from 'lodash.merge';
import { MAX_DETECTION_DEPTH } from './constants';

const debug = debugModule('snyk:find-files');

@@ -55,28 +56,12 @@ interface FindFilesRes {
const ignoreFolders = ['node_modules', '.build'];

interface FindFilesConfig {
path: string;
ignore?: string[];
filter?: string[];
levelsDeep?: number;
featureFlags?: Set<string>;
}

type DefaultFindConfig = {
path: string;
ignore: string[];
filter: string[];
levelsDeep: number;
featureFlags: Set<string>;
};

const defaultFindConfig: DefaultFindConfig = {
path: '',
ignore: [],
filter: [],
levelsDeep: 4,
featureFlags: new Set<string>(),
};
}

/**
* Find all files in given search path. Returns paths to files found.
@@ -86,8 +71,10 @@ const defaultFindConfig: DefaultFindConfig = {
* @param filter (optional) file names to find. If not provided all files are returned.
* @param levelsDeep (optional) how many levels deep to search, defaults to two, this path and one sub directory.
*/
export async function find(findConfig: FindFilesConfig): Promise<FindFilesRes> {
const config: DefaultFindConfig = assign({}, defaultFindConfig, findConfig);
export async function find(
findConfig: Partial<FindFilesConfig>,
): Promise<FindFilesRes> {
const config = getFindConfig(findConfig);
const found: string[] = [];
const foundAll: string[] = [];

@@ -152,10 +139,28 @@ function findFile(path: string, filter: string[] = []): string | null {
return null;
}

function getFindConfig(option: Partial<FindFilesConfig>): FindFilesConfig {
const result = merge(
{
path: '',
ignore: [],
filter: [],
levelsDeep: MAX_DETECTION_DEPTH,
featureFlags: new Set<string>(),
},
option,
);

if (isNaN(result.levelsDeep) || result.levelsDeep === null) {
result.levelsDeep = MAX_DETECTION_DEPTH;
}
return result;
}

async function findInDirectory(
findConfig: FindFilesConfig,
): Promise<FindFilesRes> {
const config: DefaultFindConfig = assign({}, defaultFindConfig, findConfig);
const config = getFindConfig(findConfig);
const files = await readDirectory(config.path);
const toFind = files
.filter((file) => !config.ignore.includes(file))
3 changes: 2 additions & 1 deletion src/lib/plugins/get-deps-from-plugin.ts
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ import { convertSingleResultToMultiCustom } from './convert-single-splugin-res-t
import { convertMultiResultToMultiCustom } from './convert-multi-plugin-res-to-multi-custom';
import { processYarnWorkspaces } from './nodejs-plugin/yarn-workspaces-parser';
import { ScannedProject } from '@snyk/cli-interface/legacy/common';
import { MAX_DETECTION_DEPTH } from '../constants';

const debug = debugModule('snyk-test');

@@ -43,7 +44,7 @@ export async function getDepsFromPlugin(
): Promise<pluginApi.MultiProjectResult | MultiProjectResultCustom> {
if (Object.keys(multiProjectProcessors).some((key) => options[key])) {
const scanType = options.yarnWorkspaces ? 'yarnWorkspaces' : 'allProjects';
const levelsDeep = options.detectionDepth;
const levelsDeep = options.detectionDepth || MAX_DETECTION_DEPTH;
const ignore = options.exclude ? options.exclude.split(',') : [];

const { files: targetFiles, allFilesFound } = await find({
3 changes: 3 additions & 0 deletions src/lib/plugins/get-extra-project-count.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import { legacyPlugin as pluginApi } from '@snyk/cli-interface';
import { find } from '../find-files';
import { AUTO_DETECTABLE_FILES } from '../detect';
import { Options } from '../types';
import { MAX_DETECTION_DEPTH } from '../constants';

export async function getExtraProjectCount(
root: string,
@@ -23,6 +24,8 @@ export async function getExtraProjectCount(
path: root,
ignore: [],
filter: AUTO_DETECTABLE_FILES,
levelsDeep: MAX_DETECTION_DEPTH,
featureFlags: new Set(),
});
const foundProjectsCount =
extraTargetFiles.length > 1 ? extraTargetFiles.length - 1 : undefined;
2 changes: 2 additions & 0 deletions test/acceptance/workspaces/mono-repo-nested/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
project/target
5 changes: 5 additions & 0 deletions test/acceptance/workspaces/mono-repo-nested/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Simple Monorepo

This repository contains a number of different projects, both at the root and in directories.

It is used as a simple test fixture for monorepo and multi-language support on [Snyk.io](https://snyk.io). As such, each "project" is merely the files needed to describe dependencies.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source "https://rubygems.org"

gem "rack-cache", "~> 1.1.0"
gem "rack", "~> 1.6.2"
gem "rack-protection", "~> 1.5.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
GEM
remote: https://rubygems.org/
specs:
rack (1.6.5)
rack-cache (1.1)
rack (>= 0.4)
rack-protection (1.5.3)
rack

PLATFORMS
ruby

DEPENDENCIES
rack (~> 1.6.2)
rack-cache (~> 1.1.0)
rack-protection (~> 1.5.0)

BUNDLED WITH
1.14.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source "https://rubygems.org"

gem "rack-cache", "~> 1.1.0"
gem "rack", "~> 1.6.2"
gem "rack-protection", "~> 1.5.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
GEM
remote: https://rubygems.org/
specs:
rack (1.6.5)
rack-cache (1.1)
rack (>= 0.4)
rack-protection (1.5.3)
rack

PLATFORMS
ruby

DEPENDENCIES
rack (~> 1.6.2)
rack-cache (~> 1.1.0)
rack-protection (~> 1.5.0)

BUNDLED WITH
1.14.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source :rubygems

gem "sinatra"
gem "haml"
gem "httparty"
gem "actionpack"
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
GEM
remote: http://rubygems.org/
specs:
actionpack (4.2.5)
actionview (= 4.2.5)
activesupport (= 4.2.5)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.5)
activesupport (= 4.2.5)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
activesupport (4.2.5)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
builder (3.2.2)
erubis (2.7.0)
haml (3.1.4)
httparty (0.8.1)
multi_json
multi_xml
i18n (0.7.0)
json (1.8.3)
loofah (2.0.3)
nokogiri (>= 1.5.9)
mini_portile2 (2.1.0)
minitest (5.9.1)
multi_json (1.12.1)
multi_xml (0.5.5)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
rack (1.6.4)
rack-protection (1.5.3)
rack
rack-test (0.6.3)
rack (>= 1.0)
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.7)
activesupport (>= 4.2.0.beta, < 5.0)
nokogiri (~> 1.6.0)
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
sinatra (1.3.2)
rack (~> 1.3, >= 1.3.6)
rack-protection (~> 1.2)
tilt (~> 1.3, >= 1.3.3)
thread_safe (0.3.5)
tilt (1.4.1)
tzinfo (1.2.2)
thread_safe (~> 0.1)

PLATFORMS
ruby

DEPENDENCIES
actionpack
haml
httparty
sinatra

BUNDLED WITH
1.13.2

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

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"
}
}

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

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"
}
}
35 changes: 35 additions & 0 deletions test/acceptance/workspaces/mono-repo-nested/level-1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "goof",
"version": "0.0.3",
"description": "A vulnerable todo demo application",
"homepage": "https://snyk.io/",
"repository": {
"type": "git",
"url": "https://github.com/Snyk/snyk-todo-list-demo-app/"
},
"scripts": {
"start": "node app.js",
"cleanup": "mongo express-todo --eval 'db.todos.remove({});'"
},
"dependencies": {
"body-parser": "1.9.0",
"cookie-parser": "1.3.3",
"ejs": "1.0.0",
"ejs-locals": "1.0.2",
"errorhandler": "1.2.0",
"express": "4.12.4",
"express-fileupload": "0.0.5",
"humanize-ms": "1.0.1",
"marked": "0.3.5",
"method-override": "latest",
"moment": "2.15.1",
"mongoose": "4.2.4",
"morgan": "latest",
"ms": "^0.7.1",
"npmconf": "0.0.24",
"optional": "^0.1.3",
"st": "0.2.4",
"stream-buffers": "^3.0.1",
"tap": "^5.7.0"
}
}
18 changes: 18 additions & 0 deletions test/acceptance/workspaces/mono-repo-nested/package-lock.json
14 changes: 14 additions & 0 deletions test/acceptance/workspaces/mono-repo-nested/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"
}
}
42 changes: 42 additions & 0 deletions test/jest/acceptance/snyk-test/all-projects.spec.ts
Original file line number Diff line number Diff line change
@@ -198,6 +198,48 @@ describe('snyk test --all-projects (mocked server only)', () => {
expect(stderr).toEqual('');
});

test('`test mono-repo-nested --all-projects` defaults to 4 max depth', async () => {
const project = await createProjectFromWorkspace('mono-repo-nested');

const { code, stdout, stderr } = await runSnykCLI('test --all-projects', {
cwd: project.path(),
env,
});

const backendRequests = server.getRequests().filter((req: any) => {
return req.url.includes('/api/v1/test');
});

expect(backendRequests).toHaveLength(4);
backendRequests.forEach((req: any) => {
expect(req.method).toEqual('POST');
expect(req.headers['x-snyk-cli-version']).not.toBeUndefined();
expect(req.url).toMatch('/api/v1/test');
});

expect(code).toEqual(0);

const dirSeparator = process.platform === 'win32' ? '\\' : '/';

expect(stdout).toMatch('Target file: package-lock.json');
expect(stdout).toMatch(
`Target file: level-1${dirSeparator}package.json`,
);
expect(stdout).toMatch(
`Target file: level-1${dirSeparator}level-2${dirSeparator}Gemfile.lock`,
);
expect(stdout).toMatch(
`Target file: level-1${dirSeparator}level-2${dirSeparator}level-3${dirSeparator}package-lock.json`,
);
expect(stdout).not.toMatch(
`level-1${dirSeparator}level-2${dirSeparator}level-3${dirSeparator}level-4${dirSeparator}level-5${dirSeparator}package-lock.json`,
);
expect(stdout).not.toMatch(
`level-1${dirSeparator}level-2${dirSeparator}level-3${dirSeparator}level-4${dirSeparator}level-5${dirSeparator}level-6${dirSeparator}Gemfile.lock`,
);
expect(stderr).toBe('');
});

test('`test empty --all-projects`', async () => {
const project = await createProjectFromWorkspace('empty');

24 changes: 24 additions & 0 deletions test/tap/find-files.test.ts
Original file line number Diff line number Diff line change
@@ -2,9 +2,13 @@ import * as path from 'path';
import { test } from 'tap';
import { find } from '../../src/lib/find-files';
import { getFixturePath } from '../jest/util/getFixturePath';
import { getWorkspacePath } from '../jest/util/getWorkspacePath';

const testFixture = getFixturePath('find-files');

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const skiptest = (name, _) => console.log(`Skipping ${name}`);

test('find all files in test fixture', async (t) => {
// six levels deep to find all
const { files: result, allFilesFound } = await find({
@@ -71,6 +75,26 @@ test('find all files in test fixture', async (t) => {
);
});

test('defaults to only detecting files up to 4 layers deep when undefined', async (t) => {
//
const { files: result } = await find({
path: getWorkspacePath('mono-repo-nested'),
levelsDeep: undefined,
});

t.same(result.length, 4);
});

test('defaults to only detecting files up to 4 layers deep when null', async (t) => {
//
const { files: result } = await find({
path: getWorkspacePath('mono-repo-nested'),
levelsDeep: NaN,
});

t.same(result.length, 4);
});

test('find all files in test fixture ignoring node_modules', async (t) => {
// six levels deep to ensure node_modules is tested
const { files: result } = await find({

0 comments on commit 45a74ea

Please sign in to comment.