43 Commits
0.0.1 ... 1.1.0

Author SHA1 Message Date
9afa00f61f add minimal sbom converter
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-07-09 11:26:50 +02:00
bd92d8eb87 drop plantuml snippet
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2025-07-09 08:43:37 +02:00
5a1d6903e8 test plantuml integration
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-06-25 16:05:48 +02:00
67bab6710c documentation
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-06-20 10:38:12 +02:00
f55c3da3ef solve conflicting option
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-06-11 10:40:53 +02:00
f50d821aec verbose switch
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-06-11 09:59:13 +02:00
609f33b181 use correct custom ca location
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-06-11 07:40:14 +02:00
7c8e1156aa some debug
All checks were successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-06-10 18:48:47 +02:00
226456ccd2 absolute pathes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-06-10 17:18:34 +02:00
227ef294d3 custom ca, 14
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-06-04 16:14:16 +02:00
a14e0ab2c5 custom ca, 13
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 16:13:14 +02:00
471fcb2177 custom ca, 12
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 16:01:37 +02:00
0d4ac4022a custom ca, 11
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 15:56:55 +02:00
405d66cdcb custom ca, 10
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 15:53:49 +02:00
a32d9fd643 custom ca, 9
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 15:51:21 +02:00
7f394f82ee custom ca, 8 2025-06-04 15:49:42 +02:00
c8577edf0c custom ca, 7
Some checks failed
ci/woodpecker/tag/woodpecker Pipeline failed
2025-06-04 15:45:06 +02:00
02aba34391 custom ca, 6
Some checks failed
ci/woodpecker/tag/woodpecker Pipeline failed
2025-06-04 15:42:15 +02:00
1fb4c387a7 custom ca, 5
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 15:37:48 +02:00
92b61fdae0 custom ca, 4
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 15:35:23 +02:00
4ddb6cfd30 custom ca, 3
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 15:33:14 +02:00
0eb761db27 custom ca, 2
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 15:31:14 +02:00
9cc81373dc custom ca
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 15:24:15 +02:00
b856424640 factorize stages
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 15:08:00 +02:00
d6a8f5f436 urls in variables 2025-06-04 15:01:52 +02:00
4cc4e5cec6 dockerize goal, 8
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 14:56:11 +02:00
aec0e3fb0e dockerize goal, 7
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 14:03:39 +02:00
05c5c49cd5 dockerize goal, 6
All checks were successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-06-04 14:00:46 +02:00
9156b594e3 dockerize goal, 5
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 13:48:19 +02:00
4fbda91e15 dockerize goal, 4
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 13:46:16 +02:00
50248acefb dockerize goal, 3
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 13:40:05 +02:00
c9c57445b9 dockerize goal, 2
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 13:37:56 +02:00
18631dc02a dockerize goal
Some checks are pending
ci/woodpecker/tag/woodpecker Pipeline is pending
2025-06-04 13:34:45 +02:00
ea90b8b8b0 fix defectdojo goal 1
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-06-04 13:26:35 +02:00
15b2e69960 next goal 2025-06-04 13:25:22 +02:00
9f6f769486 add gitlab ci 2025-06-04 13:22:16 +02:00
784b41f762 new upstream image
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2025-06-02 18:36:04 +02:00
bcbca70496 typo fixed
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-05-22 17:31:14 +02:00
e5fd8709a9 trivy dojo report operator
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-05-22 17:30:10 +02:00
dbf2ca3507 encrypted secrets
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-05-22 17:08:52 +02:00
83d6a7bd64 trivy-operator integration
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-05-21 22:08:24 +02:00
035da3fdca add sbom upload option
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-05-21 15:56:16 +02:00
8d56fcf7c2 md
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2025-05-16 18:47:10 +02:00
11 changed files with 513 additions and 34 deletions

1
.gitignore vendored
View File

@ -3,4 +3,5 @@ defs/
*/.venv/
__pycache__/
.*.swp
tmp/

