cyclonedx_bom/models/
composition.rs1use crate::validation::{Validate, ValidationContext, ValidationError, ValidationResult};
20
21use super::{
22 bom::{BomReference, SpecVersion},
23 signature::Signature,
24};
25
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct Composition {
28 pub bom_ref: Option<BomReference>,
29 pub aggregate: AggregateType,
30 pub assemblies: Option<Vec<BomReference>>,
31 pub dependencies: Option<Vec<BomReference>>,
32 pub vulnerabilities: Option<Vec<BomReference>>,
33 pub signature: Option<Signature>,
34}
35
36impl Validate for Composition {
37 fn validate_version(&self, version: SpecVersion) -> ValidationResult {
38 ValidationContext::new()
39 .add_field("aggregate", &self.aggregate, |at| {
40 validate_aggregate_type(at, version)
41 })
42 .add_struct_option("signature", self.signature.as_ref(), version)
43 .into()
44 }
45}
46
47#[derive(Clone, Debug, PartialEq, Eq)]
48pub struct Compositions(pub Vec<Composition>);
49
50impl Validate for Compositions {
51 fn validate_version(&self, version: SpecVersion) -> ValidationResult {
52 ValidationContext::new()
53 .add_list("composition", &self.0, |composition| {
54 composition.validate_version(version)
55 })
56 .into()
57 }
58}
59
60pub fn validate_aggregate_type(
62 aggregate_type: &AggregateType,
63 version: SpecVersion,
64) -> Result<(), ValidationError> {
65 if version <= SpecVersion::V1_4 {
66 if AggregateType::IncompleteFirstPartyProprietaryOnly < *aggregate_type {
67 return Err("Unknown aggregate type".into());
68 }
69 } else if version <= SpecVersion::V1_5
70 && matches!(aggregate_type, AggregateType::UnknownAggregateType(_))
71 {
72 return Err(ValidationError::new("Unknown aggregate type"));
73 }
74 Ok(())
75}
76
77#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, strum::Display)]
78#[strum(serialize_all = "snake_case")]
79#[repr(u16)]
80pub enum AggregateType {
81 Complete = 1,
82 Incomplete = 2,
83 IncompleteFirstPartyOnly = 3,
84 IncompleteThirdPartyOnly = 4,
85 Unknown = 5,
86 NotSpecified = 6,
87 IncompleteFirstPartyProprietaryOnly = 7,
89 IncompleteFirstPartyOpensourceOnly = 8,
91 IncompleteThirdPartyProprietaryOnly = 9,
93 IncompleteThirdPartyOpensourceOnly = 10,
95 #[doc(hidden)]
96 #[strum(default)]
97 UnknownAggregateType(String),
98}
99
100impl AggregateType {
101 pub fn new_unchecked<A: AsRef<str>>(value: A) -> Self {
102 match value.as_ref() {
103 "complete" => Self::Complete,
104 "incomplete" => Self::Incomplete,
105 "incomplete_first_party_only" => Self::IncompleteFirstPartyOnly,
106 "incomplete_first_party_propprietary_only" => Self::IncompleteFirstPartyProprietaryOnly,
107 "incomplete_first_party_opensource_only" => Self::IncompleteFirstPartyOpensourceOnly,
108 "incomplete_third_party_only" => Self::IncompleteThirdPartyOnly,
109 "incomplete_third_party_proprietary_only" => Self::IncompleteThirdPartyProprietaryOnly,
110 "incomplete_third_party_opensource_only" => Self::IncompleteThirdPartyOpensourceOnly,
111 "unknown" => Self::Unknown,
112 "not_specified" => Self::NotSpecified,
113 unknown => Self::UnknownAggregateType(unknown.to_string()),
114 }
115 }
116}
117
118#[cfg(test)]
119mod test {
120 use crate::{models::signature::Algorithm, validation};
121
122 use super::*;
123 use pretty_assertions::assert_eq;
124
125 #[test]
126 fn it_should_pass_validation() {
127 let validation_result = Compositions(vec![Composition {
128 bom_ref: Some(BomReference::new("composition-1")),
129 aggregate: AggregateType::Complete,
130 assemblies: Some(vec![BomReference::new("assembly-ref")]),
131 dependencies: Some(vec![BomReference::new("dependency-ref")]),
132 vulnerabilities: Some(vec![BomReference::new("vulnerability-ref")]),
133 signature: Some(Signature::single(Algorithm::HS512, "abcdefgh")),
134 }])
135 .validate();
136
137 assert!(validation_result.passed());
138 }
139
140 #[test]
141 fn it_should_fail_validation() {
142 let validation_result = Compositions(vec![Composition {
143 bom_ref: Some(BomReference::new("composition-1")),
144 aggregate: AggregateType::UnknownAggregateType("unknown aggregate type".to_string()),
145 assemblies: Some(vec![BomReference::new("assembly-ref")]),
146 dependencies: Some(vec![BomReference::new("dependency-ref")]),
147 vulnerabilities: Some(vec![BomReference::new("vulnerability-ref")]),
148 signature: Some(Signature::single(Algorithm::HS512, "abcdefgh")),
149 }])
150 .validate();
151
152 assert_eq!(
153 validation_result,
154 validation::list(
155 "composition",
156 [(
157 0,
158 validation::r#field("aggregate", "Unknown aggregate type")
159 )]
160 )
161 );
162 }
163}