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