@pnpm/deps.compliance.sbom 1100.4.0 → 1100.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @pnpm/deps.compliance.sbom
2
2
 
3
+ ## 1100.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 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).
8
+
9
+ `pnpm sbom` now also publishes the checksum of a `type: binary` runtime archive, which pnpm does verify.
10
+
11
+ - `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.
12
+
13
+ - Updated dependencies:
14
+ - @pnpm/config.package-is-installable@1100.1.3
15
+ - @pnpm/error@1100.1.2
16
+ - @pnpm/lockfile.utils@1101.1.0
17
+ - @pnpm/pkg-manifest.reader@1100.0.16
18
+ - @pnpm/store.index@1100.2.4
19
+ - @pnpm/store.pkg-finder@1100.0.29
20
+
3
21
  ## 1100.4.0
4
22
 
5
23
  ### 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, Registries } from '@pnpm/types';
3
+ import type { DependenciesField, ProjectId, Registries, SupportedArchitectures } from '@pnpm/types';
4
4
  import type { SbomComponentType, SbomResult } from './types.js';
5
5
  export interface WorkspacePackageInfo {
6
6
  name: string;
@@ -27,6 +27,7 @@ export interface CollectSbomComponentsOptions {
27
27
  namedRegistries?: 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;
@@ -1,5 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { normalizeNamedRegistries } 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)`
@@ -153,7 +171,7 @@ 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.integrity;
174
+ const integrity = verifiedIntegrity(pkgSnapshot.resolution);
157
175
  const resolution = pkgSnapshotToResolution(depPath, pkgSnapshot, { registries: opts.registries, namedRegistries: opts.namedRegistries });
158
176
  const tarballUrl = resolution.tarball ?? gitDownloadUrl(resolution);
159
177
  let metadata = {};
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/deps.compliance.sbom",
3
- "version": "1100.4.0",
3
+ "version": "1100.4.1",
4
4
  "description": "Generate SBOM from pnpm lockfile",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -30,19 +30,20 @@
30
30
  "!*.map"
31
31
  ],
32
32
  "dependencies": {
33
- "@cyclonedx/cyclonedx-library": "10.1.0",
33
+ "@cyclonedx/cyclonedx-library": "10.1.1",
34
34
  "@pnpm/config.normalize-registries": "1100.1.0",
35
+ "@pnpm/config.package-is-installable": "1100.1.3",
35
36
  "@pnpm/constants": "1101.0.0",
36
37
  "@pnpm/deps.compliance.license-resolver": "1100.0.1",
37
- "@pnpm/error": "1100.1.1",
38
+ "@pnpm/error": "1100.1.2",
38
39
  "@pnpm/lockfile.detect-dep-types": "1100.0.19",
39
40
  "@pnpm/lockfile.types": "1100.0.19",
40
- "@pnpm/lockfile.utils": "1101.0.0",
41
+ "@pnpm/lockfile.utils": "1101.1.0",
41
42
  "@pnpm/lockfile.walker": "1100.0.19",
42
- "@pnpm/pkg-manifest.reader": "1100.0.15",
43
+ "@pnpm/pkg-manifest.reader": "1100.0.16",
43
44
  "@pnpm/resolving.resolver-base": "1101.1.0",
44
- "@pnpm/store.index": "1100.2.3",
45
- "@pnpm/store.pkg-finder": "1100.0.28",
45
+ "@pnpm/store.index": "1100.2.4",
46
+ "@pnpm/store.pkg-finder": "1100.0.29",
46
47
  "@pnpm/types": "1101.9.0",
47
48
  "p-limit": "^7.3.1",
48
49
  "ssri": "13.0.1"
@@ -52,9 +53,9 @@
52
53
  },
53
54
  "devDependencies": {
54
55
  "@jest/globals": "30.4.1",
55
- "@pnpm/deps.compliance.sbom": "1100.4.0",
56
+ "@pnpm/deps.compliance.sbom": "1100.4.1",
56
57
  "@pnpm/logger": "1100.0.0",
57
- "@pnpm/store.cafs": "1100.1.18",
58
+ "@pnpm/store.cafs": "1100.1.19",
58
59
  "@types/ssri": "^7.1.5"
59
60
  },
60
61
  "engines": {