mirror of
https://github.com/pnpm/pnpm.git
synced 2026-07-26 07:27:20 -04:00
feat(plugin-commands-audit): show path info in audit output (#5917)
close #3073
This commit is contained in:
9
.changeset/eleven-emus-reply.md
Normal file
9
.changeset/eleven-emus-reply.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"@pnpm/reviewing.dependencies-hierarchy": minor
|
||||
"@pnpm/plugin-commands-audit": minor
|
||||
"@pnpm/audit": minor
|
||||
"@pnpm/list": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
Show dependency paths info in `pnpm audit` output [#3073](https://github.com/pnpm/pnpm/issues/3073)
|
||||
@@ -41,6 +41,7 @@
|
||||
"@pnpm/error": "workspace:*",
|
||||
"@pnpm/fetch": "workspace:*",
|
||||
"@pnpm/fetching-types": "workspace:*",
|
||||
"@pnpm/list": "workspace:*",
|
||||
"@pnpm/lockfile-types": "workspace:*",
|
||||
"@pnpm/lockfile-utils": "workspace:*",
|
||||
"@pnpm/lockfile-walker": "workspace:*",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import path from 'path'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { AgentOptions, fetchWithAgent, RetryTimeoutOptions } from '@pnpm/fetch'
|
||||
import { GetAuthHeader } from '@pnpm/fetching-types'
|
||||
@@ -5,6 +6,7 @@ import { Lockfile } from '@pnpm/lockfile-types'
|
||||
import { DependenciesField } from '@pnpm/types'
|
||||
import { lockfileToAuditTree } from './lockfileToAuditTree'
|
||||
import { AuditReport } from './types'
|
||||
import { searchForPackages, PackageNode } from '@pnpm/list'
|
||||
|
||||
export * from './types'
|
||||
|
||||
@@ -44,7 +46,12 @@ 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()}`)
|
||||
}
|
||||
return res.json() as Promise<AuditReport>
|
||||
const auditReport = await (res.json() as Promise<AuditReport>)
|
||||
return extendWithDependencyPaths(auditReport, {
|
||||
lockfile,
|
||||
lockfileDir: opts.lockfileDir,
|
||||
include: opts.include,
|
||||
})
|
||||
}
|
||||
|
||||
function getAuthHeaders (authHeaderValue: string | undefined) {
|
||||
@@ -55,6 +62,63 @@ function getAuthHeaders (authHeaderValue: string | undefined) {
|
||||
return headers
|
||||
}
|
||||
|
||||
async function extendWithDependencyPaths (auditReport: AuditReport, opts: {
|
||||
lockfile: Lockfile
|
||||
lockfileDir: string
|
||||
include?: { [dependenciesField in DependenciesField]: boolean }
|
||||
}): 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,
|
||||
}
|
||||
const _searchPackagePaths = searchPackagePaths.bind(null, searchOpts, projectDirs)
|
||||
for (const { findings, module_name: moduleName } of Object.values(advisories)) {
|
||||
for (const finding of findings) {
|
||||
finding.paths = await _searchPackagePaths(`${moduleName}@${finding.version}`)
|
||||
}
|
||||
}
|
||||
return auditReport
|
||||
}
|
||||
|
||||
async function searchPackagePaths (
|
||||
searchOpts: {
|
||||
lockfileDir: string
|
||||
depth: number
|
||||
include?: { [dependenciesField in DependenciesField]: boolean }
|
||||
},
|
||||
projectDirs: string[],
|
||||
pkg: string
|
||||
) {
|
||||
const pkgs = await searchForPackages([pkg], projectDirs, searchOpts)
|
||||
const paths: string[] = []
|
||||
|
||||
for (const pkg of pkgs) {
|
||||
_walker([
|
||||
...(pkg.optionalDependencies ?? []),
|
||||
...(pkg.dependencies ?? []),
|
||||
...(pkg.devDependencies ?? []),
|
||||
...(pkg.unsavedDependencies ?? []),
|
||||
], path.relative(searchOpts.lockfileDir, pkg.path) || '.')
|
||||
}
|
||||
return paths
|
||||
|
||||
function _walker (packages: PackageNode[], depPath: string) {
|
||||
for (const pkg of packages) {
|
||||
const nextDepPath = `${depPath}>${pkg.name}@${pkg.version}`
|
||||
if (pkg.dependencies?.length) {
|
||||
_walker(pkg.dependencies, nextDepPath)
|
||||
} else {
|
||||
paths.push(nextDepPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AuditEndpointNotExistsError extends PnpmError {
|
||||
constructor (endpoint: string) {
|
||||
const message = `The audit endpoint (at ${endpoint}) is doesn't exist.`
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
{
|
||||
"path": "../../pkg-manifest/read-project-manifest"
|
||||
},
|
||||
{
|
||||
"path": "../../reviewing/list"
|
||||
},
|
||||
{
|
||||
"path": "../lockfile-file"
|
||||
},
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"homepage": "https://github.com/pnpm/pnpm/blob/main/lockfile/plugin-commands-audit#readme",
|
||||
"devDependencies": {
|
||||
"@pnpm/plugin-commands-audit": "workspace:*",
|
||||
"@pnpm/plugin-commands-installation": "workspace:*",
|
||||
"@pnpm/test-fixtures": "workspace:*",
|
||||
"@types/ramda": "0.28.20",
|
||||
"@types/zkochan__table": "npm:@types/table@6.0.0",
|
||||
|
||||
@@ -221,6 +221,7 @@ ${JSON.stringify(newOverrides, null, 2)}`,
|
||||
['Package', advisory.module_name],
|
||||
['Vulnerable versions', advisory.vulnerable_versions],
|
||||
['Patched versions', advisory.patched_versions],
|
||||
['Paths', advisory.findings.map(({ paths }) => paths).flat().join('\n\n')],
|
||||
['More info', advisory.url],
|
||||
], TABLE_OPTIONS)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
import path from 'path'
|
||||
import { fixtures } from '@pnpm/test-fixtures'
|
||||
import { audit } from '@pnpm/plugin-commands-audit'
|
||||
import { install } from '@pnpm/plugin-commands-installation'
|
||||
import { AuditEndpointNotExistsError } from '@pnpm/audit'
|
||||
import nock from 'nock'
|
||||
import stripAnsi from 'strip-ansi'
|
||||
@@ -13,226 +14,281 @@ const registries = {
|
||||
const rawConfig = {
|
||||
registry: registries.default,
|
||||
}
|
||||
export const DEFAULT_OPTS = {
|
||||
argv: {
|
||||
original: [],
|
||||
},
|
||||
bail: true,
|
||||
bin: 'node_modules/.bin',
|
||||
ca: undefined,
|
||||
cacheDir: '../cache',
|
||||
cert: undefined,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
fetchRetries: 2,
|
||||
fetchRetryFactor: 90,
|
||||
fetchRetryMaxtimeout: 90,
|
||||
fetchRetryMintimeout: 10,
|
||||
filter: [] as string[],
|
||||
httpsProxy: undefined,
|
||||
include: {
|
||||
dependencies: true,
|
||||
devDependencies: true,
|
||||
optionalDependencies: true,
|
||||
},
|
||||
key: undefined,
|
||||
linkWorkspacePackages: true,
|
||||
localAddress: undefined,
|
||||
lock: false,
|
||||
lockStaleDuration: 90,
|
||||
networkConcurrency: 16,
|
||||
offline: false,
|
||||
pending: false,
|
||||
pnpmfile: './.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
proxy: undefined,
|
||||
rawConfig,
|
||||
rawLocalConfig: {},
|
||||
registries,
|
||||
// registry: REGISTRY,
|
||||
sort: true,
|
||||
storeDir: '../store',
|
||||
strictSsl: false,
|
||||
userAgent: 'pnpm',
|
||||
userConfig: {},
|
||||
useRunningStoreServer: false,
|
||||
useStoreServer: false,
|
||||
workspaceConcurrency: 4,
|
||||
}
|
||||
|
||||
test('audit', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.ALL_VULN_RESP)
|
||||
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
describe('plugin-commands-audit', () => {
|
||||
beforeAll(async () => {
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
frozenLockfile: true,
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
})
|
||||
})
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stripAnsi(output)).toMatchSnapshot()
|
||||
})
|
||||
test('audit', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.ALL_VULN_RESP)
|
||||
|
||||
test('audit --dev', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.DEV_VULN_ONLY_RESP)
|
||||
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
dev: true,
|
||||
production: false,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
})
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stripAnsi(output)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stripAnsi(output)).toMatchSnapshot()
|
||||
})
|
||||
test('audit --dev', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.DEV_VULN_ONLY_RESP)
|
||||
|
||||
test('audit --audit-level', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.ALL_VULN_RESP)
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
dev: true,
|
||||
production: false,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
})
|
||||
|
||||
const { output, exitCode } = await audit.handler({
|
||||
auditLevel: 'moderate',
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stripAnsi(output)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stripAnsi(output)).toMatchSnapshot()
|
||||
})
|
||||
test('audit --audit-level', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.ALL_VULN_RESP)
|
||||
|
||||
test('audit: no vulnerabilities', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.NO_VULN_RESP)
|
||||
const { output, exitCode } = await audit.handler({
|
||||
auditLevel: 'moderate',
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
})
|
||||
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-outdated-deps'),
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stripAnsi(output)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
expect(stripAnsi(output)).toBe('No known vulnerabilities found\n')
|
||||
expect(exitCode).toBe(0)
|
||||
})
|
||||
test('audit: no vulnerabilities', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.NO_VULN_RESP)
|
||||
|
||||
test('audit --json', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.ALL_VULN_RESP)
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-outdated-deps'),
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
})
|
||||
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
json: true,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
expect(stripAnsi(output)).toBe('No known vulnerabilities found\n')
|
||||
expect(exitCode).toBe(0)
|
||||
})
|
||||
|
||||
const json = JSON.parse(output)
|
||||
expect(json.metadata).toBeTruthy()
|
||||
expect(exitCode).toBe(1)
|
||||
})
|
||||
test('audit --json', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.ALL_VULN_RESP)
|
||||
|
||||
test.skip('audit does not exit with code 1 if the found vulnerabilities are having lower severity then what we asked for', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.DEV_VULN_ONLY_RESP)
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
json: true,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
})
|
||||
|
||||
const { output, exitCode } = await audit.handler({
|
||||
auditLevel: 'high',
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
dev: true,
|
||||
registries,
|
||||
const json = JSON.parse(output)
|
||||
expect(json.metadata).toBeTruthy()
|
||||
expect(exitCode).toBe(1)
|
||||
})
|
||||
|
||||
expect(exitCode).toBe(0)
|
||||
expect(stripAnsi(output)).toBe(`1 vulnerabilities found
|
||||
Severity: 1 moderate`)
|
||||
})
|
||||
test.skip('audit does not exit with code 1 if the found vulnerabilities are having lower severity then what we asked for', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.DEV_VULN_ONLY_RESP)
|
||||
|
||||
test('audit does not exit with code 1 if the registry responds with a non-200 response and ignoreRegistryErrors is used', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(500, { message: 'Something bad happened' })
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
dev: true,
|
||||
fetchRetries: 0,
|
||||
ignoreRegistryErrors: true,
|
||||
production: false,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
const { output, exitCode } = await audit.handler({
|
||||
auditLevel: 'high',
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
dev: true,
|
||||
registries,
|
||||
})
|
||||
|
||||
expect(exitCode).toBe(0)
|
||||
expect(stripAnsi(output)).toBe(`1 vulnerabilities found
|
||||
Severity: 1 moderate`)
|
||||
})
|
||||
|
||||
expect(exitCode).toBe(0)
|
||||
expect(stripAnsi(output)).toBe(`The audit endpoint (at ${registries.default}-/npm/v1/security/audits) responded with 500: {"message":"Something bad happened"}`)
|
||||
})
|
||||
test('audit does not exit with code 1 if the registry responds with a non-200 response and ignoreRegistryErrors is used', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(500, { message: 'Something bad happened' })
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
dev: true,
|
||||
fetchRetries: 0,
|
||||
ignoreRegistryErrors: true,
|
||||
production: false,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
})
|
||||
|
||||
test('audit sends authToken', async () => {
|
||||
nock(registries.default, {
|
||||
reqheaders: { authorization: 'Bearer 123' },
|
||||
})
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.NO_VULN_RESP)
|
||||
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-outdated-deps'),
|
||||
userConfig: {},
|
||||
rawConfig: {
|
||||
registry: registries.default,
|
||||
[`${registries.default.replace(/^https?:/, '')}:_authToken`]: '123',
|
||||
},
|
||||
registries,
|
||||
expect(exitCode).toBe(0)
|
||||
expect(stripAnsi(output)).toBe(`The audit endpoint (at ${registries.default}-/npm/v1/security/audits) responded with 500: {"message":"Something bad happened"}`)
|
||||
})
|
||||
|
||||
expect(stripAnsi(output)).toBe('No known vulnerabilities found\n')
|
||||
expect(exitCode).toBe(0)
|
||||
})
|
||||
test('audit sends authToken', async () => {
|
||||
nock(registries.default, {
|
||||
reqheaders: { authorization: 'Bearer 123' },
|
||||
})
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.NO_VULN_RESP)
|
||||
|
||||
test('audit endpoint does not exist', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(404, {})
|
||||
const { output, exitCode } = await audit.handler({
|
||||
dir: f.find('has-outdated-deps'),
|
||||
userConfig: {},
|
||||
rawConfig: {
|
||||
registry: registries.default,
|
||||
[`${registries.default.replace(/^https?:/, '')}:_authToken`]: '123',
|
||||
},
|
||||
registries,
|
||||
})
|
||||
|
||||
await expect(audit.handler({
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
dev: true,
|
||||
fetchRetries: 0,
|
||||
ignoreRegistryErrors: false,
|
||||
production: false,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
})).rejects.toThrow(AuditEndpointNotExistsError)
|
||||
})
|
||||
expect(stripAnsi(output)).toBe('No known vulnerabilities found\n')
|
||||
expect(exitCode).toBe(0)
|
||||
})
|
||||
|
||||
test('audit: CVEs in ignoreCves do not show up', async () => {
|
||||
const tmp = f.prepare('has-vulnerabilities')
|
||||
test('audit endpoint does not exist', async () => {
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(404, {})
|
||||
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.ALL_VULN_RESP)
|
||||
await expect(audit.handler({
|
||||
dir: f.find('has-vulnerabilities'),
|
||||
dev: true,
|
||||
fetchRetries: 0,
|
||||
ignoreRegistryErrors: false,
|
||||
production: false,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
})).rejects.toThrow(AuditEndpointNotExistsError)
|
||||
})
|
||||
|
||||
const { exitCode, output } = await audit.handler({
|
||||
auditLevel: 'moderate',
|
||||
dir: tmp,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
rootProjectManifest: {
|
||||
pnpm: {
|
||||
auditConfig: {
|
||||
ignoreCves: [
|
||||
'CVE-2019-10742',
|
||||
'CVE-2020-28168',
|
||||
'CVE-2021-3749',
|
||||
'CVE-2020-7598',
|
||||
],
|
||||
test('audit: CVEs in ignoreCves do not show up', async () => {
|
||||
const tmp = f.prepare('has-vulnerabilities')
|
||||
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.ALL_VULN_RESP)
|
||||
|
||||
const { exitCode, output } = await audit.handler({
|
||||
auditLevel: 'moderate',
|
||||
dir: tmp,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
rootProjectManifest: {
|
||||
pnpm: {
|
||||
auditConfig: {
|
||||
ignoreCves: [
|
||||
'CVE-2019-10742',
|
||||
'CVE-2020-28168',
|
||||
'CVE-2021-3749',
|
||||
'CVE-2020-7598',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stripAnsi(output)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stripAnsi(output)).toMatchSnapshot()
|
||||
})
|
||||
test('audit: CVEs in ignoreCves do not show up when JSON output is used', async () => {
|
||||
const tmp = f.prepare('has-vulnerabilities')
|
||||
|
||||
test('audit: CVEs in ignoreCves do not show up when JSON output is used', async () => {
|
||||
const tmp = f.prepare('has-vulnerabilities')
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.ALL_VULN_RESP)
|
||||
|
||||
nock(registries.default)
|
||||
.post('/-/npm/v1/security/audits')
|
||||
.reply(200, responses.ALL_VULN_RESP)
|
||||
|
||||
const { exitCode, output } = await audit.handler({
|
||||
auditLevel: 'moderate',
|
||||
dir: tmp,
|
||||
json: true,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
rootProjectManifest: {
|
||||
pnpm: {
|
||||
auditConfig: {
|
||||
ignoreCves: [
|
||||
'CVE-2019-10742',
|
||||
'CVE-2020-28168',
|
||||
'CVE-2021-3749',
|
||||
'CVE-2020-7598',
|
||||
],
|
||||
const { exitCode, output } = await audit.handler({
|
||||
auditLevel: 'moderate',
|
||||
dir: tmp,
|
||||
json: true,
|
||||
userConfig: {},
|
||||
rawConfig,
|
||||
registries,
|
||||
rootProjectManifest: {
|
||||
pnpm: {
|
||||
auditConfig: {
|
||||
ignoreCves: [
|
||||
'CVE-2019-10742',
|
||||
'CVE-2020-28168',
|
||||
'CVE-2021-3749',
|
||||
'CVE-2020-7598',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stripAnsi(output)).toMatchSnapshot()
|
||||
expect(exitCode).toBe(1)
|
||||
expect(stripAnsi(output)).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
{
|
||||
"path": "../../packages/types"
|
||||
},
|
||||
{
|
||||
"path": "../../pkg-manager/plugin-commands-installation"
|
||||
},
|
||||
{
|
||||
"path": "../../pkg-manifest/read-project-manifest"
|
||||
},
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -1652,6 +1652,9 @@ importers:
|
||||
'@pnpm/fetching-types':
|
||||
specifier: workspace:*
|
||||
version: link:../../network/fetching-types
|
||||
'@pnpm/list':
|
||||
specifier: workspace:*
|
||||
version: link:../../reviewing/list
|
||||
'@pnpm/lockfile-types':
|
||||
specifier: workspace:*
|
||||
version: link:../lockfile-types
|
||||
@@ -2015,6 +2018,9 @@ importers:
|
||||
'@pnpm/plugin-commands-audit':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
'@pnpm/plugin-commands-installation':
|
||||
specifier: workspace:*
|
||||
version: link:../../pkg-manager/plugin-commands-installation
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
|
||||
@@ -7,6 +7,8 @@ import { renderParseable } from './renderParseable'
|
||||
import { renderTree } from './renderTree'
|
||||
import { PackageDependencyHierarchy } from './types'
|
||||
|
||||
export { PackageNode } from '@pnpm/reviewing.dependencies-hierarchy'
|
||||
|
||||
const DEFAULTS = {
|
||||
alwaysPrintRootPackage: true,
|
||||
depth: 0,
|
||||
@@ -16,6 +18,41 @@ const DEFAULTS = {
|
||||
showExtraneous: true,
|
||||
}
|
||||
|
||||
export async function searchForPackages (
|
||||
packages: string[],
|
||||
projectPaths: string[],
|
||||
opts: {
|
||||
depth: number
|
||||
lockfileDir: string
|
||||
include?: { [dependenciesField in DependenciesField]: boolean }
|
||||
onlyProjects?: boolean
|
||||
registries?: Registries
|
||||
}
|
||||
) {
|
||||
const search = createPackagesSearcher(packages)
|
||||
|
||||
return Promise.all(
|
||||
Object.entries(await buildDependenciesHierarchy(projectPaths, {
|
||||
depth: opts.depth,
|
||||
include: opts.include,
|
||||
lockfileDir: opts.lockfileDir,
|
||||
onlyProjects: opts.onlyProjects,
|
||||
registries: opts.registries,
|
||||
search,
|
||||
}))
|
||||
.map(async ([projectPath, buildDependenciesHierarchy]) => {
|
||||
const entryPkg = await readProjectManifestOnly(projectPath)
|
||||
return {
|
||||
name: entryPkg.name,
|
||||
version: entryPkg.version,
|
||||
|
||||
path: projectPath,
|
||||
...buildDependenciesHierarchy,
|
||||
} as PackageDependencyHierarchy
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export async function listForPackages (
|
||||
packages: string[],
|
||||
projectPaths: string[],
|
||||
@@ -32,28 +69,7 @@ export async function listForPackages (
|
||||
) {
|
||||
const opts = { ...DEFAULTS, ...maybeOpts }
|
||||
|
||||
const search = createPackagesSearcher(packages)
|
||||
|
||||
const pkgs = await Promise.all(
|
||||
Object.entries(await buildDependenciesHierarchy(projectPaths, {
|
||||
depth: opts.depth,
|
||||
include: maybeOpts?.include,
|
||||
lockfileDir: maybeOpts?.lockfileDir,
|
||||
onlyProjects: maybeOpts?.onlyProjects,
|
||||
registries: opts.registries,
|
||||
search,
|
||||
}))
|
||||
.map(async ([projectPath, buildDependenciesHierarchy]) => {
|
||||
const entryPkg = await readProjectManifestOnly(projectPath)
|
||||
return {
|
||||
name: entryPkg.name,
|
||||
version: entryPkg.version,
|
||||
|
||||
path: projectPath,
|
||||
...buildDependenciesHierarchy,
|
||||
} as PackageDependencyHierarchy
|
||||
})
|
||||
)
|
||||
const pkgs = await searchForPackages(packages, projectPaths, opts)
|
||||
|
||||
const print = getPrinter(opts.reportAs)
|
||||
return print(pkgs, {
|
||||
|
||||
Reference in New Issue
Block a user