@cdxgen/cdxgen 13.1.0 → 13.3.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/README.md +32 -15
- package/bin/cdxgen.js +243 -122
- package/bin/evinse.js +7 -0
- package/bin/sign.js +88 -14
- package/bin/tracebom.js +64 -4
- package/bin/validate.js +29 -7
- package/bin/verify.js +79 -39
- package/data/crypto-oid.json +88 -12
- package/data/remediations.json +47 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +117 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +235 -5
- package/lib/cli/jsBom.js +108 -26
- package/lib/cli/jvmBom.js +68 -12
- package/lib/cli/managedBom.js +264 -41
- package/lib/cli/nativeBom.js +923 -15
- package/lib/core/activity.js +120 -2
- package/lib/core/env.js +44 -7
- package/lib/core/fs.js +174 -6
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/projectConfig.js +31 -4
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/bunutils.js +269 -101
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +447 -7
- package/lib/ecosystems/cmakeResolver.js +66 -0
- package/lib/ecosystems/cppEvidence.js +104 -33
- package/lib/ecosystems/ecosystems.js +260 -99
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +185 -9
- package/lib/ecosystems/jsEvidence.js +19 -20
- package/lib/ecosystems/kosi.js +1263 -0
- package/lib/ecosystems/npmutils.js +110 -9
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +120 -49
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-go.js +47 -9
- package/lib/ecosystems/parsers-js.js +171 -50
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +919 -161
- package/lib/ecosystems/parsers-nim.js +221 -0
- package/lib/ecosystems/parsers-opam.js +180 -0
- package/lib/ecosystems/parsers-perl.js +277 -0
- package/lib/ecosystems/parsers-python.js +27 -15
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +954 -33
- package/lib/ecosystems/parsers-spack.js +204 -0
- package/lib/ecosystems/parsers-stack.js +264 -0
- package/lib/ecosystems/parsers-terraform.js +194 -0
- package/lib/ecosystems/rubyutils.js +1426 -166
- package/lib/ecosystems/rusi.js +158 -7
- package/lib/evinser/evinser.js +547 -129
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/helpers/bomSigner.js +652 -221
- package/lib/inventory/agentFormulationParser.js +57 -4
- package/lib/inventory/aiCollector.js +504 -10
- package/lib/inventory/aiInventory.js +12 -0
- package/lib/inventory/analyzer.js +488 -30
- package/lib/inventory/astWorker.js +6 -0
- package/lib/inventory/atomUtils.js +568 -31
- package/lib/inventory/auditCategories.js +8 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +110 -14
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +627 -13
- package/lib/inventory/dynamic.js +39 -1
- package/lib/inventory/envcontext.js +9 -6
- package/lib/inventory/evidenceUtils.js +26 -5
- package/lib/inventory/fetchBatch.js +190 -19
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +6 -5
- package/lib/inventory/mcpConfigParser.js +166 -2
- package/lib/inventory/mcpDiscovery.js +11 -0
- package/lib/inventory/mcpServerManifest.js +389 -0
- package/lib/inventory/mcpbBundle.js +419 -0
- package/lib/inventory/plugins.js +3 -0
- package/lib/inventory/protobom.js +62 -89
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/source.js +47 -23
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/inventory/traceRunner.js +210 -21
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +440 -20
- package/lib/managers/oci.js +39 -1
- package/lib/parsers/cmakeLists.js +452 -0
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/stages/postgen/introspection/markers.js +29 -6
- package/lib/stages/postgen/postgen.js +237 -16
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +48 -10
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +1 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +27 -27
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +2 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +33 -1
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +2 -2
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +23 -0
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +33 -0
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +88 -8
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +21 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +55 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/metadataProperties.d.ts +39 -0
- package/types/lib/core/metadataProperties.d.ts.map +1 -0
- package/types/lib/core/projectConfig.d.ts +6 -2
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/userAgent.d.ts +56 -0
- package/types/lib/core/userAgent.d.ts.map +1 -0
- package/types/lib/ecosystems/bunutils.d.ts +12 -5
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +75 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
- package/types/lib/ecosystems/caxa.d.ts +14 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +23 -0
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +2 -2
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +15 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +40 -4
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +86 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- package/types/lib/ecosystems/npmutils.d.ts +42 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-go.d.ts +10 -0
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +10 -4
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
- package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +90 -3
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
- package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
- package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
- package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-r.d.ts +35 -0
- package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
- package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
- package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +143 -2
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts +13 -0
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +81 -2
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +280 -15
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +87 -0
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
- package/types/lib/helpers/bomSigner.d.ts +106 -13
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +145 -3
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +4 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/cdxrs.d.ts +8 -3
- package/types/lib/inventory/cdxrs.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts +15 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +87 -1
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +34 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchRate.d.ts +9 -6
- package/types/lib/inventory/fetchRate.d.ts.map +1 -1
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +5 -4
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
- package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
- package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
- package/types/lib/inventory/mcpbBundle.d.ts +19 -0
- package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts +18 -13
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
- package/types/lib/inventory/purl.d.ts +71 -2
- package/types/lib/inventory/purl.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
- package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
- package/types/lib/inventory/svelteUtils.d.ts +124 -0
- package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
- package/types/lib/inventory/traceRunner.d.ts +47 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +30 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +29 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +14 -0
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +76 -0
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/largeJson.d.ts +52 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +5 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -1
- package/types/lib/parsers/onnx.d.ts +28 -0
- package/types/lib/parsers/onnx.d.ts.map +1 -0
- package/types/lib/parsers/safetensors.d.ts +22 -0
- package/types/lib/parsers/safetensors.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +3 -2
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +33 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -1
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
|
@@ -2,8 +2,10 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { build, Purl } from "@cdxgen/cdx-purl";
|
|
5
|
+
import { clean } from "semver";
|
|
5
6
|
|
|
6
7
|
import { safeExistsSync } from "../core/fs.js";
|
|
8
|
+
import { parsePurlLoose } from "../inventory/purl.js";
|
|
7
9
|
import { parseNpmrc, parseNpmrcFromEnv } from "../parsers/npmrc.js";
|
|
8
10
|
|
|
9
11
|
const npmPackageHydrationFields = [
|
|
@@ -84,6 +86,89 @@ export function setNpmPeerProperty(pkg) {
|
|
|
84
86
|
addComponentProperty(pkg, "cdx:npm:package:peer", "true");
|
|
85
87
|
}
|
|
86
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Marks an npm component as imported only for its TypeScript types.
|
|
91
|
+
*
|
|
92
|
+
* @param {object} pkg Component object to annotate
|
|
93
|
+
* @returns {void}
|
|
94
|
+
*/
|
|
95
|
+
export function setNpmTypeOnlyProperty(pkg) {
|
|
96
|
+
addComponentProperty(pkg, "cdx:npm:package:type-only", "true");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Clean an npm version the way `npm publish` does before it records a release.
|
|
101
|
+
*
|
|
102
|
+
* Publishing a directory runs the manifest through `npm pkg fix`
|
|
103
|
+
* (`fixVersionField` in `@npmcli/package-json`, which calls `semver.clean` in
|
|
104
|
+
* loose mode) and then through libnpmpublish, which calls `semver.clean` again
|
|
105
|
+
* in strict mode and records the result as the packument version, the
|
|
106
|
+
* dist-tag, the `_id` and the tarball file name. Semver build metadata
|
|
107
|
+
* (`1.0.0+build.5`), a leading `v` or `=`, surrounding whitespace and leading
|
|
108
|
+
* zeros are dropped; a prerelease is kept.
|
|
109
|
+
*
|
|
110
|
+
* The `package.json` inside the published tarball keeps the original string,
|
|
111
|
+
* and so do the lockfiles npm, pnpm, yarn and bun write for a project, a
|
|
112
|
+
* workspace member or a git, file or link dependency. The cleaned form is the
|
|
113
|
+
* version the npm registry knows the package by.
|
|
114
|
+
*
|
|
115
|
+
* @param {string} version Version as a manifest or lockfile records it
|
|
116
|
+
* @returns {string|null} The cleaned version, or null when npm would reject `version` as invalid semver
|
|
117
|
+
*/
|
|
118
|
+
export function cleanNpmVersion(version) {
|
|
119
|
+
if (typeof version !== "string" || !version.trim()) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const looseClean = clean(version, { loose: true });
|
|
123
|
+
if (!looseClean) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return clean(looseClean);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Record the version npm would publish when it differs from the recorded one.
|
|
131
|
+
*
|
|
132
|
+
* Sets `cdx:npm:cleanedVersion` on an npm component whose version is valid
|
|
133
|
+
* semver but not in the form {@link cleanNpmVersion} produces, for example
|
|
134
|
+
* `1.0.0+build.5` (published as `1.0.0`). The component's `version` and `purl`
|
|
135
|
+
* keep the recorded string: that is what the manifest and lockfile say. A
|
|
136
|
+
* component whose version is already clean, or not semver at all (a git
|
|
137
|
+
* commit, a `file:` path), is left without the property, and a stale value from
|
|
138
|
+
* an earlier pass is replaced.
|
|
139
|
+
*
|
|
140
|
+
* @param {object} component Component or package object with `version` and `purl`
|
|
141
|
+
* @returns {object} The same component, for chaining
|
|
142
|
+
*/
|
|
143
|
+
export function setNpmCleanedVersionProperty(component) {
|
|
144
|
+
if (!component || typeof component !== "object") {
|
|
145
|
+
return component;
|
|
146
|
+
}
|
|
147
|
+
const purl = component.purl;
|
|
148
|
+
if (typeof purl !== "string" || !purl.startsWith("pkg:npm/")) {
|
|
149
|
+
return component;
|
|
150
|
+
}
|
|
151
|
+
const cleaned = cleanNpmVersion(component.version);
|
|
152
|
+
const wanted = cleaned && cleaned !== component.version ? cleaned : undefined;
|
|
153
|
+
const existing = component.properties?.filter(
|
|
154
|
+
(property) => property?.name === "cdx:npm:cleanedVersion",
|
|
155
|
+
);
|
|
156
|
+
if (
|
|
157
|
+
(!existing?.length && !wanted) ||
|
|
158
|
+
(existing?.length === 1 && existing[0].value === wanted)
|
|
159
|
+
) {
|
|
160
|
+
return component;
|
|
161
|
+
}
|
|
162
|
+
const properties = (component.properties || []).filter(
|
|
163
|
+
(property) => property?.name !== "cdx:npm:cleanedVersion",
|
|
164
|
+
);
|
|
165
|
+
if (wanted) {
|
|
166
|
+
properties.push({ name: "cdx:npm:cleanedVersion", value: wanted });
|
|
167
|
+
}
|
|
168
|
+
component.properties = properties;
|
|
169
|
+
return component;
|
|
170
|
+
}
|
|
171
|
+
|
|
87
172
|
/**
|
|
88
173
|
* Helper function to create a properly encoded workspace PURL
|
|
89
174
|
*
|
|
@@ -130,14 +215,18 @@ export function findMatchingNpmWorkspace(workspacePackages, packageName) {
|
|
|
130
215
|
return undefined;
|
|
131
216
|
}
|
|
132
217
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
218
|
+
// Compare the parsed coordinates: a prefix match on the purl text would let
|
|
219
|
+
// `ws` match `wsmember`, and the reference may be escaped or decoded.
|
|
220
|
+
return workspacePackages.find((workspacePackage) => {
|
|
221
|
+
const parts = parsePurlLoose(workspacePackage);
|
|
222
|
+
if (parts?.type !== "npm") {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
const fullName = parts.namespace
|
|
226
|
+
? `${parts.namespace}/${parts.name}`
|
|
227
|
+
: parts.name;
|
|
228
|
+
return fullName === packageName;
|
|
229
|
+
});
|
|
141
230
|
}
|
|
142
231
|
|
|
143
232
|
/**
|
|
@@ -201,6 +290,12 @@ export function collectNpmManifestSources(node) {
|
|
|
201
290
|
return manifestSources;
|
|
202
291
|
}
|
|
203
292
|
for (const edge of node.edgesIn) {
|
|
293
|
+
// Arborist adds a `workspace` edge from the root to every member, with an
|
|
294
|
+
// absolute `file:` spec it builds from the checkout path. No manifest
|
|
295
|
+
// declares it, and copying it would put the local path into the BOM.
|
|
296
|
+
if (edge?.workspace || edge?.type === "workspace") {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
204
299
|
const manifestSource = classifyNpmManifestSource(edge?.spec);
|
|
205
300
|
if (!manifestSource) {
|
|
206
301
|
continue;
|
|
@@ -520,7 +615,9 @@ export function parsePnpmGitLockKey(lockKey) {
|
|
|
520
615
|
* @returns {Object<string, object>} Map of lookup key to git package reference.
|
|
521
616
|
*/
|
|
522
617
|
export function buildPnpmGitPkgRefs(packages, snapshots, npmrcConfig = {}) {
|
|
523
|
-
|
|
618
|
+
// A null prototype keeps inherited Object properties (such as `constructor`)
|
|
619
|
+
// from masquerading as lookup hits for keys that were never registered.
|
|
620
|
+
const gitPkgRefs = Object.create(null);
|
|
524
621
|
const registerEntry = (lockKey, packageNode) => {
|
|
525
622
|
const resolution = packageNode?.resolution;
|
|
526
623
|
const parsed = parsePnpmGitLockKey(lockKey);
|
|
@@ -531,6 +628,10 @@ export function buildPnpmGitPkgRefs(packages, snapshots, npmrcConfig = {}) {
|
|
|
531
628
|
return;
|
|
532
629
|
}
|
|
533
630
|
const { group, name, gitSpec, fullName, packageName } = parsed;
|
|
631
|
+
if (name === "__proto__" || packageName === "__proto__") {
|
|
632
|
+
// A crafted lock key cannot become a lookup entry under this name.
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
534
635
|
const version = packageNode?.version || resolution?.commit || "";
|
|
535
636
|
const repo = resolution?.repo || "";
|
|
536
637
|
const commit = resolution?.commit || "";
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { parse as loadYaml } from "yaml";
|
|
4
|
+
|
|
5
|
+
import { tryBuildPurl } from "../inventory/purl.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Crystal parser for `shard.yml` and `shard.lock`.
|
|
9
|
+
*
|
|
10
|
+
* Shards resolves dependencies declared in `shard.yml` and records the
|
|
11
|
+
* resolved set in `shard.lock`, which pins each dependency's version and
|
|
12
|
+
* source repository. Both files are YAML and are read with the standard
|
|
13
|
+
* parser.
|
|
14
|
+
*
|
|
15
|
+
* No `crystal` purl type is registered, so shards are identified as generic
|
|
16
|
+
* packages carrying a `cdx:purl:proposedType=crystal` property, following
|
|
17
|
+
* the convention used for nix and zig. This keeps the BOM valid today and
|
|
18
|
+
* allows a mechanical migration if a type is registered upstream.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse a Crystal project from `shard.yml` and optional `shard.lock`.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} shardYmlFile Path to `shard.yml`
|
|
25
|
+
* @param {string} [shardLockFile] Path to `shard.lock`, if present
|
|
26
|
+
* @returns {{ pkgList: object[], dependencies: object[], parentComponent: object, rootInputs?: string[] }}
|
|
27
|
+
*/
|
|
28
|
+
export function parseShardsProject(shardYmlFile, shardLockFile) {
|
|
29
|
+
const manifest = readYamlFile(shardYmlFile);
|
|
30
|
+
|
|
31
|
+
const parentComponent = {};
|
|
32
|
+
if (typeof manifest?.name === "string" && manifest.name) {
|
|
33
|
+
parentComponent.type = "application";
|
|
34
|
+
parentComponent.name = manifest.name;
|
|
35
|
+
if (typeof manifest.version === "string" && manifest.version) {
|
|
36
|
+
parentComponent.version = manifest.version;
|
|
37
|
+
}
|
|
38
|
+
parentComponent.description =
|
|
39
|
+
typeof manifest.description === "string" && manifest.description
|
|
40
|
+
? manifest.description
|
|
41
|
+
: `Crystal shard: ${manifest.name}`;
|
|
42
|
+
parentComponent.properties = [
|
|
43
|
+
{ name: "internal:SrcFile", value: shardYmlFile },
|
|
44
|
+
];
|
|
45
|
+
if (typeof manifest.crystal === "string" && manifest.crystal) {
|
|
46
|
+
parentComponent.properties.push({
|
|
47
|
+
name: "cdx:crystal:languageVersion",
|
|
48
|
+
value: manifest.crystal,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const declared =
|
|
54
|
+
manifest?.dependencies && typeof manifest.dependencies === "object"
|
|
55
|
+
? Object.keys(manifest.dependencies)
|
|
56
|
+
: [];
|
|
57
|
+
const devDeclared =
|
|
58
|
+
manifest?.development_dependencies &&
|
|
59
|
+
typeof manifest.development_dependencies === "object"
|
|
60
|
+
? Object.keys(manifest.development_dependencies)
|
|
61
|
+
: [];
|
|
62
|
+
|
|
63
|
+
let lock;
|
|
64
|
+
if (shardLockFile) {
|
|
65
|
+
lock = readYamlFile(shardLockFile);
|
|
66
|
+
}
|
|
67
|
+
const lockedShards =
|
|
68
|
+
lock?.shards && typeof lock.shards === "object" ? lock.shards : undefined;
|
|
69
|
+
if (!lockedShards) {
|
|
70
|
+
// Lockless fallback: emit declared dependencies without resolved versions.
|
|
71
|
+
const pkgList = [
|
|
72
|
+
...declared.map((name) =>
|
|
73
|
+
crystalPackage(name, undefined, {
|
|
74
|
+
direct: true,
|
|
75
|
+
dev: false,
|
|
76
|
+
srcFile: shardYmlFile,
|
|
77
|
+
}),
|
|
78
|
+
),
|
|
79
|
+
...devDeclared.map((name) =>
|
|
80
|
+
crystalPackage(name, undefined, {
|
|
81
|
+
direct: true,
|
|
82
|
+
dev: true,
|
|
83
|
+
srcFile: shardYmlFile,
|
|
84
|
+
}),
|
|
85
|
+
),
|
|
86
|
+
];
|
|
87
|
+
return {
|
|
88
|
+
pkgList,
|
|
89
|
+
dependencies: [],
|
|
90
|
+
parentComponent,
|
|
91
|
+
rootInputs: pkgList.map((p) => p["bom-ref"]),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const directNames = new Set(declared);
|
|
96
|
+
const devNames = new Set(devDeclared);
|
|
97
|
+
const pkgList = [];
|
|
98
|
+
const refByName = new Map();
|
|
99
|
+
for (const [name, entry] of Object.entries(lockedShards)) {
|
|
100
|
+
const version =
|
|
101
|
+
typeof entry?.version === "string" && entry.version
|
|
102
|
+
? entry.version
|
|
103
|
+
: undefined;
|
|
104
|
+
const pkg = crystalPackage(name, version, {
|
|
105
|
+
direct: directNames.has(name),
|
|
106
|
+
dev: devNames.has(name),
|
|
107
|
+
srcFile: shardLockFile,
|
|
108
|
+
});
|
|
109
|
+
pkgList.push(pkg);
|
|
110
|
+
refByName.set(name, pkg["bom-ref"]);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// shard.lock records no dependency graph between shards, so the root links
|
|
114
|
+
// to the declared set and every other shard stands on its own version pin.
|
|
115
|
+
const rootInputs = [...directNames, ...devNames]
|
|
116
|
+
.map((name) => refByName.get(name))
|
|
117
|
+
.filter(Boolean);
|
|
118
|
+
return { pkgList, dependencies: [], parentComponent, rootInputs };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Build a component-like package record for a Crystal shard.
|
|
123
|
+
*
|
|
124
|
+
* @param {string} name Shard name
|
|
125
|
+
* @param {string|undefined} version Resolved version, if known
|
|
126
|
+
* @param {object} opts Extra context (`direct`, `dev`, `srcFile`)
|
|
127
|
+
* @returns {object} Package record
|
|
128
|
+
*/
|
|
129
|
+
function crystalPackage(name, version, opts) {
|
|
130
|
+
const purl = tryBuildPurl({
|
|
131
|
+
type: "generic",
|
|
132
|
+
name,
|
|
133
|
+
version: version || undefined,
|
|
134
|
+
});
|
|
135
|
+
const properties = [
|
|
136
|
+
{ name: "internal:SrcFile", value: opts.srcFile },
|
|
137
|
+
{ name: "cdx:purl:proposedType", value: "crystal" },
|
|
138
|
+
];
|
|
139
|
+
if (opts.direct) {
|
|
140
|
+
properties.push({
|
|
141
|
+
name: "cdx:crystal:dependency",
|
|
142
|
+
value: opts.dev ? "direct-dev" : "direct",
|
|
143
|
+
});
|
|
144
|
+
} else {
|
|
145
|
+
properties.push({ name: "cdx:crystal:dependency", value: "transitive" });
|
|
146
|
+
}
|
|
147
|
+
if (opts.dev) {
|
|
148
|
+
properties.push({ name: "cdx:crystal:scope", value: "development" });
|
|
149
|
+
}
|
|
150
|
+
const pkg = {
|
|
151
|
+
name,
|
|
152
|
+
...(version ? { version } : {}),
|
|
153
|
+
type: "library",
|
|
154
|
+
scope: opts.dev ? "optional" : "required",
|
|
155
|
+
properties,
|
|
156
|
+
};
|
|
157
|
+
if (purl) {
|
|
158
|
+
pkg.purl = purl;
|
|
159
|
+
pkg["bom-ref"] = decodeURIComponent(purl);
|
|
160
|
+
} else {
|
|
161
|
+
pkg["bom-ref"] = `library:${name}:${version || ""}`;
|
|
162
|
+
}
|
|
163
|
+
return pkg;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Read and parse a YAML file, warning instead of throwing.
|
|
168
|
+
*
|
|
169
|
+
* @param {string} filePath File to read
|
|
170
|
+
* @returns {object|undefined} Parsed value
|
|
171
|
+
*/
|
|
172
|
+
function readYamlFile(filePath) {
|
|
173
|
+
try {
|
|
174
|
+
return loadYaml(readFileSync(filePath, "utf-8"));
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.warn(`Failed to parse ${filePath}: ${error.message}`);
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -13,6 +13,34 @@ import { findLicenseId } from "../inventory/spdx.js";
|
|
|
13
13
|
import { xml2js } from "../parsers/xml.js";
|
|
14
14
|
import { extractPackageInfoFromHintPath } from "./dotnetutils.js";
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Apply a version to a NuGet component so that the `version` field and the purl
|
|
18
|
+
* always agree.
|
|
19
|
+
*
|
|
20
|
+
* A .NET manifest can state a version that is not a version: an MSBuild property
|
|
21
|
+
* such as `$(JsonVersion)`, a range such as `[3.13.3,4.0)`, a wildcard such as
|
|
22
|
+
* `2.0.*`, or nothing at all. Such a declaration cannot go into a purl, so a
|
|
23
|
+
* component that kept it in `version` while its purl carried none described one
|
|
24
|
+
* package two ways. A scanner range matching on the versionless purl then reports
|
|
25
|
+
* every advisory published against the package name.
|
|
26
|
+
*
|
|
27
|
+
* The declaration is not lost: callers record it as `cdx:nuget:declared_version_range`.
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} pkg Component to update in place. `name` must already be set.
|
|
30
|
+
* @param {String} [version] Version as stated or resolved, concrete or not
|
|
31
|
+
*
|
|
32
|
+
* @returns {Object} The same component
|
|
33
|
+
*/
|
|
34
|
+
export function applyNugetVersion(pkg, version) {
|
|
35
|
+
const concrete = concreteVersion(version);
|
|
36
|
+
if (concrete) {
|
|
37
|
+
pkg.version = concrete;
|
|
38
|
+
} else {
|
|
39
|
+
delete pkg.version;
|
|
40
|
+
}
|
|
41
|
+
return applyPurl(pkg, nugetPurl(pkg.name, concrete));
|
|
42
|
+
}
|
|
43
|
+
|
|
16
44
|
/**
|
|
17
45
|
* Method to parse .nupkg files
|
|
18
46
|
*
|
|
@@ -140,16 +168,22 @@ export function parseNuspecData(nupkgFile, nuspecData) {
|
|
|
140
168
|
continue;
|
|
141
169
|
}
|
|
142
170
|
groupPkg.name = agroupdep.id;
|
|
171
|
+
let declaredRange;
|
|
143
172
|
if (agroupdep?.version) {
|
|
144
173
|
let versionStr = agroupdep.version;
|
|
145
174
|
// version could have square brackets around them
|
|
146
175
|
if (versionStr.startsWith("[") && versionStr.endsWith("]")) {
|
|
147
176
|
versionStr = versionStr.replace(/[\[\]]/g, "");
|
|
148
177
|
}
|
|
149
|
-
|
|
150
|
-
|
|
178
|
+
// A nuspec dependency states the range the package accepts, not the
|
|
179
|
+
// version installed. Anything that is not a single version stays a
|
|
180
|
+
// declaration so the component and its purl agree - #4359.
|
|
181
|
+
if (!concreteVersion(versionStr)) {
|
|
182
|
+
declaredRange = agroupdep.version;
|
|
183
|
+
}
|
|
184
|
+
applyNugetVersion(groupPkg, versionStr);
|
|
151
185
|
} else {
|
|
152
|
-
|
|
186
|
+
applyNugetVersion(groupPkg);
|
|
153
187
|
}
|
|
154
188
|
groupPkg.scope = "optional";
|
|
155
189
|
groupPkg.properties = [
|
|
@@ -158,6 +192,12 @@ export function parseNuspecData(nupkgFile, nuspecData) {
|
|
|
158
192
|
value: nupkgFile,
|
|
159
193
|
},
|
|
160
194
|
];
|
|
195
|
+
if (declaredRange) {
|
|
196
|
+
groupPkg.properties.push({
|
|
197
|
+
name: "cdx:nuget:declared_version_range",
|
|
198
|
+
value: declaredRange,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
161
201
|
if (targetFramework) {
|
|
162
202
|
groupPkg.properties.push({
|
|
163
203
|
name: "cdx:dotnet:target_framework",
|
|
@@ -243,20 +283,15 @@ export function parseCsPkgData(pkgData, pkgFile, pkgNameVersions = {}) {
|
|
|
243
283
|
// so the declaration stays visible, and the version is resolved from a
|
|
244
284
|
// more precise manifest so the purl names the package that is present.
|
|
245
285
|
let declaredRange;
|
|
246
|
-
if (!pkg.version
|
|
286
|
+
if (!concreteVersion(pkg.version)) {
|
|
247
287
|
confidence = 0.5;
|
|
288
|
+
declaredRange = pkg.version;
|
|
248
289
|
// Backfill from a version already resolved by a more precise manifest.
|
|
249
290
|
// Concrete versions are never overridden - a disagreement means both
|
|
250
291
|
// versions must be tracked.
|
|
251
|
-
pkg.version = pkgNameVersions[pkg.name]
|
|
252
|
-
} else if (/[[\](),*]/.test(pkg.version)) {
|
|
253
|
-
confidence = 0.5;
|
|
254
|
-
if (pkgNameVersions[pkg.name]) {
|
|
255
|
-
declaredRange = pkg.version;
|
|
256
|
-
pkg.version = pkgNameVersions[pkg.name];
|
|
257
|
-
}
|
|
292
|
+
pkg.version = pkgNameVersions[pkg.name];
|
|
258
293
|
}
|
|
259
|
-
|
|
294
|
+
applyNugetVersion(pkg, pkg.version);
|
|
260
295
|
if (pkgFile) {
|
|
261
296
|
pkg.properties = [
|
|
262
297
|
{
|
|
@@ -431,6 +466,21 @@ export function getPropertyGroupTextNodes(propsFiles) {
|
|
|
431
466
|
return matches;
|
|
432
467
|
}
|
|
433
468
|
|
|
469
|
+
/**
|
|
470
|
+
* Read the trimmed text of an MSBuild property from a parsed PropertyGroup.
|
|
471
|
+
*
|
|
472
|
+
* @param {Object} propertyGroup PropertyGroup parsed by xml2js in compact mode
|
|
473
|
+
* @param {string} key Property name
|
|
474
|
+
* @returns {string|undefined} The text, or undefined when absent or empty
|
|
475
|
+
*/
|
|
476
|
+
function propertyGroupText(propertyGroup, key) {
|
|
477
|
+
const text = propertyGroup?.[key]?.[0]?._?.[0];
|
|
478
|
+
if (typeof text !== "string" || !text.trim().length) {
|
|
479
|
+
return undefined;
|
|
480
|
+
}
|
|
481
|
+
return text.trim();
|
|
482
|
+
}
|
|
483
|
+
|
|
434
484
|
/**
|
|
435
485
|
* Method to parse .csproj like xml files
|
|
436
486
|
*
|
|
@@ -479,6 +529,35 @@ export function parseCsProjData(
|
|
|
479
529
|
return pkgList;
|
|
480
530
|
}
|
|
481
531
|
const project = projects[0];
|
|
532
|
+
// SDK-style projects declare their version through the MSBuild properties
|
|
533
|
+
// that `dotnet pack` reads: PackageVersion, which defaults to Version, which
|
|
534
|
+
// in turn defaults to VersionPrefix plus an optional `-VersionSuffix`. A
|
|
535
|
+
// property may be set in any PropertyGroup, so they are collected first.
|
|
536
|
+
const sdkVersionProps = {};
|
|
537
|
+
for (const apg of project?.PropertyGroup || []) {
|
|
538
|
+
for (const key of [
|
|
539
|
+
"PackageVersion",
|
|
540
|
+
"Version",
|
|
541
|
+
"VersionPrefix",
|
|
542
|
+
"VersionSuffix",
|
|
543
|
+
]) {
|
|
544
|
+
const value = propertyGroupText(apg, key);
|
|
545
|
+
if (value !== undefined) {
|
|
546
|
+
sdkVersionProps[key] = value;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
let sdkVersion =
|
|
551
|
+
sdkVersionProps.PackageVersion ||
|
|
552
|
+
sdkVersionProps.Version ||
|
|
553
|
+
(sdkVersionProps.VersionPrefix
|
|
554
|
+
? `${sdkVersionProps.VersionPrefix}${sdkVersionProps.VersionSuffix ? `-${sdkVersionProps.VersionSuffix}` : ""}`
|
|
555
|
+
: undefined);
|
|
556
|
+
// An MSBuild property reference such as $(BuildNumber) is only known at
|
|
557
|
+
// build time, so a version that still contains one is not reported.
|
|
558
|
+
if (sdkVersion?.includes("$(")) {
|
|
559
|
+
sdkVersion = undefined;
|
|
560
|
+
}
|
|
482
561
|
// First make up a parentcomponent name based on the .csproj file name
|
|
483
562
|
if (projFile) {
|
|
484
563
|
parentComponent.name = basename(projFile).replaceAll(
|
|
@@ -536,10 +615,6 @@ export function parseCsProjData(
|
|
|
536
615
|
if (outputType === "Library") {
|
|
537
616
|
parentComponent.type = "library";
|
|
538
617
|
}
|
|
539
|
-
applyPurl(
|
|
540
|
-
parentComponent,
|
|
541
|
-
nugetPurl(parentComponent.name, parentComponent.version || "latest"),
|
|
542
|
-
);
|
|
543
618
|
// The MSBuild OutputType (Exe, WinExe, …) used to ride along as an
|
|
544
619
|
// `output_type` purl qualifier, which nuget does not define — every such
|
|
545
620
|
// purl was invalid. It is project metadata, so it belongs in properties.
|
|
@@ -712,23 +787,17 @@ export function parseCsProjData(
|
|
|
712
787
|
const exactPin = pkg.version.match(/^\[([^,[\]()]+)\]$/);
|
|
713
788
|
if (exactPin) {
|
|
714
789
|
pkg.version = exactPin[1];
|
|
715
|
-
} else if (
|
|
716
|
-
pkg.version
|
|
717
|
-
|
|
718
|
-
) {
|
|
719
|
-
const resolved =
|
|
790
|
+
} else if (!concreteVersion(pkg.version)) {
|
|
791
|
+
declaredRange = pkg.version;
|
|
792
|
+
pkg.version =
|
|
720
793
|
pkgNameVersions[pkg.name] ||
|
|
721
794
|
centralVersions[pkg.name.toLowerCase()];
|
|
722
|
-
if (resolved) {
|
|
723
|
-
declaredRange = pkg.version;
|
|
724
|
-
pkg.version = resolved;
|
|
725
|
-
}
|
|
726
795
|
}
|
|
727
796
|
}
|
|
728
797
|
// A version that could not be resolved from any source must not be
|
|
729
798
|
// stringified into the purl - `@undefined` is not a version and makes the
|
|
730
799
|
// component unmatchable. Mirrors parseCsPkgData.
|
|
731
|
-
|
|
800
|
+
applyNugetVersion(pkg, pkg.version);
|
|
732
801
|
if (projFile) {
|
|
733
802
|
pkg.properties = [
|
|
734
803
|
{
|
|
@@ -821,26 +890,27 @@ export function parseCsProjData(
|
|
|
821
890
|
});
|
|
822
891
|
}
|
|
823
892
|
const version = hintVersion ?? assemblyVersion;
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
893
|
+
// An assembly reference that states no version anywhere - no HintPath to
|
|
894
|
+
// read one from and no `Version=` on the Include - leaves the component
|
|
895
|
+
// versionless in both fields. The registry's latest release is not a
|
|
896
|
+
// stand-in: `<Reference>` resolves an assembly from a targeting pack, a
|
|
897
|
+
// lib folder or the GAC, and the project does not say which package it
|
|
898
|
+
// came from. See #4359.
|
|
899
|
+
applyNugetVersion(pkg, version);
|
|
900
|
+
if (
|
|
901
|
+
!pkg.version &&
|
|
902
|
+
(pkg.name.startsWith("System.") ||
|
|
831
903
|
pkg.name.startsWith("Mono.") ||
|
|
832
|
-
pkg.name.startsWith("Microsoft.")
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
}
|
|
904
|
+
pkg.name.startsWith("Microsoft."))
|
|
905
|
+
) {
|
|
906
|
+
// If this is a System package, then track the target frameworks
|
|
907
|
+
for (const tf of projectTargetFrameworks) {
|
|
908
|
+
pkg.properties.push({
|
|
909
|
+
name: "cdx:dotnet:target_framework",
|
|
910
|
+
value: tf,
|
|
911
|
+
});
|
|
841
912
|
}
|
|
842
913
|
}
|
|
843
|
-
pkg["bom-ref"] = pkg.purl;
|
|
844
914
|
if (assemblyName) {
|
|
845
915
|
pkg.properties.push({
|
|
846
916
|
name: "cdx:dotnet:assembly_name",
|
|
@@ -894,21 +964,22 @@ export function parseCsProjData(
|
|
|
894
964
|
}
|
|
895
965
|
}
|
|
896
966
|
}
|
|
897
|
-
|
|
898
|
-
|
|
967
|
+
if (sdkVersion) {
|
|
968
|
+
parentComponent.version = sdkVersion;
|
|
969
|
+
}
|
|
970
|
+
// The purl is built once every PropertyGroup has been read, so a version
|
|
971
|
+
// declared after OutputType is not lost.
|
|
972
|
+
if (parentComponent.name) {
|
|
899
973
|
applyPurl(
|
|
900
974
|
parentComponent,
|
|
901
975
|
nugetPurl(parentComponent.name, parentComponent.version || "latest"),
|
|
902
976
|
);
|
|
903
977
|
}
|
|
904
|
-
if (parentComponent?.purl) {
|
|
905
|
-
parentComponent["bom-ref"] = parentComponent.purl;
|
|
906
|
-
}
|
|
907
978
|
let dependencies = [];
|
|
908
979
|
if (parentComponent?.["bom-ref"]) {
|
|
909
980
|
dependencies = [
|
|
910
981
|
{
|
|
911
|
-
ref: parentComponent
|
|
982
|
+
ref: parentComponent["bom-ref"],
|
|
912
983
|
dependsOn: [...new Set(pkgList.map((p) => p["bom-ref"]))].sort(),
|
|
913
984
|
},
|
|
914
985
|
];
|