Open joshbressers opened 6 months ago
Met with @joshbressers about this issue this morning, and we think that, basically, we need to special-case OpenJDK 8 versions in Syft's CPE generation:
❯ syft -q -o json eclipse-temurin:8u392-b08-jdk | jq '.artifacts[] | select(.name == "java") | .cpes'
[
"cpe:2.3:a:oracle:openjdk:1.8.0_392-b08:*:*:*:*:*:*:*",
"cpe:2.3:a:java:java:1.8.0_392-b08:*:*:*:*:*:*:*"
]
This should instead print out something like:
[
" cpe:2.3:a:oracle:openjdk:8:update392:*:*:*:*:*:*"
... (not sure if there should be others)
]
I think it's all java versions prior to 9 that would be prefixed with a 1 in the jdk
The _
suffix seems to correspond to update, so if we enhanced the syft logic to interpret the _{XXX} as updateX in the cpe component then that would help some here. I'm not sure on the +
suffix part yet. I'm not sure there is any good place to represent that in the cpe form, so it may be that we should be stripping that part out when constructing a CPE
I wonder if we should have a more specific cataloger for detecting java jdk and jre rather than a generic binary cataloger. For instance, the image eclipse-temurin:11.0.21_9-jre-alpine
has the following with alot of useful infomration that could be surfaced. I haven't checked all of the variants yet but wonder if this file is somewhat standard
cat /opt/java/openjdk/release
IMPLEMENTOR="Eclipse Adoptium"
IMPLEMENTOR_VERSION="Temurin-11.0.21+9"
JAVA_RUNTIME_VERSION="11.0.21+9"
JAVA_VERSION="11.0.21"
JAVA_VERSION_DATE="2023-10-17"
LIBC="musl"
MODULES="java.base java.compiler java.datatransfer java.xml java.prefs java.desktop java.instrument java.logging java.management java.security.sasl java.naming java.rmi java.management.rmi java.net.http java.scripting java.security.jgss java.transaction.xa java.sql java.sql.rowset java.xml.crypto java.se java.smartcardio jdk.accessibility jdk.internal.vm.ci jdk.management jdk.unsupported jdk.internal.vm.compiler jdk.aot jdk.internal.jvmstat jdk.attach jdk.charsets jdk.compiler jdk.crypto.ec jdk.crypto.cryptoki jdk.dynalink jdk.internal.ed jdk.editpad jdk.hotspot.agent jdk.httpserver jdk.internal.le jdk.internal.opt jdk.internal.vm.compiler.management jdk.jartool jdk.javadoc jdk.jcmd jdk.management.agent jdk.jconsole jdk.jdeps jdk.jdwp.agent jdk.jdi jdk.jfr jdk.jlink jdk.jshell jdk.jsobject jdk.jstatd jdk.localedata jdk.management.jfr jdk.naming.dns jdk.naming.ldap jdk.naming.rmi jdk.net jdk.pack jdk.rmic jdk.scripting.nashorn jdk.scripting.nashorn.shell jdk.sctp jdk.security.auth jdk.security.jgss jdk.unsupported.desktop jdk.xml.dom jdk.zipfs"
OS_ARCH="x86_64"
OS_NAME="Linux"
SOURCE=".:git:ef680be160c5"
BUILD_SOURCE="git:0a454394ec842383e3d7c03aae5972ab24e10d85"
BUILD_SOURCE_REPO="https://github.com/adoptium/temurin-build.git"
SOURCE_REPO="https://github.com/adoptium/jdk11u.git"
FULL_VERSION="11.0.21+9"
SEMANTIC_VERSION="11.0.21+9"
BUILD_INFO="OS: Linux Version: 5.15.0-48-generic"
JVM_VARIANT="Hotspot"
JVM_VERSION="11.0.21+9"
IMAGE_TYPE="JRE"
Seems to be present for versions 11 and above at least for the eclipse-temurin
and ibmjava
images
Sorry, I went off on a bit of a tangent, we'd still need the same sort of adjustments to the CPE generation, I was just looking at getting additional metadata to allow differentiating between various jdk/jre distributions in future
I think we should just special case the version comparison logic in grype and forget about trying to make the CPEs perfect in syft since we know that will never be possible. Instead just have special logic in grype for when we're doing CPE version comparison for these specific packages
Ignore if this isn't helpful, and I'm not a Java expert, but this resonates a lot based on what I'm seeing (from @westonsteimel):
I wonder if we should have a more specific cataloger for detecting java jdk and jre rather than a generic binary cataloger
We use Syft/Grype in wolfictl, and noticed it wasn't finding OpenJDK or OpenJDK's published advisories here: https://openjdk.org/groups/vulnerability/advisories/
I saw at least two things contributing to this:
java
isn't even among those (whereas javac
, javadoc
, etc. are).cpe:2.3:a:oracle:jdk:...
, not cpe:2.3:a:oracle:openjdk:...
(although I did see that one from time to time).Since this was causing a fair amount of false negatives, I added a last-mile CPE addition in wolfictl based on the distro package name, but I'm not sure if that approach would make sense in Syft's context. Just mentioning this in case it helps us find a good solution upstream here that we can use in wolfictl. 😃
Some very rough initial notes which may be useful for a future java installation cataloger:
JAVA_RUNTIME_VERSION
and the IMPLEMENTOR_VERSION
. In most cases for vulnerability matching what will be needed is the JAVA_RUNTIME_VERSION because from what I've looked at thus far it seems fairly uncommon for the various OpenJDK re-distributors to package fixes that deviate from the OpenJDK patches; however, there are some implementor specific feeds that use the implementor version so having both should allow for covering all cases1.8.0_392-b5
becomes 8.0.392+5
). The future vulnerability annotation format (as well as the existing nvd overrides) will capture both ranges so that it can be consumed either way, but I think consistently preferring the semver variant in syft would make sense-ea
suffix versioning as a pre-release for fuzzy matching and maven version comparisonMayber parse all of the .properties files found under $JAVA_HOME?
Also extract the main system properties from the system class (they usually agree with what is in the release file, but that isn't always available, and sometimes the properties map has a bit more info) They seem to be embedded as a map within a java class file. The file I found the map embedded in was classes/java/lang/VersionProps.class from jmods/java.base.jmod - unsure if this will be consitent for prior versions so worth invesitgating further
grep -nr '+37' . Binary file ./bin/jwebserver matches Binary file ./bin/jarsigner matches Binary file ./bin/jfr matches Binary file ./bin/jdb matches Binary file ./bin/jstack matches Binary file ./bin/rmiregistry matches Binary file ./bin/jar matches Binary file ./bin/jcmd matches Binary file ./bin/jrunscript matches Binary file ./bin/jps matches Binary file ./bin/java matches Binary file ./bin/jhsdb matches Binary file ./bin/javap matches Binary file ./bin/jdeprscan matches Binary file ./bin/javac matches Binary file ./bin/keytool matches Binary file ./bin/jmod matches Binary file ./bin/jmap matches Binary file ./bin/jshell matches Binary file ./bin/jstat matches Binary file ./bin/jlink matches Binary file ./bin/serialver matches Binary file ./bin/javadoc matches Binary file ./bin/jinfo matches Binary file ./bin/jstatd matches Binary file ./bin/jdeps matches Binary file ./bin/jconsole matches Binary file ./bin/jpackage matches Binary file ./bin/jimage matches ./release:2:JAVA_RUNTIME_VERSION="22+37" Binary file ./lib/server/classes_nocoops.jsa matches Binary file ./lib/server/libjvm.dylib matches Binary file ./lib/server/classes.jsa matches Binary file ./lib/ct.sym matches Binary file ./lib/src.zip matches Binary file ./lib/modules matches Binary file ./lib/client/classes_nocoops.jsa matches Binary file ./lib/client/libjvm.dylib matches Binary file ./lib/client/classes.jsa matches Binary file ./jmods/jdk.jfr.jmod matches Binary file ./jmods/bin/java matches Binary file ./jmods/bin/keytool matches Binary file ./jmods/classes/java/lang/VersionProps.class matches Binary file ./jmods/java.base.jmod matches Binary file ./jmods/jdk.charsets.jmod matches Binary file ./jmods/jdk.localedata.jmod matches Binary file ./jmods/java.desktop.jmod matches Binary file ./jmods/lib/server/libjvm.dylib matches Binary file ./jmods/lib/client/libjvm.dylib matches Binary file ./jmods/javafx.web.jmod matches Binary file ./jmods/base/bin/java matches Binary file ./jmods/base/bin/keytool matches Binary file ./jmods/base/classes/java/lang/VersionProps.class matches Binary file ./jmods/base/java.base.jmod matches Binary file ./jmods/base/lib/server/libjvm.dylib matches Binary file ./jmods/base/lib/client/libjvm.dylib matches
I also found several properties files which may be worth considering find . -type f -name *.properties ./lib/javafx.properties ./lib/psfontj2d.properties ./lib/javafx.properties ./conf/logging.properties ./conf/sound.properties ./conf/net.properties ./conf/management/management.properties ./conf/jaxp.properties ./jmods/classes/sun/net/www/content-types.properties ./jmods/classes/java/time/chrono/hijrah-config-Hijrah-umalqura_islamic-umalqura.properties ./jmods/conf/net.properties ./jmods/base/classes/sun/net/www/content-types.properties ./jmods/base/classes/java/time/chrono/hijrah-config-Hijrah-umalqura_islamic-umalqura.properties
Specifically the llib/javafx.properties file could be useful for understanding which version of javafx is installed cat lib/javafx.properties ───────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │ File: lib/javafx.properties ───────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 1 │ javafx.version=22.0.0+1 2 │ javafx.runtime.version=22.0.0+1 3 │ javafx.runtime.build=b01
javafx in particular is now distributed as a separate project (OpenJFX) and has separate versioning so it would definitely be ideal to capture this as an owned separate dependency.
There is more at https://github.com/anchore/vulnerability-data-tools/blob/main/annotation_format_examples/CVE-2024-20925.toml
The CPEs that Syft emits for the binary version of OpenJDK versions appear to be incorrect.
For our example of JDK 8 we will use the eclipse-temurin:8u392-b08-jdk image (the openjdk:8 image isn't detected by Syft, which is probably a different bug that needs investigating)
We can see the Java version detected as 1.8.0_392-b08. If we look in the container this checks out
The CPE generated by Syft is then
Which is probably wrong.
If we look at the official CPE dictionary for OpenJDK CPEs we see this (I have trimmed this list for brevity)
The OpenJDK CPE names versus the versions we return in Syft do not match. This is true of OpenJDK8 as well as newer versions. For example if we look at OpenJDK 21
We get CPEs
Based on the above, I would expect the CPE for OpenJDK 21 to look like
The CPE for OpenJDK 8 above I would expect to look like
It looks like versions 8 and before use a different format than newer versions.