Skip to content

Commit

Permalink
Fix metadata tools components (#304)
Browse files Browse the repository at this point in the history
* Fix metadata tools components

Signed-off-by: andreas hilti <andreas.hilti@bluewin.ch>

* namespace tool components and services

Signed-off-by: andreas hilti <andreas.hilti@bluewin.ch>

* avoid writing null value in tools

Signed-off-by: andreas hilti <andreas.hilti@bluewin.ch>

* namespace also nested tools components

Signed-off-by: andreas hilti <andreas.hilti@bluewin.ch>

---------

Signed-off-by: andreas hilti <andreas.hilti@bluewin.ch>
  • Loading branch information
andreas-hilti authored Aug 4, 2024
1 parent ad9c7d2 commit e516bdb
Show file tree
Hide file tree
Showing 5 changed files with 679 additions and 9 deletions.
6 changes: 1 addition & 5 deletions src/CycloneDX.Core/Json/Converters/ToolChoicesConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public override void Write(
}
writer.WriteEndArray();
}
else if (value.Components != null || value.Services != null)
else
{
writer.WriteStartObject();
if (value.Components != null)
Expand All @@ -100,10 +100,6 @@ public override void Write(
}
writer.WriteEndObject();
}
else
{
writer.WriteNullValue();
}
}
}
}
51 changes: 47 additions & 4 deletions src/CycloneDX.Utils/Merge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,19 @@ public static Bom FlatMerge(Bom bom1, Bom bom2)
var toolsMerger = new ListMergeHelper<Tool>();
#pragma warning restore 618
var tools = toolsMerger.Merge(bom1.Metadata?.Tools?.Tools, bom2.Metadata?.Tools?.Tools);
if (tools != null)
var toolsComponentsMerger = new ListMergeHelper<Component>();
var toolsComponents = toolsComponentsMerger.Merge(bom1.Metadata?.Tools?.Components, bom2.Metadata?.Tools?.Components);
var toolsServicesMerger = new ListMergeHelper<Service>();
var toolsServices = toolsServicesMerger.Merge(bom1.Metadata?.Tools?.Services, bom2.Metadata?.Tools?.Services);
if (tools != null || toolsComponents != null || toolsServices != null)
{
result.Metadata = new Metadata
{
Tools = new ToolChoices
{
Tools = tools,
Components = toolsComponents,
Services = toolsServices,
}
};
}
Expand Down Expand Up @@ -254,6 +260,36 @@ bom.SerialNumber is null
{
result.Metadata.Tools.Tools.AddRange(bom.Metadata.Tools.Tools);
}
if (bom.Metadata?.Tools?.Components?.Count > 0)
{
if (result.Metadata.Tools.Components == null)
{
result.Metadata.Tools.Components = new List<Component>();
}
foreach (var component in bom.Metadata.Tools.Components)
{
NamespaceComponentBomRefs(ComponentBomRefNamespace(bom.Metadata.Component), component);
if (!result.Metadata.Tools.Components.Contains(component))
{
result.Metadata.Tools.Components.Add(component);
}
}
}
if (bom.Metadata?.Tools?.Services?.Count > 0)
{
if (result.Metadata.Tools.Services == null)
{
result.Metadata.Tools.Services = new List<Service>();
}
foreach (var service in bom.Metadata.Tools.Services)
{
service.BomRef = NamespacedBomRef(bom.Metadata.Component, service.BomRef);
if (!result.Metadata.Tools.Services.Contains(service))
{
result.Metadata.Tools.Services.Add(service);
}
}
}

var thisComponent = bom.Metadata.Component;
if (thisComponent.Components is null) bom.Metadata.Component.Components = new List<Component>();
Expand Down Expand Up @@ -344,6 +380,11 @@ private static string ComponentBomRefNamespace(Component component)
}

private static void NamespaceComponentBomRefs(Component topComponent)
{
NamespaceComponentBomRefs(ComponentBomRefNamespace(topComponent), topComponent);
}

