Skip to content

Commit ee13533

Browse files
authored
Merge pull request #630 from snyk/feat/exclude-node-modules-scan
feat: add option to disable the node_modules scan for node.js images
2 parents 91465d1 + d30714f commit ee13533

File tree

6 files changed

+2098
-38
lines changed

6 files changed

+2098
-38
lines changed

lib/analyzer/applications/node-modules-utils.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ const debug = Debug("snyk");
66

77
const nodeModulesRegex = /^(.*?)(?:[\\\/]node_modules)/;
88

9-
export { persistNodeModules, cleanupAppNodeModules, groupFilesByDirectory };
9+
export {
10+
persistNodeModules,
11+
cleanupAppNodeModules,
12+
groupNodeAppFilesByDirectory,
13+
groupNodeModulesFilesByDirectory,
14+
};
1015

1116
interface ScanPaths {
1217
tempDir: string;
@@ -147,6 +152,11 @@ function isNpmCacheDependency(filePath: string): boolean {
147152
return false;
148153
}
149154

155+
// TODO: Enable custom cache filtering if needed
156+
// function isCustomCache(filePath: string): boolean {
157+
// return (filePath.includes("cache") || filePath.includes("Cache"));
158+
// }
159+
150160
function isPnpmCacheDependency(filePath: string): boolean {
151161
if (
152162
filePath.includes("pnpm-store") ||
@@ -171,6 +181,23 @@ function getNodeModulesParentDir(filePath: string): string | null {
171181
return null;
172182
}
173183

184+
function groupNodeAppFilesByDirectory(
185+
filePathToContent: FilePathToContent,
186+
): FilesByDirMap {
187+
const filesByDir: FilesByDirMap = new Map();
188+
const filePaths = Object.keys(filePathToContent);
189+
190+
for (const filePath of filePaths) {
191+
const directory = path.dirname(filePath);
192+
193+
if (!filesByDir.has(directory)) {
194+
filesByDir.set(directory, new Set());
195+
}
196+
filesByDir.get(directory)?.add(filePath);
197+
}
198+
return filesByDir;
199+
}
200+
174201
function getGroupingDir(filePath: string): string {
175202
const nodeModulesParentDir = getNodeModulesParentDir(filePath);
176203

@@ -180,7 +207,7 @@ function getGroupingDir(filePath: string): string {
180207
return path.dirname(filePath);
181208
}
182209

183-
function groupFilesByDirectory(
210+
function groupNodeModulesFilesByDirectory(
184211
filePathToContent: FilePathToContent,
185212
): FilesByDirMap {
186213
const filesByDir: FilesByDirMap = new Map();

lib/analyzer/applications/node.ts

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import {
1818
import { LogicalRoot } from "snyk-resolve-deps/dist/types";
1919
import {
2020
cleanupAppNodeModules,
21-
groupFilesByDirectory,
21+
groupNodeAppFilesByDirectory,
22+
groupNodeModulesFilesByDirectory,
2223
persistNodeModules,
2324
} from "./node-modules-utils";
2425
import {
@@ -35,6 +36,7 @@ interface ManifestLockPathPair {
3536

3637
export async function nodeFilesToScannedProjects(
3738
filePathToContent: FilePathToContent,
39+
shouldIncludeNodeModules: boolean,
3840
): Promise<AppDepsScanResultWithoutTarget[]> {
3941
const scanResults: AppDepsScanResultWithoutTarget[] = [];
4042
/**
@@ -51,8 +53,9 @@ export async function nodeFilesToScannedProjects(
5153
return [];
5254
}
5355

54-
const fileNamesGroupedByDirectory = groupFilesByDirectory(filePathToContent);
55-
const [manifestFilePairs, nodeProjects] = findProjectsAndManifests(
56+
const fileNamesGroupedByDirectory =
57+
groupNodeAppFilesByDirectory(filePathToContent);
58+
const manifestFilePairs = findManifestLockPairsInSameDirectory(
5659
fileNamesGroupedByDirectory,
5760
);
5861

@@ -64,14 +67,22 @@ export async function nodeFilesToScannedProjects(
6467
)),
6568
);
6669
}
67-
if (nodeProjects.length !== 0) {
68-
scanResults.push(
69-
...(await depGraphFromNodeModules(
70-
filePathToContent,
71-
nodeProjects,
72-
fileNamesGroupedByDirectory,
73-
)),
70+
71+
if (shouldIncludeNodeModules) {
72+
const appNodeModulesGroupedByDirectory =
73+
groupNodeModulesFilesByDirectory(filePathToContent);
74+
const nodeProjects = findManifestNodeModulesFilesInSameDirectory(
75+
appNodeModulesGroupedByDirectory,
7476
);
77+
if (nodeProjects.length !== 0) {
78+
scanResults.push(
79+
...(await depGraphFromNodeModules(
80+
filePathToContent,
81+
nodeProjects,
82+
appNodeModulesGroupedByDirectory,
83+
)),
84+
);
85+
}
7586
}
7687

7788
return scanResults;
@@ -109,7 +120,6 @@ async function depGraphFromNodeModules(
109120
);
110121

111122
if ((pkgTree as LogicalRoot).numDependencies === 0) {
112-
await cleanupAppNodeModules(tempDir);
113123
continue;
114124
}
115125

@@ -197,13 +207,15 @@ async function depGraphFromManifestFiles(
197207
return scanResults;
198208
}
199209

200-
function findProjectsAndManifests(
210+
function findManifestLockPairsInSameDirectory(
201211
fileNamesGroupedByDirectory: FilesByDirMap,
202-
): [ManifestLockPathPair[], string[]] {
212+
): ManifestLockPathPair[] {
203213
const manifestLockPathPairs: ManifestLockPathPair[] = [];
204-
const nodeProjects: string[] = [];
205214

206215
for (const directoryPath of fileNamesGroupedByDirectory.keys()) {
216+
if (directoryPath.includes("node_modules")) {
217+
continue;
218+
}
207219
const filesInDirectory = fileNamesGroupedByDirectory.get(directoryPath);
208220
if (!filesInDirectory || filesInDirectory.size < 1) {
209221
// missing manifest files
@@ -230,12 +242,40 @@ function findProjectsAndManifests(
230242
? lockFileParser.LockfileType.npm
231243
: lockFileParser.LockfileType.yarn,
232244
});
245+
}
246+
}
247+
248+
return manifestLockPathPairs;
249+
}
250+
251+
function findManifestNodeModulesFilesInSameDirectory(
252+
fileNamesGroupedByDirectory: FilesByDirMap,
253+
): string[] {
254+
const nodeProjects: string[] = [];
255+
256+
for (const directoryPath of fileNamesGroupedByDirectory.keys()) {
257+
const filesInDirectory = fileNamesGroupedByDirectory.get(directoryPath);
258+
if (!filesInDirectory || filesInDirectory.size < 1) {
259+
// missing manifest files
260+
continue;
261+
}
262+
263+
const expectedManifest = path.join(directoryPath, "package.json");
264+
const expectedNpmLockFile = path.join(directoryPath, "package-lock.json");
265+
const expectedYarnLockFile = path.join(directoryPath, "yarn.lock");
266+
267+
const hasManifestFile = filesInDirectory.has(expectedManifest);
268+
const hasLockFile =
269+
filesInDirectory.has(expectedNpmLockFile) ||
270+
filesInDirectory.has(expectedYarnLockFile);
271+
272+
if (hasManifestFile && hasLockFile) {
233273
continue;
234274
}
235275
nodeProjects.push(directoryPath);
236276
}
237277

238-
return [manifestLockPathPairs, nodeProjects];
278+
return nodeProjects;
239279
}
240280

241281
function stripUndefinedLabels(

lib/analyzer/static-analyzer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ export async function analyze(
103103
}
104104

105105
const appScan = !isTrue(options["exclude-app-vulns"]);
106+
const nodeModulesScan = !isTrue(options["exclude-node-modules"]);
106107

107108
if (appScan) {
108109
staticAnalysisActions.push(
@@ -194,6 +195,7 @@ export async function analyze(
194195
if (appScan) {
195196
const nodeDependenciesScanResults = await nodeFilesToScannedProjects(
196197
getFileContent(extractedLayers, getNodeAppFileContentAction.actionName),
198+
nodeModulesScan,
197199
);
198200
const phpDependenciesScanResults = await phpFilesToScannedProjects(
199201
getFileContent(extractedLayers, getPhpAppFileContentAction.actionName),

lib/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ export interface PluginOptions {
195195

196196
/** Whether to disable application dependencies scanning. The default is "false" */
197197
"exclude-app-vulns": boolean | string;
198-
198+
/** Whether to disable node modules dependencies scanning. The default is "false" */
199+
"exclude-node-modules": boolean | string;
199200
/**
200201
* How many levels of (nested) JARs we should unpack
201202
* If a JAR contains other JARs (AKA JAR of JARs), we send back only the children JARs, and don't look for vulns in the parent.

0 commit comments

Comments
 (0)