Skip to content

Add step for embedding SBOM in the generated native-image#53861

Draft
jerboaa wants to merge 1 commit intoquarkusio:mainfrom
jerboaa:embed-sbom-native
Draft

Add step for embedding SBOM in the generated native-image#53861
jerboaa wants to merge 1 commit intoquarkusio:mainfrom
jerboaa:embed-sbom-native

Conversation

@jerboaa
Copy link
Copy Markdown
Contributor

@jerboaa jerboaa commented Apr 28, 2026

In #53552 @aloubyansky added the capability to have embedded (in the classpath sense) SBOMs. This patch extends this idea to also embed the SBOM in a (potentially generated) native image. The patch depends on a Mandrel change that needs to be integrated first. The Mandrel change is here:

graalvm/mandrel#962

With this patch, a build with -Dquarkus.cyclonedx.embedded.enabled=true and relevant SBOM extension added to quarkus will embed the SBOM in the native image.

Example hibernate-orm-quickstart run here:

[INFO] --- quarkus:999-SNAPSHOT:build (default-build) @ hibernate-orm-quickstart ---
[INFO] [io.quarkus.deployment.pkg.jar.NativeImageSourceJarBuilder] Building native image source jar: /home/sgehwolf/Documents/openjdk/quarkus/quarkus-quickstarts/hibernate-orm-quickstart/target/hibernate-orm-quickstart-1.0.0-SNAPSHOT-native-image-source-jar/hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner.jar
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Building native image from /home/sgehwolf/Documents/openjdk/quarkus/quarkus-quickstarts/hibernate-orm-quickstart/target/hibernate-orm-quickstart-1.0.0-SNAPSHOT-native-image-source-jar/hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner.jar
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on MANDREL 25.0.3-dev JDK 25.0.3-beta+8-ea
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildRunner] /disk/graal/upstream-sources/graalvm/mandrel-build/bin/native-image -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-DCoordinatorEnvironmentBean.transactionStatusManagerEnable=false -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.tryReflectionSetAccessible=true -J-Dio.netty.noUnsafe=false -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=3 -J-Duser.language=en -J-Duser.country=US -J-Dlogging.initial-configurator.min-level=500 -H:+UnlockExperimentalVMOptions -H:IncludeLocales=en-US -H:-UnlockExperimentalVMOptions --enable-native-access=ALL-UNNAMED -J-Dfile.encoding=UTF-8 -J--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk=ALL-UNNAMED --features=io.quarkus.jdbc.postgresql.runtime.graal.SQLXMLFeature,io.quarkus.hibernate.orm.runtime.graal.RegisterServicesForReflectionFeature,io.quarkus.hibernate.orm.runtime.graal.DisableLoggingFeature,org.hibernate.graalvm.internal.GraalVMStaticFeature,io.quarkus.caffeine.runtime.graal.CacheConstructorsFeature,io.quarkus.jackson.runtime.graal.JacksonSerializerRegistrationFeature,io.quarkus.runner.Feature,io.quarkus.runtime.graal.DisableLoggingFeature,io.quarkus.runtime.graal.JVMChecksFeature,io.quarkus.runtime.graal.SkipConsoleServiceProvidersFeature -J--add-exports=java.security.jgss/sun.security.krb5=ALL-UNNAMED -J--add-exports=java.security.jgss/sun.security.jgss=ALL-UNNAMED -J--add-opens=java.base/java.text=ALL-UNNAMED -J--add-opens=java.base/java.io=ALL-UNNAMED -J--add-opens=java.base/java.lang.invoke=ALL-UNNAMED -J--add-opens=java.base/java.util=ALL-UNNAMED --enable-sbom=embed:/META-INF/sbom/dependency.cdx.json -H:+UnlockExperimentalVMOptions -H:BuildOutputJSONFile=hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner-build-output-stats.json -H:-UnlockExperimentalVMOptions -H:+UnlockExperimentalVMOptions -H:+GenerateBuildArtifactsFile -H:-UnlockExperimentalVMOptions -H:+UnlockExperimentalVMOptions -H:+AllowFoldMethods -H:-UnlockExperimentalVMOptions -J-Djava.awt.headless=true --no-fallback --link-at-build-time -H:+UnlockExperimentalVMOptions -H:+ReportExceptionStackTraces -H:-UnlockExperimentalVMOptions -H:-AddAllCharsets --enable-url-protocols=http,https -H:NativeLinkerOption=-no-pie --enable-monitoring=heapdump,threaddump -H:+UnlockExperimentalVMOptions -H:-UseServiceLoaderFeature -H:-UnlockExperimentalVMOptions -J--add-exports=org.graalvm.nativeimage/org.graalvm.nativeimage.impl=ALL-UNNAMED --exclude-config io\.netty\.netty-codec /META-INF/native-image/io\.netty/netty-codec/generated/handlers/reflect-config\.json --exclude-config io\.netty\.netty-handler /META-INF/native-image/io\.netty/netty-handler/generated/handlers/reflect-config\.json hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner -jar hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner.jar
Warning: Option 'DynamicProxyConfigurationResources' is deprecated and might be removed in a future release: This can be caused by a proxy-config.json file in your META-INF directory. Consider including proxy configuration in the reflection section of reachability-metadata.md instead.. Please refer to the GraalVM release notes.
========================================================================================================================
GraalVM Native Image: Generating 'hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing...                                                                                    (4.4s @ 0.37GB)
 Java version: 25.0.3-beta+8-ea, vendor version: Mandrel-25.0.3-dev
 Graal compiler: optimization level: 2, target machine: x86-64-v3
 C compiler: gcc (redhat, x86_64, 15.2.1)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
 12 user-specific feature(s):
 - com.oracle.svm.thirdparty.gson.GsonFeature
 - io.quarkus.caffeine.runtime.graal.CacheConstructorsFeature
 - io.quarkus.hibernate.orm.runtime.graal.DisableLoggingFeature: Disables INFO logging during the analysis phase
 - io.quarkus.hibernate.orm.runtime.graal.RegisterServicesForReflectionFeature: Makes methods of reachable Hibernate services accessible through Class#getMethods()
 - io.quarkus.jackson.runtime.graal.JacksonSerializerRegistrationFeature: Conditionally registers Jackson SQL/XML serializers for reflection based on reachability
 - io.quarkus.jdbc.postgresql.runtime.graal.SQLXMLFeature
 - io.quarkus.runner.Feature: Auto-generated class by Quarkus from the existing extensions
 - io.quarkus.runtime.graal.DisableLoggingFeature: Adapts logging during the analysis phase
 - io.quarkus.runtime.graal.JVMChecksFeature
 - io.quarkus.runtime.graal.SkipConsoleServiceProvidersFeature: Skip unsupported console service providers when quarkus.native.auto-service-loader-registration is false
 - org.eclipse.angus.activation.nativeimage.AngusActivationFeature
 - org.hibernate.graalvm.internal.GraalVMStaticFeature: Hibernate ORM's static reflection registrations for GraalVM
