@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
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from "../../core/fs.js";
|
|
23
23
|
import { thoughtLog } from "../../core/logger.js";
|
|
24
24
|
import { dirNameStr } from "../../core/paths.js";
|
|
25
|
+
import { sanitizeBomPropertyValue } from "../../core/propertySanitizer.js";
|
|
25
26
|
import { CDXGEN_TOOL_GROUP, CDXGEN_TOOL_NAME } from "../../core/state.js";
|
|
26
27
|
import {
|
|
27
28
|
AI_INVENTORY_PROJECT_TYPES,
|
|
@@ -565,11 +566,21 @@ export async function postProcess(bomNSData, options, filePath) {
|
|
|
565
566
|
bomNSData.bomJson = applyMetadata(bomNSData.bomJson, options);
|
|
566
567
|
bomNSData.bomJson = applyLicenseEnhancement(bomNSData.bomJson, options);
|
|
567
568
|
bomNSData.bomJson = applyContainerInventoryMetadata(bomNSData.bomJson);
|
|
569
|
+
const buildOnlyComponents = extractBuildOnlyComponents(
|
|
570
|
+
bomNSData.bomJson,
|
|
571
|
+
options,
|
|
572
|
+
);
|
|
568
573
|
bomNSData.bomJson = applyFormulation(
|
|
569
574
|
bomNSData.bomJson,
|
|
570
575
|
options,
|
|
571
576
|
filePath,
|
|
572
|
-
|
|
577
|
+
buildOnlyComponents.length
|
|
578
|
+
? [...(bomNSData.formulationList || []), ...buildOnlyComponents]
|
|
579
|
+
: bomNSData.formulationList,
|
|
580
|
+
);
|
|
581
|
+
bomNSData.bomJson = applyBuildOnlyFormulation(
|
|
582
|
+
bomNSData.bomJson,
|
|
583
|
+
buildOnlyComponents,
|
|
573
584
|
);
|
|
574
585
|
bomNSData.bomJson = applyReleaseNotes(bomNSData.bomJson, options, filePath);
|
|
575
586
|
bomNSData.bomJson = applyComponentTypeFilter(bomNSData.bomJson, options);
|
|
@@ -731,24 +742,18 @@ function applyReleaseNotes(bomJson, options, filePath) {
|
|
|
731
742
|
*/
|
|
732
743
|
export function applyMetadata(bomJson, options) {
|
|
733
744
|
if (!bomJson?.components) {
|
|
734
|
-
return bomJson;
|
|
745
|
+
return applyCustomMetadataProperties(bomJson, options);
|
|
735
746
|
}
|
|
736
747
|
const bomPkgTypes = new Set();
|
|
737
748
|
const bomPkgNamespaces = new Set();
|
|
738
749
|
const bomSrcFiles = new Set();
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
if (purlObj?.namespace) {
|
|
747
|
-
bomPkgNamespaces.add(purlObj.namespace);
|
|
748
|
-
}
|
|
749
|
-
} catch (_e) {
|
|
750
|
-
// ignore
|
|
751
|
-
}
|
|
750
|
+
// Collect the manifests a component was identified from, rewriting any
|
|
751
|
+
// absolute path to a relative one as a side effect. Shared by the components
|
|
752
|
+
// and by metadata.component, which a collector such as the ruby one describes
|
|
753
|
+
// from the project's own gemspec (discussion 4410).
|
|
754
|
+
const collectSrcFiles = (comp) => {
|
|
755
|
+
if (!comp) {
|
|
756
|
+
return;
|
|
752
757
|
}
|
|
753
758
|
if (comp.properties) {
|
|
754
759
|
for (const aprop of comp.properties) {
|
|
@@ -780,6 +785,10 @@ export function applyMetadata(bomJson, options) {
|
|
|
780
785
|
Array.isArray(aidentityEvidence.methods)
|
|
781
786
|
) {
|
|
782
787
|
for (const amethod of aidentityEvidence.methods) {
|
|
788
|
+
// `value` is optional: an attestation, for one, names no file.
|
|
789
|
+
if (typeof amethod?.value !== "string") {
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
783
792
|
const rdir = relativeDir(amethod.value, options);
|
|
784
793
|
if (
|
|
785
794
|
comp.type !== "file" &&
|
|
@@ -796,7 +805,24 @@ export function applyMetadata(bomJson, options) {
|
|
|
796
805
|
}
|
|
797
806
|
}
|
|
798
807
|
}
|
|
808
|
+
};
|
|
809
|
+
for (const comp of bomJson.components) {
|
|
810
|
+
if (comp.purl) {
|
|
811
|
+
try {
|
|
812
|
+
const purlObj = Purl.parse(comp.purl);
|
|
813
|
+
if (purlObj?.type) {
|
|
814
|
+
bomPkgTypes.add(purlObj.type);
|
|
815
|
+
}
|
|
816
|
+
if (purlObj?.namespace) {
|
|
817
|
+
bomPkgNamespaces.add(purlObj.namespace);
|
|
818
|
+
}
|
|
819
|
+
} catch (_e) {
|
|
820
|
+
// ignore
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
collectSrcFiles(comp);
|
|
799
824
|
}
|
|
825
|
+
collectSrcFiles(bomJson.metadata?.component);
|
|
800
826
|
if (!bomJson.metadata.properties) {
|
|
801
827
|
bomJson.metadata.properties = [];
|
|
802
828
|
}
|
|
@@ -852,6 +878,43 @@ export function applyMetadata(bomJson, options) {
|
|
|
852
878
|
thoughtLog("BOM lacks package manifest details. Please help us improve!");
|
|
853
879
|
}
|
|
854
880
|
}
|
|
881
|
+
return applyCustomMetadataProperties(bomJson, options);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Add the metadata properties the user asked for on the command line, in the
|
|
886
|
+
* environment, or in a config file (discussion 4391), after whatever the scan
|
|
887
|
+
* itself discovered. They never replace a discovered property: the scan's own
|
|
888
|
+
* names, such as `cdx:bom:componentSrcFiles`, keep their values. A BOM without
|
|
889
|
+
* components still gets them.
|
|
890
|
+
*
|
|
891
|
+
* @param {Object} bomJson BOM JSON Object
|
|
892
|
+
* @param {Object} options CLI options, with `metadataProperties` as
|
|
893
|
+
* `[{name, value}]`
|
|
894
|
+
*
|
|
895
|
+
* @returns {Object} BOM JSON Object
|
|
896
|
+
*/
|
|
897
|
+
function applyCustomMetadataProperties(bomJson, options) {
|
|
898
|
+
if (!bomJson?.metadata || !options?.metadataProperties?.length) {
|
|
899
|
+
return bomJson;
|
|
900
|
+
}
|
|
901
|
+
bomJson.metadata.properties = bomJson.metadata.properties || [];
|
|
902
|
+
for (const aprop of options.metadataProperties) {
|
|
903
|
+
if (!aprop?.name?.length || aprop.value === undefined) {
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
const value = sanitizeBomPropertyValue(aprop.name, `${aprop.value}`);
|
|
907
|
+
if (!value?.length) {
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
if (
|
|
911
|
+
!bomJson.metadata.properties.some(
|
|
912
|
+
(p) => p.name === aprop.name && p.value === value,
|
|
913
|
+
)
|
|
914
|
+
) {
|
|
915
|
+
bomJson.metadata.properties.push({ name: aprop.name, value });
|
|
916
|
+
}
|
|
917
|
+
}
|
|
855
918
|
return bomJson;
|
|
856
919
|
}
|
|
857
920
|
|
|
@@ -964,6 +1027,154 @@ function getIdentityTechniques(comp) {
|
|
|
964
1027
|
return techniques;
|
|
965
1028
|
}
|
|
966
1029
|
|
|
1030
|
+
/**
|
|
1031
|
+
* Ensure the components moved out of the assembly have landed in the
|
|
1032
|
+
* formulation section.
|
|
1033
|
+
*
|
|
1034
|
+
* With `--include-formulation` they were handed to the full formulation pass.
|
|
1035
|
+
* Without it there is no formulation section, so a minimal one is created for
|
|
1036
|
+
* them alone: moving a component out of `components[]` and then dropping it
|
|
1037
|
+
* would lose it from the document.
|
|
1038
|
+
*
|
|
1039
|
+
* @param {Object} bomJson BOM JSON object
|
|
1040
|
+
* @param {Object[]} buildOnlyComponents Components removed from `components[]`
|
|
1041
|
+
*
|
|
1042
|
+
* @returns {Object} The same `bomJson`
|
|
1043
|
+
*/
|
|
1044
|
+
function applyBuildOnlyFormulation(bomJson, buildOnlyComponents) {
|
|
1045
|
+
if (!buildOnlyComponents?.length || !bomJson) {
|
|
1046
|
+
return bomJson;
|
|
1047
|
+
}
|
|
1048
|
+
const alreadyPlaced = (bomJson.formulation || []).some((formula) =>
|
|
1049
|
+
formula?.components?.some((comp) =>
|
|
1050
|
+
buildOnlyComponents.some((moved) => moved["bom-ref"] === comp["bom-ref"]),
|
|
1051
|
+
),
|
|
1052
|
+
);
|
|
1053
|
+
if (alreadyPlaced) {
|
|
1054
|
+
return bomJson;
|
|
1055
|
+
}
|
|
1056
|
+
if (!bomJson.formulation) {
|
|
1057
|
+
bomJson.formulation = [];
|
|
1058
|
+
}
|
|
1059
|
+
bomJson.formulation.push({
|
|
1060
|
+
"bom-ref": `${bomJson.metadata?.component?.["bom-ref"] || "cdxgen"}#build-dependencies`,
|
|
1061
|
+
components: buildOnlyComponents,
|
|
1062
|
+
});
|
|
1063
|
+
return bomJson;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Report whether a component exists only to produce the build, rather than
|
|
1068
|
+
* being part of the delivered assembly. A cargo build dependency and a
|
|
1069
|
+
* proc-macro crate are both marked with `cdx:cargo:hostOnly`.
|
|
1070
|
+
*
|
|
1071
|
+
* @param {Object} comp Component
|
|
1072
|
+
* @returns {boolean} True for a build-time-only component
|
|
1073
|
+
*/
|
|
1074
|
+
function isBuildOnlyComponent(comp) {
|
|
1075
|
+
return !!comp?.properties?.some(
|
|
1076
|
+
(property) =>
|
|
1077
|
+
property.name === "cdx:cargo:hostOnly" && property.value === "true",
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* Move the build-time-only components out of `components[]`, which describes
|
|
1083
|
+
* the delivered assembly, and hand them to the formulation section, which is
|
|
1084
|
+
* where CycloneDX describes how the assembly was produced.
|
|
1085
|
+
*
|
|
1086
|
+
* Their bom-refs stay valid because the formulation components live in the same
|
|
1087
|
+
* document, so the dependency graph is left intact. `formulation` exists from
|
|
1088
|
+
* CycloneDX 1.5, so an older document keeps them in `components[]` with their
|
|
1089
|
+
* `cdx:cargo:hostOnly` marker rather than losing them.
|
|
1090
|
+
*
|
|
1091
|
+
* @param {Object} bomJson BOM JSON object
|
|
1092
|
+
* @param {Object} options CLI options
|
|
1093
|
+
*
|
|
1094
|
+
* @returns {Object[]} The components removed from `components[]`
|
|
1095
|
+
*/
|
|
1096
|
+
export function extractBuildOnlyComponents(bomJson, options) {
|
|
1097
|
+
if (options?.specVersion < 1.5 || !bomJson?.components?.length) {
|
|
1098
|
+
return [];
|
|
1099
|
+
}
|
|
1100
|
+
const buildOnlyComponents = [];
|
|
1101
|
+
const retainedComponents = [];
|
|
1102
|
+
for (const comp of bomJson.components) {
|
|
1103
|
+
if (isBuildOnlyComponent(comp)) {
|
|
1104
|
+
buildOnlyComponents.push(comp);
|
|
1105
|
+
} else {
|
|
1106
|
+
retainedComponents.push(comp);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
if (buildOnlyComponents.length) {
|
|
1110
|
+
bomJson.components = retainedComponents;
|
|
1111
|
+
}
|
|
1112
|
+
return buildOnlyComponents;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* Report whether a component carries evidence that it is actually used: an
|
|
1117
|
+
* occurrence in the analyzed sources, or a frame in a callstack.
|
|
1118
|
+
*
|
|
1119
|
+
* @param {Object} comp Component
|
|
1120
|
+
* @returns {boolean} True when usage evidence is attached
|
|
1121
|
+
*/
|
|
1122
|
+
function hasUsageEvidence(comp) {
|
|
1123
|
+
if (comp?.evidence?.occurrences?.length) {
|
|
1124
|
+
return true;
|
|
1125
|
+
}
|
|
1126
|
+
return !!comp?.evidence?.callstack?.frames?.length;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* Re-apply the `--required-only` filter once the evidence stage has attached
|
|
1131
|
+
* occurrence and callstack data.
|
|
1132
|
+
*
|
|
1133
|
+
* A manifest can only say that a dependency is optional; the evidence says
|
|
1134
|
+
* whether it is reached. A component the analyzers observed in the sources is
|
|
1135
|
+
* promoted to `required` so it survives the filter, while an optional component
|
|
1136
|
+
* with no observed usage is dropped as before. A component scoped `excluded` -
|
|
1137
|
+
* a dev or test dependency - keeps that scope: an occurrence inside the test
|
|
1138
|
+
* sources is not evidence that it ships.
|
|
1139
|
+
*
|
|
1140
|
+
* @param {Object} bomJson BOM JSON object carrying evidence
|
|
1141
|
+
* @param {Object} options CLI options
|
|
1142
|
+
*
|
|
1143
|
+
* @returns {Object} Filtered BOM JSON
|
|
1144
|
+
*/
|
|
1145
|
+
export function applyEvidenceBasedFilter(bomJson, options) {
|
|
1146
|
+
if (!options?.requiredOnly || !bomJson?.components?.length) {
|
|
1147
|
+
return bomJson;
|
|
1148
|
+
}
|
|
1149
|
+
if (!(options.evidence || options.includeCrypto)) {
|
|
1150
|
+
return bomJson;
|
|
1151
|
+
}
|
|
1152
|
+
for (const comp of bomJson.components) {
|
|
1153
|
+
if (!hasUsageEvidence(comp)) {
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
if (!comp.properties) {
|
|
1157
|
+
comp.properties = [];
|
|
1158
|
+
}
|
|
1159
|
+
const observed = comp.evidence?.occurrences?.length
|
|
1160
|
+
? "occurrence"
|
|
1161
|
+
: "callstack";
|
|
1162
|
+
if (
|
|
1163
|
+
!comp.properties.some(
|
|
1164
|
+
(property) => property.name === "cdx:evidence:usage",
|
|
1165
|
+
)
|
|
1166
|
+
) {
|
|
1167
|
+
comp.properties.push({ name: "cdx:evidence:usage", value: observed });
|
|
1168
|
+
}
|
|
1169
|
+
if (comp.scope === "optional") {
|
|
1170
|
+
comp.scope = "required";
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
// `evidenceFilterStage` tells filterBom that the evidence is in hand, so the
|
|
1174
|
+
// scope filter it deferred earlier must run now.
|
|
1175
|
+
return filterBom(bomJson, { ...options, evidenceFilterStage: true });
|
|
1176
|
+
}
|
|
1177
|
+
|
|
967
1178
|
/**
|
|
968
1179
|
* Filter BOM based on options
|
|
969
1180
|
*
|
|
@@ -983,6 +1194,15 @@ export function filterBom(bomJson, options) {
|
|
|
983
1194
|
const allowedComponentTypes = new Set(
|
|
984
1195
|
normalizeCycloneDxComponentTypeFilter(options?.componentType),
|
|
985
1196
|
);
|
|
1197
|
+
// Evidence generation runs after this stage, so dropping the optional
|
|
1198
|
+
// components here would deny the analyzers the chance to prove that one is
|
|
1199
|
+
// used. The scope filter is re-applied by applyEvidenceBasedFilter once the
|
|
1200
|
+
// occurrence and callstack evidence is attached.
|
|
1201
|
+
const deferRequiredOnly = !!(
|
|
1202
|
+
options.requiredOnly &&
|
|
1203
|
+
!options.evidenceFilterStage &&
|
|
1204
|
+
(options.evidence || options.includeCrypto)
|
|
1205
|
+
);
|
|
986
1206
|
for (const comp of bomJson.components) {
|
|
987
1207
|
if (shouldExcludeInventoryType(comp, options)) {
|
|
988
1208
|
filtered = true;
|
|
@@ -1025,6 +1245,7 @@ export function filterBom(bomJson, options) {
|
|
|
1025
1245
|
}
|
|
1026
1246
|
if (
|
|
1027
1247
|
options.requiredOnly &&
|
|
1248
|
+
!deferRequiredOnly &&
|
|
1028
1249
|
comp.scope &&
|
|
1029
1250
|
["optional", "excluded"].includes(comp.scope)
|
|
1030
1251
|
) {
|
|
@@ -1335,7 +1556,7 @@ export function annotate(bomJson, options) {
|
|
|
1335
1556
|
for (const dep of bomJson.dependencies) {
|
|
1336
1557
|
const newRef = `${bomLinkPrefix}${stripBomLink(bomJson.serialNumber, bomJson.version, dep.ref)}`;
|
|
1337
1558
|
const newDependsOn = [];
|
|
1338
|
-
for (const adon of dep.dependsOn) {
|
|
1559
|
+
for (const adon of dep.dependsOn || []) {
|
|
1339
1560
|
newDependsOn.push(
|
|
1340
1561
|
`${bomLinkPrefix}${stripBomLink(bomJson.serialNumber, bomJson.version, adon)}`,
|
|
1341
1562
|
);
|