add minimal sbom converter
This commit is contained in:
@ -32,6 +32,7 @@ WORKDIR $APP_DIR
|
||||
|
||||
COPY src/requirements.txt .
|
||||
COPY src/sbom-dt-dd.py .
|
||||
COPY src/converter.py .
|
||||
COPY src/entrypoint.sh .
|
||||
COPY dependencytrack-client/ ./dependencytrack-client
|
||||
COPY defectdojo-client/ ./defectdojo-client
|
||||
|
96
src/converter.py
Normal file
96
src/converter.py
Normal file
@ -0,0 +1,96 @@
|
||||
from loguru import logger
|
||||
import yaml
|
||||
import uuid
|
||||
from packageurl import PackageURL
|
||||
|
||||
from cyclonedx.builder.this import this_component as cdx_lib_component
|
||||
from cyclonedx.factory.license import LicenseFactory
|
||||
from cyclonedx.model.bom import Bom
|
||||
from cyclonedx.model.component import Component, ComponentType
|
||||
from cyclonedx.model.contact import OrganizationalEntity
|
||||
from cyclonedx.model import XsUri
|
||||
from cyclonedx.output.json import JsonV1Dot5
|
||||
|
||||
class MyLocalConverterException(Exception): pass
|
||||
|
||||
def __converterClassifierToComponentType(classifier):
|
||||
componentType = ''
|
||||
match classifier:
|
||||
case 'APPLICATION':
|
||||
componentType = ComponentType.APPLICATION
|
||||
case 'FRAMEWORK':
|
||||
componentType = ComponentType.FRAMEWORK
|
||||
case 'LIBRARY':
|
||||
componentType = ComponentType.LIBRARY
|
||||
case 'CONTAINER':
|
||||
componentType = ComponentType.CONTAINER
|
||||
case 'OPERATING_SYSTEM':
|
||||
componentType = ComponentType.OPERATING_SYSTEM
|
||||
case 'DEVICE':
|
||||
componentType = ComponentType.DEVICE
|
||||
case 'FIRMWARE':
|
||||
componentType = ComponentType.FIRMWARE
|
||||
case 'FILE':
|
||||
componentType = ComponentType.FILE
|
||||
case 'PLATFORM':
|
||||
componentType = ComponentType.PLATFORM
|
||||
case 'DEVICE_DRIVER':
|
||||
componentType = ComponentType.DEVICE_DRIVER
|
||||
case 'MACHINE_LEARNING_MODEL':
|
||||
componentType = ComponentType.MACHINE_LEARNING_MODEL
|
||||
case 'DATA':
|
||||
componentType = ComponentType.DATA
|
||||
case _:
|
||||
raise MyLocalConverterException(f"No componentType for {classifier} found")
|
||||
return componentType
|
||||
|
||||
|
||||
|
||||
def minimalSbomFormatConverter(minimalSbom, classifier):
|
||||
logger.info(f"Minimal input: {minimalSbom}")
|
||||
|
||||
lc_factory = LicenseFactory()
|
||||
|
||||
minimalSbomObject = yaml.safe_load(minimalSbom)
|
||||
logger.debug(f"{minimalSbomObject=}")
|
||||
|
||||
bom = Bom()
|
||||
bom.metadata.tools.components.add(cdx_lib_component())
|
||||
bom.metadata.tools.components.add(Component(
|
||||
name='sbom-dt-dd',
|
||||
type=ComponentType.APPLICATION
|
||||
))
|
||||
|
||||
bom.metadata.component = root_component = Component(
|
||||
name=minimalSbomObject['product'],
|
||||
type=__converterClassifierToComponentType(classifier),
|
||||
version=minimalSbomObject['version'],
|
||||
licenses=[lc_factory.make_from_string(minimalSbomObject['license'])],
|
||||
supplier=OrganizationalEntity(
|
||||
name=minimalSbomObject['supplier']['name'],
|
||||
urls=[XsUri(minimalSbomObject['supplier']['url'])]
|
||||
),
|
||||
bom_ref = f"urn:uuid:{uuid.uuid4()}"
|
||||
)
|
||||
|
||||
for minimalComponentDescription in minimalSbomObject['components']:
|
||||
component = Component(
|
||||
type=ComponentType.LIBRARY,
|
||||
name=minimalComponentDescription['name'],
|
||||
version=minimalComponentDescription['version'],
|
||||
licenses=[lc_factory.make_from_string(minimalComponentDescription['license'])],
|
||||
bom_ref = f"urn:uuid:{uuid.uuid4()}"
|
||||
)
|
||||
if 'cpe' in minimalComponentDescription:
|
||||
component.cpe = minimalComponentDescription['cpe']
|
||||
if 'purl' in minimalComponentDescription:
|
||||
component.purl = PackageURL.from_string(minimalComponentDescription['purl'])
|
||||
bom.components.add(component)
|
||||
bom.register_dependency(root_component, [component])
|
||||
|
||||
outputSbom = JsonV1Dot5(bom).output_as_string(indent=2)
|
||||
logger.info(outputSbom)
|
||||
|
||||
|
||||
raise Exception("Conversion aborted")
|
||||
|
@ -1,3 +1,5 @@
|
||||
regex==2024.11.6
|
||||
loguru==0.7.3
|
||||
PyYAML==6.0.2
|
||||
cyclonedx-python-lib==10.4.1
|
||||
|
||||
|
@ -12,6 +12,9 @@ from dateutil.relativedelta import relativedelta
|
||||
import dependencytrack_api
|
||||
from dependencytrack_api.rest import ApiException as DependencyTrackApiException
|
||||
|
||||
from converter import minimalSbomFormatConverter
|
||||
|
||||
|
||||
class MyLocalException(Exception): pass
|
||||
|
||||
def executeApiCall(apiClient, ApiClass, EndpointMethod, RequestClass, requestParams, additionalParams=[]):
|
||||
@ -84,6 +87,10 @@ parser.add_argument('--uploadsbom', '-U',
|
||||
parser.add_argument('--sbomfile', '-F',
|
||||
help='Filename of existing SBOM file to upload, use together with -U, do not use together with -T',
|
||||
required=False)
|
||||
parser.add_argument('--minimalsbomformat', '-K',
|
||||
help='SBOM file comes in dedicated minimal format and will be converted into cyclonedx before uploading',
|
||||
action='store_true',
|
||||
default=False)
|
||||
parser.add_argument('--target', '-T',
|
||||
help='Target to scan, either path name for sources or docker image tag',
|
||||
required=False)
|
||||
@ -102,6 +109,7 @@ projectClassifier = args.classifier
|
||||
uploadSbomFlag = args.uploadsbom
|
||||
if uploadSbomFlag:
|
||||
sbomFileName = args.sbomfile
|
||||
minimalSbomFormat = args.minimalsbomformat
|
||||
else:
|
||||
target = args.target
|
||||
|
||||
@ -115,6 +123,11 @@ if uploadSbomFlag:
|
||||
logger.info(f"Reading SBOM from file {sbomFileName}")
|
||||
with open(sbomFileName, 'r') as sbomFile:
|
||||
sbom = sbomFile.read()
|
||||
logger.info("SBOM file read.")
|
||||
if minimalSbomFormat:
|
||||
logger.info("Start converting from minimal format into cyclonedx")
|
||||
sbom = minimalSbomFormatConverter(sbom, projectClassifier)
|
||||
logger.info("Converted")
|
||||
logger.info("Done.")
|
||||
else:
|
||||
# ------- generate SBOM ------------
|
||||
|
Reference in New Issue
Block a user