fix: prevent 'pnpm audit' from crashing with an OOM error (#9290)

* Prevent 'pnpm audit' from crashing with an OOM error by disabling the finding of dependency paths unless opted into

* feat!: remove show paths from audit output

* docs: add changeset

* fix: linting error

close #9280

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
NullVoxPopuli
2025-03-16 20:11:27 -04:00
committed by GitHub
parent e8698bbb84
commit 7f9f20227e
6 changed files with 253 additions and 918 deletions

View File

@@ -0,0 +1,7 @@
---
"@pnpm/plugin-commands-audit": major
"@pnpm/audit": major
"pnpm": patch
---
Remove dependency paths from audit output to prevent out-of-memory errors [#9280](https://github.com/pnpm/pnpm/issues/9280).

View File

@@ -34,7 +34,6 @@
"@pnpm/error": "workspace:*",
"@pnpm/fetch": "workspace:*",
"@pnpm/fetching-types": "workspace:*",
"@pnpm/list": "workspace:*",
"@pnpm/lockfile.detect-dep-types": "workspace:*",
"@pnpm/lockfile.types": "workspace:*",
"@pnpm/lockfile.utils": "workspace:*",

View File

@@ -1,15 +1,10 @@
import assert from 'assert'
import path from 'path'
import util from 'util'
import { PnpmError } from '@pnpm/error'
import { type AgentOptions, fetchWithAgent, type RetryTimeoutOptions } from '@pnpm/fetch'
import { type GetAuthHeader } from '@pnpm/fetching-types'
import { type LockfileObject } from '@pnpm/lockfile.types'
import { globalWarn } from '@pnpm/logger'
import { type DependenciesField } from '@pnpm/types'
import { lockfileToAuditTree } from './lockfileToAuditTree'
import { type AuditReport } from './types'
import { searchForPackages, flattenSearchedPackages } from '@pnpm/list'
export * from './types'
@@ -50,19 +45,7 @@ export async function audit (
if (res.status !== 200) {
throw new PnpmError('AUDIT_BAD_RESPONSE', `The audit endpoint (at ${auditUrl}) responded with ${res.status}: ${await res.text()}`)
}
const auditReport = await (res.json() as Promise<AuditReport>)
try {
return await extendWithDependencyPaths(auditReport, {
lockfile,
lockfileDir: opts.lockfileDir,
include: opts.include,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
} catch (err: unknown) {
assert(util.types.isNativeError(err))
globalWarn(`Failed to extend audit report with dependency paths: ${err.message}`)
return auditReport
}
return (res.json() as Promise<AuditReport>)
}
interface AuthHeaders {
@@ -77,46 +60,6 @@ function getAuthHeaders (authHeaderValue: string | undefined): AuthHeaders {
return headers
}
async function extendWithDependencyPaths (auditReport: AuditReport, opts: {
lockfile: LockfileObject
lockfileDir: string
include?: { [dependenciesField in DependenciesField]: boolean }
virtualStoreDirMaxLength: number
}): Promise<AuditReport> {
const { advisories } = auditReport
if (!Object.keys(advisories).length) return auditReport
const projectDirs = Object.keys(opts.lockfile.importers)
.map((importerId) => path.join(opts.lockfileDir, importerId))
const searchOpts = {
lockfileDir: opts.lockfileDir,
depth: Infinity,
include: opts.include,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
}
const _searchPackagePaths = searchPackagePaths.bind(null, searchOpts, projectDirs)
// eslint-disable-next-line @typescript-eslint/naming-convention
await Promise.all(Object.values(advisories).map(async ({ findings, module_name }) => {
await Promise.all(findings.map(async (finding) => {
finding.paths = await _searchPackagePaths(`${module_name}@${finding.version}`)
}))
}))
return auditReport
}
async function searchPackagePaths (
searchOpts: {
lockfileDir: string
depth: number
include?: { [dependenciesField in DependenciesField]: boolean }
virtualStoreDirMaxLength: number
},
projectDirs: string[],
pkg: string
): Promise<string[]> {
const pkgs = await searchForPackages([pkg], projectDirs, searchOpts)
return flattenSearchedPackages(pkgs, { lockfileDir: searchOpts.lockfileDir }).map(({ depPath }) => depPath)
}
export class AuditEndpointNotExistsError extends PnpmError {
constructor (endpoint: string) {
const message = `The audit endpoint (at ${endpoint}) is doesn't exist.`

View File

@@ -33,9 +33,6 @@
{
"path": "../../pkg-manifest/read-project-manifest"
},
{
"path": "../../reviewing/list"
},
{
"path": "../detect-dep-types"
},

View File

File diff suppressed because it is too large Load Diff

3
pnpm-lock.yaml generated
View File

@@ -3135,9 +3135,6 @@ importers:
'@pnpm/fetching-types':
specifier: workspace:*
version: link:../../network/fetching-types
'@pnpm/list':
specifier: workspace:*
version: link:../../reviewing/list
'@pnpm/lockfile.detect-dep-types':
specifier: workspace:*
version: link:../detect-dep-types