mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-19 22:09:01 +00:00
Improve null-safety of buildpack/spring-boot-buildpack-platform
See gh-46926
This commit is contained in:
+5
-3
@@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -38,7 +40,7 @@ class BuildOwner implements Owner {
|
||||
|
||||
private final long gid;
|
||||
|
||||
BuildOwner(Map<String, String> env) {
|
||||
BuildOwner(Map<String, @Nullable String> env) {
|
||||
this.uid = getValue(env, USER_PROPERTY_NAME);
|
||||
this.gid = getValue(env, GROUP_PROPERTY_NAME);
|
||||
}
|
||||
@@ -48,7 +50,7 @@ class BuildOwner implements Owner {
|
||||
this.gid = gid;
|
||||
}
|
||||
|
||||
private long getValue(Map<String, String> env, String name) {
|
||||
private long getValue(Map<String, @Nullable String> env, String name) {
|
||||
String value = env.get(name);
|
||||
Assert.state(StringUtils.hasText(value),
|
||||
() -> "Missing '" + name + "' value from the builder environment '" + env + "'");
|
||||
@@ -83,7 +85,7 @@ class BuildOwner implements Owner {
|
||||
* @return a {@link BuildOwner} instance extracted from the env
|
||||
* @throws IllegalStateException if the env does not contain the correct CNB variables
|
||||
*/
|
||||
static BuildOwner fromEnv(Map<String, String> env) {
|
||||
static BuildOwner fromEnv(Map<String, @Nullable String> env) {
|
||||
Assert.notNull(env, "'env' must not be null");
|
||||
return new BuildOwner(env);
|
||||
}
|
||||
|
||||
+3
-3
@@ -106,15 +106,15 @@ final class BuildpackCoordinates {
|
||||
private static BuildpackCoordinates fromToml(TomlParseResult toml, Path path) {
|
||||
Assert.isTrue(!toml.isEmpty(),
|
||||
() -> "Buildpack descriptor 'buildpack.toml' is required in buildpack '" + path + "'");
|
||||
Assert.hasText(toml.getString("buildpack.id"),
|
||||
() -> "Buildpack descriptor must contain ID in buildpack '" + path + "'");
|
||||
String buildpackId = toml.getString("buildpack.id");
|
||||
Assert.hasText(buildpackId, () -> "Buildpack descriptor must contain ID in buildpack '" + path + "'");
|
||||
Assert.hasText(toml.getString("buildpack.version"),
|
||||
() -> "Buildpack descriptor must contain version in buildpack '" + path + "'");
|
||||
Assert.isTrue(toml.contains("stacks") || toml.contains("order"),
|
||||
() -> "Buildpack descriptor must contain either 'stacks' or 'order' in buildpack '" + path + "'");
|
||||
Assert.isTrue(!(toml.contains("stacks") && toml.contains("order")),
|
||||
() -> "Buildpack descriptor must not contain both 'stacks' and 'order' in buildpack '" + path + "'");
|
||||
return new BuildpackCoordinates(toml.getString("buildpack.id"), toml.getString("buildpack.version"));
|
||||
return new BuildpackCoordinates(buildpackId, toml.getString("buildpack.version"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-3
@@ -160,10 +160,12 @@ public class DockerApi {
|
||||
}
|
||||
|
||||
private ApiVersion getApiVersion() {
|
||||
if (this.apiVersion == null) {
|
||||
this.apiVersion = this.system.getApiVersion();
|
||||
ApiVersion apiVersion = this.apiVersion;
|
||||
if (apiVersion == null) {
|
||||
apiVersion = this.system.getApiVersion();
|
||||
this.apiVersion = apiVersion;
|
||||
}
|
||||
return this.apiVersion;
|
||||
return apiVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
@@ -105,6 +105,9 @@ public abstract class TotalProgressListener<E extends ImageProgressUpdateEvent>
|
||||
|
||||
void update(ImageProgressUpdateEvent event) {
|
||||
String status = event.getStatus();
|
||||
if (status == null) {
|
||||
return;
|
||||
}
|
||||
if (event.getProgressDetail() != null && this.progressByStatus.containsKey(status)) {
|
||||
int current = this.progressByStatus.get(status);
|
||||
this.progressByStatus.put(status, updateProgress(current, event.getProgressDetail()));
|
||||
|
||||
+6
-5
@@ -89,7 +89,7 @@ final class DockerConfigurationMetadata {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
DockerContext forContext(String context) {
|
||||
DockerContext forContext(@Nullable String context) {
|
||||
return createDockerContext(this.configLocation, context);
|
||||
}
|
||||
|
||||
@@ -129,8 +129,9 @@ final class DockerConfigurationMetadata {
|
||||
if (currentContext == null || DEFAULT_CONTEXT.equals(currentContext)) {
|
||||
return DockerContext.empty();
|
||||
}
|
||||
Path metaPath = Path.of(configLocation, CONTEXTS_DIR, META_DIR, asHash(currentContext), CONTEXT_FILE_NAME);
|
||||
Path tlsPath = Path.of(configLocation, CONTEXTS_DIR, TLS_DIR, asHash(currentContext), DOCKER_ENDPOINT);
|
||||
String hash = asHash(currentContext);
|
||||
Path metaPath = Path.of(configLocation, CONTEXTS_DIR, META_DIR, hash, CONTEXT_FILE_NAME);
|
||||
Path tlsPath = Path.of(configLocation, CONTEXTS_DIR, TLS_DIR, hash, DOCKER_ENDPOINT);
|
||||
if (!metaPath.toFile().exists()) {
|
||||
throw new IllegalArgumentException("Docker context '" + currentContext + "' does not exist");
|
||||
}
|
||||
@@ -146,14 +147,14 @@ final class DockerConfigurationMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable String asHash(String currentContext) {
|
||||
private static String asHash(String currentContext) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = digest.digest(currentContext.getBytes(StandardCharsets.UTF_8));
|
||||
return HexFormat.of().formatHex(hash);
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
return null;
|
||||
throw new IllegalStateException("SHA-256 is not available", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -115,11 +115,11 @@ final class PemPrivateKeyParser {
|
||||
private PemPrivateKeyParser() {
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs1Rsa(byte[] bytes, String password) {
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs1Rsa(byte[] bytes, @Nullable String password) {
|
||||
return createKeySpecForAlgorithm(bytes, RSA_ALGORITHM, null);
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForSec1Ec(byte[] bytes, String password) {
|
||||
private static PKCS8EncodedKeySpec createKeySpecForSec1Ec(byte[] bytes, @Nullable String password) {
|
||||
DerElement ecPrivateKey = DerElement.of(bytes);
|
||||
Assert.state(ecPrivateKey != null, "Unable to find private key");
|
||||
Assert.state(ecPrivateKey.isType(ValueType.ENCODED, TagType.SEQUENCE),
|
||||
@@ -164,7 +164,7 @@ final class PemPrivateKeyParser {
|
||||
}
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs8(byte[] bytes, String password) {
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs8(byte[] bytes, @Nullable String password) {
|
||||
DerElement ecPrivateKey = DerElement.of(bytes);
|
||||
Assert.state(ecPrivateKey != null, "Unable to find private key");
|
||||
Assert.state(ecPrivateKey.isType(ValueType.ENCODED, TagType.SEQUENCE),
|
||||
@@ -182,7 +182,7 @@ final class PemPrivateKeyParser {
|
||||
return (algorithmName != null) ? new PKCS8EncodedKeySpec(bytes, algorithmName) : new PKCS8EncodedKeySpec(bytes);
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs8Encrypted(byte[] bytes, String password) {
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs8Encrypted(byte[] bytes, @Nullable String password) {
|
||||
return Pkcs8PrivateKeyDecryptor.decrypt(bytes, password);
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -24,6 +24,7 @@ import java.util.function.Consumer;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.MappedObject;
|
||||
|
||||
@@ -38,7 +39,7 @@ public class ImageConfig extends MappedObject {
|
||||
|
||||
private final Map<String, String> labels;
|
||||
|
||||
private final Map<String, String> configEnv;
|
||||
private final Map<String, @Nullable String> configEnv;
|
||||
|
||||
ImageConfig(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
@@ -55,12 +56,12 @@ public class ImageConfig extends MappedObject {
|
||||
return labels;
|
||||
}
|
||||
|
||||
private Map<String, String> parseConfigEnv() {
|
||||
private Map<String, @Nullable String> parseConfigEnv() {
|
||||
String[] entries = valueAt("/Env", String[].class);
|
||||
if (entries == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, String> env = new LinkedHashMap<>();
|
||||
Map<String, @Nullable String> env = new LinkedHashMap<>();
|
||||
for (String entry : entries) {
|
||||
int i = entry.indexOf('=');
|
||||
String name = (i != -1) ? entry.substring(0, i) : entry;
|
||||
@@ -88,7 +89,7 @@ public class ImageConfig extends MappedObject {
|
||||
* an empty {@code Map} is returned.
|
||||
* @return the env, never {@code null}
|
||||
*/
|
||||
public Map<String, String> getEnv() {
|
||||
public Map<String, @Nullable String> getEnv() {
|
||||
return this.configEnv;
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -115,11 +115,11 @@ final class PemPrivateKeyParser {
|
||||
private PemPrivateKeyParser() {
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs1Rsa(byte[] bytes, String password) {
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs1Rsa(byte[] bytes, @Nullable String password) {
|
||||
return createKeySpecForAlgorithm(bytes, RSA_ALGORITHM, null);
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForSec1Ec(byte[] bytes, String password) {
|
||||
private static PKCS8EncodedKeySpec createKeySpecForSec1Ec(byte[] bytes, @Nullable String password) {
|
||||
DerElement ecPrivateKey = DerElement.of(bytes);
|
||||
Assert.state(ecPrivateKey != null, "Unable to find private key");
|
||||
Assert.state(ecPrivateKey.isType(ValueType.ENCODED, TagType.SEQUENCE),
|
||||
@@ -164,7 +164,7 @@ final class PemPrivateKeyParser {
|
||||
}
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs8(byte[] bytes, String password) {
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs8(byte[] bytes, @Nullable String password) {
|
||||
DerElement ecPrivateKey = DerElement.of(bytes);
|
||||
Assert.state(ecPrivateKey != null, "Unable to find private key");
|
||||
Assert.state(ecPrivateKey.isType(ValueType.ENCODED, TagType.SEQUENCE),
|
||||
@@ -182,7 +182,7 @@ final class PemPrivateKeyParser {
|
||||
return (algorithmName != null) ? new PKCS8EncodedKeySpec(bytes, algorithmName) : new PKCS8EncodedKeySpec(bytes);
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs8Encrypted(byte[] bytes, String password) {
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs8Encrypted(byte[] bytes, @Nullable String password) {
|
||||
return Pkcs8PrivateKeyDecryptor.decrypt(bytes, password);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user