The trivy-dojo-report-operator is a Kubernetes operator developed using Kopf and Python. This operator listens for vulnerability reports generated by the Trivy Operator and forwards them to Defect Dojo for further analysis and tracking.
- Monitor Kubernetes for new Trivy vulnerability reports.
- Intercept and modify reports (e.g., for custom deduplication or enrichment) before upload.
- Push vulnerability reports to a configured Defect Dojo instance.
- Seamless integration with your existing Kubernetes cluster and security workflow.
- Developed using the Pythonic Kopf framework for easy maintenance and extensibility.
- Vulnerability reports
- RBAC Assessment reports
- Infra Assessment reports
- Config Audit reports
- Exposed secrets
- A running Kubernetes cluster (minikube, kind, or another environment)
- Trivy Operator installed and configured in the cluster
- An instance of Defect Dojo for storing vulnerability reports
- Configure Defect Dojo settings:
Update the variables in the values.yaml to match your Defect Dojo instance configuration, including the environment variables. The most important settings are the URL and the API-Key for your Defect Dojo instance:
defectDojoApiCredentials:
apiKey: "YOUR_DEFECTDOJO_API_KEY"
url: "https://YOUR.DEFECTDOJO.URL"The options closely match the options in the import-scan API-call found in the
Defect Dojo API Docs.
- Deploy the chart from the repository:
helm repo add trivy-dojo-report-operator https://telekom-mms.github.io/trivy-dojo-report-operator/
helm repo update
helm install $YOUR_RELEASE_NAME trivy-dojo-report-operator/trivy-dojo-report-operator --values YOUR_VALUES.yaml- or deploy the chart manually after cloning the git-repository:
git clone https://github.com/telekom-mms/trivy-dojo-report-operator.git
cd trivy-dojo-report-operator
helm install $YOUR_RELEASE_NAME charts/- or deploy the trivy-dojo-report-operator using the Kubernetes manifests
- update the secret in the manifest
- apply manifests
kubectl create ns mgmt
kubectl apply -f deploy/trivy-dojo-report-operator.yamlThe operator is now running in your cluster and will monitor for Trivy vulnerability reports and push them to the configured Defect Dojo instance.
You can also run the operator locally. This way you don't have to install
anything in your cluster. Just provide the Defect Dojo URL and API-Key and
optionally labels to the docker run command.
You also have to mount your kubeconfig into the container to access the cluster.
docker pull ghcr.io/telekom-mms/docker-trivy-dojo-operator
docker run -it -v /path/to/your/.kube/config:/root/.kube/config \
-e DEFECT_DOJO_API_KEY=$DEFECT_DOJO_API_KEY \
-e DEFECT_DOJO_URL=$DEFECT_DOJO_URL \
-e DEFECT_DOJO_PRODUCT_TYPE_NAME="Research and Development" \
-e LABEL="trivy-operator.resource.name" \
-e LABEL_VALUE="master-live-server" \
-e REPORTS="vulnerabilityreports"
ghcr.io/telekom-mms/docker-trivy-dojo-operatorFor a local development setup, please take a look at LOCAL-DEVELOPMENT.md.
| Variable | Default | Description |
|---|---|---|
defectDojoActive |
true |
Enable or disable Defect Dojo integration. |
defectDojoAutoCreateContext |
true |
Automatically create Engagements, Products, and Product Types in Defect Dojo. |
defectDojoCloseOldFindings |
false |
Close findings in Defect Dojo that are no longer present in the report. If a service is set, only findings for that service are closed. |
defectDojoCloseOldFindingsProductScope |
false |
If true, closes old findings across the entire product; otherwise, only within the engagement. |
defectDojoDeduplicationOnEngagement |
true |
Restrict deduplication of imported findings to the current engagement. |
defectDojoEngagementName |
engagement |
Name of the engagement in Defect Dojo. |
defectDojoEvalEngagementName |
false |
Evaluate engagement name as a Python expression. |
defectDojoEvalProductName |
false |
Evaluate product name as a Python expression. |
defectDojoEvalProductTypeName |
false |
Evaluate product type name as a Python expression. |
defectDojoEvalServiceName |
false |
Evaluate service name as a Python expression. |
defectDojoEvalEnvName |
false |
Evaluate environment name as a Python expression. |
defectDojoEvalTestTitle |
false |
Evaluate test title as a Python expression. |
defectDojoMinimumSeverity |
Info |
Minimum severity level for findings to be reported. |
defectDojoProductName |
product |
Name of the product in Defect Dojo. |
defectDojoProductTypeName |
Research and Development |
Product type in Defect Dojo. |
defectDojoServiceName |
(empty) | Name of the service in Defect Dojo. |
defectDojoEnvName |
Development |
Environment type in Defect Dojo. |
defectDojoPushToJira |
false |
Push findings to Jira via Defect Dojo. |
defectDojoTestTitle |
Kubernetes |
Title of the test in Defect Dojo. |
defectDojoVerified |
false |
Mark findings as verified in Defect Dojo. |
defectDojoDoNotReactivate |
true |
If true, do not reactivate previously closed findings when importing. |
reports |
vulnerabilityreports |
Comma-separated list of report types to send to Defect Dojo. Supported types: vulnerabilityreports, rbacassessmentreports, infraassessmentreports, configauditreports, exposedsecretreports. |
http_proxy |
(empty) | HTTP proxy setting (optional). |
https_proxy |
(empty) | HTTPS proxy setting (optional). |
excludedNamespaces |
(empty) | List of namespace globs patterns to exclude from processing. Each pattern is converted into a --namespace=! CLI argument passed to the operator Deployment. Reports from these namespaces will be ignored (optional). |
transformation.enabled |
false |
Enable the transformation hook to modify reports before upload. |
transformation.scriptFilename |
wrapper.sh |
Filename of the entrypoint script within the ConfigMap volume (mounted at /scripts/). |
transformation.interpreter |
bash |
Command used to execute the script (e.g., bash, python3, jq). |
transformation.scanType |
Generic Findings Import |
The DefectDojo scanner type (parser) to use when the transformation succeeds. |
transformation.scriptConfigMap.ref |
(empty) | Name of an existing externally-deployed ConfigMap to mount. Used when create is false. |
transformation.scriptConfigMap.create |
false |
When true, Helm creates and manages the ConfigMap from data. The name is derived automatically from the release (<release>-transform-scripts). |
transformation.scriptConfigMap.data |
{} |
Map of filename: content for the script files. Only used when create is true. |
rbac.additionalRules |
[] |
Additional RBAC rules appended to the base ClusterRole. Strictly additive — base rules are always present. Each entry is a standard RBAC rule object (apiGroups, resources, verbs). |
When setting one of the Eval*-settings to true, the corresponding name or
title will be run as a python function.
For example, set defectDojoEvalEngagementName to true and
defectDojoEngagementName to meta["creationTimestamp"], then the
creationTimestamp of the vulnerability Report Resource in Kubernetes will be
evaluated and used as the engagement name.
If you set defectDojoEngagementName to body["report"]["artifact"]["tag"],
then the engagement will get the name of the specified image-tag.
The transformation hook allows you to manipulate the report data exactly as you need it before it reaches DefectDojo. Common use cases include:
- Custom deduplication logic.
- Enriching findings with specific labels from the scanned image.
- Filtering out specific findings.
The operator serializes the raw Trivy report to a JSON string and pipes it into your script's stdin. Your script must write the final modified JSON to stdout.
If the script exits with code 0, the operator uses the modified content and switches the DefectDojo scan_type to the one configured in transformation.scanType. If the script fails, the operator automatically falls back to sending the original report with the default "Trivy Operator Scan" type.
The script files are mounted into the operator pod at /scripts/. The scriptFilename value is always just the bare filename — the /scripts/ prefix is added automatically.
There are two ways to provide the ConfigMap that holds your scripts:
Option A — Helm manages the ConfigMap (scriptConfigMap.create: true)
Helm creates the ConfigMap (named <release>-transform-scripts) from the inline data you provide:
transformation:
enabled: true
interpreter: bash
scriptFilename: wrapper.sh
scanType: Generic Findings Import
scriptConfigMap:
create: true
data:
wrapper.sh: |
#!/bin/bash
cat - | python3 /scripts/transform.py
transform.py: |
import sys, json
print(sys.stdin.read())Option B — externally-deployed ConfigMap (scriptConfigMap.ref)
Deploy the ConfigMap yourself (e.g. kubectl apply -f my-scripts-cm.yaml) and reference it by name:
transformation:
enabled: true
interpreter: bash
scriptFilename: wrapper.sh
scanType: Generic Findings Import
scriptConfigMap:
ref: my-transform-scriptsIf your transformation script needs to query Kubernetes resources (e.g. to resolve pods or read image pull secrets), the operator's ClusterRole must be extended. Use rbac.additionalRules to append rules without affecting the default minimal permissions:
rbac:
additionalRules:
- apiGroups: [""]
resources: [pods, secrets, replicationcontrollers]
verbs: [list, watch, get]
- apiGroups: [apps]
resources: [deployments, statefulsets, daemonsets, replicasets]
verbs: [get]
- apiGroups: [batch]
resources: [jobs, cronjobs]
verbs: [get]The base ClusterRole rules are always present and cannot be removed through this value.
The operator provides a Prometheus metrics endpoint(:9090/metrics), where successful and failed requests are collected.
# HELP request_processing_seconds Time spent processing request
# TYPE request_processing_seconds summary
request_processing_seconds_count 0.0
request_processing_seconds_sum 0.0
# HELP request_processing_seconds_created Time spent processing request
# TYPE request_processing_seconds_created gauge
request_processing_seconds_created 1.7507117204042602e+09
# HELP requests_total HTTP Requests
# TYPE requests_total counter
requests_total{status="failed"} 4.0
requests_total{status="success"} 1.0
# HELP requests_created HTTP Requests
# TYPE requests_created gauge
requests_created{status="failed"} 1.750711807587969e+09
requests_created{status="success"} 1.750712088603659e+09-
On push to main
- a new release version is calculated
- versions in all files are automatically updated
- k8s manifests are rendered from the helm chart and updated in deploy/
- a draft release is created
-
On publish of the release a
- new tag is created
- new release is created
- new container image is built
- new helm chart is published
GPLv3