@pnpm/deps.compliance.commands 1100.0.0 → 1101.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/audit/audit.js +19 -25
- package/lib/audit/fix.d.ts +1 -1
- package/lib/audit/fix.js +21 -15
- package/lib/audit/ignore.d.ts +1 -1
- package/lib/audit/ignore.js +26 -13
- package/package.json +23 -23
package/lib/audit/audit.js
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import { docsUrl, TABLE_OPTIONS } from '@pnpm/cli.utils';
|
|
2
2
|
import { types as allTypes } from '@pnpm/config.reader';
|
|
3
3
|
import { WANTED_LOCKFILE } from '@pnpm/constants';
|
|
4
|
-
import { audit } from '@pnpm/deps.compliance.audit';
|
|
4
|
+
import { audit, normalizeGhsaId } from '@pnpm/deps.compliance.audit';
|
|
5
5
|
import { PnpmError } from '@pnpm/error';
|
|
6
6
|
import { update } from '@pnpm/installing.commands';
|
|
7
7
|
import { readEnvLockfile, readWantedLockfile } from '@pnpm/lockfile.fs';
|
|
8
8
|
import { createGetAuthHeaderByURI } from '@pnpm/network.auth-header';
|
|
9
9
|
import { table } from '@zkochan/table';
|
|
10
10
|
import chalk, {} from 'chalk';
|
|
11
|
-
import {
|
|
11
|
+
import { pick, pickBy } from 'ramda';
|
|
12
12
|
import { renderHelp } from 'render-help';
|
|
13
13
|
import { fix } from './fix.js';
|
|
14
14
|
import { fixWithUpdate } from './fixWithUpdate.js';
|
|
15
15
|
import { ignore } from './ignore.js';
|
|
16
16
|
const AUDIT_LEVEL_NUMBER = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
info: 0,
|
|
18
|
+
low: 1,
|
|
19
|
+
moderate: 2,
|
|
20
|
+
high: 3,
|
|
21
|
+
critical: 4,
|
|
21
22
|
};
|
|
22
23
|
const AUDIT_COLOR = {
|
|
24
|
+
info: chalk.dim,
|
|
23
25
|
low: chalk.bold,
|
|
24
26
|
moderate: chalk.bold.yellow,
|
|
25
27
|
high: chalk.bold.red,
|
|
@@ -46,7 +48,7 @@ export function rcOptionsTypes() {
|
|
|
46
48
|
'production',
|
|
47
49
|
'registry',
|
|
48
50
|
], allTypes),
|
|
49
|
-
'audit-level': ['low', 'moderate', 'high', 'critical'],
|
|
51
|
+
'audit-level': ['info', 'low', 'moderate', 'high', 'critical'],
|
|
50
52
|
// For fix, use String instead of a list of allowed string values.
|
|
51
53
|
// Otherwise, an unexpected value will get coerced to true because of the Boolean type.
|
|
52
54
|
fix: [String, Boolean],
|
|
@@ -86,7 +88,7 @@ export function help() {
|
|
|
86
88
|
name: '--json',
|
|
87
89
|
},
|
|
88
90
|
{
|
|
89
|
-
description: 'Only print advisories with severity greater than or equal to one of the following: low|moderate|high|critical. Default: low',
|
|
91
|
+
description: 'Only print advisories with severity greater than or equal to one of the following: info|low|moderate|high|critical. Default: low',
|
|
90
92
|
name: '--audit-level <severity>',
|
|
91
93
|
},
|
|
92
94
|
{
|
|
@@ -108,11 +110,11 @@ export function help() {
|
|
|
108
110
|
name: '--ignore-registry-errors',
|
|
109
111
|
},
|
|
110
112
|
{
|
|
111
|
-
description: 'Ignore a vulnerability by
|
|
113
|
+
description: 'Ignore a vulnerability by its GitHub advisory ID (e.g. GHSA-xxxx-xxxx-xxxx)',
|
|
112
114
|
name: '--ignore <vulnerability>',
|
|
113
115
|
},
|
|
114
116
|
{
|
|
115
|
-
description: 'Ignore all
|
|
117
|
+
description: 'Ignore all vulnerabilities for which no fix exists',
|
|
116
118
|
name: '--ignore-unfixable',
|
|
117
119
|
},
|
|
118
120
|
],
|
|
@@ -153,7 +155,6 @@ export async function handler(opts) {
|
|
|
153
155
|
},
|
|
154
156
|
envLockfile,
|
|
155
157
|
include,
|
|
156
|
-
lockfileDir,
|
|
157
158
|
registry: opts.registries.default,
|
|
158
159
|
retry: {
|
|
159
160
|
factor: opts.fetchRetryFactor,
|
|
@@ -162,7 +163,6 @@ export async function handler(opts) {
|
|
|
162
163
|
retries: opts.fetchRetries,
|
|
163
164
|
},
|
|
164
165
|
timeout: opts.fetchTimeout,
|
|
165
|
-
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
168
|
catch (err) { // eslint-disable-line
|
|
@@ -244,6 +244,7 @@ ${newIgnores.join('\n')}`,
|
|
|
244
244
|
}
|
|
245
245
|
const vulnerabilities = auditReport.metadata.vulnerabilities;
|
|
246
246
|
const ignoredVulnerabilities = {
|
|
247
|
+
info: 0,
|
|
247
248
|
low: 0,
|
|
248
249
|
moderate: 0,
|
|
249
250
|
high: 0,
|
|
@@ -252,19 +253,12 @@ ${newIgnores.join('\n')}`,
|
|
|
252
253
|
const totalVulnerabilityCount = Object.values(vulnerabilities)
|
|
253
254
|
.reduce((sum, vulnerabilitiesCount) => sum + vulnerabilitiesCount, 0);
|
|
254
255
|
const ignoreGhsas = opts.auditConfig?.ignoreGhsas;
|
|
255
|
-
if (ignoreGhsas) {
|
|
256
|
+
if (ignoreGhsas?.length) {
|
|
257
|
+
// Compare GHSA ids after normalizing so stored entries with varying
|
|
258
|
+
// casing still match the canonical form on the advisory.
|
|
259
|
+
const ignoreSet = new Set(ignoreGhsas.map(normalizeGhsaId));
|
|
256
260
|
auditReport.advisories = pickBy(({ github_advisory_id: githubAdvisoryId, severity }) => {
|
|
257
|
-
if (!
|
|
258
|
-
return true;
|
|
259
|
-
}
|
|
260
|
-
ignoredVulnerabilities[severity] += 1;
|
|
261
|
-
return false;
|
|
262
|
-
}, auditReport.advisories);
|
|
263
|
-
}
|
|
264
|
-
const ignoreCves = opts.auditConfig?.ignoreCves;
|
|
265
|
-
if (ignoreCves) {
|
|
266
|
-
auditReport.advisories = pickBy(({ cves, severity }) => {
|
|
267
|
-
if (cves.length === 0 || difference(cves, ignoreCves).length > 0) {
|
|
261
|
+
if (!ignoreSet.has(normalizeGhsaId(githubAdvisoryId))) {
|
|
268
262
|
return true;
|
|
269
263
|
}
|
|
270
264
|
ignoredVulnerabilities[severity] += 1;
|
|
@@ -289,7 +283,7 @@ ${newIgnores.join('\n')}`,
|
|
|
289
283
|
[AUDIT_COLOR[advisory.severity](advisory.severity), chalk.bold(advisory.title)],
|
|
290
284
|
['Package', advisory.module_name],
|
|
291
285
|
['Vulnerable versions', advisory.vulnerable_versions],
|
|
292
|
-
['Patched versions', advisory.patched_versions],
|
|
286
|
+
['Patched versions', advisory.patched_versions ?? '(unknown)'],
|
|
293
287
|
[
|
|
294
288
|
'Paths',
|
|
295
289
|
(paths.length > MAX_PATHS_COUNT
|
package/lib/audit/fix.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AuditAdvisory, type AuditReport } from '@pnpm/deps.compliance.audit';
|
|
2
2
|
import type { AuditOptions } from './audit.js';
|
|
3
3
|
export interface FixResult {
|
|
4
4
|
vulnOverrides: Record<string, string>;
|
package/lib/audit/fix.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { writeSettings } from '@pnpm/config.writer';
|
|
2
|
-
import {
|
|
2
|
+
import { normalizeGhsaId } from '@pnpm/deps.compliance.audit';
|
|
3
3
|
import semver from 'semver';
|
|
4
4
|
export async function fix(auditReport, opts) {
|
|
5
|
-
const fixableAdvisories = getFixableAdvisories(Object.values(auditReport.advisories), opts.auditConfig?.
|
|
5
|
+
const fixableAdvisories = getFixableAdvisories(Object.values(auditReport.advisories), opts.auditConfig?.ignoreGhsas);
|
|
6
6
|
const vulnOverrides = createOverrides(fixableAdvisories);
|
|
7
7
|
if (Object.values(vulnOverrides).length === 0)
|
|
8
8
|
return { vulnOverrides, addedAgeExcludes: [] };
|
|
@@ -16,27 +16,33 @@ export async function fix(auditReport, opts) {
|
|
|
16
16
|
});
|
|
17
17
|
return { vulnOverrides, addedAgeExcludes };
|
|
18
18
|
}
|
|
19
|
-
function getFixableAdvisories(advisories,
|
|
20
|
-
if (
|
|
21
|
-
|
|
19
|
+
function getFixableAdvisories(advisories, ignoreGhsas) {
|
|
20
|
+
if (ignoreGhsas) {
|
|
21
|
+
// Normalize on both sides so ignore entries match regardless of casing.
|
|
22
|
+
const ignored = new Set(ignoreGhsas.map(normalizeGhsaId));
|
|
23
|
+
advisories = advisories.filter(({ github_advisory_id: ghsaId }) => !ghsaId || !ignored.has(normalizeGhsaId(ghsaId)));
|
|
22
24
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
// Only advisories with a known patched range can produce an override.
|
|
26
|
+
// patched_versions is undefined when the range couldn't be inferred from
|
|
27
|
+
// vulnerable_versions — no override is possible in that case.
|
|
28
|
+
return advisories.filter(({ patched_versions: patchedVersions }) => patchedVersions != null);
|
|
25
29
|
}
|
|
26
30
|
function createOverrides(advisories) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
advisory.patched_versions
|
|
30
|
-
|
|
31
|
+
const entries = [];
|
|
32
|
+
for (const advisory of advisories) {
|
|
33
|
+
if (!advisory.patched_versions)
|
|
34
|
+
continue;
|
|
35
|
+
entries.push([`${advisory.module_name}@${advisory.vulnerable_versions}`, advisory.patched_versions]);
|
|
36
|
+
}
|
|
37
|
+
return Object.fromEntries(entries);
|
|
31
38
|
}
|
|
32
39
|
export function createMinimumReleaseAgeExcludes(advisories) {
|
|
33
40
|
const excludes = new Set();
|
|
34
41
|
for (const advisory of advisories) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (advisory.vulnerable_versions === '>=0.0.0' || advisory.vulnerable_versions === '*')
|
|
42
|
+
const patchedVersions = advisory.patched_versions;
|
|
43
|
+
if (!patchedVersions)
|
|
38
44
|
continue;
|
|
39
|
-
const minVersion = semver.minVersion(
|
|
45
|
+
const minVersion = semver.minVersion(patchedVersions);
|
|
40
46
|
if (minVersion) {
|
|
41
47
|
excludes.add(`${advisory.module_name}@${minVersion.version}`);
|
|
42
48
|
}
|
package/lib/audit/ignore.d.ts
CHANGED
package/lib/audit/ignore.js
CHANGED
|
@@ -1,31 +1,44 @@
|
|
|
1
1
|
import { writeSettings } from '@pnpm/config.writer';
|
|
2
|
+
import { normalizeGhsaId } from '@pnpm/deps.compliance.audit';
|
|
3
|
+
import { PnpmError } from '@pnpm/error';
|
|
2
4
|
import { difference } from 'ramda';
|
|
3
5
|
export async function ignore(opts) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
// GHSA IDs are canonically uppercase; normalize on read/write so a stored
|
|
7
|
+
// "ghsa-..." or uppercase user input both match the derived id at filter
|
|
8
|
+
// time.
|
|
9
|
+
const currentGhsas = (opts?.auditConfig?.ignoreGhsas ?? []).map(normalizeGhsaId);
|
|
10
|
+
const currentUniqueGhsas = new Set(currentGhsas);
|
|
11
|
+
const advisoriesWithNoResolutions = filterAdvisoriesWithNoResolutions(Object.values(opts.auditReport.advisories));
|
|
7
12
|
if (opts.ignoreUnfixable) {
|
|
8
|
-
|
|
9
|
-
advisory.
|
|
10
|
-
|
|
13
|
+
for (const advisory of advisoriesWithNoResolutions) {
|
|
14
|
+
if (!advisory.github_advisory_id) {
|
|
15
|
+
throw new PnpmError('AUDIT_MISSING_GHSA', `Cannot ignore advisory ${advisory.id} (${advisory.module_name}): the registry did not provide a GHSA id or a resolvable url.`);
|
|
16
|
+
}
|
|
17
|
+
currentUniqueGhsas.add(normalizeGhsaId(advisory.github_advisory_id));
|
|
18
|
+
}
|
|
11
19
|
}
|
|
12
|
-
else {
|
|
13
|
-
opts.ignore
|
|
20
|
+
else if (opts.ignore) {
|
|
21
|
+
for (const ghsa of opts.ignore) {
|
|
22
|
+
currentUniqueGhsas.add(normalizeGhsaId(ghsa));
|
|
23
|
+
}
|
|
14
24
|
}
|
|
15
|
-
const
|
|
16
|
-
const
|
|
25
|
+
const newIgnoreGhsas = currentUniqueGhsas.size > 0 ? Array.from(currentUniqueGhsas) : undefined;
|
|
26
|
+
const diffGhsas = difference(newIgnoreGhsas ?? [], currentGhsas);
|
|
17
27
|
await writeSettings({
|
|
18
28
|
...opts,
|
|
19
29
|
updatedSettings: {
|
|
20
30
|
auditConfig: {
|
|
21
31
|
...opts.auditConfig,
|
|
22
|
-
|
|
32
|
+
ignoreGhsas: newIgnoreGhsas,
|
|
23
33
|
},
|
|
24
34
|
},
|
|
25
35
|
});
|
|
26
|
-
return [...
|
|
36
|
+
return [...diffGhsas];
|
|
27
37
|
}
|
|
38
|
+
// Advisories for which no override can be produced — patched_versions is
|
|
39
|
+
// undefined when pnpm couldn't infer a patched range from vulnerable_versions.
|
|
40
|
+
// That is the only "no fix available" signal the bulk endpoint provides.
|
|
28
41
|
function filterAdvisoriesWithNoResolutions(advisories) {
|
|
29
|
-
return advisories.filter(({ patched_versions: patchedVersions }) => patchedVersions
|
|
42
|
+
return advisories.filter(({ patched_versions: patchedVersions }) => patchedVersions == null);
|
|
30
43
|
}
|
|
31
44
|
//# sourceMappingURL=ignore.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/deps.compliance.commands",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1101.0.1",
|
|
4
4
|
"description": "pnpm commands for audit, licenses, and sbom",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -33,26 +33,26 @@
|
|
|
33
33
|
"ramda": "npm:@pnpm/ramda@0.28.1",
|
|
34
34
|
"render-help": "^2.0.0",
|
|
35
35
|
"semver": "^7.7.2",
|
|
36
|
-
"@pnpm/cli.command": "1100.0.0",
|
|
37
36
|
"@pnpm/cli.common-cli-options-help": "1100.0.0",
|
|
38
|
-
"@pnpm/cli.
|
|
39
|
-
"@pnpm/
|
|
37
|
+
"@pnpm/cli.utils": "1101.0.0",
|
|
38
|
+
"@pnpm/cli.command": "1100.0.0",
|
|
39
|
+
"@pnpm/cli.meta": "1100.0.1",
|
|
40
|
+
"@pnpm/config.reader": "1101.0.0",
|
|
40
41
|
"@pnpm/constants": "1100.0.0",
|
|
41
|
-
"@pnpm/
|
|
42
|
-
"@pnpm/deps.compliance.license-scanner": "1100.0.
|
|
43
|
-
"@pnpm/config.reader": "1100.0.0",
|
|
44
|
-
"@pnpm/deps.compliance.sbom": "1100.0.0",
|
|
42
|
+
"@pnpm/config.writer": "1100.0.1",
|
|
43
|
+
"@pnpm/deps.compliance.license-scanner": "1100.0.2",
|
|
45
44
|
"@pnpm/error": "1100.0.0",
|
|
46
|
-
"@pnpm/
|
|
47
|
-
"@pnpm/
|
|
48
|
-
"@pnpm/
|
|
49
|
-
"@pnpm/
|
|
50
|
-
"@pnpm/lockfile.
|
|
51
|
-
"@pnpm/
|
|
45
|
+
"@pnpm/lockfile.fs": "1100.0.1",
|
|
46
|
+
"@pnpm/deps.compliance.sbom": "1100.0.2",
|
|
47
|
+
"@pnpm/installing.commands": "1100.1.0",
|
|
48
|
+
"@pnpm/deps.compliance.audit": "1101.0.0",
|
|
49
|
+
"@pnpm/lockfile.utils": "1100.0.1",
|
|
50
|
+
"@pnpm/network.auth-header": "1100.0.1",
|
|
51
|
+
"@pnpm/lockfile.types": "1100.0.1",
|
|
52
52
|
"@pnpm/store.path": "1100.0.0",
|
|
53
|
-
"@pnpm/
|
|
54
|
-
"@pnpm/
|
|
55
|
-
"@pnpm/
|
|
53
|
+
"@pnpm/types": "1101.0.0",
|
|
54
|
+
"@pnpm/workspace.project-manifest-reader": "1100.0.2",
|
|
55
|
+
"@pnpm/lockfile.walker": "1100.0.1"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@pnpm/registry-mock": "6.0.0",
|
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
"nock": "13.3.4",
|
|
64
64
|
"read-yaml-file": "^3.0.0",
|
|
65
65
|
"tempy": "3.0.0",
|
|
66
|
-
"@pnpm/deps.compliance.commands": "
|
|
67
|
-
"@pnpm/
|
|
68
|
-
"@pnpm/
|
|
66
|
+
"@pnpm/deps.compliance.commands": "1101.0.1",
|
|
67
|
+
"@pnpm/pkg-manifest.reader": "1100.0.1",
|
|
68
|
+
"@pnpm/prepare": "1100.0.1",
|
|
69
|
+
"@pnpm/testing.mock-agent": "1100.0.1",
|
|
69
70
|
"@pnpm/testing.command-defaults": "1100.0.0",
|
|
70
|
-
"@pnpm/
|
|
71
|
-
"@pnpm/workspace.projects-filter": "1100.0.
|
|
72
|
-
"@pnpm/test-fixtures": "1100.0.0"
|
|
71
|
+
"@pnpm/test-fixtures": "1100.0.0",
|
|
72
|
+
"@pnpm/workspace.projects-filter": "1100.0.2"
|
|
73
73
|
},
|
|
74
74
|
"engines": {
|
|
75
75
|
"node": ">=22.13"
|