130
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,130 @@
stages:
- generate-api-clients
- dockerize
variables:
REGISTRY: devnexus.krohne.com:18079/repository/docker-krohne
IMAGE_NAME: $REGISTRY/$CI_PROJECT_NAME
DTRACK_API_URL: https://dtrack-api-rd.krohne.com
DEFECTDOJO_API_URL: https://defectdojo-rd.krohne.com
KROHNE_CA_URL: https://devwiki.krohnegroup.com/lib/exe/fetch.php?media=krohne-ca.crt
KROHNE_CA_CHECKSUM: a921e440a742f1e67c7714306e2c0d76
.generate-api:
stage: generate-api-clients
image: openapitools/openapi-generator-cli:v7.12.0
tags:
- linux
- docker
- bash
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_TAG'
before_script:
- curl --insecure $KROHNE_CA_URL -o krohne-ca.crt
- echo "$KROHNE_CA_CHECKSUM krohne-ca.crt" | md5sum -c
- mv krohne-ca.crt /usr/local/share/ca-certificates
- update-ca-certificates
generate-dtrack-api:
extends: .generate-api
artifacts:
paths:
- dtrack-api-client.tgz
expire_in: 1 week
script:
- curl ${DTRACK_API_URL}/api/openapi.json > dependencytrack-openapi.json
- |
docker-entrypoint.sh \
author template \
-g python \
-o dependencytrack-openapi-custom-template
- sed -i 's/import re/import regex as re/' dependencytrack-openapi-custom-template/model_anyof.mustache
- sed -i 's/import re/import regex as re/' dependencytrack-openapi-custom-template/model_generic.mustache
- |
docker-entrypoint.sh \
generate \
-i dependencytrack-openapi.json \
-g python \
-o dependencytrack-client \
--package-name dependencytrack_api \
-t dependencytrack-openapi-custom-template
- tar -czvf dtrack-api-client.tgz dependencytrack-client
generate-defectdojo-api:
extends: .generate-api
artifacts:
paths:
- defectdojo-api-client.tgz
expire_in: 1 week
script:
- curl ${DEFECTDOJO_API_URL}/api/v2/oa3/schema/?format=json > defectdojo-openapi.json
- |
docker-entrypoint.sh \
generate \
-i defectdojo-openapi.json \
-g python \
-o defectdojo-client \
--package-name defectdojo_api
- tar -czvf defectdojo-api-client.tgz defectdojo-client
dockerize:
stage: dockerize
image: devnexus.krohne.com:18079/repository/docker-krohne/krohnedockerbash:0.5
tags:
- linux
- docker
- bash
rules:
- if: '$CI_COMMIT_TAG'
script:
- tar -xzf defectdojo-api-client.tgz
- tar -xzf dtrack-api-client.tgz
- docker build --build-arg ADDITIONAL_CA_URL="$KROHNE_CA_URL"
--build-arg ADDITIONAL_CA_CHECKSUM=$KROHNE_CA_CHECKSUM
--tag $IMAGE_NAME:latest
--tag $IMAGE_NAME:$CI_COMMIT_SHA
--tag $IMAGE_NAME:$CI_COMMIT_TAG
.
- docker login -u $NEXUS_USER -p $NEXUS_PASSWORD $REGISTRY
- docker push $IMAGE_NAME:latest
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- docker push $IMAGE_NAME:$CI_COMMIT_TAG
#
# build:
# image: plugins/kaniko
# settings:
# repo: ${FORGE_NAME}/${CI_REPO}
# registry:
# from_secret: container_registry
# tags: latest,${CI_COMMIT_SHA},${CI_COMMIT_TAG}
# username:
# from_secret: container_registry_username
# password:
# from_secret: container_registry_password
# dockerfile: Dockerfile
# when:
# - event: [ push, tag ]
#
# build-for-quay:
# image: plugins/kaniko
# settings:
# repo: quay.io/wollud1969/${CI_REPO_NAME}
# registry: quay.io
# tags:
# - latest
# - ${CI_COMMIT_TAG}
# username:
# from_secret: quay_username
# password:
# from_secret: quay_password
# dockerfile: Dockerfile
# when:
# - event: [tag]
#

View File

