diff --git a/docs/reference-manual/native-image/BuildOutput.md b/docs/reference-manual/native-image/BuildOutput.md index 3b9303844ac5..3b84a20fa83a 100644 --- a/docs/reference-manual/native-image/BuildOutput.md +++ b/docs/reference-manual/native-image/BuildOutput.md @@ -271,7 +271,7 @@ The storage formats include: `embed`, which embeds the SBOM in the binary; `clas The SBOM feature is enabled by default and defaults to the `embed` option. When embedded, the SBOM size is displayed. The number of components is always displayed. -The SBOM feature can be disabled with `--enable-sbom=false`. +The SBOM feature can be disabled by using `--enable-sbom=false` on the command line. Unassociated types are displayed when certain types (such as classes, interfaces, or annotations) cannot be linked to an SBOM component. If these types contain vulnerabilities, SBOM scanning will not detect them. diff --git a/docs/security/SBOM.md b/docs/security/SBOM.md index a87c94ac9441..bb34d39abd4e 100644 --- a/docs/security/SBOM.md +++ b/docs/security/SBOM.md @@ -19,7 +19,7 @@ The SBOM is compressed to limit the SBOM's impact on the native executable size. The compressed size is typically less than 1/10,000 of the overall image size. The SBOM is stored in the `gzip` format with the exported `sbom` symbol referencing its start address and the `sbom_length` symbol referencing its size. -The SBOM feature can be disabled with `--enable-sbom=false`. +The SBOM feature can be disabled by using `--enable-sbom=false` on the command line. ## Extracting SBOM Contents diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/SBOMFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/SBOMFeature.java new file mode 100644 index 000000000000..bd9a56789971 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/SBOMFeature.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.sbom; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.option.APIOption; +import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue; +import com.oracle.svm.core.option.HostedOptionKey; + +import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.options.OptionStability; +import jdk.graal.compiler.options.OptionType; + +/** + * The Software Bill of Materials (SBOM) feature is only available for Oracle GraalVM. The purpose + * of this package is to display a helpful error message if the SBOM feature is activated with + * GraalVM Community Edition. + */ +@AutomaticallyRegisteredFeature +public class SBOMFeature implements InternalFeature { + protected static final String sbomResourceLocation = "META-INF/native-image/sbom.json"; + + public static final class Options { + public static final String name = "--enable-sbom"; + @APIOption(name = name, defaultValue = "") // + @Option(help = "Assemble a Software Bill of Materials (SBOM) for the executable or shared library based on the results from the static analysis " + + " (only available in Oracle GraalVM). Comma-separated list can contain " + + "'" + SBOMValues.StorageOption.embed + "' to store the SBOM in data sections of the binary, " + + "'" + SBOMValues.StorageOption.export + "' to save the SBOM in the output directory, " + + "'" + SBOMValues.StorageOption.classpath + "' to include the SBOM as a Java resource on the classpath at '" + sbomResourceLocation + "', " + + "'" + SBOMValues.strict + "' to abort the build if any type (such as a class, interface, or annotation) cannot be matched to an SBOM component, " + + "'" + SBOMValues.cyclonedxFormat + "' (the only format currently supported), and '" + SBOMValues.classLevel + "' to include class-level " + + "metadata. The default in Oracle Oracle GraalVM is to embed an SBOM: '" + name + "=" + SBOMValues.StorageOption.embed + "'. " + + "To disable the SBOM feature, use '" + name + "=" + SBOMValues.disableSBOM + "' on the command line.", type = OptionType.User, stability = OptionStability.STABLE) // + public static final HostedOptionKey IncludeSBOM = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter(), + (options) -> SBOMValueValidator.getInstance().validateSBOMValues(options)); + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(SBOMValueValidator.class, new UnsupportedSBOMValueValidator()); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/SBOMValueValidator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/SBOMValueValidator.java new file mode 100644 index 000000000000..032abd62c7d2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/SBOMValueValidator.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.sbom; + +import java.util.List; +import java.util.stream.Stream; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.option.LocatableMultiOptionValue; +import com.oracle.svm.core.util.UserError; + +import jdk.graal.compiler.options.OptionKey; + +/** + * Validates the values passed to '--enable-sbom'. + */ +public abstract class SBOMValueValidator { + public abstract void validateSBOMValues(OptionKey optionKey); + + static SBOMValueValidator getInstance() { + return ImageSingletons.lookup(SBOMValueValidator.class); + } + + /** + * Aborts execution if the SBOM feature is deactivated from non-command-line sources like + * 'native-image.properties'. Native Image only supports subtractive option usage from the CLI. + */ + protected static void abortIfSBOMDisabledFromOtherThanCommandLine() { + var optionalFalseOrigin = getNonCommandLikeOriginStream() + .filter(v -> v.value().equals(SBOMValues.disableSBOM)) + .findFirst(); + if (optionalFalseOrigin.isPresent()) { + List nonCommandLineValues = getNonCommandLikeOriginStream() + .map(LocatableMultiOptionValue.ValueWithOrigin::value) + .toList(); + String message = String.format("Value '%s' for option '%s' can only be used on the command line with 'native-image'. Found non-command-line option '%s=%s' from %s.", + SBOMValues.disableSBOM, SBOMFeature.Options.name, SBOMFeature.Options.name, String.join(",", nonCommandLineValues), optionalFalseOrigin.get().origin()); + throw UserError.abort(message); + } + } + + private static Stream> getNonCommandLikeOriginStream() { + return SBOMFeature.Options.IncludeSBOM.getValue().getValuesWithOrigins() + .filter(v -> !v.origin().commandLineLike()); + } + + protected static boolean isLastValueNotDisable() { + var lastOrigin = SBOMFeature.Options.IncludeSBOM.getValue().lastValueWithOrigin(); + return lastOrigin.map(v -> !v.value().equals(SBOMValues.disableSBOM)) + .orElse(true); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/SBOMValues.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/SBOMValues.java new file mode 100644 index 000000000000..07665c9fa0f9 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/SBOMValues.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.sbom; + +import java.util.Set; + +/** + * Defines the available values for the SBOM feature. See {@link SBOMFeature.Options} for more + * information about the individual values. + *

+ * Note: the only supported SBOM value for GraalVM Community Edition is + * {@link SBOMValues#disableSBOM}. + */ +public class SBOMValues { + public static final String cyclonedxFormat = "cyclonedx"; + /** + * The SBOM feature is disabled by passing '--enable-sbom=false'. + */ + public static final String disableSBOM = "false"; + public static final String strict = "strict"; + /** + * If set, then the classes, fields, constructors, and methods that are used in the image are + * collected and included in the SBOM. + */ + public static final String classLevel = "class-level"; + + public static final class StorageOption { + public static final String embed = "embed"; + public static final String export = "export"; + public static final String classpath = "classpath"; + public static final Set supportedStorageValues = Set.of(embed, export, classpath); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/UnsupportedSBOMValueValidator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/UnsupportedSBOMValueValidator.java new file mode 100644 index 000000000000..56b754598a88 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/sbom/UnsupportedSBOMValueValidator.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.sbom; + +import java.util.List; + +import com.oracle.svm.core.util.UserError; + +import jdk.graal.compiler.options.OptionKey; + +/** + * Value validator for '--enable-sbom' that aborts execution if the SBOM feature is activated. + */ +final class UnsupportedSBOMValueValidator extends SBOMValueValidator { + @Override + public void validateSBOMValues(OptionKey optionKey) { + List values = SBOMFeature.Options.IncludeSBOM.getValue().values(); + if (values.isEmpty()) { + return; + } + + SBOMValueValidator.abortIfSBOMDisabledFromOtherThanCommandLine(); + abortIfLastOriginDoesNotDisableSBOM(); + } + + private static void abortIfLastOriginDoesNotDisableSBOM() { + if (isLastValueNotDisable()) { + String message = String.format(""" + The SBOM feature is only available in Oracle GraalVM. \ + Upgrade to Oracle GraalVM or disable the SBOM feature by omitting '%s' or \ + by making sure '%s=%s' is last on the command line. + """, SBOMFeature.Options.name, SBOMFeature.Options.name, SBOMValues.disableSBOM); + UserError.abort(message); + } + } +}