1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum SbomFormat {
9 CycloneDx,
10 Spdx,
11}
12
13impl std::fmt::Display for SbomFormat {
14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 match self {
16 Self::CycloneDx => write!(f, "CycloneDX"),
17 Self::Spdx => write!(f, "SPDX"),
18 }
19 }
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct DocumentMetadata {
25 pub format: SbomFormat,
27 pub format_version: String,
29 pub spec_version: String,
31 pub serial_number: Option<String>,
33 #[serde(default, skip_serializing_if = "Option::is_none")]
38 pub doc_version: Option<u32>,
39 pub created: DateTime<Utc>,
41 pub creators: Vec<Creator>,
43 pub name: Option<String>,
45 pub security_contact: Option<String>,
47 pub vulnerability_disclosure_url: Option<String>,
49 pub support_end_date: Option<DateTime<Utc>>,
51 pub lifecycle_phase: Option<String>,
53 pub completeness_declaration: CompletenessDeclaration,
55 pub signature: Option<SignatureInfo>,
57 pub distribution_classification: Option<String>,
59 pub citations_count: usize,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
68#[non_exhaustive]
69pub enum CompletenessDeclaration {
70 Complete,
72 IncompleteFirstPartyOnly,
74 IncompleteThirdPartyOnly,
76 Incomplete,
78 #[default]
80 Unknown,
81 NotSpecified,
83}
84
85impl std::fmt::Display for CompletenessDeclaration {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 match self {
88 Self::Complete => write!(f, "complete"),
89 Self::IncompleteFirstPartyOnly => write!(f, "incomplete (first-party only)"),
90 Self::IncompleteThirdPartyOnly => write!(f, "incomplete (third-party only)"),
91 Self::Incomplete => write!(f, "incomplete"),
92 Self::Unknown => write!(f, "unknown"),
93 Self::NotSpecified => write!(f, "not specified"),
94 }
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct SignatureInfo {
101 pub algorithm: String,
103 pub has_value: bool,
105}
106
107impl DocumentMetadata {
108 #[must_use]
117 pub fn has_known_timestamp(&self) -> bool {
118 self.created.timestamp() > 0
119 }
120}
121
122impl Default for DocumentMetadata {
123 fn default() -> Self {
124 Self {
125 format: SbomFormat::CycloneDx,
126 format_version: String::new(),
127 spec_version: String::new(),
128 serial_number: None,
129 doc_version: None,
130 created: Utc::now(),
131 creators: Vec::new(),
132 name: None,
133 security_contact: None,
134 vulnerability_disclosure_url: None,
135 support_end_date: None,
136 lifecycle_phase: None,
137 completeness_declaration: CompletenessDeclaration::default(),
138 signature: None,
139 distribution_classification: None,
140 citations_count: 0,
141 }
142 }
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct Creator {
148 pub creator_type: CreatorType,
150 pub name: String,
152 pub email: Option<String>,
154}
155
156#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
158pub enum CreatorType {
159 Person,
160 Organization,
161 Tool,
162}
163
164#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
166pub struct Organization {
167 pub name: String,
169 pub urls: Vec<String>,
171 pub contacts: Vec<Contact>,
173}
174
175impl Organization {
176 #[must_use]
178 pub const fn new(name: String) -> Self {
179 Self {
180 name,
181 urls: Vec::new(),
182 contacts: Vec::new(),
183 }
184 }
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
189pub struct Contact {
190 pub name: Option<String>,
192 pub email: Option<String>,
194 pub phone: Option<String>,
196}
197
198#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
200#[non_exhaustive]
201pub enum ComponentType {
202 Application,
203 Framework,
204 #[default]
205 Library,
206 Container,
207 OperatingSystem,
208 Device,
209 Firmware,
210 File,
211 Data,
212 MachineLearningModel,
213 Platform,
214 DeviceDriver,
215 Cryptographic,
216 Other(String),
217}
218
219impl std::fmt::Display for ComponentType {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 match self {
222 Self::Application => write!(f, "application"),
223 Self::Framework => write!(f, "framework"),
224 Self::Library => write!(f, "library"),
225 Self::Container => write!(f, "container"),
226 Self::OperatingSystem => write!(f, "operating-system"),
227 Self::Device => write!(f, "device"),
228 Self::Firmware => write!(f, "firmware"),
229 Self::File => write!(f, "file"),
230 Self::Data => write!(f, "data"),
231 Self::MachineLearningModel => write!(f, "machine-learning-model"),
232 Self::Platform => write!(f, "platform"),
233 Self::DeviceDriver => write!(f, "device-driver"),
234 Self::Cryptographic => write!(f, "cryptographic"),
235 Self::Other(s) => write!(f, "{s}"),
236 }
237 }
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
248pub enum HashProvenance {
249 #[default]
251 Authored,
252 Enriched,
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct Hash {
264 pub algorithm: HashAlgorithm,
266 pub value: String,
268 #[serde(skip)]
270 pub provenance: HashProvenance,
271}
272
273impl PartialEq for Hash {
274 fn eq(&self, other: &Self) -> bool {
275 self.algorithm == other.algorithm && self.value == other.value
276 }
277}
278
279impl Eq for Hash {}
280
281impl std::hash::Hash for Hash {
282 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
283 self.algorithm.hash(state);
284 self.value.hash(state);
285 }
286}
287
288impl Hash {
289 #[must_use]
291 pub const fn new(algorithm: HashAlgorithm, value: String) -> Self {
292 Self {
293 algorithm,
294 value,
295 provenance: HashProvenance::Authored,
296 }
297 }
298
299 #[must_use]
302 pub const fn enriched(algorithm: HashAlgorithm, value: String) -> Self {
303 Self {
304 algorithm,
305 value,
306 provenance: HashProvenance::Enriched,
307 }
308 }
309}
310
311#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
313pub enum HashAlgorithm {
314 Md5,
315 Sha1,
316 Sha256,
317 Sha384,
318 Sha512,
319 Sha3_256,
320 Sha3_384,
321 Sha3_512,
322 Blake2b256,
323 Blake2b384,
324 Blake2b512,
325 Blake3,
326 Streebog256,
327 Streebog512,
328 Other(String),
329}
330
331impl std::fmt::Display for HashAlgorithm {
332 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333 match self {
334 Self::Md5 => write!(f, "MD5"),
335 Self::Sha1 => write!(f, "SHA-1"),
336 Self::Sha256 => write!(f, "SHA-256"),
337 Self::Sha384 => write!(f, "SHA-384"),
338 Self::Sha512 => write!(f, "SHA-512"),
339 Self::Sha3_256 => write!(f, "SHA3-256"),
340 Self::Sha3_384 => write!(f, "SHA3-384"),
341 Self::Sha3_512 => write!(f, "SHA3-512"),
342 Self::Blake2b256 => write!(f, "BLAKE2b-256"),
343 Self::Blake2b384 => write!(f, "BLAKE2b-384"),
344 Self::Blake2b512 => write!(f, "BLAKE2b-512"),
345 Self::Blake3 => write!(f, "BLAKE3"),
346 Self::Streebog256 => write!(f, "Streebog-256"),
347 Self::Streebog512 => write!(f, "Streebog-512"),
348 Self::Other(s) => write!(f, "{s}"),
349 }
350 }
351}
352
353#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
355pub struct ExternalReference {
356 pub ref_type: ExternalRefType,
358 pub url: String,
360 pub comment: Option<String>,
362 pub hashes: Vec<Hash>,
364}
365
366#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
368pub enum ExternalRefType {
369 Vcs,
370 IssueTracker,
371 Website,
372 Advisories,
373 Bom,
374 MailingList,
375 Social,
376 Chat,
377 Documentation,
378 Support,
379 SourceDistribution,
380 BinaryDistribution,
381 License,
382 BuildMeta,
383 BuildSystem,
384 ReleaseNotes,
385 SecurityContact,
386 ModelCard,
387 Log,
388 Configuration,
389 Evidence,
390 Formulation,
391 Attestation,
392 ThreatModel,
393 AdversaryModel,
394 RiskAssessment,
395 VulnerabilityAssertion,
396 ExploitabilityStatement,
397 Pentest,
398 StaticAnalysis,
399 DynamicAnalysis,
400 RuntimeAnalysis,
401 ComponentAnalysis,
402 Maturity,
403 Certification,
404 QualityMetrics,
405 Codified,
406 Citation,
407 Patent,
408 PatentAssertion,
409 PatentFamily,
410 Other(String),
411}
412
413impl std::fmt::Display for ExternalRefType {
414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415 match self {
416 Self::Vcs => write!(f, "vcs"),
417 Self::IssueTracker => write!(f, "issue-tracker"),
418 Self::Website => write!(f, "website"),
419 Self::Advisories => write!(f, "advisories"),
420 Self::Bom => write!(f, "bom"),
421 Self::MailingList => write!(f, "mailing-list"),
422 Self::Social => write!(f, "social"),
423 Self::Chat => write!(f, "chat"),
424 Self::Documentation => write!(f, "documentation"),
425 Self::Support => write!(f, "support"),
426 Self::SourceDistribution => write!(f, "distribution"),
427 Self::BinaryDistribution => write!(f, "distribution-intake"),
428 Self::License => write!(f, "license"),
429 Self::BuildMeta => write!(f, "build-meta"),
430 Self::BuildSystem => write!(f, "build-system"),
431 Self::ReleaseNotes => write!(f, "release-notes"),
432 Self::SecurityContact => write!(f, "security-contact"),
433 Self::ModelCard => write!(f, "model-card"),
434 Self::Log => write!(f, "log"),
435 Self::Configuration => write!(f, "configuration"),
436 Self::Evidence => write!(f, "evidence"),
437 Self::Formulation => write!(f, "formulation"),
438 Self::Attestation => write!(f, "attestation"),
439 Self::ThreatModel => write!(f, "threat-model"),
440 Self::AdversaryModel => write!(f, "adversary-model"),
441 Self::RiskAssessment => write!(f, "risk-assessment"),
442 Self::VulnerabilityAssertion => write!(f, "vulnerability-assertion"),
443 Self::ExploitabilityStatement => write!(f, "exploitability-statement"),
444 Self::Pentest => write!(f, "pentest-report"),
445 Self::StaticAnalysis => write!(f, "static-analysis-report"),
446 Self::DynamicAnalysis => write!(f, "dynamic-analysis-report"),
447 Self::RuntimeAnalysis => write!(f, "runtime-analysis-report"),
448 Self::ComponentAnalysis => write!(f, "component-analysis-report"),
449 Self::Maturity => write!(f, "maturity-report"),
450 Self::Certification => write!(f, "certification-report"),
451 Self::QualityMetrics => write!(f, "quality-metrics"),
452 Self::Codified => write!(f, "codified"),
453 Self::Citation => write!(f, "citation"),
454 Self::Patent => write!(f, "patent"),
455 Self::PatentAssertion => write!(f, "patent-assertion"),
456 Self::PatentFamily => write!(f, "patent-family"),
457 Self::Other(s) => write!(f, "{s}"),
458 }
459 }
460}
461
462#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
464#[non_exhaustive]
465pub enum DependencyType {
466 DependsOn,
468 OptionalDependsOn,
470 DevDependsOn,
472 BuildDependsOn,
474 TestDependsOn,
476 RuntimeDependsOn,
478 ProvidedDependsOn,
480 Describes,
482 Generates,
484 Contains,
486 AncestorOf,
488 VariantOf,
490 DistributionArtifact,
492 PatchFor,
494 CopyOf,
496 FileAdded,
498 FileDeleted,
500 FileModified,
502 DynamicLink,
504 StaticLink,
506 Provides,
508 Other(String),
510}
511
512impl std::fmt::Display for DependencyType {
513 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
514 match self {
515 Self::DependsOn => write!(f, "depends-on"),
516 Self::OptionalDependsOn => write!(f, "optional-depends-on"),
517 Self::DevDependsOn => write!(f, "dev-depends-on"),
518 Self::BuildDependsOn => write!(f, "build-depends-on"),
519 Self::TestDependsOn => write!(f, "test-depends-on"),
520 Self::RuntimeDependsOn => write!(f, "runtime-depends-on"),
521 Self::ProvidedDependsOn => write!(f, "provided-depends-on"),
522 Self::Describes => write!(f, "describes"),
523 Self::Generates => write!(f, "generates"),
524 Self::Contains => write!(f, "contains"),
525 Self::AncestorOf => write!(f, "ancestor-of"),
526 Self::VariantOf => write!(f, "variant-of"),
527 Self::DistributionArtifact => write!(f, "distribution-artifact"),
528 Self::PatchFor => write!(f, "patch-for"),
529 Self::CopyOf => write!(f, "copy-of"),
530 Self::FileAdded => write!(f, "file-added"),
531 Self::FileDeleted => write!(f, "file-deleted"),
532 Self::FileModified => write!(f, "file-modified"),
533 Self::DynamicLink => write!(f, "dynamic-link"),
534 Self::StaticLink => write!(f, "static-link"),
535 Self::Provides => write!(f, "provides"),
536 Self::Other(s) => write!(f, "{s}"),
537 }
538 }
539}
540
541#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
543pub enum DependencyScope {
544 #[default]
545 Required,
546 Optional,
547 Excluded,
548}
549
550impl std::fmt::Display for DependencyScope {
551 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
552 match self {
553 Self::Required => write!(f, "required"),
554 Self::Optional => write!(f, "optional"),
555 Self::Excluded => write!(f, "excluded"),
556 }
557 }
558}
559
560#[derive(Debug, Clone, Default, Serialize, Deserialize)]
562pub struct FormatExtensions {
563 pub cyclonedx: Option<serde_json::Value>,
565 pub spdx: Option<serde_json::Value>,
567 #[serde(default, skip_serializing_if = "Option::is_none")]
575 pub declarations: Option<crate::model::AttestationDeclarations>,
576}
577
578#[derive(Debug, Clone, Default, Serialize, Deserialize)]
580pub struct ComponentExtensions {
581 pub properties: Vec<Property>,
583 pub annotations: Vec<Annotation>,
585 pub raw: Option<serde_json::Value>,
592 #[serde(default, skip_serializing_if = "Option::is_none")]
601 pub source_json: Option<Box<serde_json::Value>>,
602}
603
604#[derive(Debug, Clone, Serialize, Deserialize)]
606pub struct Property {
607 pub name: String,
608 pub value: String,
609}
610
611#[derive(Debug, Clone, Serialize, Deserialize)]
613pub struct Annotation {
614 pub annotator: String,
615 pub annotation_date: DateTime<Utc>,
616 pub annotation_type: String,
617 pub comment: String,
618}
619
620#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
625#[non_exhaustive]
626pub struct MlModelInfo {
627 pub approach: Option<String>,
629 pub architecture_family: Option<String>,
631 pub architecture_name: Option<String>,
633 pub task: Option<String>,
635 pub quantization: Option<String>,
637 pub limitations: Option<String>,
639 pub training_datasets: Vec<DatasetRef>,
641 pub energy_kwh_training: Option<f64>,
643 pub model_card_url: Option<String>,
645 pub fairness: Vec<FairnessAssessment>,
649 pub ethical_considerations: Vec<EthicalConsideration>,
653 pub use_cases: Vec<String>,
656 pub performance_metrics: Vec<MetricEntry>,
659 pub data_preprocessing: Vec<String>,
663}
664
665#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
668#[non_exhaustive]
669pub struct FairnessAssessment {
670 pub group_at_risk: Option<String>,
672 pub benefits: Option<String>,
674 pub harms: Option<String>,
676 pub mitigation_strategy: Option<String>,
678}
679
680#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
683#[non_exhaustive]
684pub struct EthicalConsideration {
685 pub name: Option<String>,
687 pub mitigation_strategy: Option<String>,
689}
690
691#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
694#[non_exhaustive]
695pub struct MetricEntry {
696 pub metric_type: Option<String>,
698 pub value: Option<String>,
701 pub slice: Option<String>,
703}
704
705#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
707#[non_exhaustive]
708pub struct DatasetRef {
709 pub reference: Option<String>,
711 pub name: Option<String>,
713 pub purl: Option<String>,
716}
717
718#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
723#[non_exhaustive]
724pub struct DatasetInfo {
725 pub dataset_type: Option<String>,
727 pub sensitivity_classifications: Vec<String>,
729 pub governance_owners: Vec<String>,
731 pub intended_use: Option<String>,
735 pub confidentiality_level: Option<String>,
740 pub preprocessing: Vec<String>,
744 pub anonymization: Vec<String>,
748}