From a7fb3699ee8a715ee49d939b0a31581b786ebae2 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Tue, 15 Oct 2024 13:43:53 +0200 Subject: [PATCH] Add auto-detection for SBOMs in native-image Closes gh-40630 --- .../boot/actuate/sbom/SbomEndpoint.java | 94 +++++++++++-------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/sbom/SbomEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/sbom/SbomEndpoint.java index 503e667f72a..1d65e11f3ca 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/sbom/SbomEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/sbom/SbomEndpoint.java @@ -45,11 +45,13 @@ import org.springframework.util.StringUtils; @ImportRuntimeHints(SbomEndpointRuntimeHints.class) public class SbomEndpoint { - private static final List DEFAULT_APPLICATION_SBOM_LOCATIONS = List.of("classpath:META-INF/sbom/bom.json", - "classpath:META-INF/sbom/application.cdx.json"); - static final String APPLICATION_SBOM_ID = "application"; + private static final List AUTODETECTED_SBOMS = List.of( + new AutodetectedSbom(APPLICATION_SBOM_ID, "classpath:META-INF/sbom/bom.json", true), + new AutodetectedSbom(APPLICATION_SBOM_ID, "classpath:META-INF/sbom/application.cdx.json", true), + new AutodetectedSbom("native-image", "classpath:META-INF/native-image/sbom.json", false)); + private final SbomProperties properties; private final ResourceLoader resourceLoader; @@ -59,14 +61,26 @@ public class SbomEndpoint { public SbomEndpoint(SbomProperties properties, ResourceLoader resourceLoader) { this.properties = properties; this.resourceLoader = resourceLoader; - this.sboms = Collections.unmodifiableMap(getSboms()); + this.sboms = loadSboms(); + } + + private Map loadSboms() { + Map sboms = new HashMap<>(); + addConfiguredApplicationSbom(sboms); + addAdditionalSboms(sboms); + addAutodetectedSboms(sboms); + return Collections.unmodifiableMap(sboms); } - private Map getSboms() { - Map result = new HashMap<>(); - addKnownSboms(result); - addAdditionalSboms(result); - return result; + private void addConfiguredApplicationSbom(Map sboms) { + String location = this.properties.getApplication().getLocation(); + if (!StringUtils.hasLength(location)) { + return; + } + Resource resource = loadResource(location); + if (resource != null) { + sboms.put(APPLICATION_SBOM_ID, resource); + } } private void addAdditionalSboms(Map result) { @@ -80,34 +94,16 @@ public class SbomEndpoint { }); } - private void addKnownSboms(Map result) { - Resource applicationSbom = getApplicationSbom(); - if (applicationSbom != null) { - result.put(APPLICATION_SBOM_ID, applicationSbom); - } - } - - @ReadOperation - Sboms sboms() { - return new Sboms(new TreeSet<>(this.sboms.keySet())); - } - - @ReadOperation - Resource sbom(@Selector String id) { - return this.sboms.get(id); - } - - private Resource getApplicationSbom() { - if (StringUtils.hasLength(this.properties.getApplication().getLocation())) { - return loadResource(this.properties.getApplication().getLocation()); - } - for (String location : DEFAULT_APPLICATION_SBOM_LOCATIONS) { - Resource resource = this.resourceLoader.getResource(location); + private void addAutodetectedSboms(Map sboms) { + for (AutodetectedSbom sbom : AUTODETECTED_SBOMS) { + if (sboms.containsKey(sbom.id())) { + continue; + } + Resource resource = this.resourceLoader.getResource(sbom.resource()); if (resource.exists()) { - return resource; + sboms.put(sbom.id(), resource); } } - return null; } private Resource loadResource(String location) { @@ -125,6 +121,16 @@ public class SbomEndpoint { throw new IllegalStateException("Resource '%s' doesn't exist and it's not marked optional".formatted(location)); } + @ReadOperation + Sboms sboms() { + return new Sboms(new TreeSet<>(this.sboms.keySet())); + } + + @ReadOperation + Resource sbom(@Selector String id) { + return this.sboms.get(id); + } + record Sboms(Collection ids) implements OperationResponseBody { } @@ -146,18 +152,26 @@ public class SbomEndpoint { } } - static class SbomEndpointRuntimeHints implements RuntimeHintsRegistrar { - - @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - for (String defaultLocation : DEFAULT_APPLICATION_SBOM_LOCATIONS) { - hints.resources().registerPattern(stripClasspathPrefix(defaultLocation)); + private record AutodetectedSbom(String id, String resource, boolean needsHints) { + void registerHintsIfNeeded(RuntimeHints hints) { + if (this.needsHints) { + hints.resources().registerPattern(stripClasspathPrefix(this.resource)); } } private String stripClasspathPrefix(String location) { return location.substring("classpath:".length()); } + } + + static class SbomEndpointRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + for (AutodetectedSbom sbom : AUTODETECTED_SBOMS) { + sbom.registerHintsIfNeeded(hints); + } + } }