------------------------------------------------------------------------------------------------------------------------
 5 experimental option(s) unlocked:
 - '-H:+AllowFoldMethods' (origin(s): command line)
 - '-H:BuildOutputJSONFile' (origin(s): command line)
 - '-H:-UseServiceLoaderFeature' (origin(s): command line)
 - '-H:+GenerateBuildArtifactsFile' (origin(s): command line)
 - '-H:AddOpens' (alternative API option(s): --add-opens java.base/java.lang=ALL-UNNAMED, --add-opens java.base/java.nio=ALL-UNNAMED, --add-opens java.base/jdk.internal.misc=ALL-UNNAMED; origin(s): command line)
------------------------------------------------------------------------------------------------------------------------
Build resources:
 - 17.94GB of memory (26.8% of system memory, using available memory)
 - 22 thread(s) (100.0% of 22 available processor(s), determined at start)
[2/8] Performing analysis...  [*****]                                                                   (16.6s @ 2.68GB)
   20,977 types,  26,863 fields, and  98,202 methods found reachable
    6,976 types,     112 fields, and   6,654 methods registered for reflection
       63 types,      69 fields, and      55 methods registered for JNI access
        0 downcalls and 0 upcalls registered for foreign access
        4 native libraries: dl, pthread, rt, z
[3/8] Building universe...                                                                               (3.9s @ 2.75GB)
[4/8] Parsing methods...      [**]                                                                       (2.4s @ 2.99GB)
[5/8] Inlining methods...     [****]                                                                     (1.2s @ 2.97GB)
[6/8] Compiling methods...    [****]                                                                    (19.6s @ 2.10GB)
[7/8] Laying out methods...   [***]                                                                      (5.8s @ 2.76GB)
[8/8] Creating image...       [**]                                                                       (4.7s @ 3.13GB)
  40.09MB (45.24%) for code area:    65,064 compilation units
  42.34MB (47.78%) for image heap:  465,642 objects and 81 resources
   6.19MB ( 6.98%) for other data
  88.61MB in total image size, 83.10MB in total file size
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                                Top 10 object types in image heap:
  13.17MB java.base                                           11.36MB byte[] for code metadata
   8.56MB org.hibernate.orm.hibernate-core-7.3.2.Final.jar     6.19MB byte[] for java.lang.String
   2.07MB svm.jar (Native Image)                               4.20MB com.oracle.svm.core.hub.DynamicHubCompanion
   1.63MB org.postgresql.postgresql-42.7.10.jar                4.00MB java.lang.String
   1.45MB c.f.jackson.core.jackson-databind-2.21.2.jar         3.24MB java.lang.Class
   1.38MB hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner.jar   1.46MB byte[] for general heap data
 875.10kB modified-io.vertx.vertx-core-4.5.26.jar            938.65kB java.lang.Object[]
 716.05kB jdk.proxy4                                         770.74kB java.util.HashMap$Node
 680.91kB o.jboss.narayana.jta.narayana-jta-7.3.3.Final.jar  748.28kB c.o.svm.core.hub.DynamicHub$ReflectionMetadata
 627.53kB io.netty.netty-buffer-4.1.132.Final.jar            720.97kB java.lang.String[]
   7.91MB for 116 more packages                                8.71MB for 4844 more object types
