From 45d6de95d5ce30f71fa3d8194065aaa6be5037c6 Mon Sep 17 00:00:00 2001 From: Georgiana-Elena Maxim Date: Mon, 2 Sep 2024 17:08:16 +0300 Subject: [PATCH] fix: handle pnpm empty lockfiles --- .../pnpm/lockfile-parser/index.ts | 8 +++++- .../pnpm/lockfile-parser/lockfile-v5.ts | 7 ++++++ .../pnpm/lockfile-parser/lockfile-v9.ts | 3 ++- lib/dep-graph-builders/pnpm/parse-project.ts | 2 +- .../pnpm-lock-v5/empty-project/expected.json | 25 +++++++++++++++++++ .../pnpm-lock-v5/empty-project/package.json | 4 +++ .../pnpm-lock-v6/empty-project/expected.json | 25 +++++++++++++++++++ .../pnpm-lock-v6/empty-project/package.json | 4 +++ .../pnpm-lock-v6/empty-project/pnpm-lock.yaml | 5 ++++ .../pnpm-lock-v9/empty-project/expected.json | 25 +++++++++++++++++++ .../pnpm-lock-v9/empty-project/package.json | 4 +++ .../pnpm-lock-v9/empty-project/pnpm-lock.yaml | 9 +++++++ .../jest/dep-graph-builders/pnpm-lock.test.ts | 16 ++++++------ 13 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 test/jest/dep-graph-builders/fixtures/pnpm-lock-v5/empty-project/expected.json create mode 100644 test/jest/dep-graph-builders/fixtures/pnpm-lock-v5/empty-project/package.json create mode 100644 test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/expected.json create mode 100644 test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/package.json create mode 100644 test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/pnpm-lock.yaml create mode 100644 test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/expected.json create mode 100644 test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/package.json create mode 100644 test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/pnpm-lock.yaml diff --git a/lib/dep-graph-builders/pnpm/lockfile-parser/index.ts b/lib/dep-graph-builders/pnpm/lockfile-parser/index.ts index 7c7c73d3..f61a0924 100644 --- a/lib/dep-graph-builders/pnpm/lockfile-parser/index.ts +++ b/lib/dep-graph-builders/pnpm/lockfile-parser/index.ts @@ -8,10 +8,16 @@ import { OpenSourceEcosystems } from '@snyk/error-catalog-nodejs-public'; import { NodeLockfileVersion } from '../../../utils'; export function getPnpmLockfileParser( - pnpmLockContent: string, + pnpmLockContent: string | undefined, lockfileVersion?: NodeLockfileVersion, workspaceArgs?: PnpmWorkspaceArgs, ): PnpmLockfileParser { + // In case of no dependencies, pnpm@7 (lokfile version 5) + // does not create a lockfile at `pnpm install` + // so if there is no lockfile content, default to lockfile version 5 + if (!pnpmLockContent) { + return new LockfileV5Parser(pnpmLockContent, workspaceArgs); + } const rawPnpmLock = load(pnpmLockContent, { json: true, schema: FAILSAFE_SCHEMA, diff --git a/lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v5.ts b/lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v5.ts index ac9f256b..d96e69bd 100644 --- a/lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v5.ts +++ b/lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v5.ts @@ -5,6 +5,13 @@ import { PnpmWorkspaceArgs } from '../../types'; export class LockfileV5Parser extends PnpmLockfileParser { public constructor(rawPnpmLock: any, workspaceArgs?: PnpmWorkspaceArgs) { + // In case of no dependencies, pnpm@7 (lokfile version 5) + // does not create a lockfile at `pnpm install` + if (!rawPnpmLock) { + rawPnpmLock = { + lockfileVersion: '5', + }; + } super(rawPnpmLock, workspaceArgs); } diff --git a/lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v9.ts b/lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v9.ts index a00ebdde..21f5033a 100644 --- a/lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v9.ts +++ b/lib/dep-graph-builders/pnpm/lockfile-parser/lockfile-v9.ts @@ -16,7 +16,8 @@ export class LockfileV9Parser extends LockfileV6Parser { super(rawPnpmLock, workspaceArgs); this.settings = rawPnpmLock.settings; this.packages = {}; - Object.entries(rawPnpmLock.snapshots).forEach( + this.snapshots = rawPnpmLock.snapshots || {}; + Object.entries(this.snapshots).forEach( ([depPath, versionData]: [string, any]) => { const normalizedDepPath = this.excludeTransPeerDepsVersions(depPath); this.packages[normalizedDepPath] = { diff --git a/lib/dep-graph-builders/pnpm/parse-project.ts b/lib/dep-graph-builders/pnpm/parse-project.ts index 2371bd12..a4ffcd14 100644 --- a/lib/dep-graph-builders/pnpm/parse-project.ts +++ b/lib/dep-graph-builders/pnpm/parse-project.ts @@ -7,7 +7,7 @@ import { NodeLockfileVersion } from '../../utils'; export const parsePnpmProject = async ( pkgJsonContent: string, - pnpmLockContent: string, + pnpmLockContent: string | undefined, options: PnpmProjectParseOptions, lockfileVersion?: NodeLockfileVersion, ): Promise => { diff --git a/test/jest/dep-graph-builders/fixtures/pnpm-lock-v5/empty-project/expected.json b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v5/empty-project/expected.json new file mode 100644 index 00000000..72b3be68 --- /dev/null +++ b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v5/empty-project/expected.json @@ -0,0 +1,25 @@ +{ + "schemaVersion": "1.3.0", + "pkgManager": { + "name": "pnpm" + }, + "pkgs": [ + { + "id": "empty-project@1.0.0", + "info": { + "name": "empty-project", + "version": "1.0.0" + } + } + ], + "graph": { + "rootNodeId": "root-node", + "nodes": [ + { + "nodeId": "root-node", + "pkgId": "empty-project@1.0.0", + "deps": [] + } + ] + } +} diff --git a/test/jest/dep-graph-builders/fixtures/pnpm-lock-v5/empty-project/package.json b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v5/empty-project/package.json new file mode 100644 index 00000000..31696321 --- /dev/null +++ b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v5/empty-project/package.json @@ -0,0 +1,4 @@ +{ + "name": "empty-project", + "version": "1.0.0" +} diff --git a/test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/expected.json b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/expected.json new file mode 100644 index 00000000..72b3be68 --- /dev/null +++ b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/expected.json @@ -0,0 +1,25 @@ +{ + "schemaVersion": "1.3.0", + "pkgManager": { + "name": "pnpm" + }, + "pkgs": [ + { + "id": "empty-project@1.0.0", + "info": { + "name": "empty-project", + "version": "1.0.0" + } + } + ], + "graph": { + "rootNodeId": "root-node", + "nodes": [ + { + "nodeId": "root-node", + "pkgId": "empty-project@1.0.0", + "deps": [] + } + ] + } +} diff --git a/test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/package.json b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/package.json new file mode 100644 index 00000000..31696321 --- /dev/null +++ b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/package.json @@ -0,0 +1,4 @@ +{ + "name": "empty-project", + "version": "1.0.0" +} diff --git a/test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/pnpm-lock.yaml b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/pnpm-lock.yaml new file mode 100644 index 00000000..2b9f1883 --- /dev/null +++ b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v6/empty-project/pnpm-lock.yaml @@ -0,0 +1,5 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false diff --git a/test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/expected.json b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/expected.json new file mode 100644 index 00000000..72b3be68 --- /dev/null +++ b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/expected.json @@ -0,0 +1,25 @@ +{ + "schemaVersion": "1.3.0", + "pkgManager": { + "name": "pnpm" + }, + "pkgs": [ + { + "id": "empty-project@1.0.0", + "info": { + "name": "empty-project", + "version": "1.0.0" + } + } + ], + "graph": { + "rootNodeId": "root-node", + "nodes": [ + { + "nodeId": "root-node", + "pkgId": "empty-project@1.0.0", + "deps": [] + } + ] + } +} diff --git a/test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/package.json b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/package.json new file mode 100644 index 00000000..31696321 --- /dev/null +++ b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/package.json @@ -0,0 +1,4 @@ +{ + "name": "empty-project", + "version": "1.0.0" +} diff --git a/test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/pnpm-lock.yaml b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/pnpm-lock.yaml new file mode 100644 index 00000000..9b60ae17 --- /dev/null +++ b/test/jest/dep-graph-builders/fixtures/pnpm-lock-v9/empty-project/pnpm-lock.yaml @@ -0,0 +1,9 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} diff --git a/test/jest/dep-graph-builders/pnpm-lock.test.ts b/test/jest/dep-graph-builders/pnpm-lock.test.ts index 6038bda0..90eeac20 100644 --- a/test/jest/dep-graph-builders/pnpm-lock.test.ts +++ b/test/jest/dep-graph-builders/pnpm-lock.test.ts @@ -1,5 +1,5 @@ import { join } from 'path'; -import { readFileSync } from 'fs'; +import { existsSync, readFileSync } from 'fs'; import { parsePnpmProject } from '../../../lib/dep-graph-builders'; import { OpenSourceEcosystems } from '@snyk/error-catalog-nodejs-public'; import { InvalidUserInputError } from '../../../lib'; @@ -24,6 +24,7 @@ describe.each(['pnpm-lock-v5', 'pnpm-lock-v6', 'pnpm-lock-v9'])( 'npm-protocol', 'scoped-override', 'alias-sub-dependency', + 'empty-project', ])('[simple tests] project: %s ', (fixtureName) => { jest.setTimeout(50 * 1000); it('matches expected', async () => { @@ -34,13 +35,14 @@ describe.each(['pnpm-lock-v5', 'pnpm-lock-v6', 'pnpm-lock-v9'])( ), 'utf8', ); - const pkgLockContent = readFileSync( - join( - __dirname, - `./fixtures/${lockFileVersionPath}/${fixtureName}/pnpm-lock.yaml`, - ), - 'utf8', + const lockfilePath = join( + __dirname, + `./fixtures/${lockFileVersionPath}/${fixtureName}/pnpm-lock.yaml`, ); + let pkgLockContent: string | undefined = undefined; + if (existsSync(lockfilePath)) { + pkgLockContent = readFileSync(lockfilePath, 'utf8'); + } const newDepGraph = await parsePnpmProject( pkgJsonContent,