@ -1,4 +1,4 @@
FROM python:3.12.10-alpine3.21
FROM python:3.12.10-alpine3.22
ENV DTRACK_API_URL=""
ENV DTRACK_TOKEN=""
@ -6,18 +6,33 @@ ENV DEFECTDOJO_URL=""
ENV DEFECTDOJO_TOKEN=""
ARG APP_DIR=/opt/app
ARG ADDITIONAL_CA_URL="x"
ARG ADDITIONAL_CA_CHECKSUM="y"
RUN \
set -e &&\
apk add --no-cache syft &&\
adduser -s /bin/sh -D user &&\
mkdir -p $APP_DIR &&\
chown user:user $APP_DIR
chown user:user $APP_DIR &&\
echo $ADDITIONAL_CA_URL &&\
echo $ADDITIONAL_CA_CHECKSUM &&\
if [ "$ADDITIONAL_CA_URL" != "x" ]; then \
cd /usr/local/share/ca-certificates; \
wget --no-check-certificate -O custom-ca.crt $ADDITIONAL_CA_URL; \
echo "$ADDITIONAL_CA_CHECKSUM custom-ca.crt" | md5sum -c; \
/usr/sbin/update-ca-certificates; \
echo "custom ca added"; \
else \
echo "no additional ca"; \
fi
USER user
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

100
readme.md
View File