------------------------------------------------------------------------------------------------------------------------
Recommendations:
 FUTR: Use '--future-defaults=all' to prepare for future releases.
 HEAP: Set max heap for improved and more predictable memory usage.
 CPU:  Enable more CPU features with '-march=native' for improved performance.
------------------------------------------------------------------------------------------------------------------------
                       4.1s (6.7% of total time) in 657 GCs | Peak RSS: 4.32GB | CPU load: 14.48
------------------------------------------------------------------------------------------------------------------------
Build artifacts:
 /home/sgehwolf/Documents/openjdk/quarkus/quarkus-quickstarts/hibernate-orm-quickstart/target/hibernate-orm-quickstart-1.0.0-SNAPSHOT-native-image-source-jar/build-artifacts.json (build_info)
 /home/sgehwolf/Documents/openjdk/quarkus/quarkus-quickstarts/hibernate-orm-quickstart/target/hibernate-orm-quickstart-1.0.0-SNAPSHOT-native-image-source-jar/hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner (executable)
 /home/sgehwolf/Documents/openjdk/quarkus/quarkus-quickstarts/hibernate-orm-quickstart/target/hibernate-orm-quickstart-1.0.0-SNAPSHOT-native-image-source-jar/hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner-build-output-stats.json (build_info)
