diff --git a/CHANGELOG.md b/CHANGELOG.md index f581580..62b07f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ - Added the ability to extract patches from a derivation and include them in the SBOM. - Added the ability to include multiple source URLs as external references. +- Added the ability to extract CPEs from Nix packages. "Guessed" CPEs in + the `possibleCPEs` field are included as evidence in the SBOM. ### Fixed diff --git a/nix/packages/transformer.nix b/nix/packages/transformer.nix index 6154436..e724fa3 100644 --- a/nix/packages/transformer.nix +++ b/nix/packages/transformer.nix @@ -44,5 +44,6 @@ rustPlatform.buildRustPackage (finalAttrs: { license = licenses.mit; maintainers = with lib.maintainers; [ nikstur ]; mainProgram = "bombon-transformer"; + identifiers.cpeParts = lib.meta.cpePatchVersionInUpdateWithVendor "nikstur" finalAttrs.version; }; }) diff --git a/rust/transformer/src/cyclonedx.rs b/rust/transformer/src/cyclonedx.rs index cd4ae3d..6007e49 100644 --- a/rust/transformer/src/cyclonedx.rs +++ b/rust/transformer/src/cyclonedx.rs @@ -10,8 +10,10 @@ use cyclonedx_bom::external_models::uri::{Purl, Uri}; use cyclonedx_bom::models::attached_text::AttachedText; use cyclonedx_bom::models::bom::{Bom, UrnUuid}; use cyclonedx_bom::models::code::{Diff, Patch, PatchClassification, Patches}; -use cyclonedx_bom::models::component::Pedigree; -use cyclonedx_bom::models::component::{Classification, Component, Components, Scope}; +use cyclonedx_bom::models::component::{Classification, Component, Components, Cpe, Scope}; +use cyclonedx_bom::models::component::{ + ComponentEvidence, ConfidenceScore, Identity, IdentityField, Method, Methods, Pedigree, +}; use cyclonedx_bom::models::external_reference::{ self, ExternalReference, ExternalReferenceType, ExternalReferences, }; @@ -164,6 +166,13 @@ impl CycloneDXComponent { if let Some(meta) = derivation.meta { component.licenses = convert_licenses(&meta); component.description = meta.description.map(|s| NormalizedString::new(&s)); + if let Some(identifiers) = meta.identifiers { + if let Some(cpe) = identifiers.cpe { + component.cpe = Some(Cpe::new(&cpe)); + } else if let Some(possible_cpes) = identifiers.possible_cpes { + component.evidence = cpes_to_evidence(&possible_cpes); + } + } if let Some(homepage) = meta.homepage { external_references.push(convert_homepage(&homepage)); } @@ -210,6 +219,39 @@ fn convert_licenses(meta: &Meta) -> Option { })) } +fn cpes_to_evidence(possible_cpes: &[derivation::Cpe]) -> Option { + if possible_cpes.is_empty() { + return None; + } + let methods = Methods( + possible_cpes + .iter() + .map(|cpe| Method { + // Because we extract this information from the package definition. + // See https://cyclonedx.org/guides/OWASP_CycloneDX-Authoritative-Guide-to-SBOM-en.pdf p.63 + technique: "manifest-analysis".to_string(), + // Safety: division could panic here but we've already prevented len() from being 0 above. + #[allow(clippy::cast_precision_loss)] + confidence: ConfidenceScore::new(1.0 / possible_cpes.len() as f32), + value: cpe.cpe.clone(), + }) + .collect(), + ); + // ComponentEvidence and Identity do not have Default implementations. + Some(ComponentEvidence { + identity: Some(Identity { + field: IdentityField::Cpe, + methods: Some(methods), + confidence: None, + tools: None, + }), + licenses: None, + copyright: None, + occurrences: None, + callstack: None, + }) +} + fn convert_src(src: &Src) -> Vec { assert!( !src.urls.is_empty(), diff --git a/rust/transformer/src/derivation.rs b/rust/transformer/src/derivation.rs index 07e021c..d91a85f 100644 --- a/rust/transformer/src/derivation.rs +++ b/rust/transformer/src/derivation.rs @@ -41,6 +41,19 @@ pub struct Meta { pub license: Option, pub homepage: Option, pub description: Option, + pub identifiers: Option, +} + +#[derive(Deserialize, Clone, Debug, Default)] +pub struct Identifiers { + pub cpe: Option, + #[serde(rename = "possibleCPEs")] + pub possible_cpes: Option>, +} + +#[derive(Deserialize, Clone, Debug, Default)] +pub struct Cpe { + pub cpe: Option, } #[derive(Deserialize, Clone, Debug)]