From 3ab556274c82e1e014a6edd211681c3a5d2da102 Mon Sep 17 00:00:00 2001 From: James Thompson Date: Thu, 5 Sep 2024 20:40:08 +1000 Subject: [PATCH] #348 implement deep copy rather than serialization Signed-off-by: James Thompson --- src/CycloneDX.Core/BomUtils.cs | 4 +- src/CycloneDX.Core/Models/Annotation.cs | 31 +++++++- src/CycloneDX.Core/Models/AnnotatorChoice.cs | 14 +++- src/CycloneDX.Core/Models/AttachedText.cs | 13 +++- src/CycloneDX.Core/Models/Bom.cs | 48 +++++++++--- src/CycloneDX.Core/Models/Callstack.cs | 30 +++++++- src/CycloneDX.Core/Models/Command.cs | 13 +++- src/CycloneDX.Core/Models/Commit.cs | 15 +++- src/CycloneDX.Core/Models/Component.cs | 77 ++++++++++++++----- src/CycloneDX.Core/Models/Composition.cs | 15 +++- src/CycloneDX.Core/Models/Data.cs | 32 +++++++- .../Models/DataClassification.cs | 12 ++- src/CycloneDX.Core/Models/DataFlow.cs | 22 +++++- src/CycloneDX.Core/Models/DataGovernance.cs | 20 ++++- .../Models/DataflowSourceDestination.cs | 11 ++- src/CycloneDX.Core/Models/DatasetChoice.cs | 12 ++- src/CycloneDX.Core/Models/DatasetChoices.cs | 12 ++- src/CycloneDX.Core/Models/Dependency.cs | 12 ++- src/CycloneDX.Core/Models/Diff.cs | 13 +++- .../Models/EnvironmentVarChoice.cs | 12 ++- .../Models/EnvironmentVarChoices.cs | 9 ++- src/CycloneDX.Core/Models/Event.cs | 20 ++++- src/CycloneDX.Core/Models/Evidence.cs | 21 ++++- src/CycloneDX.Core/Models/EvidenceIdentity.cs | 14 +++- src/CycloneDX.Core/Models/EvidenceMethods.cs | 12 ++- .../Models/EvidenceOccurrence.cs | 11 ++- src/CycloneDX.Core/Models/EvidenceTools.cs | 10 ++- .../Models/ExternalReference.cs | 15 +++- src/CycloneDX.Core/Models/Formula.cs | 20 ++++- .../Models/GraphicsCollection.cs | 23 +++++- src/CycloneDX.Core/Models/Hash.cs | 12 ++- .../Models/IdentifiableAction.cs | 15 +++- src/CycloneDX.Core/Models/Input.cs | 23 +++++- src/CycloneDX.Core/Models/Issue.cs | 16 +++- src/CycloneDX.Core/Models/License.cs | 27 +++++-- src/CycloneDX.Core/Models/LicenseChoice.cs | 23 +++++- src/CycloneDX.Core/Models/Licensing.cs | 25 +++++- src/CycloneDX.Core/Models/Lifecycles.cs | 16 +++- src/CycloneDX.Core/Models/Metadata.cs | 35 +++++++-- src/CycloneDX.Core/Models/ModelCard.cs | 38 ++++++++- .../Models/ModelCardConsiderations.cs | 40 +++++++++- src/CycloneDX.Core/Models/ModelParameters.cs | 39 +++++++++- src/CycloneDX.Core/Models/Note.cs | 12 ++- .../Models/OrganizationalContact.cs | 14 +++- .../Models/OrganizationalEntity.cs | 15 +++- .../Models/OrganizationalEntityOrContact.cs | 12 ++- src/CycloneDX.Core/Models/Output.cs | 24 +++++- src/CycloneDX.Core/Models/Parameter.cs | 13 +++- src/CycloneDX.Core/Models/Patch.cs | 14 +++- src/CycloneDX.Core/Models/Pedigree.cs | 20 ++++- src/CycloneDX.Core/Models/Property.cs | 12 ++- src/CycloneDX.Core/Models/ReleaseNotes.cs | 27 ++++++- .../Models/ResourceReferenceChoice.cs | 12 ++- .../Models/ResourceReferenceChoices.cs | 10 ++- src/CycloneDX.Core/Models/Service.cs | 63 +++++++++++---- .../Models/ServiceDataChoices.cs | 12 ++- src/CycloneDX.Core/Models/Source.cs | 12 ++- src/CycloneDX.Core/Models/Step.cs | 17 +++- src/CycloneDX.Core/Models/Swid.cs | 16 +++- src/CycloneDX.Core/Models/Tool.cs | 16 +++- src/CycloneDX.Core/Models/ToolChoices.cs | 23 ++++-- src/CycloneDX.Core/Models/Trigger.cs | 54 ++++++++++--- src/CycloneDX.Core/Models/Volume.cs | 30 ++++++-- .../Models/Vulnerabilities/Advisory.cs | 12 ++- .../Models/Vulnerabilities/Affects.cs | 12 ++- .../Models/Vulnerabilities/Analysis.cs | 22 +++++- .../Models/Vulnerabilities/Credits.cs | 13 +++- .../Models/Vulnerabilities/ProofOfConcept.cs | 13 +++- .../Models/Vulnerabilities/Rating.cs | 16 +++- .../Models/Vulnerabilities/Reference.cs | 12 ++- .../Models/Vulnerabilities/Vulnerability.cs | 48 +++++++++--- src/CycloneDX.Core/Models/Workflow.cs | 34 +++++++- src/CycloneDX.Core/Models/WorkflowTask.cs | 34 ++++++-- src/CycloneDX.Core/Models/Workspace.cs | 32 ++++++-- 74 files changed, 1348 insertions(+), 210 deletions(-) diff --git a/src/CycloneDX.Core/BomUtils.cs b/src/CycloneDX.Core/BomUtils.cs index 27c14fce..1e49ce0c 100644 --- a/src/CycloneDX.Core/BomUtils.cs +++ b/src/CycloneDX.Core/BomUtils.cs @@ -254,9 +254,7 @@ internal static Bom CopyBomAndDowngrade(Bom bom) public static Bom Copy(this Bom bom) { - var protoBom = Protobuf.Serializer.SerializeForDeepCopy(bom); - var bomCopy = Protobuf.Serializer.Deserialize(protoBom); - return bomCopy; + return (Bom)bom.Clone(); } public static void EnqueueMany(this Queue queue, IEnumerable items) diff --git a/src/CycloneDX.Core/Models/Annotation.cs b/src/CycloneDX.Core/Models/Annotation.cs index bf10b666..7deecdb9 100644 --- a/src/CycloneDX.Core/Models/Annotation.cs +++ b/src/CycloneDX.Core/Models/Annotation.cs @@ -20,17 +20,26 @@ using System.Xml.Serialization; using System.Text.Json.Serialization; using ProtoBuf; +using System.Linq; namespace CycloneDX.Models { [ProtoContract] - public class Annotation + public class Annotation : ICloneable { [XmlType("subject")] - public class XmlAnnotationSubject + public class XmlAnnotationSubject : ICloneable { [XmlAttribute("ref")] public string Ref { get; set; } + + public object Clone() + { + return new XmlAnnotationSubject() + { + Ref = this.Ref + }; + } } [XmlAttribute("bom-ref")] @@ -80,10 +89,24 @@ public DateTime? Timestamp get => _timestamp; set { _timestamp = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeTimestamp() { return Timestamp != null; } - + [XmlElement("text")] [ProtoMember(5)] public string Text { get; set; } + + public bool ShouldSerializeTimestamp() { return Timestamp != null; } + + public object Clone() + { + return new Annotation() + { + Annotator = (AnnotatorChoice)this.Annotator.Clone(), + BomRef = this.BomRef, + Subjects = this.Subjects.Select(x => x).ToList(), + Text = this.Text, + Timestamp = this.Timestamp, + XmlSubjects = this.XmlSubjects.Select(x => (XmlAnnotationSubject)x.Clone()).ToList() + }; + } } } diff --git a/src/CycloneDX.Core/Models/AnnotatorChoice.cs b/src/CycloneDX.Core/Models/AnnotatorChoice.cs index b996fec3..222578f7 100644 --- a/src/CycloneDX.Core/Models/AnnotatorChoice.cs +++ b/src/CycloneDX.Core/Models/AnnotatorChoice.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class AnnotatorChoice + public class AnnotatorChoice : ICloneable { [XmlElement("organization")] [ProtoMember(1)] @@ -38,5 +39,16 @@ public class AnnotatorChoice [XmlElement("service")] [ProtoMember(4)] public Service Service { get; set; } + + public object Clone() + { + return new AnnotatorChoice() + { + Component = (Component)this.Component.Clone(), + Individual = (OrganizationalContact)this.Individual.Clone(), + Organization = (OrganizationalEntity)this.Organization.Clone(), + Service = (Service)this.Service.Clone(), + }; + } } } diff --git a/src/CycloneDX.Core/Models/AttachedText.cs b/src/CycloneDX.Core/Models/AttachedText.cs index 26b8d004..2465e631 100644 --- a/src/CycloneDX.Core/Models/AttachedText.cs +++ b/src/CycloneDX.Core/Models/AttachedText.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class AttachedText + public class AttachedText : ICloneable { [XmlAttribute("content-type")] [ProtoMember(1)] @@ -34,5 +35,15 @@ public class AttachedText [XmlText] [ProtoMember(3)] public string Content { get; set; } + + public object Clone() + { + return new AttachedText() + { + Content = Content, + ContentType = ContentType, + Encoding = Encoding + }; + } } } diff --git a/src/CycloneDX.Core/Models/Bom.cs b/src/CycloneDX.Core/Models/Bom.cs index ed3c6644..4cb29854 100644 --- a/src/CycloneDX.Core/Models/Bom.cs +++ b/src/CycloneDX.Core/Models/Bom.cs @@ -21,6 +21,7 @@ using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; +using CycloneDX.Models.Vulnerabilities; using ProtoBuf; namespace CycloneDX.Models @@ -29,7 +30,7 @@ namespace CycloneDX.Models [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] [XmlRoot("bom", IsNullable=false)] [ProtoContract] - public class Bom + public class Bom : ICloneable { [XmlIgnore] public string BomFormat => "CycloneDX"; @@ -111,8 +112,7 @@ public int NonNullableVersion Version = value; } } - public bool ShouldSerializeNonNullableVersion() { return Version.HasValue; } - + [XmlElement("metadata")] [ProtoMember(4)] public Metadata Metadata { get; set; } @@ -126,20 +126,17 @@ public int NonNullableVersion [XmlArrayItem("service")] [ProtoMember(6)] public List Services { get; set; } - public bool ShouldSerializeServices() { return Services?.Count > 0; } - + [XmlArray("externalReferences")] [XmlArrayItem("reference")] [ProtoMember(7)] public List ExternalReferences { get; set; } - public bool ShouldSerializeExternalReferences() { return ExternalReferences?.Count > 0; } - + [XmlArray("dependencies")] [XmlArrayItem("dependency")] [ProtoMember(8)] public List Dependencies { get; set; } - public bool ShouldSerializeDependencies() { return Dependencies?.Count > 0; } - + [XmlArray("compositions")] [XmlArrayItem("composition")] [ProtoMember(9)] @@ -149,24 +146,51 @@ public int NonNullableVersion [XmlArrayItem("vulnerability")] [ProtoMember(10)] public List Vulnerabilities { get; set; } - public bool ShouldSerializeVulnerabilities() { return Vulnerabilities?.Count > 0; } [XmlArray("annotations")] [XmlArrayItem("annotation")] [ProtoMember(11)] public List Annotations { get; set; } - public bool ShouldSerializeAnnotations() { return Annotations?.Count > 0; } [XmlArray("properties")] [XmlArrayItem("property")] [ProtoMember(12)] public List Properties { get; set; } - public bool ShouldSerializeProperties() { return Properties?.Count > 0; } [XmlArray("formulation")] [XmlArrayItem("formula")] [ProtoMember(13)] public List Formulation { get; set; } + + public bool ShouldSerializeNonNullableVersion() { return Version.HasValue; } + public bool ShouldSerializeServices() { return Services?.Count > 0; } + public bool ShouldSerializeExternalReferences() { return ExternalReferences?.Count > 0; } + public bool ShouldSerializeDependencies() { return Dependencies?.Count > 0; } + public bool ShouldSerializeVulnerabilities() { return Vulnerabilities?.Count > 0; } + public bool ShouldSerializeAnnotations() { return Annotations?.Count > 0; } + public bool ShouldSerializeProperties() { return Properties?.Count > 0; } public bool ShouldSerializeFormulation() { return Formulation?.Count > 0; } + + public object Clone() + { + return new Bom() + { + Annotations = this.Annotations.Select(x => (Annotation)x.Clone()).ToList(), + Components = this.Components.Select(x => (Component)x.Clone()).ToList(), + Compositions = this.Compositions.Select(x => (Composition)x.Clone()).ToList(), + Dependencies = this.Dependencies.Select(x => (Dependency)x.Clone()).ToList(), + ExternalReferences = this.ExternalReferences.Select(x => (ExternalReference)x.Clone()).ToList(), + Formulation = this.Formulation.Select(x => (Formula)x.Clone()).ToList(), + Metadata = (Metadata)this.Metadata.Clone(), + NonNullableVersion = this.NonNullableVersion, + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + SerialNumber = this.SerialNumber, + Services = this.Services.Select(x => (Service)x.Clone()).ToList(), + SpecVersion = this.SpecVersion, + SpecVersionString = this.SpecVersionString, + Version = this.Version, + Vulnerabilities = this.Vulnerabilities.Select(x => (Vulnerability)x.Clone()).ToList(), + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Callstack.cs b/src/CycloneDX.Core/Models/Callstack.cs index 522d68db..b7713447 100644 --- a/src/CycloneDX.Core/Models/Callstack.cs +++ b/src/CycloneDX.Core/Models/Callstack.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Linq.Expressions; using System.Text.Json.Serialization; using System.Xml; @@ -28,11 +29,11 @@ namespace CycloneDX.Models { [XmlType("callstack")] [ProtoContract] - public class Callstack + public class Callstack : ICloneable { [XmlType("frame")] [ProtoContract] - public class Frame + public class Frame : ICloneable { [XmlElement("package")] [ProtoMember(1)] @@ -50,7 +51,6 @@ public class Frame [XmlArrayItem("parameter")] [ProtoMember(4)] public List Parameters { get; set; } - public bool ShouldSerializeParameters() { return Parameters?.Count > 0; } [XmlElement("line")] [ProtoMember(5)] @@ -63,11 +63,35 @@ public class Frame [XmlElement("fullFilename")] [ProtoMember(7)] public string FullFilename { get; set; } + + public bool ShouldSerializeParameters() { return Parameters?.Count > 0; } + + public object Clone() + { + return new Frame() + { + Column = this.Column, + FullFilename = this.FullFilename, + Function = this.Function, + Line = this.Line, + Module = this.Module, + Package = this.Package, + Parameters = this.Parameters.Select(x => x).ToList() + }; + } } [XmlArray("frames")] [XmlArrayItem("frame")] [ProtoMember(1)] public List Frames { get; set; } + + public object Clone() + { + return new Callstack() + { + Frames = this.Frames.Select(x => (Frame)x.Clone()).ToList() + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Command.cs b/src/CycloneDX.Core/Models/Command.cs index f0bfc1ff..5925bc94 100644 --- a/src/CycloneDX.Core/Models/Command.cs +++ b/src/CycloneDX.Core/Models/Command.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("command")] [ProtoContract] - public class Command + public class Command : ICloneable { [XmlElement("executed")] [ProtoMember(1)] @@ -36,6 +37,16 @@ public class Command [XmlArrayItem("property")] [ProtoMember(2)] public List Properties { get; set; } + public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new Command() + { + Executed = this.Executed, + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList() + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Commit.cs b/src/CycloneDX.Core/Models/Commit.cs index bd0ea2d1..adcc9de5 100644 --- a/src/CycloneDX.Core/Models/Commit.cs +++ b/src/CycloneDX.Core/Models/Commit.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class Commit + public class Commit : ICloneable { [XmlElement("uid")] [ProtoMember(1)] @@ -42,5 +43,17 @@ public class Commit [XmlElement("message")] [ProtoMember(5)] public string Message { get; set; } + + public object Clone() + { + return new Commit() + { + Author = (IdentifiableAction)this.Author.Clone(), + Committer = (IdentifiableAction)this.Committer.Clone(), + Message = this.Message, + Uid = this.Uid, + Url = this.Url, + }; + } } } diff --git a/src/CycloneDX.Core/Models/Component.cs b/src/CycloneDX.Core/Models/Component.cs index 64bf1e50..6c96fd99 100644 --- a/src/CycloneDX.Core/Models/Component.cs +++ b/src/CycloneDX.Core/Models/Component.cs @@ -19,6 +19,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Net; using System.Text.Json.Serialization; using System.Xml.Serialization; @@ -29,7 +30,7 @@ namespace CycloneDX.Models [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] [XmlType("component")] [ProtoContract] - public class Component: IEquatable + public class Component: ICloneable, IEquatable { [ProtoContract] public enum Classification @@ -133,18 +134,15 @@ public ComponentScope NonNullableScope Scope = value; } } - public bool ShouldSerializeNonNullableScope() { return Scope.HasValue; } - + [XmlArray("hashes")] [ProtoMember(12)] public List Hashes { get; set; } - public bool ShouldSerializeHashes() { return Hashes?.Count > 0; } - + [XmlIgnore] [ProtoMember(13)] public List Licenses { get; set; } - [XmlElement("licenses")] [JsonIgnore, ProtoIgnore] [EditorBrowsable(EditorBrowsableState.Never)] @@ -154,9 +152,7 @@ public LicenseChoiceList LicensesSerialized get { return Licenses != null ? new LicenseChoiceList(Licenses) : null; } set { Licenses = value.Licenses; } } - [EditorBrowsable(EditorBrowsableState.Never)] - public bool ShouldSerializeLicensesSerialized() { return Licenses?.Count > 0; } - + [XmlElement("copyright")] [ProtoMember(14)] public string Copyright { get; set; } @@ -190,8 +186,7 @@ public bool NonNullableModified Modified = value; } } - public bool ShouldSerializeNonNullableModified() { return Modified.HasValue; } - + [XmlElement("pedigree")] [ProtoMember(19)] public Pedigree Pedigree { get; set; } @@ -200,18 +195,12 @@ public bool NonNullableModified [XmlArrayItem("reference")] [ProtoMember(20)] public List ExternalReferences { get; set; } - public bool ShouldSerializeExternalReferences() { return ExternalReferences?.Count > 0; } - - //In the xml format, Properties is in front of Components. - //XML serialization uses the member order unless explicitly specified differently. - public bool ShouldSerializeComponents() { return Components?.Count > 0; } - + [XmlArray("properties")] [XmlArrayItem("property")] [ProtoMember(22)] public List Properties { get; set; } - public bool ShouldSerializeProperties() { return Properties?.Count > 0; } - + [XmlArray("components")] [ProtoMember(21)] public List Components { get; set; } @@ -223,7 +212,6 @@ public bool NonNullableModified [XmlElement("releaseNotes")] [ProtoMember(24)] public ReleaseNotes ReleaseNotes { get; set; } - public bool ShouldSerializeReleaseNotes() { return ReleaseNotes != null; } [XmlElement("modelCard")] [ProtoMember(25)] @@ -253,5 +241,54 @@ public override int GetHashCode() { return Json.Serializer.Serialize(this).GetHashCode(); } + + public bool ShouldSerializeNonNullableScope() { return Scope.HasValue; } + public bool ShouldSerializeHashes() { return Hashes?.Count > 0; } + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeLicensesSerialized() { return Licenses?.Count > 0; } + public bool ShouldSerializeNonNullableModified() { return Modified.HasValue; } + public bool ShouldSerializeExternalReferences() { return ExternalReferences?.Count > 0; } + + //In the xml format, Properties is in front of Components. + //XML serialization uses the member order unless explicitly specified differently. + public bool ShouldSerializeComponents() { return Components?.Count > 0; } + public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + public bool ShouldSerializeReleaseNotes() { return ReleaseNotes != null; } + + public object Clone() + { + return new Component() + { + Author = this.Author, + BomRef = this.BomRef, + Components = this.Components.Select(x => (Component)x.Clone()).ToList(), + Copyright = this.Copyright, + Cpe = this.Cpe, + Data = (Data)this.Data.Clone(), + Description = this.Description, + Evidence = (Evidence)this.Evidence.Clone(), + ExternalReferences = this.ExternalReferences.Select(x => (ExternalReference)x.Clone()).ToList(), + Group = this.Group, + Hashes = this.Hashes.Select(x => (Hash)x.Clone()).ToList(), + Licenses = this.Licenses.Select(x => (LicenseChoice)x.Clone()).ToList(), + LicensesSerialized = (LicenseChoiceList)this.LicensesSerialized.Clone(), + MimeType = this.MimeType, + ModelCard = (ModelCard)this.ModelCard.Clone(), + Modified = this.Modified, + Name = this.Name, + NonNullableModified = this.NonNullableModified, + NonNullableScope = this.NonNullableScope, + Pedigree = (Pedigree)this.Pedigree.Clone(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Publisher = this.Publisher, + Purl = this.Purl, + ReleaseNotes = (ReleaseNotes)this.ReleaseNotes.Clone(), + Scope = this.Scope, + Supplier = (OrganizationalEntity)this.Supplier.Clone(), + Swid = (Swid)this.Swid.Clone(), + Type = this.Type, + Version = this.Version, + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Composition.cs b/src/CycloneDX.Core/Models/Composition.cs index cf5cb654..41ec9f62 100644 --- a/src/CycloneDX.Core/Models/Composition.cs +++ b/src/CycloneDX.Core/Models/Composition.cs @@ -21,11 +21,12 @@ using System.Xml.Serialization; using System.Text.Json.Serialization; using ProtoBuf; +using System.Linq; namespace CycloneDX.Models { [ProtoContract] - public class Composition : IXmlSerializable, IEquatable + public class Composition : ICloneable, IXmlSerializable, IEquatable { [ProtoContract] public enum AggregateType @@ -215,5 +216,17 @@ public override int GetHashCode() { return CycloneDX.Json.Serializer.Serialize(this).GetHashCode(); } + + public object Clone() + { + return new Composition() + { + Aggregate = this.Aggregate, + Assemblies = this.Assemblies.Select(x => x).ToList(), + BomRef = this.BomRef, + Dependencies = this.Dependencies.Select(x => x).ToList(), + Vulnerabilities = this.Vulnerabilities.Select(x => x).ToList(), + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Data.cs b/src/CycloneDX.Core/Models/Data.cs index 7741a68a..7276cc7e 100644 --- a/src/CycloneDX.Core/Models/Data.cs +++ b/src/CycloneDX.Core/Models/Data.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -25,7 +26,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class Data + public class Data : ICloneable { [ProtoContract] public enum DataType @@ -43,7 +44,7 @@ public enum DataType } [ProtoContract] - public class DataContents + public class DataContents : ICloneable { [XmlElement("attachment")] [ProtoMember(1)] @@ -57,7 +58,18 @@ public class DataContents [XmlArrayItem("property")] [ProtoMember(22)] public List Properties { get; set; } + public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new DataContents() + { + Attachment = (AttachedText)this.Attachment.Clone(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Url = this.Url + }; + } } [JsonPropertyName("bom-ref")] @@ -96,5 +108,21 @@ public class DataContents [XmlElement("governance")] [ProtoMember(9)] public DataGovernance Governance { get; set; } + + public object Clone() + { + return new Data() + { + BomRef = this.BomRef, + Classification = this.Classification, + Contents = (DataContents)this.Contents.Clone(), + Description = this.Description, + Governance = (DataGovernance)this.Governance.Clone(), + Graphics = (GraphicsCollection)this.Graphics.Clone(), + Name = this.Name, + SensitiveData = this.SensitiveData, + Type = this.Type, + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/DataClassification.cs b/src/CycloneDX.Core/Models/DataClassification.cs index 1defc8a2..a769ec07 100644 --- a/src/CycloneDX.Core/Models/DataClassification.cs +++ b/src/CycloneDX.Core/Models/DataClassification.cs @@ -15,6 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; using System.Text.Json.Serialization; using System.Xml.Serialization; @@ -25,7 +26,7 @@ namespace CycloneDX.Models // this is the version that was prior to v1.5 [XmlType("classification")] [ProtoContract] - public class DataClassification + public class DataClassification : ICloneable { [XmlAttribute("flow")] [ProtoMember(1, IsRequired=true)] @@ -34,5 +35,14 @@ public class DataClassification [XmlText] [ProtoMember(2)] public string Classification { get; set; } + + public object Clone() + { + return new DataClassification() + { + Classification = this.Classification, + Flow = this.Flow + }; + } } } diff --git a/src/CycloneDX.Core/Models/DataFlow.cs b/src/CycloneDX.Core/Models/DataFlow.cs index 099d6d1b..3abf6c85 100644 --- a/src/CycloneDX.Core/Models/DataFlow.cs +++ b/src/CycloneDX.Core/Models/DataFlow.cs @@ -15,7 +15,9 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -24,7 +26,7 @@ namespace CycloneDX.Models { [XmlType("dataflow")] [ProtoContract] - public class DataFlow + public class DataFlow : ICloneable { [XmlIgnore] [JsonPropertyName("flow")] @@ -67,11 +69,27 @@ public DataClassification XmlClassification { [XmlElement("source")] [ProtoMember(5)] public List Source { get; set; } - public bool ShouldSerializeSource() => Source != null; [XmlElement("destination")] [ProtoMember(6)] public List Destination { get; set; } + + public bool ShouldSerializeSource() => Source != null; public bool ShouldSerializeDestination() => Destination != null; + + public object Clone() + { + return new DataFlow() + { + Classification = this.Classification, + Description = this.Description, + Destination = this.Destination.Select(x => (DataflowSourceDestination)x.Clone()).ToList(), + Flow = this.Flow, + Governance = (DataGovernance)this.Governance.Clone(), + Name = this.Name, + Source = this.Source.Select(x => (DataflowSourceDestination)x.Clone()).ToList(), + XmlClassification = (DataClassification)this.XmlClassification.Clone(), + }; + } } } diff --git a/src/CycloneDX.Core/Models/DataGovernance.cs b/src/CycloneDX.Core/Models/DataGovernance.cs index 0b8b939e..b9183758 100644 --- a/src/CycloneDX.Core/Models/DataGovernance.cs +++ b/src/CycloneDX.Core/Models/DataGovernance.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,24 +27,35 @@ namespace CycloneDX.Models { [XmlType("data-governance")] [ProtoContract] - public class DataGovernance + public class DataGovernance : ICloneable { [XmlArray("custodians")] [XmlArrayItem("custodian")] [ProtoMember(1)] public List Custodians { get; set; } - public bool ShouldSerializeCustodians() { return Custodians?.Count > 0; } [XmlArray("stewards")] [XmlArrayItem("steward")] [ProtoMember(2)] public List Stewards { get; set; } - public bool ShouldSerializeStewards() { return Stewards?.Count > 0; } - + [XmlArray("owners")] [XmlArrayItem("owner")] [ProtoMember(3)] public List Owners { get; set; } + + public bool ShouldSerializeCustodians() { return Custodians?.Count > 0; } + public bool ShouldSerializeStewards() { return Stewards?.Count > 0; } public bool ShouldSerializeOwners() { return Owners?.Count > 0; } + + public object Clone() + { + return new DataGovernance() + { + Custodians = this.Custodians.Select(x => (OrganizationalEntityOrContact)x.Clone()).ToList(), + Owners = this.Owners.Select(x => (OrganizationalEntityOrContact)x.Clone()).ToList(), + Stewards = this.Stewards.Select(x => (OrganizationalEntityOrContact)x.Clone()).ToList(), + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/DataflowSourceDestination.cs b/src/CycloneDX.Core/Models/DataflowSourceDestination.cs index e2b4157b..e89b91de 100644 --- a/src/CycloneDX.Core/Models/DataflowSourceDestination.cs +++ b/src/CycloneDX.Core/Models/DataflowSourceDestination.cs @@ -15,16 +15,25 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class DataflowSourceDestination + public class DataflowSourceDestination : ICloneable { [XmlElement("url")] [ProtoMember(1)] public string Url { get; set; } + + public object Clone() + { + return new DataflowSourceDestination() + { + Url = this.Url + }; + } } } diff --git a/src/CycloneDX.Core/Models/DatasetChoice.cs b/src/CycloneDX.Core/Models/DatasetChoice.cs index c50d9684..3b2937d7 100644 --- a/src/CycloneDX.Core/Models/DatasetChoice.cs +++ b/src/CycloneDX.Core/Models/DatasetChoice.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class DatasetChoice + public class DatasetChoice : ICloneable { [XmlElement("dataset")] [ProtoMember(1)] @@ -30,5 +31,14 @@ public class DatasetChoice [XmlElement("ref")] [ProtoMember(2)] public string Ref { get; set; } + + public object Clone() + { + return new DatasetChoice() + { + DataSet = this.DataSet, + Ref = this.Ref + }; + } } } diff --git a/src/CycloneDX.Core/Models/DatasetChoices.cs b/src/CycloneDX.Core/Models/DatasetChoices.cs index 6dafcb10..1f51ee0c 100644 --- a/src/CycloneDX.Core/Models/DatasetChoices.cs +++ b/src/CycloneDX.Core/Models/DatasetChoices.cs @@ -15,7 +15,9 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json.Serialization; using System.Xml; using System.Xml.Serialization; @@ -24,7 +26,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class DatasetChoices : List, IXmlSerializable + public class DatasetChoices : List, ICloneable, IXmlSerializable { private static XmlSerializer _datasetSerializer; private static XmlSerializer GetDatasetSerializer() @@ -38,7 +40,13 @@ private static XmlSerializer GetDatasetSerializer() return _datasetSerializer; } - + + public object Clone() + { + return this.Select(x => (DatasetChoice)x.Clone()).ToList(); + + } + public System.Xml.Schema.XmlSchema GetSchema() { return null; } diff --git a/src/CycloneDX.Core/Models/Dependency.cs b/src/CycloneDX.Core/Models/Dependency.cs index 7b0fe510..7a3dd1ca 100644 --- a/src/CycloneDX.Core/Models/Dependency.cs +++ b/src/CycloneDX.Core/Models/Dependency.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("dependency")] [ProtoContract] - public class Dependency: IEquatable + public class Dependency : ICloneable, IEquatable { [XmlAttribute("ref")] [ProtoMember(1)] @@ -36,6 +37,15 @@ public class Dependency: IEquatable [ProtoMember(2)] public List Dependencies { get; set; } + public object Clone() + { + return new Dependency() + { + Dependencies = this.Dependencies.Select(x => (Dependency)x.Clone()).ToList(), + Ref = this.Ref, + }; + } + public override bool Equals(object obj) { var other = obj as Dependency; diff --git a/src/CycloneDX.Core/Models/Diff.cs b/src/CycloneDX.Core/Models/Diff.cs index 3eda6e66..33d42322 100644 --- a/src/CycloneDX.Core/Models/Diff.cs +++ b/src/CycloneDX.Core/Models/Diff.cs @@ -15,13 +15,15 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; +using System.Runtime.InteropServices; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class Diff + public class Diff : ICloneable { [XmlElement("text")] [ProtoMember(1)] @@ -29,5 +31,14 @@ public class Diff [XmlElement("url")] [ProtoMember(2)] public string Url { get; set; } + + public object Clone() + { + return new Diff() + { + Text = (AttachedText)this.Text.Clone(), + Url = this.Url + }; + } } } diff --git a/src/CycloneDX.Core/Models/EnvironmentVarChoice.cs b/src/CycloneDX.Core/Models/EnvironmentVarChoice.cs index 733b6ed0..565c5edb 100644 --- a/src/CycloneDX.Core/Models/EnvironmentVarChoice.cs +++ b/src/CycloneDX.Core/Models/EnvironmentVarChoice.cs @@ -15,6 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Text.Json.Serialization; using System.Xml; using System.Xml.Serialization; @@ -23,12 +24,21 @@ namespace CycloneDX.Models { [ProtoContract] - public class EnvironmentVarChoice + public class EnvironmentVarChoice : ICloneable { [ProtoMember(1)] public Property Property { get; set; } [ProtoMember(2)] public string Value { get; set; } + + public object Clone() + { + return new EnvironmentVarChoice() + { + Property = this.Property, + Value = this.Value + }; + } } } diff --git a/src/CycloneDX.Core/Models/EnvironmentVarChoices.cs b/src/CycloneDX.Core/Models/EnvironmentVarChoices.cs index b7df48d8..75e253d4 100644 --- a/src/CycloneDX.Core/Models/EnvironmentVarChoices.cs +++ b/src/CycloneDX.Core/Models/EnvironmentVarChoices.cs @@ -15,7 +15,9 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json.Serialization; using System.Xml; using System.Xml.Serialization; @@ -24,8 +26,13 @@ namespace CycloneDX.Models { [ProtoContract] - public class EnvironmentVarChoices : List, IXmlSerializable + public class EnvironmentVarChoices : List, ICloneable, IXmlSerializable { + public object Clone() + { + return this.Select(x => (EnvironmentVarChoice)x.Clone()).ToList(); + } + public System.Xml.Schema.XmlSchema GetSchema() { return null; } diff --git a/src/CycloneDX.Core/Models/Event.cs b/src/CycloneDX.Core/Models/Event.cs index ae9d4196..dad15932 100644 --- a/src/CycloneDX.Core/Models/Event.cs +++ b/src/CycloneDX.Core/Models/Event.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("event")] [ProtoContract] - public class Event + public class Event : ICloneable { [XmlElement("uid")] [ProtoMember(1)] @@ -44,7 +45,6 @@ public DateTime? TimeReceived get => _timeReceived; set { _timeReceived = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeTimeReceived() { return TimeReceived != null; } [XmlElement("data")] [ProtoMember(4)] @@ -62,6 +62,22 @@ public DateTime? TimeReceived [XmlArrayItem("property")] [ProtoMember(7)] public List Properties { get; set; } + + public bool ShouldSerializeTimeReceived() { return TimeReceived != null; } public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new Event() + { + Data = (AttachedText)this.Data.Clone(), + Description = this.Description, + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Source = (ResourceReferenceChoice)this.Source.Clone(), + Target = (ResourceReferenceChoice)this.Target.Clone(), + TimeReceived = this.TimeReceived, + Uid = this.Uid + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Evidence.cs b/src/CycloneDX.Core/Models/Evidence.cs index cead04c9..d734938a 100644 --- a/src/CycloneDX.Core/Models/Evidence.cs +++ b/src/CycloneDX.Core/Models/Evidence.cs @@ -19,6 +19,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Linq.Expressions; using System.Text.Json.Serialization; using System.Xml; @@ -29,7 +30,7 @@ namespace CycloneDX.Models { [XmlType("evidence")] [ProtoContract] - public class Evidence + public class Evidence : ICloneable { [XmlIgnore] [ProtoMember(1)] @@ -43,8 +44,6 @@ public LicenseChoiceList LicensesSerialized get { return Licenses != null ? new LicenseChoiceList(Licenses) : null; } set { Licenses = value.Licenses; } } - [EditorBrowsable(EditorBrowsableState.Never)] - public bool ShouldSerializeLicensesSerialized() { return Licenses?.Count > 0; } [XmlArray("copyright", Order = 4)] [XmlArrayItem("text")] @@ -63,5 +62,21 @@ public LicenseChoiceList LicensesSerialized [XmlElement("callstack", Order = 2)] [ProtoMember(5)] public Callstack Callstack { get; set; } + + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeLicensesSerialized() { return Licenses?.Count > 0; } + + public object Clone() + { + return new Evidence() + { + Callstack = (Callstack)this.Callstack.Clone(), + Copyright = this.Copyright, + Identity = (EvidenceIdentity)this.Identity.Clone(), + Licenses = this.Licenses.Select(x => (LicenseChoice)x.Clone()).ToList(), + LicensesSerialized = (LicenseChoiceList)this.LicensesSerialized.Clone(), + Occurrences = this.Occurrences.Select(x => (EvidenceOccurrence)x.Clone()).ToList(), + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/EvidenceIdentity.cs b/src/CycloneDX.Core/Models/EvidenceIdentity.cs index a9c3c18a..543a7c25 100644 --- a/src/CycloneDX.Core/Models/EvidenceIdentity.cs +++ b/src/CycloneDX.Core/Models/EvidenceIdentity.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Linq.Expressions; using System.Text.Json.Serialization; using System.Xml; @@ -28,7 +29,7 @@ namespace CycloneDX.Models { [XmlType("evidence-identity")] [ProtoContract] - public class EvidenceIdentity + public class EvidenceIdentity : ICloneable { [ProtoContract] public enum EvidenceFieldType @@ -67,5 +68,16 @@ public enum EvidenceFieldType [XmlElement("tools")] [ProtoMember(4)] public EvidenceTools Tools { get; set; } + + public object Clone() + { + return new EvidenceIdentity() + { + Confidence = this.Confidence, + Field = this.Field, + Methods = this.Methods.Select(x => (EvidenceMethods)x.Clone()).ToList(), + Tools = (EvidenceTools)this.Tools.Clone() + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/EvidenceMethods.cs b/src/CycloneDX.Core/Models/EvidenceMethods.cs index dce4170e..da275353 100644 --- a/src/CycloneDX.Core/Models/EvidenceMethods.cs +++ b/src/CycloneDX.Core/Models/EvidenceMethods.cs @@ -28,7 +28,7 @@ namespace CycloneDX.Models { [XmlType("evidence-methods")] [ProtoContract] - public class EvidenceMethods + public class EvidenceMethods : ICloneable { [ProtoContract] public enum EvidenceTechnique @@ -66,5 +66,15 @@ public enum EvidenceTechnique [XmlElement("value")] [ProtoMember(3)] public string Value { get; set; } + + public object Clone() + { + return new EvidenceMethods() + { + Confidence = this.Confidence, + Technique = this.Technique, + Value = this.Value + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/EvidenceOccurrence.cs b/src/CycloneDX.Core/Models/EvidenceOccurrence.cs index 126ce97a..7783e50d 100644 --- a/src/CycloneDX.Core/Models/EvidenceOccurrence.cs +++ b/src/CycloneDX.Core/Models/EvidenceOccurrence.cs @@ -28,7 +28,7 @@ namespace CycloneDX.Models { [XmlType("evidence-occurrence")] [ProtoContract] - public class EvidenceOccurrence + public class EvidenceOccurrence : ICloneable { [JsonPropertyName("bom-ref")] [XmlAttribute("bom-ref")] @@ -38,5 +38,14 @@ public class EvidenceOccurrence [XmlElement("location")] [ProtoMember(2)] public string Location { get; set; } + + public object Clone() + { + return new EvidenceOccurrence() + { + BomRef = this.BomRef, + Location = this.Location + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/EvidenceTools.cs b/src/CycloneDX.Core/Models/EvidenceTools.cs index 2e675b1c..3c007326 100644 --- a/src/CycloneDX.Core/Models/EvidenceTools.cs +++ b/src/CycloneDX.Core/Models/EvidenceTools.cs @@ -22,10 +22,18 @@ namespace CycloneDX.Models { - public class EvidenceTools : List, IXmlSerializable + public class EvidenceTools : List, ICloneable, IXmlSerializable { private readonly string _elementName = "tool"; + public object Clone() + { + return new EvidenceTools() + { + //to do determine how to handle this + }; + } + public System.Xml.Schema.XmlSchema GetSchema() { return null; } diff --git a/src/CycloneDX.Core/Models/ExternalReference.cs b/src/CycloneDX.Core/Models/ExternalReference.cs index 69204fca..1b5ba35e 100644 --- a/src/CycloneDX.Core/Models/ExternalReference.cs +++ b/src/CycloneDX.Core/Models/ExternalReference.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")] [ProtoContract] - public class ExternalReference : IEquatable + public class ExternalReference : ICloneable, IEquatable { [ProtoContract] public enum ExternalReferenceType @@ -126,6 +127,7 @@ public enum ExternalReferenceType [XmlArray("hashes")] [ProtoMember(4)] public List Hashes { get; set; } + public bool ShouldSerializeHashes() { return Hashes?.Count > 0; } public override bool Equals(object obj) @@ -148,5 +150,16 @@ public override int GetHashCode() { return CycloneDX.Json.Serializer.Serialize(this).GetHashCode(); } + + public object Clone() + { + return new ExternalReference() + { + Comment = this.Comment, + Hashes = this.Hashes.Select(x => (Hash)x.Clone()).ToList(), + Type = this.Type, + Url = this.Url, + }; + } } } diff --git a/src/CycloneDX.Core/Models/Formula.cs b/src/CycloneDX.Core/Models/Formula.cs index d0b5ffed..d43a6e4e 100644 --- a/src/CycloneDX.Core/Models/Formula.cs +++ b/src/CycloneDX.Core/Models/Formula.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("formula")] [ProtoContract] - public class Formula + public class Formula : ICloneable { [JsonPropertyName("bom-ref")] [XmlAttribute("bom-ref")] @@ -42,8 +43,7 @@ public class Formula [XmlArrayItem("service")] [ProtoMember(3)] public List Services { get; set; } - public bool ShouldSerializeServices() { return Services?.Count > 0; } - + [XmlArray("workflows")] [XmlArrayItem("workflow")] [ProtoMember(4)] @@ -53,6 +53,20 @@ public class Formula [XmlArrayItem("property")] [ProtoMember(5)] public List Properties { get; set; } + + public bool ShouldSerializeServices() { return Services?.Count > 0; } public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new Formula() + { + BomRef = this.BomRef, + Components = this.Components.Select(x => (Component)x.Clone()).ToList(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Services = this.Services.Select(x => (Service)x.Clone()).ToList(), + Workflows = this.Workflows.Select(x => (Workflow)x.Clone()).ToList() + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/GraphicsCollection.cs b/src/CycloneDX.Core/Models/GraphicsCollection.cs index cc35bb19..d47fb642 100644 --- a/src/CycloneDX.Core/Models/GraphicsCollection.cs +++ b/src/CycloneDX.Core/Models/GraphicsCollection.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,10 +27,10 @@ namespace CycloneDX.Models { [XmlType("graphics")] [ProtoContract] - public class GraphicsCollection + public class GraphicsCollection : ICloneable { [ProtoContract] - public class Graphic + public class Graphic : ICloneable { [XmlElement("name")] [ProtoMember(1)] @@ -38,6 +39,15 @@ public class Graphic [XmlElement("image")] [ProtoMember(2)] public AttachedText Image { get; set; } + + public object Clone() + { + return new Graphic() + { + Image = (AttachedText)this.Image.Clone(), + Name = this.Name, + }; + } } [XmlElement("description")] @@ -48,5 +58,14 @@ public class Graphic [XmlArrayItem("graphic")] [ProtoMember(2)] public List Collection { get; set; } + + public object Clone() + { + return new GraphicsCollection() + { + Collection = this.Collection.Select(x => (Graphic)x.Clone()).ToList(), + Description = this.Description, + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Hash.cs b/src/CycloneDX.Core/Models/Hash.cs index 567f8446..b68961a8 100644 --- a/src/CycloneDX.Core/Models/Hash.cs +++ b/src/CycloneDX.Core/Models/Hash.cs @@ -15,6 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; @@ -22,7 +23,7 @@ namespace CycloneDX.Models { [XmlType("hash")] [ProtoContract] - public class Hash + public class Hash : ICloneable { [ProtoContract] public enum HashAlgorithm @@ -62,5 +63,14 @@ public enum HashAlgorithm [XmlText] [ProtoMember(2)] public string Content { get; set; } + + public object Clone() + { + return new Hash() + { + Alg = this.Alg, + Content = this.Content + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/IdentifiableAction.cs b/src/CycloneDX.Core/Models/IdentifiableAction.cs index 8cc58365..8dcf163b 100644 --- a/src/CycloneDX.Core/Models/IdentifiableAction.cs +++ b/src/CycloneDX.Core/Models/IdentifiableAction.cs @@ -22,7 +22,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class IdentifiableAction + public class IdentifiableAction : ICloneable { private DateTime? _timestamp; [XmlElement("timestamp")] @@ -32,7 +32,6 @@ public DateTime? Timestamp get => _timestamp; set { _timestamp = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeTimestamp() { return Timestamp != null; } [XmlElement("name")] [ProtoMember(2)] @@ -41,5 +40,17 @@ public DateTime? Timestamp [XmlElement("email")] [ProtoMember(3)] public string Email { get; set; } + + public bool ShouldSerializeTimestamp() { return Timestamp != null; } + + public object Clone() + { + return new IdentifiableAction() + { + Email = this.Email, + Name = this.Name, + Timestamp = this.Timestamp, + }; + } } } diff --git a/src/CycloneDX.Core/Models/Input.cs b/src/CycloneDX.Core/Models/Input.cs index d8366765..e9901665 100644 --- a/src/CycloneDX.Core/Models/Input.cs +++ b/src/CycloneDX.Core/Models/Input.cs @@ -19,6 +19,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -27,7 +28,7 @@ namespace CycloneDX.Models { [XmlType("input")] [ProtoContract] - public class Input + public class Input : ICloneable { [XmlElement("resource")] [ProtoMember(3)] @@ -45,12 +46,10 @@ public class Input [XmlArrayItem("parameter")] [ProtoMember(4)] public List Parameters { get; set; } - public bool ShouldSerializeParameters() { return Parameters?.Count > 0; } [XmlElement("environmentVars")] [ProtoMember(5)] public EnvironmentVarChoices EnvironmentVars { get; set; } - public bool ShouldSerializeEnvironmentVars() { return EnvironmentVars != null; } [XmlElement("data")] [ProtoMember(6)] @@ -60,6 +59,24 @@ public class Input [XmlArrayItem("property")] [ProtoMember(7)] public List Properties { get; set; } + + public bool ShouldSerializeParameters() { return Parameters?.Count > 0; } + public bool ShouldSerializeEnvironmentVars() { return EnvironmentVars != null; } + public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new Input() + { + Data = (AttachedText)this.Data.Clone(), + EnvironmentVars = (EnvironmentVarChoices)this.EnvironmentVars.Clone(), + Parameters = this.Parameters.Select(x => (Parameter)x.Clone()).ToList(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Resource = (ResourceReferenceChoice)this.Resource.Clone(), + Source = (ResourceReferenceChoice)this.Source.Clone(), + Target = (ResourceReferenceChoice)this.Target.Clone(), + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Issue.cs b/src/CycloneDX.Core/Models/Issue.cs index d8f3f584..1f3a7433 100644 --- a/src/CycloneDX.Core/Models/Issue.cs +++ b/src/CycloneDX.Core/Models/Issue.cs @@ -15,6 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; using System.Xml.Serialization; using ProtoBuf; @@ -22,7 +23,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class Issue + public class Issue : ICloneable { [ProtoContract] public enum IssueClassification @@ -61,5 +62,18 @@ public enum IssueClassification [XmlArrayItem("url")] [ProtoMember(6)] public List References { get; set; } + + public object Clone() + { + return new Issue() + { + Description = this.Description, + Id = this.Id, + Name = this.Name, + References = this.References, + Source = (Source)this.Source.Clone(), + Type = this.Type + }; + } } } diff --git a/src/CycloneDX.Core/Models/License.cs b/src/CycloneDX.Core/Models/License.cs index 00edcc72..4ee8e31d 100644 --- a/src/CycloneDX.Core/Models/License.cs +++ b/src/CycloneDX.Core/Models/License.cs @@ -15,7 +15,9 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -24,18 +26,16 @@ namespace CycloneDX.Models { [XmlType("license")] [ProtoContract] - public class License + public class License : ICloneable { [XmlElement("id")] [ProtoMember(1)] public string Id { get; set; } - public bool ShouldSerializeId() { return Id != null; } - + [XmlElement("name")] [ProtoMember(2)] public string Name { get; set; } - public bool ShouldSerializeName() { return string.IsNullOrEmpty(Id); } - + [XmlElement("text")] [ProtoMember(3)] public AttachedText Text { get; set; } @@ -57,6 +57,23 @@ public class License [XmlArrayItem("property")] [ProtoMember(7)] public List Properties { get; set; } + + public bool ShouldSerializeId() { return Id != null; } + public bool ShouldSerializeName() { return string.IsNullOrEmpty(Id); } public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new License() + { + Id = this.Id, + BomRef = this.BomRef, + Licensing = (Licensing)this.Licensing.Clone(), + Name = this.Name, + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Text = (AttachedText)this.Text.Clone(), + Url = this.Url, + }; + } } } diff --git a/src/CycloneDX.Core/Models/LicenseChoice.cs b/src/CycloneDX.Core/Models/LicenseChoice.cs index 47639ed5..5e2e3345 100644 --- a/src/CycloneDX.Core/Models/LicenseChoice.cs +++ b/src/CycloneDX.Core/Models/LicenseChoice.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Text.Json.Serialization; using System.Xml.Serialization; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class LicenseChoice + public class LicenseChoice : ICloneable { [XmlElement("license")] [ProtoMember(1)] @@ -40,10 +41,20 @@ public class LicenseChoice [JsonPropertyName("bom-ref")] [ProtoMember(3)] public string BomRef { get; set; } + + public object Clone() + { + return new LicenseChoice() + { + BomRef = this.BomRef, + Expression = this.Expression, + License = (License)this.License.Clone() + }; + } } // This is a workaround to serialize licenses correctly - public class LicenseChoiceList : IXmlSerializable + public class LicenseChoiceList : ICloneable, IXmlSerializable { public LicenseChoiceList(List licenses) { @@ -123,5 +134,13 @@ public void WriteXml(System.Xml.XmlWriter writer) } } + + public object Clone() + { + return new LicenseChoiceList() + { + Licenses = this.Licenses.Select(x => (LicenseChoice)x.Clone()).ToList() + }; + } } } diff --git a/src/CycloneDX.Core/Models/Licensing.cs b/src/CycloneDX.Core/Models/Licensing.cs index ac62118a..784b7133 100644 --- a/src/CycloneDX.Core/Models/Licensing.cs +++ b/src/CycloneDX.Core/Models/Licensing.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -27,7 +28,7 @@ namespace CycloneDX.Models [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] [XmlType("licensing")] [ProtoContract] - public class Licensing + public class Licensing : ICloneable { [ProtoContract] public enum LicenseType @@ -93,7 +94,6 @@ public enum LicenseType [XmlArrayItem("licenseType")] [ProtoMember(6)] public List LicenseTypes { get; set; } - public bool ShouldSerializeLicenseTypes() { return LicenseTypes?.Count > 0; } private DateTime? _lastRenewal; [XmlElement("lastRenewal")] @@ -103,7 +103,6 @@ public DateTime? LastRenewal get => _lastRenewal; set { _lastRenewal = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeLastRenewal() { return LastRenewal != null; } private DateTime? _expiration; [XmlElement("expiration")] @@ -113,6 +112,26 @@ public DateTime? Expiration get => _expiration; set { _expiration = BomUtils.UtcifyDateTime(value); } } + + public bool ShouldSerializeLicenseTypes() { return LicenseTypes?.Count > 0; } + public bool ShouldSerializeLastRenewal() { return LastRenewal != null; } public bool ShouldSerializeExpiration() { return Expiration != null; } + + public object Clone() + { + return new Licensing() + { + AltIds = this.AltIds.Select(x => x).ToList(), + Expiration = this.Expiration, + LastRenewal = this.LastRenewal, + Licensee = (OrganizationalEntityOrContact)this.Licensee.Clone(), + LicenseTypes = this.LicenseTypes.Select(x => x).ToList(), + Licensor = (OrganizationalEntityOrContact)this.Licensor.Clone(), + PurchaseOrder = this.PurchaseOrder, + Purchaser = (OrganizationalEntityOrContact)this.Purchaser.Clone(), + _expiration = this._expiration, + _lastRenewal = this._lastRenewal, + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Lifecycles.cs b/src/CycloneDX.Core/Models/Lifecycles.cs index 48165d47..6bdc600d 100644 --- a/src/CycloneDX.Core/Models/Lifecycles.cs +++ b/src/CycloneDX.Core/Models/Lifecycles.cs @@ -23,7 +23,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class Lifecycles + public class Lifecycles : ICloneable { [ProtoContract] public enum LifecyclePhase @@ -50,7 +50,6 @@ public enum LifecyclePhase [ProtoMember(1)] [JsonIgnore] public LifecyclePhase Phase { get; set; } - public bool ShouldSerializePhase() => Phase != LifecyclePhase.Null; [XmlIgnore] [JsonPropertyName("phase")] @@ -67,5 +66,18 @@ public LifecyclePhase? JsonPhase [XmlElement("description")] [ProtoMember(3)] public string Description { get; set; } + + public bool ShouldSerializePhase() => Phase != LifecyclePhase.Null; + + public object Clone() + { + return new Lifecycles() + { + Description = this.Description, + JsonPhase = this.JsonPhase, + Name = this.Name, + Phase = this.Phase, + }; + } } } diff --git a/src/CycloneDX.Core/Models/Metadata.cs b/src/CycloneDX.Core/Models/Metadata.cs index fad8b4e3..46e4901e 100644 --- a/src/CycloneDX.Core/Models/Metadata.cs +++ b/src/CycloneDX.Core/Models/Metadata.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -25,7 +26,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class Metadata + public class Metadata : ICloneable { private DateTime? _timestamp; [XmlElement("timestamp")] @@ -35,7 +36,6 @@ public DateTime? Timestamp get => _timestamp; set { _timestamp = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeTimestamp() { return Timestamp != null; } [XmlElement("tools")] public ToolChoices Tools { get; set; } @@ -85,8 +85,6 @@ public List ProtobufTools [XmlIgnore] [ProtoMember(7)] public List Licenses { get; set; } - public bool ShouldSerializeLicenses() { return Licenses?.Count > 0; } - [XmlElement("licenses")] [JsonIgnore, ProtoIgnore] @@ -97,19 +95,40 @@ public LicenseChoiceList LicensesSerialized get { return Licenses != null ? new LicenseChoiceList(Licenses) : null; } set { Licenses = value.Licenses; } } - [EditorBrowsable(EditorBrowsableState.Never)] - public bool ShouldSerializeLicensesSerialized() { return Licenses?.Count > 0; } - + [XmlArray("properties")] [XmlArrayItem("property")] [ProtoMember(8)] public List Properties { get; set; } - public bool ShouldSerializeProperties() { return Properties?.Count > 0; } [XmlArray("lifecycles")] [XmlArrayItem("lifecycle")] [ProtoMember(9)] public List Lifecycles { get; set; } + + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeLicensesSerialized() { return Licenses?.Count > 0; } + public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + public bool ShouldSerializeTimestamp() { return Timestamp != null; } + public bool ShouldSerializeLicenses() { return Licenses?.Count > 0; } public bool ShouldSerializeLifecycles() { return Lifecycles?.Count > 0; } + + public object Clone() + { + return new Metadata() + { + Authors = this.Authors.Select(x => (OrganizationalContact)x.Clone()).ToList(), + Component = (Component)this.Component.Clone(), + Licenses = this.Licenses.Select(x => (LicenseChoice)x.Clone()).ToList(), + LicensesSerialized = (LicenseChoiceList)this.LicensesSerialized.Clone(), + Lifecycles = this.Lifecycles.Select(x => (Lifecycles)x.Clone()).ToList(), + Manufacture = (OrganizationalEntity)this.Manufacture.Clone(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + ProtobufTools = this.ProtobufTools.Select(x => (Tool)x.Clone()).ToList(), + Supplier = (OrganizationalEntity)this.Supplier.Clone(), + Timestamp = this.Timestamp, + Tools = (ToolChoices)this.Tools.Clone(), + }; + } } } diff --git a/src/CycloneDX.Core/Models/ModelCard.cs b/src/CycloneDX.Core/Models/ModelCard.cs index 00afe51e..e320a1f6 100644 --- a/src/CycloneDX.Core/Models/ModelCard.cs +++ b/src/CycloneDX.Core/Models/ModelCard.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("modelCard")] [ProtoContract] - public class ModelCard + public class ModelCard : ICloneable { [ProtoContract] public enum ModelParameterApproachType @@ -44,10 +45,10 @@ public enum ModelParameterApproachType } [ProtoContract] - public class ModelCardQuantitativeAnalysis + public class ModelCardQuantitativeAnalysis : ICloneable { [ProtoContract] - public class PerformanceMetric + public class PerformanceMetric : ICloneable { [ProtoContract] public class PerformanceMetricConfidenceInterval @@ -76,6 +77,17 @@ public class PerformanceMetricConfidenceInterval [XmlElement("confidenceInterval")] [ProtoMember(4)] public PerformanceMetricConfidenceInterval ConfidenceInterval { get; set; } + + public object Clone() + { + return new PerformanceMetric() + { + ConfidenceInterval = this.ConfidenceInterval, + Slice = this.Slice, + Type = this.Type, + Value = this.Value + }; + } } [XmlArray("performanceMetrics")] @@ -86,6 +98,15 @@ public class PerformanceMetricConfidenceInterval [XmlElement("graphics")] [ProtoMember(2)] public GraphicsCollection Graphics { get; set; } + + public object Clone() + { + return new ModelCardQuantitativeAnalysis() + { + Graphics = (GraphicsCollection)this.Graphics.Clone(), + PerformanceMetrics = this.PerformanceMetrics.Select(x => (PerformanceMetric)x.Clone()).ToList() + }; + } } [JsonPropertyName("bom-ref")] @@ -104,5 +125,16 @@ public class PerformanceMetricConfidenceInterval [XmlElement("considerations")] [ProtoMember(4)] public ModelCardConsiderations Considerations { get; set; } + + public object Clone() + { + return new ModelCard() + { + BomRef = this.BomRef, + Considerations = (ModelCardConsiderations)this.Considerations.Clone(), + ModelParameters = (ModelParameters)this.ModelParameters.Clone(), + QuantitativeAnalysis = (ModelCardQuantitativeAnalysis)this.QuantitativeAnalysis.Clone(), + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/ModelCardConsiderations.cs b/src/CycloneDX.Core/Models/ModelCardConsiderations.cs index d42c88b7..c4094bbf 100644 --- a/src/CycloneDX.Core/Models/ModelCardConsiderations.cs +++ b/src/CycloneDX.Core/Models/ModelCardConsiderations.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,10 +27,10 @@ namespace CycloneDX.Models { [XmlType("modelCardConsiderations")] [ProtoContract] - public class ModelCardConsiderations + public class ModelCardConsiderations : ICloneable { [ProtoContract] - public class ModelCardEthicalConsideration + public class ModelCardEthicalConsideration : ICloneable { [XmlElement("name")] [ProtoMember(1)] @@ -38,10 +39,19 @@ public class ModelCardEthicalConsideration [XmlElement("mitigationStrategy")] [ProtoMember(2)] public string MitigationStrategy { get; set; } + + public object Clone() + { + return new ModelCardEthicalConsideration() + { + Name = this.Name, + MitigationStrategy = this.MitigationStrategy + }; + } } [ProtoContract] - public class ModelCardFairnessAssessment + public class ModelCardFairnessAssessment : ICloneable { [XmlElement("groupAtRisk")] [ProtoMember(1)] @@ -58,6 +68,17 @@ public class ModelCardFairnessAssessment [XmlElement("mitigationStrategy")] [ProtoMember(4)] public string MitigationStrategy { get; set; } + + public object Clone() + { + return new ModelCardFairnessAssessment() + { + Benefits = this.Benefits, + GroupAtRisk = this.GroupAtRisk, + Harms = this.Harms, + MitigationStrategy = this.MitigationStrategy + }; + } } [XmlArray("users")] @@ -89,5 +110,18 @@ public class ModelCardFairnessAssessment [XmlArrayItem("fairnessAssessment")] [ProtoMember(6)] public List FairnessAssessments { get; set; } + + public object Clone() + { + return new ModelCardConsiderations() + { + EthicalConsiderations = this.EthicalConsiderations.Select(x => (ModelCardEthicalConsideration)x.Clone()).ToList(), + FairnessAssessments = this.FairnessAssessments.Select(x => (ModelCardFairnessAssessment)x.Clone()).ToList(), + PerformanceTradeoffs = this.PerformanceTradeoffs.Select(x => x).ToList(), + TechnicalLimitations = this.TechnicalLimitations.Select(x => x).ToList(), + UseCases = this.UseCases.Select(x => x).ToList(), + Users = this.Users.Select(x => x).ToList(), + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/ModelParameters.cs b/src/CycloneDX.Core/Models/ModelParameters.cs index a568bb54..43db5def 100644 --- a/src/CycloneDX.Core/Models/ModelParameters.cs +++ b/src/CycloneDX.Core/Models/ModelParameters.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,14 +27,22 @@ namespace CycloneDX.Models { [XmlType("model-parameters")] [ProtoContract] - public class ModelParameters + public class ModelParameters : ICloneable { [ProtoContract] - public class ModelApproach + public class ModelApproach : ICloneable { [XmlElement("type")] [ProtoMember(1)] - public ModelCard.ModelParameterApproachType Type { get; set; } + public ModelCard.ModelParameterApproachType Type { get; set; } + + public object Clone() + { + return new ModelApproach() + { + Type = this.Type + }; + } } [ProtoContract] @@ -49,11 +58,19 @@ public class ModelDataset } [ProtoContract] - public class MachineLearningInputOutputParameter + public class MachineLearningInputOutputParameter : ICloneable { [XmlElement("format")] [ProtoMember(1)] public string Format { get; set; } + + public object Clone() + { + return new MachineLearningInputOutputParameter() + { + Format = this.Format + }; + } } [XmlElement("approach")] @@ -85,5 +102,19 @@ public class MachineLearningInputOutputParameter [XmlArrayItem("output")] [ProtoMember(7)] public List Outputs { get; set; } + + public object Clone() + { + return new ModelParameters() + { + Approach = (ModelApproach)this.Approach.Clone(), + ArchitectureFamily = this.ArchitectureFamily, + Datasets = (DatasetChoices)this.Datasets.Clone(), + Inputs = this.Inputs.Select(x => (MachineLearningInputOutputParameter)x.Clone()).ToList(), + ModelArchitecture = this.ModelArchitecture, + Outputs = this.Outputs.Select(x => (MachineLearningInputOutputParameter)x.Clone()).ToList(), + Task = this.Task + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Note.cs b/src/CycloneDX.Core/Models/Note.cs index feaea13e..a7cd0580 100644 --- a/src/CycloneDX.Core/Models/Note.cs +++ b/src/CycloneDX.Core/Models/Note.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class Note + public class Note : ICloneable { [XmlElement("locale")] [ProtoMember(1)] @@ -30,5 +31,14 @@ public class Note [XmlElement("text")] [ProtoMember(2)] public AttachedText Text { get; set; } + + public object Clone() + { + return new Note() + { + Locale = this.Locale, + Text = this.Text + }; + } } } diff --git a/src/CycloneDX.Core/Models/OrganizationalContact.cs b/src/CycloneDX.Core/Models/OrganizationalContact.cs index de677b62..dcbd4cc4 100644 --- a/src/CycloneDX.Core/Models/OrganizationalContact.cs +++ b/src/CycloneDX.Core/Models/OrganizationalContact.cs @@ -15,6 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -22,7 +23,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class OrganizationalContact + public class OrganizationalContact : ICloneable { [XmlElement("name")] [ProtoMember(1)] @@ -40,5 +41,16 @@ public class OrganizationalContact [XmlAttribute("bom-ref")] [ProtoMember(4)] public string BomRef { get; set; } + + public object Clone() + { + return new OrganizationalContact() + { + BomRef = this.BomRef, + Name = this.Name, + Email = this.Email, + Phone = this.Phone, + }; + } } } diff --git a/src/CycloneDX.Core/Models/OrganizationalEntity.cs b/src/CycloneDX.Core/Models/OrganizationalEntity.cs index 77f60c03..eb44c43b 100644 --- a/src/CycloneDX.Core/Models/OrganizationalEntity.cs +++ b/src/CycloneDX.Core/Models/OrganizationalEntity.cs @@ -15,7 +15,9 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -23,7 +25,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class OrganizationalEntity + public class OrganizationalEntity : ICloneable { [XmlElement("name")] [ProtoMember(1)] @@ -41,5 +43,16 @@ public class OrganizationalEntity [XmlAttribute("bom-ref")] [ProtoMember(4)] public string BomRef { get; set; } + + public object Clone() + { + return new OrganizationalEntity() + { + BomRef = this.BomRef, + Contact = this.Contact.Select(x => (OrganizationalContact)x.Clone()).ToList(), + Name = this.Name, + Url = this.Url.Select(x => x).ToList() + }; + } } } diff --git a/src/CycloneDX.Core/Models/OrganizationalEntityOrContact.cs b/src/CycloneDX.Core/Models/OrganizationalEntityOrContact.cs index 9db9fb40..5a874536 100644 --- a/src/CycloneDX.Core/Models/OrganizationalEntityOrContact.cs +++ b/src/CycloneDX.Core/Models/OrganizationalEntityOrContact.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class OrganizationalEntityOrContact + public class OrganizationalEntityOrContact : ICloneable { [XmlElement("organization")] [ProtoMember(1)] @@ -30,5 +31,14 @@ public class OrganizationalEntityOrContact [XmlElement("individual")] [ProtoMember(2)] public OrganizationalContact Individual { get; set; } + + public object Clone() + { + return new OrganizationalEntityOrContact() + { + Individual = (OrganizationalContact)this.Individual.Clone(), + Organization = (OrganizationalEntity)this.Organization.Clone() + }; + } } } diff --git a/src/CycloneDX.Core/Models/Output.cs b/src/CycloneDX.Core/Models/Output.cs index 001e227b..09a75504 100644 --- a/src/CycloneDX.Core/Models/Output.cs +++ b/src/CycloneDX.Core/Models/Output.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("output")] [ProtoContract] - public class Output + public class Output : ICloneable { [ProtoContract] public enum OutputType @@ -52,8 +53,7 @@ public enum OutputType [XmlElement("type")] [ProtoMember(1)] public OutputType? Type { get; set; } - public bool ShouldSerializeType() { return Type != null; } - + [XmlElement("source")] [ProtoMember(2)] public ResourceReferenceChoice Source { get; set; } @@ -69,12 +69,28 @@ public enum OutputType [XmlElement("environmentVars")] [ProtoMember(6)] public EnvironmentVarChoices EnvironmentVars { get; set; } - public bool ShouldSerializeEnvironmentVars() { return EnvironmentVars?.Count > 0; } [XmlArray("properties")] [XmlArrayItem("property")] [ProtoMember(7)] public List Properties { get; set; } + + public bool ShouldSerializeType() { return Type != null; } + public bool ShouldSerializeEnvironmentVars() { return EnvironmentVars?.Count > 0; } public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new Output() + { + Data = (AttachedText)this.Data.Clone(), + EnvironmentVars = (EnvironmentVarChoices)this.EnvironmentVars.Clone(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Resource = (ResourceReferenceChoice)this.Resource.Clone(), + Source = (ResourceReferenceChoice)this.Source.Clone(), + Target = (ResourceReferenceChoice)this.Target.Clone(), + Type = this.Type + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Parameter.cs b/src/CycloneDX.Core/Models/Parameter.cs index 4de2b485..2e3efc51 100644 --- a/src/CycloneDX.Core/Models/Parameter.cs +++ b/src/CycloneDX.Core/Models/Parameter.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class Parameter + public class Parameter : ICloneable { [XmlElement("name")] [ProtoMember(1)] @@ -34,5 +35,15 @@ public class Parameter [XmlElement("data-type")] [ProtoMember(3)] public string DataType { get; set; } + + public object Clone() + { + return new Parameter() + { + DataType = this.DataType, + Name = this.Name, + Value = this.Value + }; + } } } diff --git a/src/CycloneDX.Core/Models/Patch.cs b/src/CycloneDX.Core/Models/Patch.cs index cdc5e2cf..e65486d9 100644 --- a/src/CycloneDX.Core/Models/Patch.cs +++ b/src/CycloneDX.Core/Models/Patch.cs @@ -15,14 +15,16 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class Patch + public class Patch : ICloneable { [ProtoContract] public enum PatchClassification @@ -51,5 +53,15 @@ public enum PatchClassification [XmlArrayItem("issue")] [ProtoMember(3)] public List Resolves { get; set; } + + public object Clone() + { + return new Patch() + { + Diff = (Diff)this.Diff.Clone(), + Resolves = this.Resolves.Select(x => (Issue)x.Clone()).ToList(), + Type = this.Type + }; + } } } diff --git a/src/CycloneDX.Core/Models/Pedigree.cs b/src/CycloneDX.Core/Models/Pedigree.cs index 537d6e87..254c6fd5 100644 --- a/src/CycloneDX.Core/Models/Pedigree.cs +++ b/src/CycloneDX.Core/Models/Pedigree.cs @@ -15,14 +15,16 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class Pedigree + public class Pedigree : ICloneable { [XmlArray("ancestors")] [XmlArrayItem("component")] @@ -48,10 +50,24 @@ public class Pedigree [XmlArrayItem("patch")] [ProtoMember(5)] public List Patches { get; set; } - public bool ShouldSerializePatches() { return Patches?.Count > 0; } [XmlElement("notes")] [ProtoMember(6)] public string Notes { get; set; } + + public bool ShouldSerializePatches() { return Patches?.Count > 0; } + + public object Clone() + { + return new Pedigree() + { + Ancestors = this.Ancestors.Select(x => (Component)x.Clone()).ToList(), + Commits = this.Commits.Select(x => (Commit)x.Clone()).ToList(), + Descendants = this.Descendants.Select(x => (Component)x.Clone()).ToList(), + Notes = this.Notes, + Patches = this.Patches.Select(x => (Patch)x.Clone()).ToList(), + Variants = this.Variants.Select(x => (Component)x.Clone()).ToList() + }; + } } } diff --git a/src/CycloneDX.Core/Models/Property.cs b/src/CycloneDX.Core/Models/Property.cs index a61b45f2..881eff85 100644 --- a/src/CycloneDX.Core/Models/Property.cs +++ b/src/CycloneDX.Core/Models/Property.cs @@ -15,6 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; using System.Xml.Serialization; using ProtoBuf; @@ -22,7 +23,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class Property + public class Property : ICloneable { [XmlAttribute("name")] [ProtoMember(1)] @@ -31,5 +32,14 @@ public class Property [XmlText] [ProtoMember(2)] public string Value { get; set; } + + public object Clone() + { + return new Property() + { + Name = this.Name, + Value = this.Value + }; + } } } diff --git a/src/CycloneDX.Core/Models/ReleaseNotes.cs b/src/CycloneDX.Core/Models/ReleaseNotes.cs index b8a33ce9..e3bb29a7 100644 --- a/src/CycloneDX.Core/Models/ReleaseNotes.cs +++ b/src/CycloneDX.Core/Models/ReleaseNotes.cs @@ -17,13 +17,14 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class ReleaseNotes + public class ReleaseNotes : ICloneable { [XmlElement("type")] [ProtoMember(1)] @@ -53,8 +54,7 @@ public DateTime? Timestamp get => _timestamp; set { _timestamp = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeTimestamp() { return Timestamp != null; } - + [XmlArray("aliases")] [XmlArrayItem("alias")] [ProtoMember(7)] @@ -79,5 +79,26 @@ public DateTime? Timestamp [XmlArrayItem("property")] [ProtoMember(11)] public List Properties { get; set; } + + public bool ShouldSerializeTimestamp() { return Timestamp != null; } + + public object Clone() + { + return new ReleaseNotes() + { + Aliases = this.Aliases.Select(x => x).ToList(), + Description = this.Description, + FeaturedImage = this.FeaturedImage, + Notes = this.Notes.Select(x => (Note)x.Clone()).ToList(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Resolves = this.Resolves.Select(x => (Issue)x.Clone()).ToList(), + SocialImage = this.SocialImage, + Tags = this.Tags.Select(x => x).ToList(), + Timestamp = this.Timestamp, + Title = this.Title, + Type = this.Type, + }; + } + } } diff --git a/src/CycloneDX.Core/Models/ResourceReferenceChoice.cs b/src/CycloneDX.Core/Models/ResourceReferenceChoice.cs index dc8153b4..02fada27 100644 --- a/src/CycloneDX.Core/Models/ResourceReferenceChoice.cs +++ b/src/CycloneDX.Core/Models/ResourceReferenceChoice.cs @@ -15,6 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Text.Json.Serialization; using System.Xml; using System.Xml.Serialization; @@ -23,7 +24,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class ResourceReferenceChoice : IXmlSerializable + public class ResourceReferenceChoice : ICloneable, IXmlSerializable { private static XmlSerializer _extRefSerializer; private static XmlSerializer GetExternalReferenceSerializer() @@ -72,5 +73,14 @@ public void WriteXml(XmlWriter writer) { GetExternalReferenceSerializer().Serialize(writer, this.ExternalReference); } } + + public object Clone() + { + return new ResourceReferenceChoice() + { + ExternalReference = this.ExternalReference, + Ref = this.Ref + }; + } } } diff --git a/src/CycloneDX.Core/Models/ResourceReferenceChoices.cs b/src/CycloneDX.Core/Models/ResourceReferenceChoices.cs index eb47fbd5..8bf90abf 100644 --- a/src/CycloneDX.Core/Models/ResourceReferenceChoices.cs +++ b/src/CycloneDX.Core/Models/ResourceReferenceChoices.cs @@ -15,7 +15,10 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; using System.Text.Json.Serialization; using System.Xml; using System.Xml.Serialization; @@ -24,10 +27,15 @@ namespace CycloneDX.Models { [ProtoContract] - public class ResourceReferenceChoices : List, IXmlSerializable + public class ResourceReferenceChoices : List, ICloneable, IXmlSerializable { private string _elementName = "resourceReference"; + public object Clone() + { + return this.Select(x => x.Clone()).ToList(); + } + public System.Xml.Schema.XmlSchema GetSchema() { return null; } diff --git a/src/CycloneDX.Core/Models/Service.cs b/src/CycloneDX.Core/Models/Service.cs index d27c1df1..b8466d70 100644 --- a/src/CycloneDX.Core/Models/Service.cs +++ b/src/CycloneDX.Core/Models/Service.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -25,7 +26,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class Service: IEquatable + public class Service: ICloneable, IEquatable { public Service() { @@ -81,8 +82,7 @@ public bool NonNullableAuthenticated Authenticated = value; } } - public bool ShouldSerializeNonNullableAuthenticated() { return Authenticated.HasValue; } - + [XmlIgnore] [JsonPropertyName("x-trust-boundary")] [ProtoMember(9)] @@ -101,7 +101,6 @@ public bool NonNullableXTrustBoundary XTrustBoundary = value; } } - public bool ShouldSerializeNonNullableXTrustBoundary() { return XTrustBoundary.HasValue; } [XmlElement("trustZone")] [ProtoMember(16)] @@ -111,8 +110,7 @@ public bool NonNullableXTrustBoundary [JsonPropertyName("data")] [ProtoMember(10)] public List Data { get; set; } - public bool ShouldSerializeData() => Data?.Count > 0; - + [XmlElement("data")] [JsonIgnore] public ServiceDataChoices XmlData @@ -165,8 +163,7 @@ public ServiceDataChoices XmlData } } } - public bool ShouldSerializeXmlData() => ShouldSerializeData(); - + [XmlIgnore] [ProtoMember(11)] public List Licenses { get; set; } @@ -180,31 +177,35 @@ public LicenseChoiceList LicensesSerialized get { return Licenses != null ? new LicenseChoiceList(Licenses) : null; } set { Licenses = value.Licenses; } } - [EditorBrowsable(EditorBrowsableState.Never)] - public bool ShouldSerializeLicensesSerialized() { return Licenses?.Count > 0; } - + [XmlArray("externalReferences")] [XmlArrayItem("reference")] [ProtoMember(12)] public List ExternalReferences { get; set; } - public bool ShouldSerializeExternalReferences() => ExternalReferences?.Count > 0; - + [XmlArray("services")] [XmlArrayItem("service")] [ProtoMember(13)] public List Services { get; set; } - public bool ShouldSerializeServices() => Services?.Count > 0; - + [XmlElement("releaseNotes")] [ProtoMember(15)] public ReleaseNotes ReleaseNotes { get; set; } - public bool ShouldSerializeReleaseNotes() { return ReleaseNotes != null; } - + [XmlArray("properties")] [XmlArrayItem("property")] [ProtoMember(14)] public List Properties { get; set; } + public bool ShouldSerializeNonNullableAuthenticated() { return Authenticated.HasValue; } + public bool ShouldSerializeNonNullableXTrustBoundary() { return XTrustBoundary.HasValue; } + public bool ShouldSerializeData() => Data?.Count > 0; + public bool ShouldSerializeXmlData() => ShouldSerializeData(); + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeLicensesSerialized() { return Licenses?.Count > 0; } + public bool ShouldSerializeExternalReferences() => ExternalReferences?.Count > 0; + public bool ShouldSerializeServices() => Services?.Count > 0; + public bool ShouldSerializeReleaseNotes() { return ReleaseNotes != null; } public bool ShouldSerializeProperties() => Properties?.Count > 0; public override bool Equals(object obj) @@ -227,5 +228,33 @@ public override int GetHashCode() { return CycloneDX.Json.Serializer.Serialize(this).GetHashCode(); } + + public object Clone() + { + return new Service() + { + Authenticated = this.Authenticated, + NonNullableAuthenticated = this.NonNullableAuthenticated, + BomRef = this.BomRef, + Data = this.Data.Select(x => (DataFlow)x.Clone()).ToList(), + Description = this.Description, + Endpoints = this.Endpoints.Select(x => x).ToList(), + ExternalReferences = this.ExternalReferences.Select(x => (ExternalReference)x.Clone()).ToList(), + Group = this.Group, + Licenses = this.Licenses.Select(x => (LicenseChoice)x.Clone()).ToList(), + LicensesSerialized = (LicenseChoiceList)this.LicensesSerialized.Clone(), + Name = this.Name, + NonNullableXTrustBoundary = this.NonNullableXTrustBoundary, + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Provider = (OrganizationalEntity)this.Provider.Clone(), + ReleaseNotes = (ReleaseNotes)this.ReleaseNotes.Clone(), + Services = this.Services.Select(x => (Service)x.Clone()).ToList(), + SpecVersion = this.SpecVersion, + TrustZone = this.TrustZone, + Version = this.Version, + XmlData = (ServiceDataChoices)this.XmlData.Clone(), + XTrustBoundary = this.XTrustBoundary, + }; + } } } diff --git a/src/CycloneDX.Core/Models/ServiceDataChoices.cs b/src/CycloneDX.Core/Models/ServiceDataChoices.cs index 51a64076..c07e4697 100644 --- a/src/CycloneDX.Core/Models/ServiceDataChoices.cs +++ b/src/CycloneDX.Core/Models/ServiceDataChoices.cs @@ -25,7 +25,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class ServiceDataChoices : IXmlSerializable + public class ServiceDataChoices : ICloneable, IXmlSerializable { internal SpecificationVersion SpecVersion { get; set; } @@ -87,5 +87,15 @@ public bool ShouldSerialize() { return DataClassifications?.Count > 0 || DataFlows?.Count > 0; } + + public object Clone() + { + return new ServiceDataChoices() + { + DataClassifications = this.DataClassifications, + DataFlows = this.DataFlows, + SpecVersion = this.SpecVersion + }; + } } } diff --git a/src/CycloneDX.Core/Models/Source.cs b/src/CycloneDX.Core/Models/Source.cs index 1c6bbead..e64af53c 100644 --- a/src/CycloneDX.Core/Models/Source.cs +++ b/src/CycloneDX.Core/Models/Source.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models { [ProtoContract] - public class Source + public class Source : ICloneable { [XmlElement("name")] [ProtoMember(1)] @@ -30,5 +31,14 @@ public class Source [XmlElement("url")] [ProtoMember(2)] public string Url { get; set; } + + public object Clone() + { + return new Source() + { + Name = this.Name, + Url = this.Url + }; + } } } diff --git a/src/CycloneDX.Core/Models/Step.cs b/src/CycloneDX.Core/Models/Step.cs index 962c1e1e..d4b89c92 100644 --- a/src/CycloneDX.Core/Models/Step.cs +++ b/src/CycloneDX.Core/Models/Step.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("step")] [ProtoContract] - public class Step + public class Step : ICloneable { [XmlElement("name")] [ProtoMember(1)] @@ -40,12 +41,24 @@ public class Step [XmlArrayItem("command")] [ProtoMember(3)] public List Commands { get; set; } - public bool ShouldSerializeCommands() { return Commands?.Count > 0; } [XmlArray("properties")] [XmlArrayItem("property")] [ProtoMember(4)] public List Properties { get; set; } + + public bool ShouldSerializeCommands() { return Commands?.Count > 0; } public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new Step() + { + Commands = this.Commands.Select(x => (Command)x.Clone()).ToList(), + Description = this.Description, + Name = this.Name, + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList() + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Swid.cs b/src/CycloneDX.Core/Models/Swid.cs index 4a0ccac2..d91d0f72 100644 --- a/src/CycloneDX.Core/Models/Swid.cs +++ b/src/CycloneDX.Core/Models/Swid.cs @@ -23,7 +23,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class Swid + public class Swid : ICloneable { [XmlAttribute("tagId")] [ProtoMember(1)] @@ -52,5 +52,19 @@ public class Swid [XmlAttribute("url")] [ProtoMember(7)] public string Url { get; set; } + + public object Clone() + { + return new Swid() + { + Name = this.Name, + Patch = this.Patch, + TagId = this.TagId, + TagVersion = this.TagVersion, + Text = (AttachedText)this.Text.Clone(), + Url = this.Url, + Version = this.Version, + }; + } } } diff --git a/src/CycloneDX.Core/Models/Tool.cs b/src/CycloneDX.Core/Models/Tool.cs index 48282a17..c70542c3 100644 --- a/src/CycloneDX.Core/Models/Tool.cs +++ b/src/CycloneDX.Core/Models/Tool.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using ProtoBuf; @@ -24,7 +25,7 @@ namespace CycloneDX.Models { [Obsolete("Tool is deprecated and will be removed in a future version")] [ProtoContract] - public class Tool: IEquatable + public class Tool : ICloneable, IEquatable { [XmlElement("vendor")] [ProtoMember(1)] @@ -46,6 +47,7 @@ public class Tool: IEquatable [XmlArrayItem("reference")] [ProtoMember(5)] public List ExternalReferences { get; set; } + public bool ShouldSerializeExternalReferences() { return ExternalReferences?.Count > 0; } public override bool Equals(object obj) @@ -68,5 +70,17 @@ public override int GetHashCode() { return CycloneDX.Json.Serializer.Serialize(this).GetHashCode(); } + + public object Clone() + { + return new Tool() + { + ExternalReferences = this.ExternalReferences.Select(x => (ExternalReference)x.Clone()).ToList(), + Hashes = this.Hashes.Select(x => (Hash)x.Clone()).ToList(), + Name = this.Name, + Vendor = this.Vendor, + Version = this.Version, + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/ToolChoices.cs b/src/CycloneDX.Core/Models/ToolChoices.cs index e9059e57..0e1fb2de 100644 --- a/src/CycloneDX.Core/Models/ToolChoices.cs +++ b/src/CycloneDX.Core/Models/ToolChoices.cs @@ -15,7 +15,9 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json.Serialization; using System.Xml; using System.Xml.Serialization; @@ -24,7 +26,7 @@ namespace CycloneDX.Models { [ProtoContract] - public class ToolChoices : IXmlSerializable + public class ToolChoices : ICloneable, IXmlSerializable { internal SpecificationVersion SpecVersion { get; set; } @@ -36,13 +38,10 @@ public class ToolChoices : IXmlSerializable [ProtoMember(2)] public List Components { get; set; } - public bool ShouldSerializeComponents() => Components?.Count > 0; - + [ProtoMember(3)] public List Services { get; set; } - public bool ShouldSerializeServices() => Services?.Count > 0; - public ToolChoices() { SpecVersion = SpecificationVersionHelpers.CurrentVersion; @@ -123,5 +122,19 @@ public void WriteXml(System.Xml.XmlWriter writer) { writer.WriteEndElement(); } } + + public bool ShouldSerializeComponents() => Components?.Count > 0; + public bool ShouldSerializeServices() => Services?.Count > 0; + + public object Clone() + { + return new ToolChoices() + { + Components = this.Components.Select(x => (Component)x.Clone()).ToList(), + Services = this.Services.Select(x => (Service)x.Clone()).ToList(), + SpecVersion = this.SpecVersion, + Tools = this.Tools.Select(x => (Tool)x.Clone()).ToList() + }; + } } } diff --git a/src/CycloneDX.Core/Models/Trigger.cs b/src/CycloneDX.Core/Models/Trigger.cs index 5b8407b2..8d827715 100644 --- a/src/CycloneDX.Core/Models/Trigger.cs +++ b/src/CycloneDX.Core/Models/Trigger.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("trigger")] [ProtoContract] - public class Trigger + public class Trigger : ICloneable { [ProtoContract] public enum TriggerType @@ -42,7 +43,7 @@ public enum TriggerType } [ProtoContract] - public class Condition + public class Condition : ICloneable { [XmlElement("description")] [ProtoMember(1)] @@ -56,7 +57,18 @@ public class Condition [XmlArrayItem("property")] [ProtoMember(3)] public List Properties { get; set; } + public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new Condition() + { + Description = this.Description, + Expression = this.Expression, + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList() + }; + } } [JsonPropertyName("bom-ref")] @@ -79,8 +91,7 @@ public class Condition [XmlElement("resourceReferences")] [ProtoMember(6)] public ResourceReferenceChoices ResourceReferences { get; set; } - public bool ShouldSerializeResourceReferences() { return ResourceReferences?.Count > 0; } - + [XmlElement("type")] [ProtoMember(7)] public TriggerType Type { get; set; } @@ -93,8 +104,7 @@ public class Condition [XmlArrayItem("condition")] [ProtoMember(9)] public List Conditions { get; set; } - public bool ShouldSerializeConditions() { return Conditions?.Count > 0; } - + private DateTime? _timeActivated; [XmlElement("timeActivated")] [ProtoMember(10)] @@ -103,22 +113,44 @@ public DateTime? TimeActivated get => _timeActivated; set { _timeActivated = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeTimeActivated() { return TimeActivated != null; } [XmlArray("inputs")] [ProtoMember(11)] public List Inputs { get; set; } - public bool ShouldSerializeInputs() { return Inputs?.Count > 0; } - + [XmlArray("outputs")] [ProtoMember(12)] public List Outputs { get; set; } - public bool ShouldSerializeOutputs() { return Outputs?.Count > 0; } - + [XmlArray("properties")] [XmlArrayItem("property")] [ProtoMember(5)] public List Properties { get; set; } + + public bool ShouldSerializeResourceReferences() { return ResourceReferences?.Count > 0; } + public bool ShouldSerializeConditions() { return Conditions?.Count > 0; } + public bool ShouldSerializeTimeActivated() { return TimeActivated != null; } + public bool ShouldSerializeInputs() { return Inputs?.Count > 0; } + public bool ShouldSerializeOutputs() { return Outputs?.Count > 0; } public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new Trigger() + { + BomRef = this.BomRef, + Conditions = this.Conditions.Select(x => (Condition)x.Clone()).ToList(), + Description = this.Description, + Event = (Event)this.Event.Clone(), + Inputs = this.Inputs.Select(x => (Input)x.Clone()).ToList(), + Name = this.Name, + Outputs = this.Outputs.Select(x => (Output)x.Clone()).ToList(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + ResourceReferences = (ResourceReferenceChoices)this.ResourceReferences.Clone(), + TimeActivated = this.TimeActivated, + Type = this.Type, + Uid = this.Uid, + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Volume.cs b/src/CycloneDX.Core/Models/Volume.cs index 25a76eeb..6519ec2b 100644 --- a/src/CycloneDX.Core/Models/Volume.cs +++ b/src/CycloneDX.Core/Models/Volume.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("volume")] [ProtoContract] - public class Volume + public class Volume : ICloneable { [ProtoContract] public enum VolumeMode @@ -74,8 +75,7 @@ public bool NonNullablePersistent Persistent = value; } } - public bool ShouldSerializeNonNullablePersistent() { return Persistent.HasValue; } - + // XML serialization doesn't like nullable value types [XmlIgnore] [ProtoMember(7)] @@ -93,12 +93,32 @@ public bool NonNullableRemote Remote = value; } } - public bool ShouldSerializeNonNullableRemote() { return Remote.HasValue; } - + [XmlArray("properties")] [XmlArrayItem("property")] [ProtoMember(8)] public List Properties { get; set; } + + public bool ShouldSerializeNonNullablePersistent() { return Persistent.HasValue; } + public bool ShouldSerializeNonNullableRemote() { return Remote.HasValue; } public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new Volume() + { + Mode = this.Mode, + Name = this.Name, + NonNullablePersistent = this.NonNullablePersistent, + NonNullableRemote = this.NonNullableRemote, + Path = this.Path, + Persistent = this.Persistent, + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Remote = this.Remote, + SizeAllocated = this.SizeAllocated, + Uid = this.Uid, + + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Vulnerabilities/Advisory.cs b/src/CycloneDX.Core/Models/Vulnerabilities/Advisory.cs index 267cb4fa..0c1c249e 100644 --- a/src/CycloneDX.Core/Models/Vulnerabilities/Advisory.cs +++ b/src/CycloneDX.Core/Models/Vulnerabilities/Advisory.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models.Vulnerabilities { [ProtoContract] - public class Advisory + public class Advisory : ICloneable { [XmlElement("title")] [ProtoMember(1)] @@ -30,5 +31,14 @@ public class Advisory [XmlElement("url")] [ProtoMember(2)] public string Url { get; set; } + + public object Clone() + { + return new Advisory() + { + Title = this.Title, + Url = this.Url + }; + } } } diff --git a/src/CycloneDX.Core/Models/Vulnerabilities/Affects.cs b/src/CycloneDX.Core/Models/Vulnerabilities/Affects.cs index 3dfe543e..0f2df1ec 100644 --- a/src/CycloneDX.Core/Models/Vulnerabilities/Affects.cs +++ b/src/CycloneDX.Core/Models/Vulnerabilities/Affects.cs @@ -15,6 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; using System.Text.Json.Serialization; using System.Xml.Serialization; @@ -23,7 +24,7 @@ namespace CycloneDX.Models.Vulnerabilities { [ProtoContract] - public class Affects + public class Affects : ICloneable { [XmlElement("ref")] [ProtoMember(1)] @@ -34,5 +35,14 @@ public class Affects [JsonPropertyName("versions")] [ProtoMember(2)] public List Versions { get; set; } + + public object Clone() + { + return new Affects() + { + Ref = this.Ref, + Versions = this.Versions + }; + } } } diff --git a/src/CycloneDX.Core/Models/Vulnerabilities/Analysis.cs b/src/CycloneDX.Core/Models/Vulnerabilities/Analysis.cs index c0411f46..f5120e6d 100644 --- a/src/CycloneDX.Core/Models/Vulnerabilities/Analysis.cs +++ b/src/CycloneDX.Core/Models/Vulnerabilities/Analysis.cs @@ -17,13 +17,14 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models.Vulnerabilities { [ProtoContract] - public class Analysis + public class Analysis : ICloneable { [XmlElement("state")] [ProtoMember(1)] @@ -50,8 +51,7 @@ public DateTime? FirstIssued get => _firstIssued; set { _firstIssued = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeFirstIssued() { return FirstIssued != null; } - + private DateTime? _lastUpdated; [XmlElement("lastUpdated")] [ProtoMember(6)] @@ -60,6 +60,22 @@ public DateTime? LastUpdated get => _lastUpdated; set { _lastUpdated = BomUtils.UtcifyDateTime(value); } } + + public bool ShouldSerializeFirstIssued() { return FirstIssued != null; } + public bool ShouldSerializeLastUpdated() { return LastUpdated != null; } + + public object Clone() + { + return new Analysis() + { + Detail = this.Detail, + FirstIssued = this.FirstIssued, + Justification = this.Justification, + LastUpdated = this.LastUpdated, + Response = this.Response.Select(x => x).ToList(), + State = this.State + }; + } } } diff --git a/src/CycloneDX.Core/Models/Vulnerabilities/Credits.cs b/src/CycloneDX.Core/Models/Vulnerabilities/Credits.cs index 93603711..4ed49772 100644 --- a/src/CycloneDX.Core/Models/Vulnerabilities/Credits.cs +++ b/src/CycloneDX.Core/Models/Vulnerabilities/Credits.cs @@ -15,14 +15,16 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models.Vulnerabilities { [ProtoContract] - public class Credits + public class Credits : ICloneable { [XmlArray("organizations")] [XmlArrayItem("organization")] @@ -33,5 +35,14 @@ public class Credits [XmlArrayItem("individual")] [ProtoMember(2)] public List Individuals { get; set; } + + public object Clone() + { + return new Credits() + { + Individuals = this.Individuals.Select(x => (OrganizationalContact)x.Clone()).ToList(), + Organizations = this.Organizations.Select(x => (OrganizationalEntity)x.Clone()).ToList() + }; + } } } diff --git a/src/CycloneDX.Core/Models/Vulnerabilities/ProofOfConcept.cs b/src/CycloneDX.Core/Models/Vulnerabilities/ProofOfConcept.cs index 9017d166..c195dfec 100644 --- a/src/CycloneDX.Core/Models/Vulnerabilities/ProofOfConcept.cs +++ b/src/CycloneDX.Core/Models/Vulnerabilities/ProofOfConcept.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -24,7 +25,7 @@ namespace CycloneDX.Models.Vulnerabilities { [ProtoContract] - public class ProofOfConcept + public class ProofOfConcept : ICloneable { [XmlElement("reproductionSteps")] [ProtoMember(1)] @@ -39,5 +40,15 @@ public class ProofOfConcept [JsonPropertyName("supportingMaterial")] [ProtoMember(3)] public List SupportingMaterials { get; set; } + + public object Clone() + { + return new ProofOfConcept() + { + Environment = this.Environment, + ReproductionSteps = this.ReproductionSteps, + SupportingMaterials = this.SupportingMaterials.Select(x => (AttachedText)x.Clone()).ToList() + }; + } } } diff --git a/src/CycloneDX.Core/Models/Vulnerabilities/Rating.cs b/src/CycloneDX.Core/Models/Vulnerabilities/Rating.cs index acb2be22..a5a7d2cf 100644 --- a/src/CycloneDX.Core/Models/Vulnerabilities/Rating.cs +++ b/src/CycloneDX.Core/Models/Vulnerabilities/Rating.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models.Vulnerabilities { [ProtoContract] - public class Rating + public class Rating : ICloneable { [XmlElement("source")] [ProtoMember(1)] @@ -46,5 +47,18 @@ public class Rating [XmlElement("justification")] [ProtoMember(6)] public string Justification { get; set; } + + public object Clone() + { + return new Rating() + { + Justification = this.Justification, + Method = this.Method, + Score = this.Score, + Severity = this.Severity, + Source = (Source)this.Source.Clone(), + Vector = this.Vector + }; + } } } diff --git a/src/CycloneDX.Core/Models/Vulnerabilities/Reference.cs b/src/CycloneDX.Core/Models/Vulnerabilities/Reference.cs index 53a0aa16..0de71885 100644 --- a/src/CycloneDX.Core/Models/Vulnerabilities/Reference.cs +++ b/src/CycloneDX.Core/Models/Vulnerabilities/Reference.cs @@ -15,13 +15,14 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright (c) OWASP Foundation. All Rights Reserved. +using System; using System.Xml.Serialization; using ProtoBuf; namespace CycloneDX.Models.Vulnerabilities { [ProtoContract] - public class Reference + public class Reference : ICloneable { [XmlElement("id")] [ProtoMember(1)] @@ -30,5 +31,14 @@ public class Reference [XmlElement("source")] [ProtoMember(2)] public Source Source { get; set; } + + public object Clone() + { + return new Reference() + { + Id = this.Id, + Source = this.Source + }; + } } } diff --git a/src/CycloneDX.Core/Models/Vulnerabilities/Vulnerability.cs b/src/CycloneDX.Core/Models/Vulnerabilities/Vulnerability.cs index 83b6b68b..4d899d9f 100644 --- a/src/CycloneDX.Core/Models/Vulnerabilities/Vulnerability.cs +++ b/src/CycloneDX.Core/Models/Vulnerabilities/Vulnerability.cs @@ -20,11 +20,12 @@ using System.Xml.Serialization; using System.Text.Json.Serialization; using ProtoBuf; +using System.Linq; namespace CycloneDX.Models.Vulnerabilities { [ProtoContract] - public class Vulnerability: IEquatable + public class Vulnerability : ICloneable, IEquatable { [XmlAttribute("bom-ref")] [JsonPropertyName("bom-ref")] @@ -43,8 +44,7 @@ public class Vulnerability: IEquatable [XmlArrayItem("reference")] [ProtoMember(4)] public List References { get; set; } - public bool ShouldSerializeReferences() { return References?.Count > 0; } - + [XmlArray("ratings")] [XmlArrayItem("rating")] [ProtoMember(5)] @@ -88,7 +88,6 @@ public DateTime? Created get => _created; set { _created = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeCreated() { return Created != null; } private DateTime? _published; [XmlElement("published")] @@ -98,7 +97,6 @@ public DateTime? Published get => _published; set { _published = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializePublished() { return Published != null; } private DateTime? _updated; [XmlElement("updated")] @@ -108,7 +106,6 @@ public DateTime? Updated get => _updated; set { _updated = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeUpdated() { return Updated != null; } private DateTime? _rejected; [XmlElement("rejected")] @@ -118,7 +115,6 @@ public DateTime? Rejected get => _rejected; set { _rejected = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeRejected() { return Rejected != null; } [XmlElement("credits")] [ProtoMember(14)] @@ -127,8 +123,7 @@ public DateTime? Rejected [XmlElement("tools")] [ProtoMember(15)] public ToolChoices Tools { get; set; } - public bool ShouldSerializeTools() => Tools != null && (Tools.Tools != null || Tools.Components != null || Tools.Services != null); - + [XmlElement("analysis")] [ProtoMember(16)] public Analysis Analysis { get; set; } @@ -142,6 +137,13 @@ public DateTime? Rejected [XmlArrayItem("property")] [ProtoMember(18)] public List Properties { get; set; } + + public bool ShouldSerializeReferences() { return References?.Count > 0; } + public bool ShouldSerializeCreated() { return Created != null; } + public bool ShouldSerializePublished() { return Published != null; } + public bool ShouldSerializeUpdated() { return Updated != null; } + public bool ShouldSerializeRejected() { return Rejected != null; } + public bool ShouldSerializeTools() => Tools != null && (Tools.Tools != null || Tools.Components != null || Tools.Services != null); public bool ShouldSerializeProperties() { return Properties?.Count > 0; } public override bool Equals(object obj) @@ -164,5 +166,33 @@ public override int GetHashCode() { return CycloneDX.Json.Serializer.Serialize(this).GetHashCode(); } + + public object Clone() + { + return new Vulnerability() + { + Advisories = this.Advisories.Select(x => (Advisory)x.Clone()).ToList(), + Affects = this.Affects.Select(x => (Affects)x.Clone()).ToList(), + Analysis = (Analysis)this.Analysis.Clone(), + BomRef = this.BomRef, + Created = this.Created, + Credits = (Credits)this.Credits.Clone(), + CWES = this.CWES.Select(x => x).ToList(), + Description = this.Description, + Detail = this.Detail, + Id = this.Id, + ProofOfConcept = (ProofOfConcept)this.ProofOfConcept.Clone(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + Published = this.Published, + Ratings = this.Ratings.Select(x => (Rating)x.Clone()).ToList(), + Recommendation = this.Recommendation, + References = this.References.Select(x => (Reference)x.Clone()).ToList(), + Rejected = this.Rejected, + Source = (Source)this.Source.Clone(), + Tools = (ToolChoices)this.Tools.Clone(), + Updated = this.Updated, + Workaround = this.Workaround, + }; + } } } diff --git a/src/CycloneDX.Core/Models/Workflow.cs b/src/CycloneDX.Core/Models/Workflow.cs index 88d630e3..cd819b21 100644 --- a/src/CycloneDX.Core/Models/Workflow.cs +++ b/src/CycloneDX.Core/Models/Workflow.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("workflow")] [ProtoContract] - public class Workflow + public class Workflow : ICloneable { [JsonPropertyName("bom-ref")] [XmlAttribute("bom-ref")] @@ -90,8 +91,7 @@ public DateTime? TimeStart get => _timeStart; set { _timeStart = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeTimeStart() { return TimeStart != null; } - + private DateTime? _timeEnd; [XmlElement("timeEnd")] [ProtoMember(15)] @@ -100,7 +100,6 @@ public DateTime? TimeEnd get => _timeEnd; set { _timeEnd = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeTimeEnd() { return TimeEnd != null; } [XmlArray("workspaces")] [XmlArrayItem("workspace")] @@ -116,6 +115,33 @@ public DateTime? TimeEnd [XmlArrayItem("property")] [ProtoMember(5)] public List Properties { get; set; } + + public bool ShouldSerializeTimeStart() { return TimeStart != null; } + public bool ShouldSerializeTimeEnd() { return TimeEnd != null; } public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public object Clone() + { + return new Workflow() + { + BomRef = this.BomRef, + Description = this.Description, + Inputs = this.Inputs.Select(x => (Input)x.Clone()).ToList(), + Name = this.Name, + Outputs = this.Outputs.Select(x => (Output)x.Clone()).ToList(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + ResourceReferences = (ResourceReferenceChoices)this.ResourceReferences.Clone(), + RuntimeTopologies = this.RuntimeTopologies.Select(x => (Dependency)x.Clone()).ToList(), + Steps = this.Steps.Select(x => (Step)x.Clone()).ToList(), + TaskDependencies = this.TaskDependencies.Select(x => (Dependency)x.Clone()).ToList(), + Tasks = this.Tasks.Select(x => (WorkflowTask)x.Clone()).ToList(), + TaskTypes = this.TaskTypes.Select(x => x).ToList(), + TimeEnd = this.TimeEnd, + TimeStart = this.TimeStart, + Trigger = (Trigger)this.Trigger.Clone(), + Uid = this.Uid, + Workspaces = this.Workspaces.Select(x => (Workspace)x.Clone()).ToList() + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/WorkflowTask.cs b/src/CycloneDX.Core/Models/WorkflowTask.cs index 9eb0d514..d3fbaca1 100644 --- a/src/CycloneDX.Core/Models/WorkflowTask.cs +++ b/src/CycloneDX.Core/Models/WorkflowTask.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("task")] [ProtoContract] - public class WorkflowTask + public class WorkflowTask : ICloneable { [ProtoContract] public enum TaskType @@ -78,7 +79,6 @@ public enum TaskType [XmlArrayItem("property")] [ProtoMember(5)] public List Properties { get; set; } - public bool ShouldSerializeProperties() { return Properties?.Count > 0; } [XmlElement("resourceReferences")] [ProtoMember(6)] @@ -113,8 +113,7 @@ public DateTime? TimeStart get => _timeStart; set { _timeStart = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeTimeStart() { return TimeStart != null; } - + private DateTime? _timeEnd; [XmlElement("timeEnd")] [ProtoMember(15)] @@ -123,7 +122,6 @@ public DateTime? TimeEnd get => _timeEnd; set { _timeEnd = BomUtils.UtcifyDateTime(value); } } - public bool ShouldSerializeTimeEnd() { return TimeEnd != null; } [XmlArray("workspaces")] [XmlArrayItem("workspace")] @@ -134,5 +132,31 @@ public DateTime? TimeEnd [XmlArrayItem("dependency")] [ProtoMember(17)] public List RuntimeTopologies { get; set; } + + public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + public bool ShouldSerializeTimeStart() { return TimeStart != null; } + public bool ShouldSerializeTimeEnd() { return TimeEnd != null; } + + public object Clone() + { + return new WorkflowTask() + { + BomRef = this.BomRef, + Description = this.Description, + Inputs = this.Inputs.Select(x => (Input)x.Clone()).ToList(), + Name = this.Name, + Outputs = this.Outputs.Select(x => (Output)x.Clone()).ToList(), + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + ResourceReferences = this.ResourceReferences, + RuntimeTopologies = this.RuntimeTopologies.Select(x => (Dependency)x.Clone()).ToList(), + Steps = this.Steps.Select(x => (Step)x.Clone()).ToList(), + TaskTypes = this.TaskTypes.Select(x => x).ToList(), + TimeEnd = this.TimeEnd, + TimeStart = this.TimeStart, + Trigger = (Trigger)this.Trigger.Clone(), + Uid = this.Uid, + Workspaces = this.Workspaces.Select(x => (Workspace)x.Clone()).ToList(), + }; + } } } \ No newline at end of file diff --git a/src/CycloneDX.Core/Models/Workspace.cs b/src/CycloneDX.Core/Models/Workspace.cs index ccc43f79..a295af3a 100644 --- a/src/CycloneDX.Core/Models/Workspace.cs +++ b/src/CycloneDX.Core/Models/Workspace.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using ProtoBuf; @@ -26,7 +27,7 @@ namespace CycloneDX.Models { [XmlType("workspace")] [ProtoContract] - public class Workspace + public class Workspace : ICloneable { [ProtoContract] public enum AccessModeType @@ -60,8 +61,7 @@ public enum AccessModeType [XmlArrayItem("alias")] [ProtoMember(4)] public List Aliases { get; set; } - public bool ShouldSerializeAliases() { return Aliases?.Count > 0; } - + [XmlElement("description")] [ProtoMember(5)] public string Description { get; set; } @@ -70,12 +70,10 @@ public enum AccessModeType [XmlArrayItem("property")] [ProtoMember(6)] public List Properties { get; set; } - public bool ShouldSerializeProperties() { return Properties?.Count > 0; } [XmlElement("resourceReferences")] [ProtoMember(7)] public ResourceReferenceChoices ResourceReferences { get; set; } - public bool ShouldSerializeResourceReferences() { return ResourceReferences?.Count > 0; } [XmlElement("accessMode")] [ProtoMember(8)] @@ -96,5 +94,29 @@ public enum AccessModeType [XmlElement("volume")] [ProtoMember(12)] public Volume Volume { get; set; } + + public bool ShouldSerializeAliases() { return Aliases?.Count > 0; } + public bool ShouldSerializeProperties() { return Properties?.Count > 0; } + + public bool ShouldSerializeResourceReferences() { return ResourceReferences?.Count > 0; } + + public object Clone() + { + return new Workspace() + { + AccessMode = this.AccessMode, + Aliases = this.Aliases.Select(x => x).ToList(), + BomRef = this.BomRef, + Description = this.Description, + ManagedDateType = this.ManagedDateType, + MountPath = this.MountPath, + Name = this.Name, + Properties = this.Properties.Select(x => (Property)x.Clone()).ToList(), + ResourceReferences = (ResourceReferenceChoices)this.ResourceReferences.Clone(), + Uid = this.Uid, + Volume = (Volume)this.Volume.Clone(), + VolumeRequest = this.VolumeRequest, + }; + } } } \ No newline at end of file