com.metaeffekt.artifact.analysis.bom.cyclonedx.CycloneDxImporter Maven / Gradle / Ivy
The newest version!
package com.metaeffekt.artifact.analysis.bom.cyclonedx;
import com.metaeffekt.artifact.analysis.bom.BomConstants;
import org.cyclonedx.exception.ParseException;
import org.cyclonedx.model.Bom;
import org.cyclonedx.model.Component;
import org.cyclonedx.model.Metadata;
import org.cyclonedx.model.Property;
import org.cyclonedx.parsers.BomParserFactory;
import org.cyclonedx.parsers.Parser;
import org.metaeffekt.core.inventory.processor.model.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Stack;
/**
* This class is responsible for importing bom's or json files containing bom's to an inventory. The class is to be
* mainly used in conjunction with the {@link CycloneDxExporter} but should work with any other cyclonedx-bom
* provided, though fields tracked through the use of {@link org.cyclonedx.model.Property} will not be imported or
* might be imported incorrectly.
*/
public class CycloneDxImporter {
// Flag, when true the metadata-component is listed in the artifacts sheet in addition to the assets sheet.
boolean includeMetadataComponent;
public CycloneDxImporter(boolean includeMetadataComponent) {
this.includeMetadataComponent = includeMetadataComponent;
}
/**
* Main entrypoint when importing from json file containing a bom.
*
* @param bomFile the json file containing a bom
* @return the imported inventory.
*/
public Inventory importFromFile(File bomFile) {
try {
final Parser parser = BomParserFactory.createParser(bomFile);
final Bom parsedBom = parser.parse(bomFile);
return importFromBom(parsedBom);
} catch (ParseException e) {
throw new RuntimeException("CycloneDX built-in parser failed to parse file.", e);
}
}
/**
* Main entrypoint when importing from a {@link Bom} object directly.
*
* @param bom the bom
* @return the imported inventory.
*/
public Inventory importFromBom(Bom bom) {
Inventory inventory = new Inventory();
List artifacts = new ArrayList<>();
List assets = new ArrayList<>();
importAssetsAndArtifacts(bom, artifacts, assets);
importHierarchy(bom, artifacts, assets);
inventory.setArtifacts(artifacts);
inventory.setAssetMetaData(assets);
return inventory;
}
/**
* Collects all components in the bom and runs them through the {@link ModelBaseMapper} which maps all fields and
* values to their respective counterpart in our inventory. Iterative approach used to avoid issues with deeply
* nested component hierarchies.
*
* @param bom the bom
* @param artifacts a list of artifacts
* @param assets a list of assets
*/
private void importAssetsAndArtifacts(Bom bom, List artifacts, List assets) {
List allComponents = new ArrayList<>();
Stack componentStack = new Stack<>();
ModelBaseMapper modelBaseMapper = new ModelBaseMapper();
for (Component component : bom.getComponents()) {
componentStack.push(component);
}
// Metadata component mapping preparations
Metadata metadata = bom.getMetadata();
Component metadataComponent = null;
if (metadata != null) {
metadataComponent = bom.getMetadata().getComponent();
}
if (metadataComponent != null) {
Property property = new Property();
property.setName(BomConstants.INVENTORY_CLASS);
property.setValue("assets");
Property propertyPrimary = new Property();
propertyPrimary.setName(Constants.KEY_PRIMARY);
propertyPrimary.setValue("yes");
metadataComponent.addProperty(property);
metadataComponent.addProperty(propertyPrimary);
AbstractModelBase modelBase = modelBaseMapper.map(metadataComponent, true);
if (modelBase instanceof Artifact) {
artifacts.add((Artifact) modelBase);
} else {
assets.add((AssetMetaData) modelBase);
}
}
while (!componentStack.isEmpty()) {
Component currentComponent = componentStack.pop();
allComponents.add(currentComponent);
if (currentComponent.getComponents() != null) {
for (Component childComponent : currentComponent.getComponents()) {
componentStack.push(childComponent);
}
}
}
for (Component component : allComponents) {
AbstractModelBase modelBase = modelBaseMapper.map(component, false);
if (modelBase instanceof Artifact) {
artifacts.add((Artifact) modelBase);
} else {
assets.add((AssetMetaData) modelBase);
}
}
}
/**
* Imports the relationship hierarchy contained in the bom. Currently capable of mapping DESCRIBES and CONTAINS
* relationships.
*
* @param bom the bom
* @param artifacts list of artifacts
* @param assets list of assets
*/
private void importHierarchy(Bom bom, List artifacts, List assets) {
Stack componentStack = new Stack<>();
for (Component component : bom.getComponents()) {
componentStack.push(component);
}
while (!componentStack.isEmpty()) {
Component currentComponent = componentStack.pop();
if (currentComponent.getComponents() == null) {
continue;
}
for (Component childComponent : currentComponent.getComponents()) {
Optional artifact =
artifacts.stream().filter(a -> a.get(Constants.KEY_ID).equals(childComponent.getName()))
.findFirst();
Optional asset =
assets.stream().filter(a -> a.get(Constants.KEY_ASSET_ID).equals(childComponent.getName()))
.findFirst();
artifact.ifPresent(a -> a.set(currentComponent.getName(), Constants.MARKER_CONTAINS));
asset.ifPresent(a -> a.set(currentComponent.getName(), Constants.MARKER_CONTAINS));
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy