diff --git a/package-lock.json b/package-lock.json index b0a624e1963..f184e8b94a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,7 +71,7 @@ "snyk-gradle-plugin": "3.24.6", "snyk-module": "3.1.0", "snyk-mvn-plugin": "2.31.3", - "snyk-nodejs-lockfile-parser": "1.44.0", + "snyk-nodejs-lockfile-parser": "1.45.0", "snyk-nuget-plugin": "1.23.5", "snyk-php-plugin": "1.9.2", "snyk-policy": "^1.25.0", @@ -16942,9 +16942,9 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "node_modules/snyk-nodejs-lockfile-parser": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.44.0.tgz", - "integrity": "sha512-A3vyLP2Vp0Zf14qXYiJsBL8wb+0RKdiBt5gpA1FcQDSePcC6UfLV3LsGVQucJIUE9dwB6t6RoeM34TL3+yEWdg==", + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.45.0.tgz", + "integrity": "sha512-lzy/V00kfOUOPpJ0dSAw2vfPrFEKdEljKMUWPs4EBOzwoDuBuuFUj95zYSFhN+qfnM6DlZSW0mDWmLM2Ka4V3A==", "dependencies": { "@snyk/dep-graph": "^2.3.0", "@snyk/graphlib": "2.1.9-patch.3", @@ -33220,9 +33220,9 @@ } }, "snyk-nodejs-lockfile-parser": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.44.0.tgz", - "integrity": "sha512-A3vyLP2Vp0Zf14qXYiJsBL8wb+0RKdiBt5gpA1FcQDSePcC6UfLV3LsGVQucJIUE9dwB6t6RoeM34TL3+yEWdg==", + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.45.0.tgz", + "integrity": "sha512-lzy/V00kfOUOPpJ0dSAw2vfPrFEKdEljKMUWPs4EBOzwoDuBuuFUj95zYSFhN+qfnM6DlZSW0mDWmLM2Ka4V3A==", "requires": { "@snyk/dep-graph": "^2.3.0", "@snyk/graphlib": "2.1.9-patch.3", diff --git a/package.json b/package.json index 904566404f8..3470ea99cd9 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "snyk-gradle-plugin": "3.24.6", "snyk-module": "3.1.0", "snyk-mvn-plugin": "2.31.3", - "snyk-nodejs-lockfile-parser": "1.44.0", + "snyk-nodejs-lockfile-parser": "1.45.0", "snyk-nuget-plugin": "1.23.5", "snyk-php-plugin": "1.9.2", "snyk-policy": "^1.25.0", diff --git a/src/lib/plugins/nodejs-plugin/index.ts b/src/lib/plugins/nodejs-plugin/index.ts index a713269c6f9..1d500d8280f 100644 --- a/src/lib/plugins/nodejs-plugin/index.ts +++ b/src/lib/plugins/nodejs-plugin/index.ts @@ -4,6 +4,8 @@ import * as types from '../types'; import * as analytics from '../../analytics'; import { MissingTargetFileError } from '../../errors/missing-targetfile-error'; import { MultiProjectResult } from '@snyk/cli-interface/legacy/plugin'; +import { DepGraph } from '@snyk/dep-graph'; +import { PkgTree } from 'snyk-nodejs-lockfile-parser'; export async function inspect( root: string, @@ -18,12 +20,29 @@ export async function inspect( targetFile.endsWith('yarn.lock'); const getLockFileDeps = isLockFileBased && !options.traverseNodeModules; - const depTree: any = getLockFileDeps + const depRes: PkgTree | DepGraph = getLockFileDeps ? await lockParser.parse(root, targetFile, options) : await modulesParser.parse(root, targetFile, options); - if (depTree?.meta?.lockfileVersion) { - analytics.add('lockfileVersion', depTree.meta.lockfileVersion); + const isDepGraph = isResDepGraph(depRes); + + let scannedProjects: any[] = []; + if (isDepGraph) { + if ((depRes as DepGraph)?.pkgManager.version) { + analytics.add( + 'lockfileVersion', + (depRes as DepGraph)?.pkgManager.version, + ); + } + scannedProjects = [{ depGraph: depRes }]; + } else { + if ((depRes as PkgTree)?.meta?.lockfileVersion) { + analytics.add( + 'lockfileVersion', + (depRes as PkgTree)?.meta?.lockfileVersion, + ); + } + scannedProjects = [{ depTree: depRes }]; } return { @@ -31,6 +50,10 @@ export async function inspect( name: 'snyk-nodejs-lockfile-parser', runtime: process.version, }, - scannedProjects: [{ depTree }], + scannedProjects, }; } + +function isResDepGraph(depRes: PkgTree | DepGraph): boolean { + return 'rootPkg' in depRes; +} diff --git a/src/lib/plugins/nodejs-plugin/npm-lock-parser.ts b/src/lib/plugins/nodejs-plugin/npm-lock-parser.ts index b4cf8713321..72ae5acac33 100644 --- a/src/lib/plugins/nodejs-plugin/npm-lock-parser.ts +++ b/src/lib/plugins/nodejs-plugin/npm-lock-parser.ts @@ -5,14 +5,17 @@ import { spinner } from '../../spinner'; import * as analytics from '../../analytics'; import * as fs from 'fs'; import * as lockFileParser from 'snyk-nodejs-lockfile-parser'; -import { PkgTree } from 'snyk-nodejs-lockfile-parser'; +import { PkgTree, InvalidUserInputError } from 'snyk-nodejs-lockfile-parser'; import { Options } from '../types'; +import { DepGraph } from '@snyk/dep-graph'; +import { NodeLockfileVersion } from 'snyk-nodejs-lockfile-parser/dist/utils'; +import { ProjectParseOptions } from 'snyk-nodejs-lockfile-parser/dist/dep-graph-builders/types'; export async function parse( root: string, targetFile: string, options: Options, -): Promise { +): Promise { const lockFileFullPath = path.resolve(root, targetFile); if (!fs.existsSync(lockFileFullPath)) { throw new Error( @@ -27,16 +30,16 @@ export async function parse( if (!fs.existsSync(manifestFileFullPath)) { throw new Error( `Could not find package.json at ${manifestFileFullPath} ` + - `(lockfile found at ${targetFile})`, + `(lockfile found at ${targetFile})`, ); } if (fs.existsSync(shrinkwrapFullPath)) { throw new Error( 'Both `npm-shrinkwrap.json` and `package-lock.json` were found in ' + - fullPath.dir + - '.\n' + - 'Please run your command again specifying `--file=package.json` flag.', + fullPath.dir + + '.\n' + + 'Please run your command again specifying `--file=package.json` flag.', ); } @@ -47,9 +50,29 @@ export async function parse( }); const resolveModuleSpinnerLabel = `Analyzing npm dependencies for ${lockFileFullPath}`; debug(resolveModuleSpinnerLabel); + + const strictOutOfSync = options.strictOutOfSync !== false; + const lockfileVersion = lockFileParser.getLockfileVersionFromFile(targetFile); + if ( + lockfileVersion === NodeLockfileVersion.YarnLockV1 || + lockfileVersion === NodeLockfileVersion.YarnLockV2 + ) { + return buildDepGraph( + root, + manifestFileFullPath, + lockFileFullPath, + lockfileVersion, + { + includeDevDeps: options.dev || false, + includeOptionalDeps: true, + strictOutOfSync, + pruneCycles: true, + }, + ); + } + try { await spinner(resolveModuleSpinnerLabel); - const strictOutOfSync = options.strictOutOfSync !== false; return lockFileParser.buildDepTreeFromFiles( root, manifestFileFullPath, @@ -61,3 +84,45 @@ export async function parse( await spinner.clear(resolveModuleSpinnerLabel)(); } } + +async function buildDepGraph( + root: string, + manifestFilePath: string, + lockfilePath: string, + lockfileVersion: NodeLockfileVersion, + options: ProjectParseOptions, +): Promise { + const manifestFileFullPath = path.resolve(root, manifestFilePath); + const lockFileFullPath = path.resolve(root, lockfilePath); + + if (!fs.existsSync(manifestFileFullPath)) { + throw new InvalidUserInputError( + 'Target file package.json not found at ' + + `location: ${manifestFileFullPath}`, + ); + } + if (!fs.existsSync(lockFileFullPath)) { + throw new InvalidUserInputError( + 'Lockfile not found at location: ' + lockFileFullPath, + ); + } + + const manifestFileContents = fs.readFileSync(manifestFileFullPath, 'utf-8'); + const lockFileContents = fs.readFileSync(lockFileFullPath, 'utf-8'); + + switch (lockfileVersion) { + case NodeLockfileVersion.YarnLockV1: + return await lockFileParser.parseYarnLockV1Project( + manifestFileContents, + lockFileContents, + options, + ); + case NodeLockfileVersion.YarnLockV2: + return lockFileParser.parseYarnLockV2Project( + manifestFileContents, + lockFileContents, + options, + ); + } + throw new Error('Failed to build dep graph from current project'); +}