# How do we let users know that we know about this CVE?
We are trying to figure out how to communicate false-positives to the community.
One problem we have solved early on was to let the community know that us (maintainers) will not review dumps coming from scanners, unless they contain some form of impact analysis. This policy is recorded in https://github.com/cert-manager/community/blob/main/SECURITY.md.
The source of the problem remains: people will still see these vulns and ask us to fix them. In this document, I go over a few ideas to mitigate this.
## How do we track vulns?
A few months ago, Ash started tracking Govulncheck warnings for all main branches as well as all supported releases https://cert-manager-dashboard.sgtcodfish.com/. It also displays the Trivy results that we run nightly on the two latest cert-manager releases.
Although we do track vulns and are able to cut the noise using Govulncheck, we unfortunately don't tell anyone else.
## GitHub Advisories
GitHub Advisories (https://github.com/cert-manager/cert-manager/security/advisories)
but they aren't well suited for communicating false positives due to third-party deps... we do use publish when a third party vuln does affect cert-manager, though, but it's just not well suited for OCI images.
GitHub Advisories is only really useful for language packages (Go, for example) as it would show up in everybody's scanners. That said, in the case of a Go program like cert-manager, GitHub Advisories being looked up by most scanners, the advisory would show up... Let's take a few examples:
Talking about Trivy, we already maintain a [`.trivyignore`](https://github.com/cert-manager/cert-manager/blob/0e98ca4a/.trivyignore) file, but it's only meant to ignore CVEs at the time we release. We don't run Trivy to track CVEs of currently supported releases. In fact, we currently (as of 4th Sept 2025) have a "high" severity CVE reported for all of our images:
```
$ trivy image quay.io/jetstack/cert-manager-controller:v1.18.2
┌─────────────────────────────────────────────────────────────────┬──────────┬─────────────────┬─────────┐
│ Target │ Type │ Vulnerabilities │ Secrets │
├─────────────────────────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤
│ quay.io/jetstack/cert-manager-controller:v1.18.2 (debian 12.11) │ debian │ 0 │ - │
├─────────────────────────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤
│ app/cmd/controller/controller │ gobinary │ 1 │ - │
└─────────────────────────────────────────────────────────────────┴──────────┴─────────────────┴─────────┘
┌─────────┬────────────────┬──────────┬────────┬───────────────────┬─────────────────┬────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├─────────┼────────────────┼──────────┼────────┼───────────────────┼─────────────────┼────────────────────────────────────────────┤
│ stdlib │ CVE-2025-47907 │ HIGH │ fixed │ v1.24.4 │ 1.23.12, 1.24.6 │ database/sql: Postgres Scan Race Condition │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2025-47907 │
└─────────┴────────────────┴──────────┴────────┴───────────────────┴─────────────────┴────────────────────────────────────────────┘
```
A while back, we decided in [20230302.gomod.md](https://github.com/cert-manager/cert-manager/blob/0e98ca4aa/design/20230302.gomod.md?plain=1#L74) to migrate to per-binary Go modules to reduce the blast radius of CVEs in Go libraries. Our goal was to "eliminate CVE reports for any supported release of cert-manager" (also mentioned [in the 1.12 release notes](https://cert-manager.io/docs/releases/release-notes/release-notes-1.12/#faster-response-to-cves-by-reducing-transitive-dependencies)). We could get closer to that goal by tracking all CVEs and using an OpenVEX document brings us closer to that goal of eliminating CVE reports.
Note that even though our policy is to ignore false-positive CVEs, we have repeatedly pleased scanners in the past: in [v1.10.2](https://cert-manager.io/docs/releases/release-notes/release-notes-1.10/#v1102-changes-since-v1101), [v1.12.15](https://cert-manager.io/docs/releases/release-notes/release-notes-1.12/#v11215), [v1.12.17](https://cert-manager.io/docs/releases/release-notes/release-notes-1.12/#v11217), in [v1.16.5](https://cert-manager.io/docs/releases/release-notes/release-notes-1.16/#v1165), [v1.16.3](https://cert-manager.io/docs/releases/release-notes/release-notes-1.16/#v1163), and in [v1.15.5](https://cert-manager.io/docs/releases/release-notes/release-notes-1.15/#v1155).
## Example
Let's take the example of CVE-2025-47907. To create an OpenVEX document, I used [vexctl](https://github.com/openvex/vexctl). I wrote a quick script since lots of images are affected:
```bash
#!/bin/bash
VERSION=v1.18.2
CTL_VERSION=v1.12.17
(
for p in linux/amd64 linux/arm64 linux/arm/v7 linux/ppc64le linux/s390x; do
for c in controller webhook cainjector startupapicheck acmesolver; do
echo "quay.io/jetstack/cert-manager-$c:$VERSION --platform $p"
done
echo "quay.io/jetstack/cert-manager-ctl:$CTL_VERSION --platform $p"
done
) |
xargs -P1 -L1 trivy image --format cyclonedx --quiet | jq .metadata.component.purl -r |
sed 's|^|--product "|g' | sed 's|$|"|g' |
xargs vexctl create \
--author="Mael Valais <mael@vls.dev>" \
--vuln="CVE-2025-47907" \
--status="not_affected" \
--justification="component_not_present" \
--impact-statement="cert-manager does not import the Go database/sql package"
```
The resulting document an [OpenVEX document](https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md):
```json
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://openvex.dev/docs/public/vex-4ae94619722f6a8d7d306ea05b02fef4a71bb7048cacc89603160ae61abfc06a",
"author": "Mael Valais <mael@vls.dev>",
"timestamp": "2025-09-04T16:40:07.17876+02:00",
"version": 1,
"statements": [
{
"vulnerability": {
"name": "CVE-2025-47907"
},
"timestamp": "2025-09-04T16:40:07.178762+02:00",
"products": [
{
"@id": "pkg:oci/cert-manager-controller@sha256%3A81316365dc0b713eddddfbf9b8907b2939676e6c0e12beec0f9625f202a36d16?arch=amd64&repository_url=quay.io%2Fjetstack%2Fcert-manager-controller"
},
{
"@id": "pkg:oci/cert-manager-webhook@sha256%3A9431f0d8b5103b06cc6138564f471ac02c6b2638c2fa399d81e28a01d817ae73?arch=amd64&repository_url=quay.io%2Fjetstack%2Fcert-manager-webhook"
},
{
"@id": "pkg:oci/cert-manager-cainjector@sha256%3Aaf59e01ad9756a1034fbf948330e75702e5d79b3577f323f6a9947707ba262fc?arch=amd64&repository_url=quay.io%2Fjetstack%2Fcert-manager-cainjector"
},
{
"@id": "pkg:oci/cert-manager-startupapicheck@sha256%3A1075e097415167caa584b203292dfb57e46866df0adb76fe769ef5cb0297ccc4?arch=amd64&repository_url=quay.io%2Fjetstack%2Fcert-manager-startupapicheck"
},
{
"@id": "pkg:oci/cert-manager-acmesolver@sha256%3A1c81a771e3e3a210466aa25f5fc05ce5c286e0eb90d96563cc0275aaa50788c2?arch=amd64&repository_url=quay.io%2Fjetstack%2Fcert-manager-acmesolver"
},
{
"@id": "pkg:oci/cert-manager-ctl@sha256%3Ae38c3691ffd3f8d207d05e53a887a783c9010f2d4a395a7319ce68973ed629ae?arch=amd64&repository_url=quay.io%2Fjetstack%2Fcert-manager-ctl"
},
{
"@id": "pkg:oci/cert-manager-controller@sha256%3A81316365dc0b713eddddfbf9b8907b2939676e6c0e12beec0f9625f202a36d16?arch=arm64&repository_url=quay.io%2Fjetstack%2Fcert-manager-controller"
},
{
"@id": "pkg:oci/cert-manager-webhook@sha256%3A9431f0d8b5103b06cc6138564f471ac02c6b2638c2fa399d81e28a01d817ae73?arch=arm64&repository_url=quay.io%2Fjetstack%2Fcert-manager-webhook"
},
{
"@id": "pkg:oci/cert-manager-cainjector@sha256%3Aaf59e01ad9756a1034fbf948330e75702e5d79b3577f323f6a9947707ba262fc?arch=arm64&repository_url=quay.io%2Fjetstack%2Fcert-manager-cainjector"
},
{
"@id": "pkg:oci/cert-manager-startupapicheck@sha256%3A1075e097415167caa584b203292dfb57e46866df0adb76fe769ef5cb0297ccc4?arch=arm64&repository_url=quay.io%2Fjetstack%2Fcert-manager-startupapicheck"
},
{
"@id": "pkg:oci/cert-manager-acmesolver@sha256%3A1c81a771e3e3a210466aa25f5fc05ce5c286e0eb90d96563cc0275aaa50788c2?arch=arm64&repository_url=quay.io%2Fjetstack%2Fcert-manager-acmesolver"
},
{
"@id": "pkg:oci/cert-manager-ctl@sha256%3Ae38c3691ffd3f8d207d05e53a887a783c9010f2d4a395a7319ce68973ed629ae?arch=arm64&repository_url=quay.io%2Fjetstack%2Fcert-manager-ctl"
},
{
"@id": "pkg:oci/cert-manager-controller@sha256%3A81316365dc0b713eddddfbf9b8907b2939676e6c0e12beec0f9625f202a36d16?arch=arm&repository_url=quay.io%2Fjetstack%2Fcert-manager-controller"
},
{
"@id": "pkg:oci/cert-manager-webhook@sha256%3A9431f0d8b5103b06cc6138564f471ac02c6b2638c2fa399d81e28a01d817ae73?arch=arm&repository_url=quay.io%2Fjetstack%2Fcert-manager-webhook"
},
{
"@id": "pkg:oci/cert-manager-cainjector@sha256%3Aaf59e01ad9756a1034fbf948330e75702e5d79b3577f323f6a9947707ba262fc?arch=arm&repository_url=quay.io%2Fjetstack%2Fcert-manager-cainjector"
},
{
"@id": "pkg:oci/cert-manager-startupapicheck@sha256%3A1075e097415167caa584b203292dfb57e46866df0adb76fe769ef5cb0297ccc4?arch=arm&repository_url=quay.io%2Fjetstack%2Fcert-manager-startupapicheck"
},
{
"@id": "pkg:oci/cert-manager-acmesolver@sha256%3A1c81a771e3e3a210466aa25f5fc05ce5c286e0eb90d96563cc0275aaa50788c2?arch=arm&repository_url=quay.io%2Fjetstack%2Fcert-manager-acmesolver"
},
{
"@id": "pkg:oci/cert-manager-ctl@sha256%3Ae38c3691ffd3f8d207d05e53a887a783c9010f2d4a395a7319ce68973ed629ae?arch=arm&repository_url=quay.io%2Fjetstack%2Fcert-manager-ctl"
},
{
"@id": "pkg:oci/cert-manager-controller@sha256%3A81316365dc0b713eddddfbf9b8907b2939676e6c0e12beec0f9625f202a36d16?arch=ppc64le&repository_url=quay.io%2Fjetstack%2Fcert-manager-controller"
},
{
"@id": "pkg:oci/cert-manager-webhook@sha256%3A9431f0d8b5103b06cc6138564f471ac02c6b2638c2fa399d81e28a01d817ae73?arch=ppc64le&repository_url=quay.io%2Fjetstack%2Fcert-manager-webhook"
},
{
"@id": "pkg:oci/cert-manager-cainjector@sha256%3Aaf59e01ad9756a1034fbf948330e75702e5d79b3577f323f6a9947707ba262fc?arch=ppc64le&repository_url=quay.io%2Fjetstack%2Fcert-manager-cainjector"
},
{
"@id": "pkg:oci/cert-manager-startupapicheck@sha256%3A1075e097415167caa584b203292dfb57e46866df0adb76fe769ef5cb0297ccc4?arch=ppc64le&repository_url=quay.io%2Fjetstack%2Fcert-manager-startupapicheck"
},
{
"@id": "pkg:oci/cert-manager-acmesolver@sha256%3A1c81a771e3e3a210466aa25f5fc05ce5c286e0eb90d96563cc0275aaa50788c2?arch=ppc64le&repository_url=quay.io%2Fjetstack%2Fcert-manager-acmesolver"
},
{
"@id": "pkg:oci/cert-manager-ctl@sha256%3Ae38c3691ffd3f8d207d05e53a887a783c9010f2d4a395a7319ce68973ed629ae?arch=ppc64le&repository_url=quay.io%2Fjetstack%2Fcert-manager-ctl"
},
{
"@id": "pkg:oci/cert-manager-controller@sha256%3A81316365dc0b713eddddfbf9b8907b2939676e6c0e12beec0f9625f202a36d16?arch=s390x&repository_url=quay.io%2Fjetstack%2Fcert-manager-controller"
},
{
"@id": "pkg:oci/cert-manager-webhook@sha256%3A9431f0d8b5103b06cc6138564f471ac02c6b2638c2fa399d81e28a01d817ae73?arch=s390x&repository_url=quay.io%2Fjetstack%2Fcert-manager-webhook"
},
{
"@id": "pkg:oci/cert-manager-cainjector@sha256%3Aaf59e01ad9756a1034fbf948330e75702e5d79b3577f323f6a9947707ba262fc?arch=s390x&repository_url=quay.io%2Fjetstack%2Fcert-manager-cainjector"
},
{
"@id": "pkg:oci/cert-manager-startupapicheck@sha256%3A1075e097415167caa584b203292dfb57e46866df0adb76fe769ef5cb0297ccc4?arch=s390x&repository_url=quay.io%2Fjetstack%2Fcert-manager-startupapicheck"
},
{
"@id": "pkg:oci/cert-manager-acmesolver@sha256%3A1c81a771e3e3a210466aa25f5fc05ce5c286e0eb90d96563cc0275aaa50788c2?arch=s390x&repository_url=quay.io%2Fjetstack%2Fcert-manager-acmesolver"
},
{
"@id": "pkg:oci/cert-manager-ctl@sha256%3Ae38c3691ffd3f8d207d05e53a887a783c9010f2d4a395a7319ce68973ed629ae?arch=s390x&repository_url=quay.io%2Fjetstack%2Fcert-manager-ctl"
}
],
"status": "not_affected",
"justification": "component_not_present",
"impact_statement": "cert-manager does not import the Go database/sql package"
}
]
}
```
The `@id` fields above are "purl" URLs, see [PURL-SPECIFICATION.rst](https://github.com/package-url/purl-spec/blob/main/PURL-SPECIFICATION.rst).
Note: the only [VEX document](https://www.cisa.gov/sites/default/files/publications/VEX_Use_Cases_Aprill2022.pdf) implementation I know of is the https://openvex.dev/ specification that encodes VEX documents using JSON-LD.
Now, let's use this VEX document and pass it to Trivy:
```
$ trivy image quay.io/jetstack/cert-manager-controller:v1.18.2 --vex vex.json --show-suppressed -q
Report Summary
┌─────────────────────────────────────────────────────────────────┬──────────┬─────────────────┬─────────┐
│ Target │ Type │ Vulnerabilities │ Secrets │
├─────────────────────────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤
│ quay.io/jetstack/cert-manager-controller:v1.18.2 (debian 12.11) │ debian │ 0 │ - │
├─────────────────────────────────────────────────────────────────┼──────────┼─────────────────┼─────────┤
│ app/cmd/controller/controller │ gobinary │ 0 │ - │
└─────────────────────────────────────────────────────────────────┴──────────┴─────────────────┴─────────┘
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)
app/cmd/controller/controller (gobinary)
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
Suppressed Vulnerabilities (Total: 1)
┌─────────┬────────────────┬──────────┬──────────────┬───────────────────────┬──────────┐
│ Library │ Vulnerability │ Severity │ Status │ Statement │ Source │
├─────────┼────────────────┼──────────┼──────────────┼───────────────────────┼──────────┤
│ stdlib │ CVE-2025-47907 │ HIGH │ not_affected │ component_not_present │ vex.json │
└─────────┴────────────────┴──────────┴──────────────┴───────────────────────┴──────────┘
```
No more CVEs!
Not sure about the real-world usability of `--vex` since folks would have to somehow download the DEX document first...
But at least we should have a written explanation of why we are ignoring certain CVEs, and OpenVEX seems like a good fit.
## Difference with .trivyignore
The `.trivyignore` file lets you ignore specific CVEs:
```bash
# the version detection is wrongly looking at apiserver packages with versions < 1 - but all apiserver packages have
# a major version of 0. In any case this is a vuln in Kubernetes clusters, not in our code.
CVE-2020-8559
```
`.trivyignore` is missing a ton of context: for which images have we ignored these CVEs? What is the reason for ignoring that CVE?
## Ideas
### Where do we share these advisories?
We could put it in github.com/cert-manager/advisories similar to Wolfi's https://github.com/wolfi-dev/advisories. Note that Wolfi stopped using OpenVEX and are now using their own schema to keep track of advisories (see https://github.com/wolfi-dev/advisories/issues/54#issuecomment-1611289271). We could then use https://raw.github.com to distribute it.
Even better, we could attach them as OCI attachments: https://www.chainguard.dev/unchained/putting-vex-to-work. There is a command for that:
```bash
vexctl attest --sign --attach vex.json
```
### Publishing VEX docoments along side images
Docker Scout and Trivy support reading VEX documents when they are attached in the attestation blob:
https://docs.docker.com/scout/how-tos/create-exceptions-vex/
https://trivy.dev/latest/docs/supply-chain/vex/oci/
### Does Artifact Hub manage false positives?
I've asked Artifact Hub how one project can communicate about false positives:
https://github.com/artifacthub/hub/issues/4531
### Distributing OpenVEX documents
There are a few ways of distributing the OpenVEX documents: some projects store them in a GitHub repo such as [Ubuntu](https://github.com/canonical/ubuntu-security-notices/blob/050e0308d982d8d32de2f9288a712460603aa460/vex/usn/USN-4110-2.json#L4), [Flux Operator](https://github.com/controlplaneio-fluxcd/flux-operator/blob/62aa4933d0fc8721514c71ed4e35705576482f0b/config/data/flux-vex/v2.6.json#L4), and [Rancher](https://github.com/rancher/vexhub) do maintain OpenVEX documents.
[Cilium](https://github.com/cilium/cilium/pull/30768#issue-2134799687) did, but they [stopped in 2024](https://github.com/cilium/cilium/pull/32905). You can use `trivy image ... --vex repo` to make that work. It's also possible to attach the OpenVEX alongside the attestation document in the OCI manifest... but I haven't found any project doing that. For context, both Docker Scout and Trivy support doing that:
- https://docs.docker.com/scout/how-tos/create-exceptions-vex/
- https://trivy.dev/latest/docs/supply-chain/vex/oci/