From 211f3b82c0b206d47fe6cba066d6fe083d828452 Mon Sep 17 00:00:00 2001 From: Cody Kaup Date: Fri, 10 Jan 2025 15:33:14 -0600 Subject: [PATCH] Skip lock file parsing if it's too large --- node-src/lib/findChangedDependencies.test.ts | 7 ++++++- node-src/lib/getDependencies.test.ts | 22 ++++++++++++++++++-- node-src/lib/getDependencies.ts | 12 +++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/node-src/lib/findChangedDependencies.test.ts b/node-src/lib/findChangedDependencies.test.ts index 7fc77c1d4..d712deff3 100644 --- a/node-src/lib/findChangedDependencies.test.ts +++ b/node-src/lib/findChangedDependencies.test.ts @@ -1,5 +1,6 @@ +import { statSync as unMockedStatSync } from 'fs'; import { buildDepTreeFromFiles } from 'snyk-nodejs-lockfile-parser'; -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest'; import { Context } from '..'; import * as git from '../git/git'; @@ -9,6 +10,10 @@ import TestLogger from './testLogger'; vi.mock('snyk-nodejs-lockfile-parser'); vi.mock('yarn-or-npm'); vi.mock('../git/git'); +vi.mock('fs'); + +const statSync = unMockedStatSync as Mock; +statSync.mockReturnValue({ size: 1 }); const getRepositoryRoot = vi.mocked(git.getRepositoryRoot); const checkoutFile = vi.mocked(git.checkoutFile); diff --git a/node-src/lib/getDependencies.test.ts b/node-src/lib/getDependencies.test.ts index 30ab767eb..9d69f6bc2 100644 --- a/node-src/lib/getDependencies.test.ts +++ b/node-src/lib/getDependencies.test.ts @@ -1,16 +1,22 @@ +import { statSync as unMockedStatSync } from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, Mock, vi } from 'vitest'; import packageJson from '../__mocks__/dependencyChanges/plain-package.json'; import { checkoutFile } from '../git/git'; -import { getDependencies } from './getDependencies'; +import { getDependencies, MAX_LOCK_FILE_SIZE } from './getDependencies'; import TestLogger from './testLogger'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ctx = { log: new TestLogger() } as any; +vi.mock('fs'); + +const statSync = unMockedStatSync as Mock; +statSync.mockReturnValue({ size: 1 }); + describe('getDependencies', () => { it('should return a set of dependencies', async () => { const dependencies = await getDependencies(ctx, { @@ -99,4 +105,16 @@ describe('getDependencies', () => { ]) ); }); + + it('should bail if the lock file is too large to parse', async () => { + statSync.mockReturnValue({ size: MAX_LOCK_FILE_SIZE + 1000 }); + + await expect(() => + getDependencies(ctx, { + rootPath: path.join(__dirname, '../__mocks__/dependencyChanges'), + manifestPath: 'plain-package.json', + lockfilePath: 'plain-yarn.lock', + }) + ).rejects.toThrowError(); + }); }); diff --git a/node-src/lib/getDependencies.ts b/node-src/lib/getDependencies.ts index 63340dec9..b31b93671 100644 --- a/node-src/lib/getDependencies.ts +++ b/node-src/lib/getDependencies.ts @@ -1,7 +1,11 @@ +import { statSync } from 'fs'; +import path from 'path'; import { buildDepTreeFromFiles, PkgTree } from 'snyk-nodejs-lockfile-parser'; import { Context } from '../types'; +export const MAX_LOCK_FILE_SIZE = 10_485_760; // 10 MB + export const getDependencies = async ( ctx: Context, { @@ -19,6 +23,14 @@ export const getDependencies = async ( strictOutOfSync?: boolean; } ) => { + // We can run into OOM errors if the lock file is too large. Therefore, we bail early and skip + // lock file parsing because some TurboSnap is better than no TurboSnap. + const stats = statSync(path.resolve(rootPath, lockfilePath)); + if (stats.size > MAX_LOCK_FILE_SIZE) { + ctx.log.warn({ lockfilePath }, 'Lock file too large to parse, skipping'); + throw new Error('Lock file too large to parse'); + } + try { const headTree = await buildDepTreeFromFiles( rootPath,