paketo-buildpacks / bellsoft-liberica

A Cloud Native Buildpack that provides the Bellsoft Liberica implementations of JREs and JDKs
Apache License 2.0
71 stars 19 forks source link

Mismatch between JRE/JDK version and cpes+version under metadata.dependencies in v9.8.0 #346

Open xyloman opened 1 year ago

xyloman commented 1 year ago

I was scanning the SBOM provided for the bellsoft-liberica buildpack and was expecting to see 5 vulnerabilities reported using grype. However, zero vulnerabilities where detected because the version in the SBOM is 11.0.16 however, the version of java packaged by the buildpack in 9.8.0 I believe is version 11.0.16.1 as reported from my running container.

$ /layers/paketo-buildpacks_bellsoft-liberica/jre/bin/java -version
Picked up JAVA_TOOL_OPTIONS: -Dmanagement.endpoint.health.probes.add-additional-paths="true" -Dmanagement.endpoint.health.show-details=always -Dmanagement.endpoints.web.base-path="/actuator" -Dmanagement.endpoints.web.exposure.include=* -Dmanagement.health.probes.enabled="true" -Dmanagement.server.port="8081" -Dserver.port="8080" -Dserver.shutdown.grace-period="24s"
openjdk version "11.0.16.1" 2022-08-12 LTS
OpenJDK Runtime Environment (build 11.0.16.1+1-LTS)
OpenJDK 64-Bit Server VM (build 11.0.16.1+1-LTS, mixed mode)

Expected Behavior

I would expect the cpes section of the SBOM to be equal to 11.0.16.1 for both JDK and JRE

"cpe:2.3:a:oracle:jdk:11.0.16.1:*:*:*:*:*:*:*"
"cpe:2.3:a:oracle:jre:11.0.16.1:*:*:*:*:*:*:*"

Current Behavior

The version returned is 11.0.16

Possible Solution

My understanding is the SBOM is generated using the information in the buildpack.toml.

Steps to Reproduce

  1. Use kpack or pack to build the image using v9.8.0 of this buildpack.
  2. Use the pack cli to download the sbom pack sbom download localhost:5000/supply-chain/tanzu-java-web-app-default@sha256:333bb5b40eea3306b9425f6f167b71ee5e41c9aae1734d6fda8bcb68b90b4a95 --remote
  3. Use the grype cli to perform a scan of the SBOM to generate a vulnerability report grype layers/sbom/launch/paketo-buildpacks_bellsoft-liberica/jre/sbom.syft.json
  4. Notice the output indicates that there are no vulnerabilities found.

Motivations

Scanners such as grype, trivy, etc, have a difficult time discovering the JRE flavor and version included via buildpacks. To accomplish this outcome we need to scan the SBOM for the bellsoft layer, in addition, the performing a full image scan. Performing a full image scan will not detect the JRE installed.

dmikusa commented 1 year ago

This is due to the way that version numbers for dependencies are handled in buildpacks. At the moment, all dependencies are forced to have semver version numbers. That why the dependencies use 11.0.x and not 11.0.x.y. Cause 11.0.x.y is not semver compatible.

At any rate, the tooling & pipelines only have the 3-digit semver version number when managing the buildpack.toml metadata so that's all that gets put into the SBOM.

Would it work for you if we put cpe:2.3:a:oracle:jdk:11.0.16*:*:*:*:*:*:*:*? Is that valid to have a wildcard in the version? I know you can put wildcards in some spots, but I'm not super familiar with the CPE format. Short term, we could inject wildcards there if it helps. Otherwise this is a long-term problem, because we have to move away from assuming semver everywhere, and that's a big change.

xyloman commented 1 year ago

Unfortunately, the CPE is an opaque identifier. In the case of the JDK the NVD database organizes CVEs based on the CPE name. In this case, the cpeName is equal to cpe:2.3:a:oracle:jre:11.0.16.1:*:*:*:*:*:*:*. Vulnerability databases such as NVD will associate the CVEs with them. Here is an example query:

curl "https://services.nvd.nist.gov/rest/json/cves/2.0?startIndex=0&cpeName=cpe:2.3:a:oracle:jre:11.0.16.1:*:*:*:*:*:*:*" | jq

This will return all of the CVEs associated with the cpeName. This is an example of data sources that providers like AquaSecurity will aggregate.

xyloman commented 1 year ago

Furthermore, purl-spec is another field that can be filled out in the SBOM instead of cpe. using purl-spec is an opaque identifier and the version is not required to be semver. In this case, that might not be wise because there is not a type to map to for a standalone jre pulled via a tar.gz over a URL. I bring this up because the NVD and the OVS databases are not requiring semver for packages.

dmikusa commented 1 year ago

Thanks for sharing that. It's definitely helpful to better understand how that metadata is being used.

We'll certainly want to get this addressed, but we're limited in what we can do now as we cannot internally use non-semver numbers. It's going to take some investigation to see if it'd be possible to make CI changes that could store the full version number in the CPE, but still, put the forced semver number where buildpacks need it. That would seem to be the least friction approach, but I don't know off-hand if that'll be possible.

If that's not possible, this would be something that has to wait until we redesign how this metadata information is being stored. Long term, we're looking to get this information out of buildpack.toml, and part of that would be redesigning our CI and we will absolutely be moving away from forced semver.

modulo11 commented 1 year ago

I guess it should be possible to return a custom CPE in the output of the action, like it's done here. Haven't seen a check if that value is passed directly to update-buildpack-dependency.

Looking at the API response from Bellsoft, the CPE could then be constructed like "cpe:2.3:a:oracle:jre:$featureVersion.$interimVersion.$updateVersion.$patchVersion:*:*:*:*:*:*:*"], ignoring $patchVersion if it's zero.

dmikusa commented 1 year ago

I guess it should be possible to return a custom CPE in the output of the action, like it's done here. Haven't seen a check if that value is passed directly to update-buildpack-dependency.

That would possibly work, but it's not exactly how things were designed. It's bending the intent a bit.

The CPE value that gets written to the output is intended to be the replace value of the search and replace that is done when an update occurs. The output value is fed into the update dependency tool and is paired with a regex pattern that is used to implement the search part of search and replace.

I suppose you could output the full CPE and then have the regex match the full CPE, or something like that, so that it would search and replace the full thing on a version update.

The problem is that in many places (like here, and the function that it's calling) we are treating the version as semver and the version, in this case, is not valid semver. So even something as simple as selecting the "latest" version needs this.

You might be able to work through this somehow, but to be clear, the assumption of semver compliance runs very deep through our CI pipelines and tools. That contract has to stay enforced for the time being. Any acceptable solution would have to be very careful to only change the CPE and not anything else with the version selection or output.