What is VEX

VEX and connection to vulnerability reports.

VEX (Vulnerability Exploitability eXchange) is a document format that allows vendors and operators to indicate whether a specific vulnerability (CVE) is applicable in a given product, component, or image. The goal is to reduce noise in scan reports: not all CVEs found are actually exploitable in your context (package version used, disabled feature, sandbox, etc.).

The VEX document links the artifact (for example, a container image), the vulnerability (CVE identifier) and the applicability status (affected / not_affected / fixed, and others), optionally with a justification. This helps:

  • focus on really applicable vulnerabilities;
  • reasonably exclude false positives in reports and policies (for example, “banning pods with critical vulnerabilities” taking into account VEX).

The VEX specification is developed within CISA and is associated with SBOM (Software Bill of Materials) formats. VEX support in scanning tools (including Trivy) allows such documents to be taken into account when generating reports and filtering vulnerabilities.


Use in the Shturval platform

In the platform, image scanning is based on Trivy (Container Image Scanning Module, shturval-scanner). For every unfixed vulnerability in a platform component, the Shturval release ships a VEX report that explains why that CVE cannot be exploited in Shturval. Marked vulnerabilities are removed from the active list so that the GUI and policies (for example, blocking pods with critical CVEs) only count vulnerabilities that actually apply.

When the module is enabled, the cluster creates VulnerabilityReports. They are shown in the Security section on the Image Analysis page.

Platform VEX is enabled with a ShturvalServicePatch (SSP) on shturval-scanner: the operator reads documents from the VEX repository during the scan.

What you see in the GUI after enabling VEX

After you enable VEX, the Image Analysis page keeps only the CVEs that still need to be handled. Vulnerabilities that Shturval marked as not applicable disappear from the table — the Critical / High / Medium / Low summary does not count them either. That is expected: they were removed because they are not exploitable on the platform, not because the scanner missed them.

To see which vulnerabilities were excluded, you can set trivy.showSuppressed: true in the SSP. It is off by default, and excluded CVEs are not stored anywhere. When suppressed vulnerabilities are enabled, the report contains data in report.modifiedFindings. Each entry has the CVE identifier, status, exclusion reason, and source (VEX).

Administration → Custom resources → API group aquasecurity.github.io → VulnerabilityReport.

List reports (the Modified column is the number of excluded CVEs):

kubectl get vulnerabilityreports -A

report.modifiedFindings for a specific report:

kubectl get vulnerabilityreport <name> -n <namespace> -o jsonpath='{.report.modifiedFindings}'

Enable VEX with SSP

Set shturval-scanner to Auto before applying the SSP, otherwise it may not take effect. How to create an SSP: How to manage CustomValues.

Manifest parameters:

ParameterDescriptionTypeExample
vexRepo.enabledLets Trivy look up the VEX repository (--vex repo). Disabled by default.booleantrue
vexRepo.baseUrlRepository base URL without /vex/{release}.stringhttps://public.shturval.tech/repository/shturval_bin_public
vexRepo.releaseRelease whose VEX documents are used. Final URL: {baseUrl}/vex/{release}.string"2.15.0"
vexRepo.nameEntry name in repository.yaml. Default shturval.stringshturval
vexRepo.insecureSkip TLS verification for the VEX repository.booleanfalse
vexRepo.skipUpdateDo not refresh VEX during the scan (--skip-vex-repo-update). Use only when the scanner must not contact the repository.booleanfalse
vexRepo.preloadDownload VEX in the scan-job init container (trivy vex repo download). For a closed circuit with a mirror, keep true and skipUpdate: false.booleanfalse
trivy.showSuppressedStore excluded vulnerabilities in report.modifiedFindings. Default false.booleantrue

If the repository requires authentication, set vexRepo.username and vexRepo.password, or vexRepo.token.

apiVersion: ops.shturval.tech/v1beta2
kind: ShturvalServicePatch
metadata:
  name: shturval-scanner-vex
spec:
  shturvalServiceConfigName: shturval-scanner
  patchOrder: 100
  customvalues:
    trivy:
      showSuppressed: true
    vexRepo:
      enabled: true
      baseUrl: https://public.shturval.tech/repository/shturval_bin_public
      release: "2.15.0"
      name: shturval
      insecure: false
      skipUpdate: false
      preload: false

VEX files list images as r.shturval.tech/.... In a closed circuit the scanner pulls the same images from your mirror. For the markup to match, the report must keep the r.shturval.tech name instead of replacing it with the mirror address.

Set the mirror in trivy.configFile.registry.mirrors: layers come from the mirror, the original name stays in the report. Do not use trivy.registry.mirror — it rewrites the name to the mirror FQDN, and VEX no longer recognizes the images.

In the example, replace mirror.example.com with the address of your mirror.

If the cluster can reach the mirror, set preload: true and skipUpdate: false: Trivy downloads VEX from the mirror at {baseUrl}/vex/{release}. Use skipUpdate: true only when the scanner must not contact the repository. How to put VEX on the mirror: Working with the mirror after installation.

apiVersion: ops.shturval.tech/v1beta2
kind: ShturvalServicePatch
metadata:
  name: shturval-scanner-vex
spec:
  shturvalServiceConfigName: shturval-scanner
  patchOrder: 100
  customvalues:
    trivy:
      showSuppressed: true
      "-registry": {}
      configFile:
        registry:
          mirrors:
            r.shturval.tech:
              - mirror.example.com
        insecure: true
    vexRepo:
      enabled: true
      baseUrl: https://mirror.example.com/repository/shturval_bin_public
      release: "2.15.0"
      name: shturval
      insecure: true
      skipUpdate: false
      preload: true

The "-registry": {} key removes a registry name rewrite that is already set on the service. If you leave it in place, images in the report get the mirror address again, and VEX no longer recognizes them.

  1. Prepare an SSP manifest for shturval-scanner using the example above. Set the platform version in vexRepo.release and the repository address in vexRepo.baseUrl.

  2. Import the manifest through Importing manifests.

    Screenshot: importing SSP

    Import SSP

  3. Open Services and repositories → Installed services → Container image scanning module (shturval-scanner). The Applied ShturvalServicePatch tab should show the SSP shturval-scanner-vex. When it is applied, the Service tab status is Patched.

  4. Connect to the cluster in the CLI and delete previously created VulnerabilityReport objects:

    kubectl get vulnerabilityreports -A -o json | jq -r '.items[] | "\(.metadata.namespace) \(.metadata.name)"' | while read ns name; do kubectl delete vulnerabilityreport "$name" -n "$ns"; done
    
    Screenshot: deleting VulnerabilityReports

    Deleting VulnerabilityReports

    Container image scanning restarts automatically.

  5. Check that scanning starts and new VulnerabilityReports appear:

    kubectl get vulnerabilityreports -A
    
    Screenshot: new VulnerabilityReports

    Checking VulnerabilityReports

  6. In the cluster GUI open Security → Image Analysis and confirm that marked vulnerabilities are gone from the main list.

    Screenshots: report before and after enabling VEX

    Image Analysis before enabling VEX

    Image Analysis after enabling VEX

    If the SSP sets trivy.showSuppressed: true, you can check excluded vulnerabilities in the resource:

    kubectl get vulnerabilityreports.aquasecurity.github.io -A -o json | jq -r '.items[] | select((.report.modifiedFindings // []) | length > 0) | "\(.metadata.namespace)/\(.metadata.name)\tmodified=\(.report.modifiedFindings | length)\tvulns=\((.report.vulnerabilities // []) | length)"'
    

Additional