Scan reports and dashboards¶
Every scanner in CI reports to the same three places: GitHub code scanning, the run's artifacts, and (optionally) a DefectDojo instance. This page is the map.
Where findings land¶
| Scanner | Workflow | Code scanning tool | Artifact | DefectDojo scan type |
|---|---|---|---|---|
| CodeQL | codeql.yml |
CodeQL | — | — |
| OpenSSF Scorecard | scorecard.yml |
ossf-scorecard | scorecard-sarif |
— |
| Trivy fs (vuln + IaC misconfig) | trivy.yml |
Trivy | trivy-report |
Trivy Scan (JSON) |
| OWASP ZAP baseline | zap.yml |
OWASP ZAP | zap-report |
ZAP Scan (JSON) |
| grype (release images) | release.yml |
grype-<image> |
— | SARIF |
| ScanCode (vendored-file licenses) | license-scan.yml |
— | scancode-report |
ScanCode Scan |
| SonarQube | sonarqube.yml |
— | — | server-side pull (below) |
| dependency-review (PR gate) | ci.yml |
— | — | — |
| gitleaks (secrets) | static-gates.yml |
— | — | — |
GitHub code scanning (Security → Code scanning) is the single list for everything that emits SARIF: filter by tool to isolate one scanner. Trivy, ZAP, grype, CodeQL and Scorecard all land there.
SonarQube findings stay on the SonarQube dashboard itself
(vars.SONAR_HOST_URL); there is no SARIF path for them. The workflow only
reports the quality-gate result as a check.
License violations on dependencies are a PR gate, not a report: the
dependency-review job in ci.yml comments and fails on denied licenses.
The license-scan workflow covers the files we redistribute (vendored JS,
bundled fonts, the Keycloak theme) and fails when ScanCode detects a license
id outside scripts/ci/license-allowlist.txt.
The web image carries no package manager. The release gate stops only on a
fixable critical finding, so High advisories need their own guard. The node
base image ships npm, corepack and yarn with bundled dependencies that have
fixable High advisories no base-image bump has cleared, and the console never
runs them (HEALTHCHECK and CMD call node only), so the runtime stage of
web/Dockerfile deletes them. scripts/ci/web-image-deps.sh (in
static-gates.yml and make gates) fails when that rm disappears or when
web/package-lock.json resolves next or sharp below the patched versions
(raise the floors in the script when a newer advisory lands). The Node
service images that must keep npm (mcp-stdio-runner, workspace-runner,
browser-runner) are not covered by this guard.
What the scanners leave out¶
Both exclusions live in a file next to the workflow, with the reason beside each path, so a report can be read as "what ships".
- CodeQL reads
.github/codeql/codeql-config.yml. Itspaths-ignoredrops test and tooling code (tests/,web/e2e/,**/src/test/,*.test.ts(x),*.spec.ts,web/scripts/), the bundled documentation assets andexamples/. The workflow still runs on every pull request (it has no workflow-level path filter, soCodeQL OKalways reports); only alerts in those paths are not raised. The query suite stayssecurity-extended, set incodeql.yml. - Trivy reads
trivy.yaml(trivy-configon both scan steps), which skipsdeploy/helm/examplesandtests/load: example values files and kind load-test manifests, where a replica count or a missing resource limit is not a finding.infra/terraform/modulesis scanned on purpose: those are reference modules the operator hardens (seeinfra/terraform/README.md), and their findings are that operator's checklist.
DefectDojo (all findings, one dashboard)¶
DefectDojo is the optional aggregation layer: it ingests every scanner's report, dedupes findings across runs, and tracks mitigated/reopened state — including SonarQube, which the other sinks cannot show.
Run one locally¶
# in deploy/compose/.env (or the shell): DD_ADMIN_PASSWORD, DD_SECRET_KEY,
# DD_CREDENTIAL_AES_256_KEY, DD_DATABASE_PASSWORD
docker compose -f deploy/compose/defectdojo.yml \
--env-file deploy/compose/.env up -d --wait
# http://localhost:8082 — log in as `admin`
Get an API token via the user menu → API v2 Key.
Point CI at it¶
- Repository variable:
DEFECTDOJO_URL(e.g.https://dojo.example.com) - Repository secret:
DEFECTDOJO_API_KEY
While either is unset, scripts/ci/defectdojo-import.sh skips with a notice
and every workflow stays green — the same opt-in pattern as
vars.SONAR_HOST_URL.
Each workflow imports into its own engagement (trivy-fs, zap-baseline,
license-scan, Release <version>) under an auto-created product named
after the repository (DEFECTDOJO_PRODUCT), in the product type VegaDuta
(DEFECTDOJO_PRODUCT_TYPE); reimports dedupe and close findings that disappear between runs.
The ZAP baseline and license scans set DEFECTDOJO_CLOSE_OLD_FINDINGS=false
because their coverage varies between runs.
SonarQube → DefectDojo¶
SonarQube has no report file to push. Instead, on the product in DefectDojo, add a Tool Configuration of type SonarQube pointing at your SonarQube host and token; the SonarQube API Import scan type pulls findings server-side.
Report artifacts¶
| Artifact | Contents |
|---|---|
trivy-report |
trivy.sarif + trivy.json |
zap-report |
report_json.json, report_md.md, report_html.html, zap.sarif |
zap_scan |
same files, uploaded by the baseline action itself |
scancode-report |
scancode.json |
scorecard-sarif |
results.sarif |
core-coverage |
per-module JaCoCo reports (also feeds SonarQube) |
sdk-sbom / sbom-<image> |
CycloneDX / SPDX SBOMs |