mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-04 23:34:58 -04:00
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:
7
.changeset/stale-impalas-marry.md
Normal file
7
.changeset/stale-impalas-marry.md
Normal 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).
|
||||
@@ -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:*",
|
||||
|
||||
@@ -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.`
|
||||
|
||||
@@ -33,9 +33,6 @@
|
||||
{
|
||||
"path": "../../pkg-manifest/read-project-manifest"
|
||||
},
|
||||
{
|
||||
"path": "../../reviewing/list"
|
||||
},
|
||||
{
|
||||
"path": "../detect-dep-types"
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user