UNPKG

snyk-docker-plugin

Version:
254 lines 12.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.shouldBuildDepTree = exports.getLockFileVersion = exports.nodeFilesToScannedProjects = void 0; const dep_graph_1 = require("@snyk/dep-graph"); const Debug = require("debug"); const path = require("path"); const lockFileParser = require("snyk-nodejs-lockfile-parser"); const resolveDeps = require("snyk-resolve-deps"); const debug = Debug("snyk"); const errors_1 = require("@snyk/composer-lockfile-parser/dist/errors"); const snyk_nodejs_lockfile_parser_1 = require("snyk-nodejs-lockfile-parser"); const node_modules_utils_1 = require("./node-modules-utils"); async function nodeFilesToScannedProjects(filePathToContent, shouldIncludeNodeModules) { const scanResults = []; /** * TODO: Add support for Yarn workspaces! * https://github.com/snyk/nodejs-lockfile-parser/blob/af8ba81930e950156b539281ecf41c1bc63dacf4/test/lib/yarn-workflows.test.ts#L7-L17 * * When building the ScanResult ensure the workspace is stored in scanResult.identity.args: * args: { * rootWorkspace: <path-of-workspace>, * }; */ if (Object.keys(filePathToContent).length === 0) { return []; } const fileNamesGroupedByDirectory = (0, node_modules_utils_1.groupNodeAppFilesByDirectory)(filePathToContent); const manifestFilePairs = findManifestLockPairsInSameDirectory(fileNamesGroupedByDirectory); if (manifestFilePairs.length !== 0) { scanResults.push(...(await depGraphFromManifestFiles(filePathToContent, manifestFilePairs))); } if (shouldIncludeNodeModules) { const appNodeModulesGroupedByDirectory = (0, node_modules_utils_1.groupNodeModulesFilesByDirectory)(filePathToContent); const nodeProjects = findManifestNodeModulesFilesInSameDirectory(appNodeModulesGroupedByDirectory); if (nodeProjects.length !== 0) { scanResults.push(...(await depGraphFromNodeModules(filePathToContent, nodeProjects, appNodeModulesGroupedByDirectory))); } } return scanResults; } exports.nodeFilesToScannedProjects = nodeFilesToScannedProjects; async function depGraphFromNodeModules(filePathToContent, nodeProjects, fileNamesGroupedByDirectory) { const scanResults = []; for (const project of nodeProjects) { const { tempDir, tempProjectPath, manifestPath } = await (0, node_modules_utils_1.persistNodeModules)(project, filePathToContent, fileNamesGroupedByDirectory); if (!tempDir) { continue; } if (!tempProjectPath) { await (0, node_modules_utils_1.cleanupAppNodeModules)(tempDir); continue; } try { const pkgTree = await resolveDeps(tempProjectPath, { dev: false, noFromArrays: true, }); if (pkgTree.numDependencies === 0) { continue; } const depGraph = await dep_graph_1.legacy.depTreeToGraph(pkgTree, pkgTree.type || "npm"); scanResults.push({ facts: [ { type: "depGraph", data: depGraph, }, { type: "testedFiles", data: manifestPath ? manifestPath : path.join(project, "node_modules"), }, ], identity: { type: depGraph.pkgManager.name, targetFile: manifestPath ? manifestPath : path.join(project, "node_modules"), }, }); } catch (error) { debug(`An error occurred while analysing node_modules dir: ${error.message}`); } finally { await (0, node_modules_utils_1.cleanupAppNodeModules)(tempDir); } } return scanResults; } async function depGraphFromManifestFiles(filePathToContent, manifestFilePairs) { const scanResults = []; const shouldIncludeDevDependencies = false; const shouldBeStrictForManifestAndLockfileOutOfSync = false; for (const pathPair of manifestFilePairs) { let depGraph; try { const lockfileVersion = getLockFileVersion(pathPair.lock, filePathToContent[pathPair.lock]); depGraph = shouldBuildDepTree(lockfileVersion) ? await buildDepGraphFromDepTree(filePathToContent[pathPair.manifest], filePathToContent[pathPair.lock], pathPair.lockType, shouldIncludeDevDependencies, shouldBeStrictForManifestAndLockfileOutOfSync) : await buildDepGraph(filePathToContent[pathPair.manifest], filePathToContent[pathPair.lock], lockfileVersion, shouldIncludeDevDependencies, shouldBeStrictForManifestAndLockfileOutOfSync); } catch (err) { debug(`An error occurred while analysing a pair of manifest and lock files: ${err.message}`); continue; } const depGraphFact = { type: "depGraph", data: depGraph, }; const testedFilesFact = { type: "testedFiles", data: [path.basename(pathPair.manifest), path.basename(pathPair.lock)], }; scanResults.push({ facts: [depGraphFact, testedFilesFact], identity: { type: depGraph.pkgManager.name, targetFile: pathPair.manifest, }, }); } return scanResults; } function findManifestLockPairsInSameDirectory(fileNamesGroupedByDirectory) { const manifestLockPathPairs = []; for (const directoryPath of fileNamesGroupedByDirectory.keys()) { if (directoryPath.includes("node_modules")) { continue; } const filesInDirectory = fileNamesGroupedByDirectory.get(directoryPath); if (!filesInDirectory || filesInDirectory.size < 1) { // missing manifest files continue; } const expectedManifest = path.join(directoryPath, "package.json"); const expectedNpmLockFile = path.join(directoryPath, "package-lock.json"); const expectedYarnLockFile = path.join(directoryPath, "yarn.lock"); const hasManifestFile = filesInDirectory.has(expectedManifest); const hasLockFile = filesInDirectory.has(expectedNpmLockFile) || filesInDirectory.has(expectedYarnLockFile); if (hasManifestFile && hasLockFile) { manifestLockPathPairs.push({ manifest: expectedManifest, // TODO: correlate filtering action with expected lockfile types lock: filesInDirectory.has(expectedNpmLockFile) ? expectedNpmLockFile : expectedYarnLockFile, lockType: filesInDirectory.has(expectedNpmLockFile) ? lockFileParser.LockfileType.npm : lockFileParser.LockfileType.yarn, }); } } return manifestLockPathPairs; } function findManifestNodeModulesFilesInSameDirectory(fileNamesGroupedByDirectory) { const nodeProjects = []; for (const directoryPath of fileNamesGroupedByDirectory.keys()) { const filesInDirectory = fileNamesGroupedByDirectory.get(directoryPath); if (!filesInDirectory || filesInDirectory.size < 1) { // missing manifest files continue; } const expectedManifest = path.join(directoryPath, "package.json"); const expectedNpmLockFile = path.join(directoryPath, "package-lock.json"); const expectedYarnLockFile = path.join(directoryPath, "yarn.lock"); const hasManifestFile = filesInDirectory.has(expectedManifest); const hasLockFile = filesInDirectory.has(expectedNpmLockFile) || filesInDirectory.has(expectedYarnLockFile); if (hasManifestFile && hasLockFile) { continue; } nodeProjects.push(directoryPath); } return nodeProjects; } function stripUndefinedLabels(parserResult) { const optionalLabels = parserResult.labels; const mandatoryLabels = {}; if (optionalLabels) { for (const currentLabelName of Object.keys(optionalLabels)) { if (optionalLabels[currentLabelName] !== undefined) { mandatoryLabels[currentLabelName] = optionalLabels[currentLabelName]; } } } const parserResultWithProperLabels = Object.assign({}, parserResult, { labels: mandatoryLabels, }); return parserResultWithProperLabels; } async function buildDepGraph(manifestFileContents, lockFileContents, lockfileVersion, shouldIncludeDevDependencies, shouldBeStrictForManifestAndLockfileOutOfSync) { switch (lockfileVersion) { case snyk_nodejs_lockfile_parser_1.NodeLockfileVersion.YarnLockV1: return await lockFileParser.parseYarnLockV1Project(manifestFileContents, lockFileContents, { includeDevDeps: shouldIncludeDevDependencies, includeOptionalDeps: true, includePeerDeps: false, pruneLevel: "withinTopLevelDeps", strictOutOfSync: shouldBeStrictForManifestAndLockfileOutOfSync, }); case snyk_nodejs_lockfile_parser_1.NodeLockfileVersion.YarnLockV2: return await lockFileParser.parseYarnLockV2Project(manifestFileContents, lockFileContents, { includeDevDeps: shouldIncludeDevDependencies, includeOptionalDeps: true, pruneWithinTopLevelDeps: true, strictOutOfSync: shouldBeStrictForManifestAndLockfileOutOfSync, }); case snyk_nodejs_lockfile_parser_1.NodeLockfileVersion.NpmLockV2: case snyk_nodejs_lockfile_parser_1.NodeLockfileVersion.NpmLockV3: return await lockFileParser.parseNpmLockV2Project(manifestFileContents, lockFileContents, { includeDevDeps: shouldIncludeDevDependencies, includeOptionalDeps: true, pruneCycles: true, strictOutOfSync: shouldBeStrictForManifestAndLockfileOutOfSync, }); } throw new Error("Failed to build dep graph from current project, unknown lockfile version : " + lockfileVersion.toString() + "."); } async function buildDepGraphFromDepTree(manifestFileContents, lockFileContents, lockfileType, shouldIncludeDevDependencies, shouldBeStrictForManifestAndLockfileOutOfSync) { const parserResult = await lockFileParser.buildDepTree(manifestFileContents, lockFileContents, shouldIncludeDevDependencies, lockfileType, shouldBeStrictForManifestAndLockfileOutOfSync); const strippedLabelsParserResult = stripUndefinedLabels(parserResult); return await dep_graph_1.legacy.depTreeToGraph(strippedLabelsParserResult, lockfileType); } function getLockFileVersion(lockFilePath, lockFileContents) { let lockfileVersion; if (lockFilePath.endsWith("package-lock.json")) { lockfileVersion = (0, snyk_nodejs_lockfile_parser_1.getNpmLockfileVersion)(lockFileContents); } else if (lockFilePath.endsWith("yarn.lock")) { lockfileVersion = (0, snyk_nodejs_lockfile_parser_1.getYarnLockfileVersion)(lockFileContents); } else if (lockFilePath.endsWith("pnpm-lock.yaml")) { lockfileVersion = (0, snyk_nodejs_lockfile_parser_1.getPnpmLockfileVersion)(lockFileContents); } else { throw new errors_1.InvalidUserInputError(`Unknown lockfile ${lockFilePath}. ` + "Please provide either package-lock.json, yarn.lock or pnpm-lock.yaml"); } return lockfileVersion; } exports.getLockFileVersion = getLockFileVersion; function shouldBuildDepTree(lockfileVersion) { return !(lockfileVersion === snyk_nodejs_lockfile_parser_1.NodeLockfileVersion.YarnLockV1 || lockfileVersion === snyk_nodejs_lockfile_parser_1.NodeLockfileVersion.YarnLockV2 || lockfileVersion === snyk_nodejs_lockfile_parser_1.NodeLockfileVersion.NpmLockV2 || lockfileVersion === snyk_nodejs_lockfile_parser_1.NodeLockfileVersion.NpmLockV3); } exports.shouldBuildDepTree = shouldBuildDepTree; //# sourceMappingURL=node.js.map