private static void NamespaceComponentBomRefs(string bomRefNamespace, Component topComponent)
{
var components = new Stack<Component>();
components.Push(topComponent);
Expand All @@ -353,12 +394,14 @@ private static void NamespaceComponentBomRefs(Component topComponent)
var currentComponent = components.Pop();

if (currentComponent.Components != null)
foreach (var subComponent in currentComponent.Components)
{
components.Push(subComponent);
foreach (var subComponent in currentComponent.Components)
{
components.Push(subComponent);
}
}

currentComponent.BomRef = NamespacedBomRef(topComponent, currentComponent.BomRef);
currentComponent.BomRef = NamespacedBomRef(bomRefNamespace, currentComponent.BomRef);
}
}

Expand Down
216 changes: 216 additions & 0 deletions tests/CycloneDX.Utils.Tests/MergeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,222 @@ public void HierarchicalMergeComponentsTest()
Snapshot.Match(result);
}

[Fact]
public void HierarchicalMergeToolsComponentsTest()
{
var subject = new Component
{
Name = "Thing",
Version = "1",
};

var sbom1 = new Bom
{
Metadata = new Metadata
{
Component = new Component
{
Name = "System1",
Version = "1",
BomRef = "System1@1"
},
Tools = new ToolChoices
{
Components = new List<Component>
{
new Component
{
Name = "ToolComponent1",
Version = "1",
BomRef = "ToolComponent1@1",
}
}
}
},
Components = new List<Component>
{
new Component
{
Name = "Component1",
Version = "1",
BomRef = "Component1@1"
}
},
Dependencies = new List<Dependency>
{
new Dependency
{
Ref = "System1@1",
Dependencies = new List<Dependency>
{
new Dependency
{
Ref = "Component1@1"
}
}
}
},
};
var sbom2 = new Bom
{
Metadata = new Metadata
{
Component = new Component
{
Name = "System2",
Version = "1",
BomRef = "System2@1"
},
Tools = new ToolChoices
{
Components = new List<Component>
{
new Component
{
Name = "ToolComponent2",
Version = "1",
BomRef = "ToolComponent2@1",
}
}
}
},
Components = new List<Component>
{
new Component
{
Name = "Component2",
Version = "1",
BomRef = "Component2@1"
}
},
Dependencies = new List<Dependency>
{
new Dependency
{
Ref = "System2@1",
Dependencies = new List<Dependency>
{
new Dependency
{
Ref = "Component2@1"
}
}
}
},
};

var result = CycloneDXUtils.HierarchicalMerge(new[] { sbom1, sbom2 }, subject);

Snapshot.Match(result);
}

[Fact]
public void HierarchicalMergeDuplicatedToolsComponentsTest()
{
var subject = new Component
{
Name = "Thing",
Version = "1",
};

var sbom1 = new Bom
{
Metadata = new Metadata
{
Component = new Component
{
Name = "System1",
Version = "1",
BomRef = "System1@1"
},
Tools = new ToolChoices
{
Components = new List<Component>
{
new Component
{
Name = "ToolComponent1",
Version = "1",
}
}
}
},
Components = new List<Component>
{
new Component
{
Name = "Component1",
Version = "1",
BomRef = "Component1@1"
}
},
Dependencies = new List<Dependency>
{
new Dependency
{
Ref = "System1@1",
Dependencies = new List<Dependency>
{
new Dependency
{
Ref = "Component1@1"
}
}
}
},
};
var sbom2 = new Bom
{
Metadata = new Metadata
{
Component = new Component
{
Name = "System2",
Version = "1",
BomRef = "System2@1"
},
Tools = new ToolChoices
{
Components = new List<Component>
{
new Component
{
Name = "ToolComponent1",
Version = "1",
}
}
}
},
Components = new List<Component>
{
new Component
{
Name = "Component2",
Version = "1",
BomRef = "Component2@1"
}
},
Dependencies = new List<Dependency>
{
new Dependency
{
Ref = "System2@1",
Dependencies = new List<Dependency>
{
new Dependency
{
Ref = "Component2@1"
}
}
}
},
};

var result = CycloneDXUtils.HierarchicalMerge(new[] { sbom1, sbom2 }, subject);

Snapshot.Match(result);
}

[Fact]
public void HierarchicalMergeVulnerabilitiesTest()
{
Expand Down
Loading

0 comments on commit e516bdb

Please sign in to comment.