@ -1,6 +1,96 @@
# Python Client Packages for the DependencyTrack and DefectDojo API
# DependencyTrack and DefectDojo Automation
## Download the OpenAPI definitions
## Using
### Distribution
The glue logic comes in a docker image and can be started as a docker container. Due to the dependencies, especially the ones related to the
APIs of DependencyTrack and DefectDojo this approach has been chosen.
The image is available at
```
quay.io/wollud1969/dtrack-defectdojo-automation
```
and at
```
devnexus.krohne.com:18079/repository/docker-krohne/dtrack-defectdojo-automation
```
The tag to be used at the moment is `1.0.5`.
### Start script
On Linux I've created two files to start the beast:
env-sbom-dd-dt
```
DTRACK_API_URL=https://dtrack-api-rd.krohne.com
DEFECTDOJO_URL=https://defectdojo-rd.krohne.com
DTRACK_TOKEN=...
DEFECTDOJO_TOKEN=...
```
The correct values for the tokens must be set here, obviously.
sbom-dd-dt.sh
```
#!/bin/bash
docker run -t -v $PWD:/work --rm --env-file ~/env-sbom-dt-dd devnexus.krohne.com:18079/repository/docker-krohne/dtrack-defectdojo-automation:1.0.5 "$@"
```
I've both files directly in my home-dir.
### File locations
When using the container and the script, you must consider that the container has no full access to your filesystem and you need to mount required parts of your filesystem into the container. In the above script I do this with the option `-v $PWD:/work`. This option mounts the current directory (the one from where you are starting the script and thus the container) into the directory `/work` within the container.
This is required when scanning a directory or uploading a prepared SBOM file.
### Options of the container/script
The container has the glue logic script as entrypoint. To find out about the options, call
```
dehottgw@DE01RDDEV01:~$ docker run -t -v $PWD:/work --rm --env-file ~/env-sbom-dt-dd devnexus.krohne.com:18079/repository/docker-krohne/dtrack-defectdojo-automation:1.0.5 -- -h
usage: sbom-dt-dd.py [-h] --name NAME --version VERSION --description DESCRIPTION --type TYPE --classifier
{APPLICATION,FRAMEWORK,LIBRARY,CONTAINER,OPERATING_SYSTEM,DEVICE,FIRMWARE,FILE,PLATFORM,DEVICE_DRIVER,MACHINE_LEARNING_MODEL,DATA}
[--uploadsbom] [--sbomfile SBOMFILE] [--target TARGET] [--verbose]
sbom-dt-dd.py: error: the following arguments are required: --name/-n, --version/-v, --description/-d, --type/-t, --classifier/-c
dehottgw@DE01RDDEV01:~$
```
Note the double-dash at the end of the commandline before the `-h`. It is necessary, otherwise the `-h` would be considered as an option for the docker command itself.
### SBOM upload example
For this example I've a file `combined-sbom.json` in the directory `software1`:
```
cd software1/
~/sbom-dt-dd.sh --name software1-server --version 0.0.1 --description "Server software for the Software1 platform" --type 1 --classifier APPLICATION --uploadsbom --sbomfile /work/combined-sbom.json -V
```
## Building
### Python Client Packages for the DependencyTrack and DefectDojo API
#### Download the OpenAPI definitions
```
curl https://dtrack-api.hottis.de/api/openapi.json \
@ -10,7 +100,7 @@ curl https://defectdojo.hottis.de/api/v2/oa3/schema/?format=json \
```
## Naive Generation of the Client Package for DefectDojo
#### Naive Generation of the Client Package for DefectDojo
```
docker run \
@ -28,7 +118,7 @@ docker run \
For DefectDojo the naive code generation works.
## Naive Generation of the Client Package for DependencyTrack
#### Naive Generation of the Client Package for DependencyTrack
```
docker run \
@ -43,7 +133,7 @@ docker run \
--package-name dependencytrack_api
```
## Fixed Generation of the Client Package for DependencyTrack
#### Fixed Generation of the Client Package for DependencyTrack
In the OpenAPI definition of DependencyTrack a regex is used which is not understood by Python's
default regex implement `re`, which in turn is hardwired in the openapi-generator provided code.

9
src/ENV.asc Normal file
View File

@ -0,0 +1,9 @@
-----BEGIN PGP MESSAGE-----
jA0ECQMIapWTXVBqXIb+0sAdAaPkf/oMhzDAm6T4mEFScMs5BJa444hJEkLgYSAS
upN+QQSY5/x0OdoghQmaUXmRcu17kaFyzFsS+EaHymru4mpmOpwS/+YerHrhpfNF
j3YfhW/sM6v2wYJAq+8utkPhATC36LxwgTZRbGBFGgFCG7fUHlldPO1DeVJvoQNe
idpfg5irM+x78XC7tDJOdYYrvDcz0EELBuwB7V78ZHUfjLcvKek1exhLOq8+V60A
nZhsoaELIEfCQx52ayF1TbvNdqOTCXpWHfgE9A9aw2eqRbMn9P+HOdeRz1t0+1s=
=rEHE
-----END PGP MESSAGE-----

96
src/converter.py Normal file
View 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")

View File

@ -1,11 +1,11 @@
#!/bin/sh
# entrypoint.sh
source ./.venv/bin/activate
source /opt/app/.venv/bin/activate
PYTHONPATH="$PYTHONPATH:./dependencytrack-client"
PYTHONPATH="$PYTHONPATH:./defectdojo-client"
PYTHONPATH="$PYTHONPATH:/opt/app/dependencytrack-client"
PYTHONPATH="$PYTHONPATH:/opt/app/defectdojo-client"
export PYTHONPATH
exec python sbom-dt-dd.py "$@"
exec python /opt/app/sbom-dt-dd.py "$@"

View File

@ -1,3 +1,5 @@
regex==2024.11.6
loguru==0.7.3
PyYAML==6.0.2
cyclonedx-python-lib==10.4.1

View File

@ -12,11 +12,16 @@ 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=[]):
try:
logger.info(f"Calling {ApiClass}.{EndpointMethod} with {RequestClass} ({additionalParams}, {requestParams})")
logger.info(f"Calling {ApiClass=}.{EndpointMethod=} with {RequestClass=})")
if VERBOSE:
logger.debug(f"{additionalParams=}, {requestParams=}")
instance = ApiClass(apiClient)
if RequestClass:
request = RequestClass(**requestParams)
@ -44,6 +49,7 @@ def generateSBOM(target='.', name='dummyName', version='0.0.0'):
logger.error(f"SBOM scanner failed: {e.stderr}")
raise MyLocalException(e)
# ---- main starts here with preparation of config -----------------------------------------------------------------------
try:
DTRACK_API_URL = os.environ["DTRACK_API_URL"]
@ -70,37 +76,75 @@ parser.add_argument('--type', '-t',
required=True)
parser.add_argument('--classifier', '-c',
help='Project Classifier from DependencyTrack',
choices=['APPLICATION', 'FRAMEWORK', 'LIBRARY', 'CONTAINER', 'OPERATING_SYSTEM', 'DEVICE', 'FIRMWARE', 'FILE', 'PLATFORM', 'DEVICE_DRIVER', 'MACHINE_LEARNING_MODEL', 'DATA'],
choices=['APPLICATION', 'FRAMEWORK', 'LIBRARY', 'CONTAINER', 'OPERATING_SYSTEM', 'DEVICE',
'FIRMWARE', 'FILE', 'PLATFORM', 'DEVICE_DRIVER', 'MACHINE_LEARNING_MODEL', 'DATA'],
required=True)
parser.add_argument('--uploadsbom', '-U',
help='Upload a already existing SBOM instead of generating it. Give the SBOM file at -F instead of a target',
required=False,
action='store_true',
default=False)
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=True)
required=False)
parser.add_argument('--verbose', '-V',
help='A lot of debug output',
required=False,
action='store_true',
default=False)
args = parser.parse_args()
projectName = args.name
projectVersion = args.version
projectDescription = args.description
productType = args.type
projectClassifier = args.classifier
target = args.target
uploadSbomFlag = args.uploadsbom
if uploadSbomFlag:
sbomFileName = args.sbomfile
minimalSbomFormat = args.minimalsbomformat
else:
target = args.target
VERBOSE = args.verbose
logger.info(f"Generating SBOM for {target}")
sbom = generateSBOM(target, projectName, projectVersion)
logger.info("Done.")
# ---- main starts here --------------------------------------------------------------------------------------------------
if uploadSbomFlag:
# ------- read uploaded SBOM -------------
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 ------------
logger.info(f"Generating SBOM for {target}")
sbomJson = generateSBOM(target, projectName, projectVersion)
sbom = json.dumps(sbomJson)
logger.info("Done.")
# ------- create product and engagement in DefectDojo -------
defectdojo_configuration = defectdojo_api.Configuration(
host = DEFECTDOJO_URL
)
defectdojo_configuration.api_key['tokenAuth'] = DEFECTDOJO_TOKEN
defectdojo_configuration.api_key_prefix['tokenAuth'] = 'Token'
dependencytrack_configuration = dependencytrack_api.Configuration(
host = f"{DTRACK_API_URL}/api"
)
dependencytrack_configuration.debug = False
dependencytrack_configuration.api_key['ApiKeyAuth'] = DTRACK_TOKEN
with defectdojo_api.ApiClient(defectdojo_configuration) as defectdojo_api_client:
print("Create product in DefectDojo")
productName = f"{projectName}:{projectVersion}"
@ -134,6 +178,14 @@ with defectdojo_api.ApiClient(defectdojo_configuration) as defectdojo_api_client
engagement_id = engagement_response.id
print(f"{engagement_id=}")
# ------- create project in DependencyTrack, connect project to engagement in DefectDojo, upload SBOM --------
dependencytrack_configuration = dependencytrack_api.Configuration(
host = f"{DTRACK_API_URL}/api"
)
dependencytrack_configuration.debug = False
dependencytrack_configuration.api_key['ApiKeyAuth'] = DTRACK_TOKEN
with dependencytrack_api.ApiClient(dependencytrack_configuration) as dependencytrack_api_client:
project_response = \
executeApiCall(
@ -149,9 +201,12 @@ with dependencytrack_api.ApiClient(dependencytrack_configuration) as dependencyt
print(f"{project_uuid=}")
properties = [
{ 'group_name': "integrations", 'property_name': "defectdojo.engagementId", 'property_value': str(engagement_id), 'property_type': "STRING" },
{ 'group_name': "integrations", 'property_name': "defectdojo.doNotReactivate", 'property_value': "true", 'property_type': "BOOLEAN" },
{ 'group_name': "integrations", 'property_name': "defectdojo.reimport", 'property_value': "true", 'property_type': "BOOLEAN" }
{ 'group_name': "integrations", 'property_name': "defectdojo.engagementId",
'property_value': str(engagement_id), 'property_type': "STRING" },
{ 'group_name': "integrations", 'property_name': "defectdojo.doNotReactivate",
'property_value': "true", 'property_type': "BOOLEAN" },
{ 'group_name': "integrations", 'property_name': "defectdojo.reimport",
'property_value': "true", 'property_type': "BOOLEAN" }
]
for property in properties:
executeApiCall(
@ -170,6 +225,6 @@ with dependencytrack_api.ApiClient(dependencytrack_configuration) as dependencyt
dependencytrack_api.BomApi.upload_bom,
None,
None,
[ None, False, projectName, projectVersion, None, None, None, None, True, json.dumps(sbom) ]
[ None, False, projectName, projectVersion, None, None, None, None, True, sbom ]
)

14
todo.md
View File

@ -1,11 +1,13 @@
- 2025-04-04
- Dirk K.
- DefectDojo - Jira Integration
- Monitor SLA expiry on DefectDojo
- Workflow for review of assessments in DefectDojo
- Trivy-Deployment in cluster shall be integrated with DefectDojo
- [ ] DefectDojo - Jira Integration
- [ ] Monitor SLA expiry on DefectDojo
- [ ] Workflow for review of assessments in DefectDojo
- [x] Trivy-Deployment in cluster shall be integrated with DefectDojo
- [Import Trivy Operator reports into DefectDojo](https://medium.com/@alexander.murylev/implementing-centralized-security-scanning-across-multiple-kubernetes-clusters-with-trivy-and-989f3d5b0f4a)
- [Trivy Dojo Report Operator by Telekom](https://github.com/telekom-mms/trivy-dojo-report-operator)
- Thomas O.
- DefectDojo and/or DependencyTrack shall notify via mail in case of new vulnerabilities
- add switch to glue logic to disable integrated SBOM generator and read externally
- [ ] DefectDojo and/or DependencyTrack shall notify via mail in case of new vulnerabilities
- [x] add switch to glue logic to disable integrated SBOM generator and read externally
generated SBOM from file

View File

@ -0,0 +1,79 @@
# Integration of the Trivy Operator in Kubernetes with DefectDojo
## Installation of the Trivy Operator
*namespace*
```
security
```
*install.sh*
```
#!/bin/bash
NAMESPACE=$(cat namespace)
VERSION=0.28.1
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm repo update
helm upgrade --install trivy-operator aqua/trivy-operator \
-f values.yml \
--namespace $NAMESPACE \
--version $VERSION
```
*values.yml*
```
trivy:
timeout: "10m0s"
operator:
scanJobTimeout: 10m
targetNamespaces: "homea"
```
If `targetNamespaces` is skipped, all namespaces will be scanned. If only a limited set of namespaces shall be scanned, put those namespace comma-separated into this option.
## Installation of the Trivy Dojo Report Operator
*namespace*
```
security
```
*install.sh*
```
#!/bin/bash
NAMESPACE=$(cat namespace)
VERSION=0.8.8
helm repo add trivy-dojo-report-operator https://telekom-mms.github.io/trivy-dojo-report-operator/
helm repo update
helm install chart-name trivy-dojo-report-operator/trivy-dojo-report-operator \
-f values.yml \
--namespace $NAMESPACE \
--version $VERSION
```
*values.yml*
```
defectDojoApiCredentials:
apiKey: "geheim"
url: "https://defectdojo.hottis.de"
operator:
trivyDojoReportOperator:
env:
defectDojoEvalEngagementName: "true"
defectDojoEngagementName: "body['report']['artifact']['tag']"
defectDojoEvalProductName: "true"
defectDojoProductName: "meta['namespace']+':'+meta['name']"
```
Make sure to set the correct apiKey. And make sure not to store it in a repo. A secure approach will be provided.
Details on this operator can be found [here](https://medium.com/@alexander.murylev/implementing-centralized-security-scanning-across-multiple-kubernetes-clusters-with-trivy-and-989f3d5b0f4a) and [here](https://github.com/telekom-mms/trivy-dojo-report-operator).