# How do we let users know that we know about this CVE?
We've always struggled with CVE reports full of false-positives and scanner pleasing patch releases. We've tried mitigating that by letting people know that we ignore all scanner dumps sent to the security mailing list if they don't contain some form of impact analysis. This policy is recorded in https://github.com/cert-manager/community/blob/main/SECURITY.md.
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's great, but I think we should still address non-affecting CVEs reported by tools like Trivy.~ It does display Trivy results for all supported releases!
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.
## 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
```