========================================================================================================================
Finished generating 'hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner' in 1m 0s.
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildRunner] objcopy --strip-debug hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 67314ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:12 min
[INFO] Finished at: 2026-04-28T15:35:05+02:00
[INFO] ------------------------------------------------------------------------

Note the new --enable-sbom=embed:/META-INF/sbom/dependency.cdx.json option being added when generating the native-image.

The embedded SBOM can then be retrieved by native-image-utils as proposed in oracle/graal#13394. Example:

./mandrel-build/bin/native-image-utils extract-sbom --image-path=/home/sgehwolf/Documents/openjdk/quarkus/quarkus-quickstarts/hibernate-orm-quickstart/target/hibernate-orm-quickstart-1.0.0-SNAPSHOT-runner | jq | head -n50
{
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "version": 1,
  "metadata": {
    "timestamp": "2026-04-28T13:33:59Z",
    "tools": {
      "components": [
        {
          "type": "library",
          "bom-ref": "pkg:maven/io.quarkus/quarkus-cyclonedx-generator@999-SNAPSHOT?type=jar",
          "group": "io.quarkus",
          "name": "quarkus-cyclonedx-generator",
          "version": "999-SNAPSHOT",
          "description": "Quarkus CycloneDX generator library",
          "hashes": [
            {
              "alg": "MD5",
              "content": "6f692f20a73bd647ceeb9f83df02064c"
            },
            {
              "alg": "SHA-1",
              "content": "6ccfb8e2ec14a8105674454aad6941ab5bf45760"
            },
            {
              "alg": "SHA-256",
              "content": "28034449a0b064149e4cd8e8e840ca0e5f4aa2d0a739cde73eb65f5718f86d5f"
            },
            {
              "alg": "SHA-512",
              "content": "8225c13712b711f2360f1b7f8e88e3eb8fbf2778a86f06ef39b3f5f383abea65cf6b57a03099ba3d9222547cb2ed3ead7d98473377dbbd67533dbf5fc82d88e7"
            },
            {
              "alg": "SHA3-256",
              "content": "f87254ecb7f2a7339df01c995750e883b409d4a385e5f4be57842da85a13edab"
            },
            {
              "alg": "SHA3-512",
              "content": "12e591a3d5e5c361dbba07b936e2417274f91d1944e5797227f5fac33d38cffb9c462104d9d92909b1ea5868a9f3740723513f81c6154e50246a0558eb4ff603"
            },
            {
              "alg": "SHA-384",
              "content": "7f83be65547eeb0246adf07b9e93f4305b49c1d3189ff7305a6528a2db29b124f3c579494fe972e0a8e562a1694144a3"
            },
            {
              "alg": "SHA3-384",
              "content": "6366cd89968847c7812e14d92ba7a71675c5e97f5b06842f407c3b5e31dac3d895569e67f8452d81d227f2c3adfd3a2d"
            }
          ],
          "licenses": [

@jerboaa
Copy link
Copy Markdown
Contributor Author

jerboaa commented Apr 28, 2026

/cc @zakkak @aloubyansky

@jerboaa
Copy link
Copy Markdown
Contributor Author

jerboaa commented Apr 28, 2026

Keeping as draft until the Mandrel change is in.

if (graalVMVersion.getDistribution() == Distribution.MANDREL) {
embedOptionValue += ":" + sbomResourceName;
}
nativeImageArgs.add("--enable-sbom=" + embedOptionValue);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jerboaa I guess if the Mandrel version (or GraalVM) used does not support this argument then the build will fail? And the recommendation will be to disable SBOM embedding in that case?

Copy link
Copy Markdown
Contributor Author

@jerboaa jerboaa Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aloubyansky Yes, the build will (currently) fail for a mandrel version that doesn't support it. The idea was to conditionalize on the mandrel version that has support. I haven't added it yet, since I didn't know which version that would be. But given the discussion in graalvm/mandrel#962 it might change how we handle it. E.g. if the feature moves to quarkus.

/cc @zakkak

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants