Skip to content

Commit a7fb369

Browse files
committed
Add auto-detection for SBOMs in native-image
Closes gh-40630
1 parent a66d3d2 commit a7fb369

File tree

1 file changed

+54
-40
lines changed
  • spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/sbom

1 file changed

+54
-40
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/sbom/SbomEndpoint.java

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@
4545
@ImportRuntimeHints(SbomEndpointRuntimeHints.class)
4646
public class SbomEndpoint {
4747

48-
private static final List<String> DEFAULT_APPLICATION_SBOM_LOCATIONS = List.of("classpath:META-INF/sbom/bom.json",
49-
"classpath:META-INF/sbom/application.cdx.json");
50-
5148
static final String APPLICATION_SBOM_ID = "application";
5249

50+
private static final List<AutodetectedSbom> AUTODETECTED_SBOMS = List.of(
51+
new AutodetectedSbom(APPLICATION_SBOM_ID, "classpath:META-INF/sbom/bom.json", true),
52+
new AutodetectedSbom(APPLICATION_SBOM_ID, "classpath:META-INF/sbom/application.cdx.json", true),
53+
new AutodetectedSbom("native-image", "classpath:META-INF/native-image/sbom.json", false));
54+
5355
private final SbomProperties properties;
5456

5557
private final ResourceLoader resourceLoader;
@@ -59,14 +61,26 @@ public class SbomEndpoint {
5961
public SbomEndpoint(SbomProperties properties, ResourceLoader resourceLoader) {
6062
this.properties = properties;
6163
this.resourceLoader = resourceLoader;
62-
this.sboms = Collections.unmodifiableMap(getSboms());
64+
this.sboms = loadSboms();
65+
}
66+
67+
private Map<String, Resource> loadSboms() {
68+
Map<String, Resource> sboms = new HashMap<>();
69+
addConfiguredApplicationSbom(sboms);
70+
addAdditionalSboms(sboms);
71+
addAutodetectedSboms(sboms);
72+
return Collections.unmodifiableMap(sboms);
6373
}
6474

65-
private Map<String, Resource> getSboms() {
66-
Map<String, Resource> result = new HashMap<>();
67-
addKnownSboms(result);
68-
addAdditionalSboms(result);
69-
return result;
75+
private void addConfiguredApplicationSbom(Map<String, Resource> sboms) {
76+
String location = this.properties.getApplication().getLocation();
77+
if (!StringUtils.hasLength(location)) {
78+
return;
79+
}
80+
Resource resource = loadResource(location);
81+
if (resource != null) {
82+
sboms.put(APPLICATION_SBOM_ID, resource);
83+
}
7084
}
7185

7286
private void addAdditionalSboms(Map<String, Resource> result) {
@@ -80,34 +94,16 @@ private void addAdditionalSboms(Map<String, Resource> result) {
8094
});
8195
}
8296

83-
private void addKnownSboms(Map<String, Resource> result) {
84-
Resource applicationSbom = getApplicationSbom();
85-
if (applicationSbom != null) {
86-
result.put(APPLICATION_SBOM_ID, applicationSbom);
87-
}
88-
}
89-
90-
@ReadOperation
91-
Sboms sboms() {
92-
return new Sboms(new TreeSet<>(this.sboms.keySet()));
93-
}
94-
95-
@ReadOperation
96-
Resource sbom(@Selector String id) {
97-
return this.sboms.get(id);
98-
}
99-
100-
private Resource getApplicationSbom() {
101-
if (StringUtils.hasLength(this.properties.getApplication().getLocation())) {
102-
return loadResource(this.properties.getApplication().getLocation());
103-
}
104-
for (String location : DEFAULT_APPLICATION_SBOM_LOCATIONS) {
105-
Resource resource = this.resourceLoader.getResource(location);
97+
private void addAutodetectedSboms(Map<String, Resource> sboms) {
98+
for (AutodetectedSbom sbom : AUTODETECTED_SBOMS) {
99+
if (sboms.containsKey(sbom.id())) {
100+
continue;
101+
}
102+
Resource resource = this.resourceLoader.getResource(sbom.resource());
106103
if (resource.exists()) {
107-
return resource;
104+
sboms.put(sbom.id(), resource);
108105
}
109106
}
110-
return null;
111107
}
112108

113109
private Resource loadResource(String location) {
@@ -125,6 +121,16 @@ private Resource loadResource(String location) {
125121
throw new IllegalStateException("Resource '%s' doesn't exist and it's not marked optional".formatted(location));
126122
}
127123

124+
@ReadOperation
125+
Sboms sboms() {
126+
return new Sboms(new TreeSet<>(this.sboms.keySet()));
127+
}
128+
129+
@ReadOperation
130+
Resource sbom(@Selector String id) {
131+
return this.sboms.get(id);
132+
}
133+
128134
record Sboms(Collection<String> ids) implements OperationResponseBody {
129135
}
130136

@@ -146,18 +152,26 @@ private static String stripOptionalPrefix(String location) {
146152
}
147153
}
148154

149-
static class SbomEndpointRuntimeHints implements RuntimeHintsRegistrar {
150-
151-
@Override
152-
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
153-
for (String defaultLocation : DEFAULT_APPLICATION_SBOM_LOCATIONS) {
154-
hints.resources().registerPattern(stripClasspathPrefix(defaultLocation));
155+
private record AutodetectedSbom(String id, String resource, boolean needsHints) {
156+
void registerHintsIfNeeded(RuntimeHints hints) {
157+
if (this.needsHints) {
158+
hints.resources().registerPattern(stripClasspathPrefix(this.resource));
155159
}
156160
}
157161

158162
private String stripClasspathPrefix(String location) {
159163
return location.substring("classpath:".length());
160164
}
165+
}
166+
167+
static class SbomEndpointRuntimeHints implements RuntimeHintsRegistrar {
168+
169+
@Override
170+
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
171+
for (AutodetectedSbom sbom : AUTODETECTED_SBOMS) {
172+
sbom.registerHintsIfNeeded(hints);
173+
}
174+
}
161175

162176
}
163177

0 commit comments

Comments
 (0)