@pnpm/deps.compliance.sbom 1100.4.0 → 1101.0.0
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/CHANGELOG.md +53 -0
- package/lib/collectComponents.d.ts +4 -3
- package/lib/collectComponents.js +42 -6
- package/lib/purl.js +2 -2
- package/package.json +17 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# @pnpm/deps.compliance.sbom
|
|
2
2
|
|
|
3
|
+
## 1101.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- The three registry lookups are now named for what they are keyed by, so that none of them is called `registries` — a name the `registries` setting itself has taken:
|
|
8
|
+
|
|
9
|
+
| before | after |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `Config.registries` | `Config.registriesByScope` |
|
|
12
|
+
| `Config.namedRegistries` | `Config.registriesByPrefix` |
|
|
13
|
+
| `Config.registryOptions` | `Config.registryOptionsByUrl` |
|
|
14
|
+
|
|
15
|
+
The same rename applies to the `RegistryContext` fields, the `Registries` and `NamedRegistries` types (now `RegistriesByScope` and `RegistriesByPrefix`), `normalizeRegistries` / `normalizeNamedRegistries` (now `normalizeRegistriesByScope` / `normalizeRegistriesByPrefix`), and the `BUILTIN_NAMED_REGISTRIES` constant (now `BUILTIN_REGISTRIES_BY_PREFIX`).
|
|
16
|
+
|
|
17
|
+
This is an internal rename: no setting, error code, lockfile field, or `.pnpmfile.cjs` hook field changes. A `preResolution` hook still reads `ctx.registries`, which is the name pacquet passes as well. The `registries` and `namedRegistries` settings are read under the names users write them.
|
|
18
|
+
|
|
19
|
+
The pnpr resolve request sends `registriesByPrefix` where it sent `namedRegistries`. A pnpr server and its clients must be on matching versions, which is already the case for an experimental server.
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies:
|
|
24
|
+
- @pnpm/config.normalize-registries@1101.0.0
|
|
25
|
+
- @pnpm/config.package-is-installable@1100.1.4
|
|
26
|
+
- @pnpm/constants@1102.0.0
|
|
27
|
+
- @pnpm/error@1100.1.3
|
|
28
|
+
- @pnpm/lockfile.detect-dep-types@1100.0.20
|
|
29
|
+
- @pnpm/lockfile.types@1100.0.20
|
|
30
|
+
- @pnpm/lockfile.utils@1102.0.0
|
|
31
|
+
- @pnpm/lockfile.walker@1100.0.20
|
|
32
|
+
- @pnpm/pkg-manifest.reader@1100.0.17
|
|
33
|
+
- @pnpm/resolving.resolver-base@1101.1.1
|
|
34
|
+
- @pnpm/store.index@1100.2.5
|
|
35
|
+
- @pnpm/store.pkg-finder@1100.0.30
|
|
36
|
+
- @pnpm/types@1102.0.0
|
|
37
|
+
|
|
38
|
+
## 1100.4.1
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- An `integrity` recorded on a git dependency's resolution (`resolution: {type: git, repo, commit, integrity: sha512-…}`) is no longer treated as a checksum. pnpm never verifies a git checkout against such a hash — the commit pins the content — so it is now dropped when the lockfile is rewritten, and `pnpm sbom` no longer republishes it as a CycloneDX/SPDX checksum. Lockfiles carrying one also load again instead of failing with `ERR_PNPM_BROKEN_LOCKFILE` [#13042](https://github.com/pnpm/pnpm/issues/13042).
|
|
43
|
+
|
|
44
|
+
`pnpm sbom` now also publishes the checksum of a `type: binary` runtime archive, which pnpm does verify.
|
|
45
|
+
|
|
46
|
+
- `pnpm sbom` no longer emits components for optional platform-specific dependencies that cannot be installed on the current platform (for example, the native `@rolldown/binding-*` variants for other operating systems). Such packages are present in the lockfile but are never downloaded, so their license (and other metadata) could not be resolved and they appeared in the SBOM without one. `pnpm sbom --lockfile-only` still describes the whole lockfile graph, which is platform-independent by design.
|
|
47
|
+
|
|
48
|
+
- Updated dependencies:
|
|
49
|
+
- @pnpm/config.package-is-installable@1100.1.3
|
|
50
|
+
- @pnpm/error@1100.1.2
|
|
51
|
+
- @pnpm/lockfile.utils@1101.1.0
|
|
52
|
+
- @pnpm/pkg-manifest.reader@1100.0.16
|
|
53
|
+
- @pnpm/store.index@1100.2.4
|
|
54
|
+
- @pnpm/store.pkg-finder@1100.0.29
|
|
55
|
+
|
|
3
56
|
## 1100.4.0
|
|
4
57
|
|
|
5
58
|
### Minor Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LockfileObject } from '@pnpm/lockfile.types';
|
|
2
2
|
import type { Resolution } from '@pnpm/resolving.resolver-base';
|
|
3
|
-
import type { DependenciesField, ProjectId,
|
|
3
|
+
import type { DependenciesField, ProjectId, RegistriesByScope, SupportedArchitectures } from '@pnpm/types';
|
|
4
4
|
import type { SbomComponentType, SbomResult } from './types.js';
|
|
5
5
|
export interface WorkspacePackageInfo {
|
|
6
6
|
name: string;
|
|
@@ -23,10 +23,11 @@ export interface CollectSbomComponentsOptions {
|
|
|
23
23
|
include?: {
|
|
24
24
|
[dependenciesField in DependenciesField]: boolean;
|
|
25
25
|
};
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
registriesByScope: RegistriesByScope;
|
|
27
|
+
registriesByPrefix?: Record<string, string>;
|
|
28
28
|
lockfileDir: string;
|
|
29
29
|
includedImporterIds?: ProjectId[];
|
|
30
|
+
supportedArchitectures?: SupportedArchitectures;
|
|
30
31
|
lockfileOnly?: boolean;
|
|
31
32
|
storeDir?: string;
|
|
32
33
|
virtualStoreDirMaxLength?: number;
|
package/lib/collectComponents.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { normalizeRegistriesByPrefix } from '@pnpm/config.normalize-registries';
|
|
3
|
+
import { checkPackageInstallability } from '@pnpm/config.package-is-installable';
|
|
3
4
|
import { PnpmError } from '@pnpm/error';
|
|
4
5
|
import { DepType, detectDepTypes } from '@pnpm/lockfile.detect-dep-types';
|
|
5
6
|
import { nameVerFromPkgSnapshot, pkgSnapshotToResolution } from '@pnpm/lockfile.utils';
|
|
@@ -132,6 +133,23 @@ async function walkStep(step, parentPurl, depTypes, componentsMap, relationships
|
|
|
132
133
|
const { name, version, nonSemverVersion, registryName } = nameVerFromPkgSnapshot(depPath, pkgSnapshot);
|
|
133
134
|
if (!name || !version)
|
|
134
135
|
return;
|
|
136
|
+
// An optional dependency for another platform is in the lockfile but was
|
|
137
|
+
// never fetched, so the store holds no metadata to describe it with.
|
|
138
|
+
// `checkPackageInstallability`, not `packageIsInstallable`: the latter
|
|
139
|
+
// reports every skip through the install loggers, which reading a
|
|
140
|
+
// lockfile must not do.
|
|
141
|
+
if (!opts.lockfileOnly && pkgSnapshot.optional === true && checkPackageInstallability(pkgSnapshot.id ?? depPath, {
|
|
142
|
+
name,
|
|
143
|
+
version,
|
|
144
|
+
cpu: pkgSnapshot.cpu,
|
|
145
|
+
os: pkgSnapshot.os,
|
|
146
|
+
libc: pkgSnapshot.libc,
|
|
147
|
+
}, {
|
|
148
|
+
optional: true,
|
|
149
|
+
supportedArchitectures: opts.supportedArchitectures,
|
|
150
|
+
}) != null) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
135
153
|
// Resolve the alias before the purl is built. An unknown alias would
|
|
136
154
|
// otherwise yield an unqualified purl that collides with the same
|
|
137
155
|
// package from the default registry, and the `componentsMap.has(purl)`
|
|
@@ -140,9 +158,9 @@ async function walkStep(step, parentPurl, depTypes, componentsMap, relationships
|
|
|
140
158
|
// artifact from a compliance document.
|
|
141
159
|
const registryUrl = registryName == null
|
|
142
160
|
? undefined
|
|
143
|
-
:
|
|
161
|
+
: normalizeRegistriesByPrefix(opts.registriesByPrefix)[registryName];
|
|
144
162
|
if (registryName != null && registryUrl == null) {
|
|
145
|
-
throw new PnpmError('MISSING_NAMED_REGISTRY', `Cannot describe package "${depPath}": it was resolved from the named registry '${registryName}:', which is not present in the
|
|
163
|
+
throw new PnpmError('MISSING_NAMED_REGISTRY', `Cannot describe package "${depPath}": it was resolved from the named registry '${registryName}:', which is not present in the registriesByPrefix setting.`, { hint: `Add '${registryName}' to the registriesByPrefix setting in pnpm-workspace.yaml.` });
|
|
146
164
|
}
|
|
147
165
|
const purl = buildPurl({
|
|
148
166
|
name,
|
|
@@ -153,12 +171,12 @@ async function walkStep(step, parentPurl, depTypes, componentsMap, relationships
|
|
|
153
171
|
relationships.push({ from: parentPurl, to: purl });
|
|
154
172
|
if (componentsMap.has(purl))
|
|
155
173
|
return;
|
|
156
|
-
const integrity = pkgSnapshot.resolution
|
|
157
|
-
const resolution = pkgSnapshotToResolution(depPath, pkgSnapshot, {
|
|
174
|
+
const integrity = verifiedIntegrity(pkgSnapshot.resolution);
|
|
175
|
+
const resolution = pkgSnapshotToResolution(depPath, pkgSnapshot, { registriesByScope: opts.registriesByScope, registriesByPrefix: opts.registriesByPrefix });
|
|
158
176
|
const tarballUrl = resolution.tarball ?? gitDownloadUrl(resolution);
|
|
159
177
|
let metadata = {};
|
|
160
178
|
if (metadataOpts) {
|
|
161
|
-
metadata = await getPkgMetadata(depPath, pkgSnapshot, {
|
|
179
|
+
metadata = await getPkgMetadata(depPath, pkgSnapshot, { registriesByScope: opts.registriesByScope, registriesByPrefix: opts.registriesByPrefix }, metadataOpts);
|
|
162
180
|
}
|
|
163
181
|
const component = {
|
|
164
182
|
name,
|
|
@@ -225,4 +243,22 @@ export function resolveWorkspaceDeps(lockfile, importerIds, include) {
|
|
|
225
243
|
}
|
|
226
244
|
return { links, additionalImporterIds };
|
|
227
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* The resolution's integrity, but only where pnpm verifies the downloaded
|
|
248
|
+
* bytes against it: the tarball/registry hash and a `type: binary` runtime
|
|
249
|
+
* archive's. Nothing checks a git checkout against a hash, so an `integrity`
|
|
250
|
+
* recorded on one is not a checksum and is never published as one.
|
|
251
|
+
*
|
|
252
|
+
* Read from an untyped lockfile, so the shape is probed rather than trusted:
|
|
253
|
+
* reading a checksum out of a malformed resolution yields nothing instead of
|
|
254
|
+
* throwing, and a non-string integrity never reaches `ssri.parse` downstream.
|
|
255
|
+
* (A malformed resolution still fails the walk further along, in
|
|
256
|
+
* `pkgSnapshotToResolution` — this only keeps the checksum lookup total.)
|
|
257
|
+
*/
|
|
258
|
+
function verifiedIntegrity(resolution) {
|
|
259
|
+
const { type, integrity } = (resolution ?? {});
|
|
260
|
+
if (typeof integrity !== 'string')
|
|
261
|
+
return undefined;
|
|
262
|
+
return (type === undefined || type === 'binary') ? integrity : undefined;
|
|
263
|
+
}
|
|
228
264
|
//# sourceMappingURL=collectComponents.js.map
|
package/lib/purl.js
CHANGED
|
@@ -28,7 +28,7 @@ export function buildPurl(opts) {
|
|
|
28
28
|
* Reduce a registry URL to the parts that identify the registry, dropping
|
|
29
29
|
* anything that could carry a secret.
|
|
30
30
|
*
|
|
31
|
-
* An SBOM is meant to be published, and a `
|
|
31
|
+
* An SBOM is meant to be published, and a `registriesByPrefix` entry may
|
|
32
32
|
* legitimately embed credentials — as userinfo
|
|
33
33
|
* (`https://user:token@npm.example.com/`) or in a query string
|
|
34
34
|
* (`?api_key=…`). Origin and path are kept because two registries can
|
|
@@ -37,7 +37,7 @@ export function buildPurl(opts) {
|
|
|
37
37
|
* qualifier exists to prevent.
|
|
38
38
|
*
|
|
39
39
|
* A URL that doesn't parse is returned unchanged: it has no userinfo to
|
|
40
|
-
* strip, and `
|
|
40
|
+
* strip, and `registriesByPrefix` values are validated as http(s) URLs when
|
|
41
41
|
* the resolver is constructed, so this is unreachable in practice.
|
|
42
42
|
*/
|
|
43
43
|
function sanitizeRegistryUrl(url) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/deps.compliance.sbom",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1101.0.0",
|
|
4
4
|
"description": "Generate SBOM from pnpm lockfile",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -30,20 +30,21 @@
|
|
|
30
30
|
"!*.map"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@cyclonedx/cyclonedx-library": "10.
|
|
34
|
-
"@pnpm/config.normalize-registries": "
|
|
35
|
-
"@pnpm/
|
|
33
|
+
"@cyclonedx/cyclonedx-library": "10.2.0",
|
|
34
|
+
"@pnpm/config.normalize-registries": "1101.0.0",
|
|
35
|
+
"@pnpm/config.package-is-installable": "1100.1.4",
|
|
36
|
+
"@pnpm/constants": "1102.0.0",
|
|
36
37
|
"@pnpm/deps.compliance.license-resolver": "1100.0.1",
|
|
37
|
-
"@pnpm/error": "1100.1.
|
|
38
|
-
"@pnpm/lockfile.detect-dep-types": "1100.0.
|
|
39
|
-
"@pnpm/lockfile.types": "1100.0.
|
|
40
|
-
"@pnpm/lockfile.utils": "
|
|
41
|
-
"@pnpm/lockfile.walker": "1100.0.
|
|
42
|
-
"@pnpm/pkg-manifest.reader": "1100.0.
|
|
43
|
-
"@pnpm/resolving.resolver-base": "1101.1.
|
|
44
|
-
"@pnpm/store.index": "1100.2.
|
|
45
|
-
"@pnpm/store.pkg-finder": "1100.0.
|
|
46
|
-
"@pnpm/types": "
|
|
38
|
+
"@pnpm/error": "1100.1.3",
|
|
39
|
+
"@pnpm/lockfile.detect-dep-types": "1100.0.20",
|
|
40
|
+
"@pnpm/lockfile.types": "1100.0.20",
|
|
41
|
+
"@pnpm/lockfile.utils": "1102.0.0",
|
|
42
|
+
"@pnpm/lockfile.walker": "1100.0.20",
|
|
43
|
+
"@pnpm/pkg-manifest.reader": "1100.0.17",
|
|
44
|
+
"@pnpm/resolving.resolver-base": "1101.1.1",
|
|
45
|
+
"@pnpm/store.index": "1100.2.5",
|
|
46
|
+
"@pnpm/store.pkg-finder": "1100.0.30",
|
|
47
|
+
"@pnpm/types": "1102.0.0",
|
|
47
48
|
"p-limit": "^7.3.1",
|
|
48
49
|
"ssri": "13.0.1"
|
|
49
50
|
},
|
|
@@ -52,9 +53,9 @@
|
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"@jest/globals": "30.4.1",
|
|
55
|
-
"@pnpm/deps.compliance.sbom": "
|
|
56
|
+
"@pnpm/deps.compliance.sbom": "1101.0.0",
|
|
56
57
|
"@pnpm/logger": "1100.0.0",
|
|
57
|
-
"@pnpm/store.cafs": "1100.
|
|
58
|
+
"@pnpm/store.cafs": "1100.2.0",
|
|
58
59
|
"@types/ssri": "^7.1.5"
|
|
59
60
|
},
|
|
60
61
|
"engines": {
|