Skip to main content

sbom_tools/model/
metadata.rs

1//! Metadata structures for SBOM documents and components.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6/// SBOM format type
7#[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            SbomFormat::CycloneDx => write!(f, "CycloneDX"),
17            SbomFormat::Spdx => write!(f, "SPDX"),
18        }
19    }
20}
21
22/// Document-level metadata
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct DocumentMetadata {
25    /// SBOM format type
26    pub format: SbomFormat,
27    /// Format version (e.g., "1.5" for CycloneDX)
28    pub format_version: String,
29    /// Specification version
30    pub spec_version: String,
31    /// Serial number or document namespace
32    pub serial_number: Option<String>,
33    /// Creation timestamp
34    pub created: DateTime<Utc>,
35    /// Creators/authors
36    pub creators: Vec<Creator>,
37    /// Document name
38    pub name: Option<String>,
39    /// Security contact for vulnerability disclosure (CRA requirement)
40    pub security_contact: Option<String>,
41    /// URL for vulnerability disclosure policy/portal
42    pub vulnerability_disclosure_url: Option<String>,
43    /// Support/end-of-life date for security updates
44    pub support_end_date: Option<DateTime<Utc>>,
45}
46
47impl Default for DocumentMetadata {
48    fn default() -> Self {
49        Self {
50            format: SbomFormat::CycloneDx,
51            format_version: String::new(),
52            spec_version: String::new(),
53            serial_number: None,
54            created: Utc::now(),
55            creators: Vec::new(),
56            name: None,
57            security_contact: None,
58            vulnerability_disclosure_url: None,
59            support_end_date: None,
60        }
61    }
62}
63
64/// Creator information
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct Creator {
67    /// Creator type
68    pub creator_type: CreatorType,
69    /// Creator name or identifier
70    pub name: String,
71    /// Optional email
72    pub email: Option<String>,
73}
74
75/// Type of creator
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
77pub enum CreatorType {
78    Person,
79    Organization,
80    Tool,
81}
82
83/// Organization/supplier information
84#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
85pub struct Organization {
86    /// Organization name
87    pub name: String,
88    /// Contact URLs
89    pub urls: Vec<String>,
90    /// Contact emails
91    pub contacts: Vec<Contact>,
92}
93
94impl Organization {
95    /// Create a new organization with just a name
96    pub fn new(name: String) -> Self {
97        Self {
98            name,
99            urls: Vec::new(),
100            contacts: Vec::new(),
101        }
102    }
103}
104
105/// Contact information
106#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
107pub struct Contact {
108    /// Contact name
109    pub name: Option<String>,
110    /// Email address
111    pub email: Option<String>,
112    /// Phone number
113    pub phone: Option<String>,
114}
115
116/// Component type classification
117#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
118pub enum ComponentType {
119    Application,
120    Framework,
121    #[default]
122    Library,
123    Container,
124    OperatingSystem,
125    Device,
126    Firmware,
127    File,
128    Data,
129    MachineLearningModel,
130    Platform,
131    DeviceDriver,
132    Cryptographic,
133    Other(String),
134}
135
136impl std::fmt::Display for ComponentType {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        match self {
139            ComponentType::Application => write!(f, "application"),
140            ComponentType::Framework => write!(f, "framework"),
141            ComponentType::Library => write!(f, "library"),
142            ComponentType::Container => write!(f, "container"),
143            ComponentType::OperatingSystem => write!(f, "operating-system"),
144            ComponentType::Device => write!(f, "device"),
145            ComponentType::Firmware => write!(f, "firmware"),
146            ComponentType::File => write!(f, "file"),
147            ComponentType::Data => write!(f, "data"),
148            ComponentType::MachineLearningModel => write!(f, "machine-learning-model"),
149            ComponentType::Platform => write!(f, "platform"),
150            ComponentType::DeviceDriver => write!(f, "device-driver"),
151            ComponentType::Cryptographic => write!(f, "cryptographic"),
152            ComponentType::Other(s) => write!(f, "{}", s),
153        }
154    }
155}
156
157/// Cryptographic hash
158#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
159pub struct Hash {
160    /// Hash algorithm
161    pub algorithm: HashAlgorithm,
162    /// Hash value (hex encoded)
163    pub value: String,
164}
165
166impl Hash {
167    /// Create a new hash
168    pub fn new(algorithm: HashAlgorithm, value: String) -> Self {
169        Self { algorithm, value }
170    }
171}
172
173/// Hash algorithm types
174#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
175pub enum HashAlgorithm {
176    Md5,
177    Sha1,
178    Sha256,
179    Sha384,
180    Sha512,
181    Sha3_256,
182    Sha3_384,
183    Sha3_512,
184    Blake2b256,
185    Blake2b384,
186    Blake2b512,
187    Blake3,
188    Other(String),
189}
190
191impl std::fmt::Display for HashAlgorithm {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        match self {
194            HashAlgorithm::Md5 => write!(f, "MD5"),
195            HashAlgorithm::Sha1 => write!(f, "SHA-1"),
196            HashAlgorithm::Sha256 => write!(f, "SHA-256"),
197            HashAlgorithm::Sha384 => write!(f, "SHA-384"),
198            HashAlgorithm::Sha512 => write!(f, "SHA-512"),
199            HashAlgorithm::Sha3_256 => write!(f, "SHA3-256"),
200            HashAlgorithm::Sha3_384 => write!(f, "SHA3-384"),
201            HashAlgorithm::Sha3_512 => write!(f, "SHA3-512"),
202            HashAlgorithm::Blake2b256 => write!(f, "BLAKE2b-256"),
203            HashAlgorithm::Blake2b384 => write!(f, "BLAKE2b-384"),
204            HashAlgorithm::Blake2b512 => write!(f, "BLAKE2b-512"),
205            HashAlgorithm::Blake3 => write!(f, "BLAKE3"),
206            HashAlgorithm::Other(s) => write!(f, "{}", s),
207        }
208    }
209}
210
211/// External reference
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct ExternalReference {
214    /// Reference type
215    pub ref_type: ExternalRefType,
216    /// URL or locator
217    pub url: String,
218    /// Comment or description
219    pub comment: Option<String>,
220    /// Hash of the referenced content
221    pub hashes: Vec<Hash>,
222}
223
224/// External reference types
225#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
226pub enum ExternalRefType {
227    Vcs,
228    IssueTracker,
229    Website,
230    Advisories,
231    Bom,
232    MailingList,
233    Social,
234    Chat,
235    Documentation,
236    Support,
237    SourceDistribution,
238    BinaryDistribution,
239    License,
240    BuildMeta,
241    BuildSystem,
242    ReleaseNotes,
243    SecurityContact,
244    ModelCard,
245    Log,
246    Configuration,
247    Evidence,
248    Formulation,
249    Attestation,
250    ThreatModel,
251    AdversaryModel,
252    RiskAssessment,
253    VulnerabilityAssertion,
254    ExploitabilityStatement,
255    Pentest,
256    StaticAnalysis,
257    DynamicAnalysis,
258    RuntimeAnalysis,
259    ComponentAnalysis,
260    Maturity,
261    Certification,
262    QualityMetrics,
263    Codified,
264    Other(String),
265}
266
267impl std::fmt::Display for ExternalRefType {
268    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269        match self {
270            ExternalRefType::Vcs => write!(f, "vcs"),
271            ExternalRefType::IssueTracker => write!(f, "issue-tracker"),
272            ExternalRefType::Website => write!(f, "website"),
273            ExternalRefType::Advisories => write!(f, "advisories"),
274            ExternalRefType::Bom => write!(f, "bom"),
275            ExternalRefType::MailingList => write!(f, "mailing-list"),
276            ExternalRefType::Social => write!(f, "social"),
277            ExternalRefType::Chat => write!(f, "chat"),
278            ExternalRefType::Documentation => write!(f, "documentation"),
279            ExternalRefType::Support => write!(f, "support"),
280            ExternalRefType::SourceDistribution => write!(f, "distribution"),
281            ExternalRefType::BinaryDistribution => write!(f, "distribution-intake"),
282            ExternalRefType::License => write!(f, "license"),
283            ExternalRefType::BuildMeta => write!(f, "build-meta"),
284            ExternalRefType::BuildSystem => write!(f, "build-system"),
285            ExternalRefType::ReleaseNotes => write!(f, "release-notes"),
286            ExternalRefType::SecurityContact => write!(f, "security-contact"),
287            ExternalRefType::ModelCard => write!(f, "model-card"),
288            ExternalRefType::Log => write!(f, "log"),
289            ExternalRefType::Configuration => write!(f, "configuration"),
290            ExternalRefType::Evidence => write!(f, "evidence"),
291            ExternalRefType::Formulation => write!(f, "formulation"),
292            ExternalRefType::Attestation => write!(f, "attestation"),
293            ExternalRefType::ThreatModel => write!(f, "threat-model"),
294            ExternalRefType::AdversaryModel => write!(f, "adversary-model"),
295            ExternalRefType::RiskAssessment => write!(f, "risk-assessment"),
296            ExternalRefType::VulnerabilityAssertion => write!(f, "vulnerability-assertion"),
297            ExternalRefType::ExploitabilityStatement => write!(f, "exploitability-statement"),
298            ExternalRefType::Pentest => write!(f, "pentest-report"),
299            ExternalRefType::StaticAnalysis => write!(f, "static-analysis-report"),
300            ExternalRefType::DynamicAnalysis => write!(f, "dynamic-analysis-report"),
301            ExternalRefType::RuntimeAnalysis => write!(f, "runtime-analysis-report"),
302            ExternalRefType::ComponentAnalysis => write!(f, "component-analysis-report"),
303            ExternalRefType::Maturity => write!(f, "maturity-report"),
304            ExternalRefType::Certification => write!(f, "certification-report"),
305            ExternalRefType::QualityMetrics => write!(f, "quality-metrics"),
306            ExternalRefType::Codified => write!(f, "codified"),
307            ExternalRefType::Other(s) => write!(f, "{}", s),
308        }
309    }
310}
311
312/// Dependency relationship type
313#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
314pub enum DependencyType {
315    /// Direct dependency
316    DependsOn,
317    /// Optional dependency
318    OptionalDependsOn,
319    /// Development dependency
320    DevDependsOn,
321    /// Build dependency
322    BuildDependsOn,
323    /// Test dependency
324    TestDependsOn,
325    /// Runtime dependency
326    RuntimeDependsOn,
327    /// Provided dependency (e.g., Java provided scope)
328    ProvidedDependsOn,
329    /// Describes relationship (SPDX)
330    Describes,
331    /// Generates relationship
332    Generates,
333    /// Contains relationship
334    Contains,
335    /// Ancestor of
336    AncestorOf,
337    /// Variant of
338    VariantOf,
339    /// Distribution artifact
340    DistributionArtifact,
341    /// Patch for
342    PatchFor,
343    /// Copy of
344    CopyOf,
345    /// File added
346    FileAdded,
347    /// File deleted
348    FileDeleted,
349    /// File modified
350    FileModified,
351    /// Dynamic link
352    DynamicLink,
353    /// Static link
354    StaticLink,
355    /// Other relationship
356    Other(String),
357}
358
359impl std::fmt::Display for DependencyType {
360    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
361        match self {
362            DependencyType::DependsOn => write!(f, "depends-on"),
363            DependencyType::OptionalDependsOn => write!(f, "optional-depends-on"),
364            DependencyType::DevDependsOn => write!(f, "dev-depends-on"),
365            DependencyType::BuildDependsOn => write!(f, "build-depends-on"),
366            DependencyType::TestDependsOn => write!(f, "test-depends-on"),
367            DependencyType::RuntimeDependsOn => write!(f, "runtime-depends-on"),
368            DependencyType::ProvidedDependsOn => write!(f, "provided-depends-on"),
369            DependencyType::Describes => write!(f, "describes"),
370            DependencyType::Generates => write!(f, "generates"),
371            DependencyType::Contains => write!(f, "contains"),
372            DependencyType::AncestorOf => write!(f, "ancestor-of"),
373            DependencyType::VariantOf => write!(f, "variant-of"),
374            DependencyType::DistributionArtifact => write!(f, "distribution-artifact"),
375            DependencyType::PatchFor => write!(f, "patch-for"),
376            DependencyType::CopyOf => write!(f, "copy-of"),
377            DependencyType::FileAdded => write!(f, "file-added"),
378            DependencyType::FileDeleted => write!(f, "file-deleted"),
379            DependencyType::FileModified => write!(f, "file-modified"),
380            DependencyType::DynamicLink => write!(f, "dynamic-link"),
381            DependencyType::StaticLink => write!(f, "static-link"),
382            DependencyType::Other(s) => write!(f, "{}", s),
383        }
384    }
385}
386
387/// Dependency scope
388#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
389pub enum DependencyScope {
390    #[default]
391    Required,
392    Optional,
393    Excluded,
394}
395
396/// Format-specific extensions that don't map to the canonical model
397#[derive(Debug, Clone, Default, Serialize, Deserialize)]
398pub struct FormatExtensions {
399    /// CycloneDX-specific extensions
400    pub cyclonedx: Option<serde_json::Value>,
401    /// SPDX-specific extensions
402    pub spdx: Option<serde_json::Value>,
403}
404
405/// Component-level extensions
406#[derive(Debug, Clone, Default, Serialize, Deserialize)]
407pub struct ComponentExtensions {
408    /// Properties from CycloneDX
409    pub properties: Vec<Property>,
410    /// Annotations from SPDX
411    pub annotations: Vec<Annotation>,
412    /// Raw extension data
413    pub raw: Option<serde_json::Value>,
414}
415
416/// Key-value property
417#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct Property {
419    pub name: String,
420    pub value: String,
421}
422
423/// Annotation/comment
424#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct Annotation {
426    pub annotator: String,
427    pub annotation_date: DateTime<Utc>,
428    pub annotation_type: String,
429    pub comment: String,
430}