CVE-2022-46166 (published 2022-12-09T21:15Z)

Spring boot admins is an open source administrative user interface for management of spring boot applications. All users who run Spring Boot Admin Server, having enabled Notifiers (e.g. Teams-Notifier) and write access to environment variables via UI are affected. Users are advised to upgrade to the most recent releases of Spring Boot Admin 2.6.10 and 2.7.8 to resolve this issue. Users unable to upgrade may disable any notifier or disable write access (POST request) on `/env` actuator endpoint.\n\n

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-94
cwe_name: Improper Control of Generation of Code ('Code Injection')
description: The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
extended_description: 
            When a product allows a user's input to contain code syntax, it might be possible for an attacker to craft the code in such a way that it will alter the intended control flow of the product. Such an alteration could lead to arbitrary code execution.
            Injection problems encompass a wide variety of issues -- all mitigated in very different ways. For this reason, the most effective way to discuss these weaknesses is to note the distinct features which classify them as injection weaknesses. The most important issue to note is that all injection problems share one thing in common -- i.e., they allow for the injection of control plane data into the user-controlled data plane. This means that the execution of the process may be altered by sending code in through legitimate data channels, using no other mechanism. While buffer overflows, and many other flaws, involve the use of some further issue to gain execution, injection problems need only for the data to be parsed. The most classic instantiations of this category of weakness are SQL injection and format string vulnerabilities.
         
         

Fix information:
repo_url: https://github.com/codecentric/spring-boot-admin
commit_details: repo_url: https://github.com/codecentric/spring-boot-admin
author: Stephan Köninger
author_date: 2022-11-30 15:47:40+01:00
msg: feat: improve notifiers
repository: repo_url: https://github.com/codecentric/spring-boot-admin
repo_name: codecentric/spring-boot-admin
description: Admin UI for administration of spring boot applications
repo_language: Java
owner: codecentric
file_changes: filename: DingTalkNotifier.java
change_type: ModificationType.MODIFY
diff: @@ -17,6 +17,7 @@
 package de.codecentric.boot.admin.server.notify;
 
 import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -29,7 +30,8 @@ import org.springframework.context.expression.MapAccessor;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
+import org.springframework.expression.spel.support.SimpleEvaluationContext;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
@@ -101,8 +103,9 @@ public class DingTalkNotifier extends AbstractStatusChangeNotifier {
 		root.put("event", event);
 		root.put("instance", instance);
 		root.put("lastStatus", getLastStatus(event.getInstance()));
-		StandardEvaluationContext context = new StandardEvaluationContext(root);
-		context.addPropertyAccessor(new MapAccessor());
+		SimpleEvaluationContext context = SimpleEvaluationContext
+				.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess(), new MapAccessor())
+				.withRootObject(root).build();
 		return message.getValue(context, String.class);
 	}
 
@@ -110,8 +113,8 @@ public class DingTalkNotifier extends AbstractStatusChangeNotifier {
 		try {
 			String stringToSign = timestamp + "\n" + secret;
 			Mac mac = Mac.getInstance("HmacSHA256");
-			mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
-			byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
+			mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
+			byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
 			return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
 		}
 		catch (Exception ex) {

programming_language: Java
filename: DiscordNotifier.java
change_type: ModificationType.MODIFY
diff: @@ -26,7 +26,8 @@ import org.springframework.context.expression.MapAccessor;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
+import org.springframework.expression.spel.support.SimpleEvaluationContext;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
@@ -117,8 +118,9 @@ public class DiscordNotifier extends AbstractStatusChangeNotifier {
 		root.put("event", event);
 		root.put("instance", instance);
 		root.put("lastStatus", getLastStatus(event.getInstance()));
-		StandardEvaluationContext context = new StandardEvaluationContext(root);
-		context.addPropertyAccessor(new MapAccessor());
+		SimpleEvaluationContext context = SimpleEvaluationContext
+				.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess(), new MapAccessor())
+				.withRootObject(root).build();
 		return message.getValue(context, String.class);
 	}
 

programming_language: Java
filename: HipchatNotifier.java
change_type: ModificationType.MODIFY
diff: @@ -26,7 +26,8 @@ import org.springframework.context.expression.MapAccessor;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
+import org.springframework.expression.spel.support.SimpleEvaluationContext;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
@@ -96,7 +97,7 @@ public class HipchatNotifier extends AbstractStatusChangeNotifier {
 		if (url == null) {
 			throw new IllegalStateException("'url' must not be null.");
 		}
-		return String.format("%s/room/%s/notification?auth_token=%s", url.toString(), roomId, authToken);
+		return String.format("%s/room/%s/notification?auth_token=%s", url, roomId, authToken);
 	}
 
 	protected HttpEntity<Map<String, Object>> createHipChatNotification(InstanceEvent event, Instance instance) {
@@ -121,8 +122,9 @@ public class HipchatNotifier extends AbstractStatusChangeNotifier {
 		root.put("event", event);
 		root.put("instance", instance);
 		root.put("lastStatus", getLastStatus(event.getInstance()));
-		StandardEvaluationContext context = new StandardEvaluationContext(root);
-		context.addPropertyAccessor(new MapAccessor());
+		SimpleEvaluationContext context = SimpleEvaluationContext
+				.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess(), new MapAccessor())
+				.withRootObject(root).build();
 		return description.getValue(context, String.class);
 	}
 
@@ -136,17 +138,13 @@ public class HipchatNotifier extends AbstractStatusChangeNotifier {
 		}
 	}
 
-	public void setUrl(@Nullable URI url) {
-		this.url = url;
-	}
-
 	@Nullable
 	public URI getUrl() {
 		return url;
 	}
 
-	public void setAuthToken(@Nullable String authToken) {
-		this.authToken = authToken;
+	public void setUrl(@Nullable URI url) {
+		this.url = url;
 	}
 
 	@Nullable
@@ -154,8 +152,8 @@ public class HipchatNotifier extends AbstractStatusChangeNotifier {
 		return authToken;
 	}
 
-	public void setRoomId(@Nullable String roomId) {
-		this.roomId = roomId;
+	public void setAuthToken(@Nullable String authToken) {
+		this.authToken = authToken;
 	}
 
 	@Nullable
@@ -163,22 +161,26 @@ public class HipchatNotifier extends AbstractStatusChangeNotifier {
 		return roomId;
 	}
 
-	public void setNotify(boolean notify) {
-		this.notify = notify;
+	public void setRoomId(@Nullable String roomId) {
+		this.roomId = roomId;
 	}
 
 	public boolean isNotify() {
 		return notify;
 	}
 
-	public void setDescription(String description) {
-		this.description = parser.parseExpression(description, ParserContext.TEMPLATE_EXPRESSION);
+	public void setNotify(boolean notify) {
+		this.notify = notify;
 	}
 
 	public String getDescription() {
 		return description.getExpressionString();
 	}
 
+	public void setDescription(String description) {
+		this.description = parser.parseExpression(description, ParserContext.TEMPLATE_EXPRESSION);
+	}
+
 	public void setRestTemplate(RestTemplate restTemplate) {
 		this.restTemplate = restTemplate;
 	}

programming_language: Java
filename: LetsChatNotifier.java
change_type: ModificationType.MODIFY
diff: @@ -27,7 +27,8 @@ import org.springframework.context.expression.MapAccessor;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
+import org.springframework.expression.spel.support.SimpleEvaluationContext;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpMethod;
@@ -119,8 +120,9 @@ public class LetsChatNotifier extends AbstractStatusChangeNotifier {
 		root.put("event", event);
 		root.put("instance", instance);
 		root.put("lastStatus", getLastStatus(event.getInstance()));
-		StandardEvaluationContext context = new StandardEvaluationContext(root);
-		context.addPropertyAccessor(new MapAccessor());
+		SimpleEvaluationContext context = SimpleEvaluationContext
+				.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess(), new MapAccessor())
+				.withRootObject(root).build();
 		return message.getValue(context, String.class);
 	}
 
@@ -128,25 +130,21 @@ public class LetsChatNotifier extends AbstractStatusChangeNotifier {
 		this.restTemplate = restTemplate;
 	}
 
-	public void setUrl(@Nullable URI url) {
-		this.url = url;
-	}
-
 	@Nullable
 	public URI getUrl() {
 		return url;
 	}
 
-	public void setUsername(String username) {
-		this.username = username;
+	public void setUrl(@Nullable URI url) {
+		this.url = url;
 	}
 
 	public String getUsername() {
 		return username;
 	}
 
-	public void setRoom(@Nullable String room) {
-		this.room = room;
+	public void setUsername(String username) {
+		this.username = username;
 	}
 
 	@Nullable
@@ -154,8 +152,8 @@ public class LetsChatNotifier extends AbstractStatusChangeNotifier {
 		return room;
 	}
 
-	public void setToken(@Nullable String token) {
-		this.token = token;
+	public void setRoom(@Nullable String room) {
+		this.room = room;
 	}
 
 	@Nullable
@@ -163,12 +161,16 @@ public class LetsChatNotifier extends AbstractStatusChangeNotifier {
 		return token;
 	}
 
-	public void setMessage(String message) {
-		this.message = parser.parseExpression(message, ParserContext.TEMPLATE_EXPRESSION);
+	public void setToken(@Nullable String token) {
+		this.token = token;
 	}
 
 	public String getMessage() {
 		return message.getExpressionString();
 	}
 
+	public void setMessage(String message) {
+		this.message = parser.parseExpression(message, ParserContext.TEMPLATE_EXPRESSION);
+	}
+
 }

programming_language: Java
filename: MicrosoftTeamsNotifier.java
change_type: ModificationType.MODIFY
diff: @@ -30,10 +30,12 @@ import lombok.Data;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.expression.MapAccessor;
+import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
+import org.springframework.expression.spel.support.SimpleEvaluationContext;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
@@ -141,7 +143,7 @@ public class MicrosoftTeamsNotifier extends AbstractStatusChangeNotifier {
 	@Override
 	protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
 		Message message;
-		StandardEvaluationContext context = createEvaluationContext(event, instance);
+		EvaluationContext context = createEvaluationContext(event, instance);
 		if (event instanceof InstanceRegisteredEvent) {
 			message = getRegisteredMessage(instance, context);
 		}
@@ -172,23 +174,23 @@ public class MicrosoftTeamsNotifier extends AbstractStatusChangeNotifier {
 				|| super.shouldNotify(event, instance);
 	}
 
-	protected Message getDeregisteredMessage(Instance instance, StandardEvaluationContext context) {
+	protected Message getDeregisteredMessage(Instance instance, EvaluationContext context) {
 		String activitySubtitle = evaluateExpression(context, deregisterActivitySubtitle);
 		return createMessage(instance, deRegisteredTitle, activitySubtitle, context);
 	}
 
-	protected Message getRegisteredMessage(Instance instance, StandardEvaluationContext context) {
+	protected Message getRegisteredMessage(Instance instance, EvaluationContext context) {
 		String activitySubtitle = evaluateExpression(context, registerActivitySubtitle);
 		return createMessage(instance, registeredTitle, activitySubtitle, context);
 	}
 
-	protected Message getStatusChangedMessage(Instance instance, StandardEvaluationContext context) {
+	protected Message getStatusChangedMessage(Instance instance, EvaluationContext context) {
 		String activitySubtitle = evaluateExpression(context, statusActivitySubtitle);
 		return createMessage(instance, statusChangedTitle, activitySubtitle, context);
 	}
 
 	protected Message createMessage(Instance instance, String registeredTitle, String activitySubtitle,
-			StandardEvaluationContext context) {
+			EvaluationContext context) {
 		List<Fact> facts = new ArrayList<>();
 		facts.add(new Fact(STATUS_KEY, instance.getStatusInfo().getStatus()));
 		facts.add(new Fact(SERVICE_URL_KEY, instance.getRegistration().getServiceUrl()));
@@ -203,18 +205,18 @@ public class MicrosoftTeamsNotifier extends AbstractStatusChangeNotifier {
 				.themeColor(evaluateExpression(context, themeColor)).sections(singletonList(section)).build();
 	}
 
-	protected String evaluateExpression(StandardEvaluationContext context, Expression expression) {
+	protected String evaluateExpression(EvaluationContext context, Expression expression) {
 		return Objects.requireNonNull(expression.getValue(context, String.class));
 	}
 
-	protected StandardEvaluationContext createEvaluationContext(InstanceEvent event, Instance instance) {
+	protected EvaluationContext createEvaluationContext(InstanceEvent event, Instance instance) {
 		Map<String, Object> root = new HashMap<>();
 		root.put("event", event);
 		root.put("instance", instance);
 		root.put("lastStatus", getLastStatus(event.getInstance()));
-		StandardEvaluationContext context = new StandardEvaluationContext(root);
-		context.addPropertyAccessor(new MapAccessor());
-		return context;
+		return SimpleEvaluationContext
+				.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess(), new MapAccessor())
+				.withRootObject(root).build();
 	}
 
 	@Nullable

programming_language: Java
filename: OpsGenieNotifier.java
change_type: ModificationType.MODIFY
diff: @@ -26,7 +26,8 @@ import org.springframework.context.expression.MapAccessor;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
+import org.springframework.expression.spel.support.SimpleEvaluationContext;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpMethod;
@@ -118,7 +119,7 @@ public class OpsGenieNotifier extends AbstractStatusChangeNotifier {
 	protected String buildUrl(InstanceEvent event, Instance instance) {
 		if ((event instanceof InstanceStatusChangedEvent)
 				&& (StatusInfo.STATUS_UP.equals(((InstanceStatusChangedEvent) event).getStatusInfo().getStatus()))) {
-			return String.format("%s/%s/close", url.toString(), generateAlias(instance));
+			return String.format("%s/%s/close", url, generateAlias(instance));
 		}
 		return url.toString();
 	}
@@ -172,8 +173,9 @@ public class OpsGenieNotifier extends AbstractStatusChangeNotifier {
 		root.put("event", event);
 		root.put("instance", instance);
 		root.put("lastStatus", getLastStatus(event.getInstance()));
-		StandardEvaluationContext context = new StandardEvaluationContext(root);
-		context.addPropertyAccessor(new MapAccessor());
+		SimpleEvaluationContext context = SimpleEvaluationContext
+				.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess(), new MapAccessor())
+				.withRootObject(root).build();
 		return description.getValue(context, String.class);
 	}
 
@@ -183,15 +185,15 @@ public class OpsGenieNotifier extends AbstractStatusChangeNotifier {
 				((InstanceStatusChangedEvent) event).getStatusInfo().getStatus());
 	}
 
-	public void setApiKey(@Nullable String apiKey) {
-		this.apiKey = apiKey;
-	}
-
 	@Nullable
 	public String getApiKey() {
 		return apiKey;
 	}
 
+	public void setApiKey(@Nullable String apiKey) {
+		this.apiKey = apiKey;
+	}
+
 	public void setDescription(String description) {
 		this.description = parser.parseExpression(description, ParserContext.TEMPLATE_EXPRESSION);
 	}

programming_language: Java
filename: PagerdutyNotifier.java
change_type: ModificationType.MODIFY
diff: @@ -26,7 +26,8 @@ import org.springframework.context.expression.MapAccessor;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
+import org.springframework.expression.spel.support.SimpleEvaluationContext;
 import org.springframework.web.client.RestTemplate;
 import reactor.core.publisher.Mono;
 
@@ -132,9 +133,9 @@ public class PagerdutyNotifier extends AbstractStatusChangeNotifier {
 		root.put("event", event);
 		root.put("instance", instance);
 		root.put("lastStatus", getLastStatus(event.getInstance()));
-		StandardEvaluationContext context = new StandardEvaluationContext(root);
-		context.addPropertyAccessor(new MapAccessor());
-
+		SimpleEvaluationContext context = SimpleEvaluationContext
+				.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess(), new MapAccessor())
+				.withRootObject(root).build();
 		return description.getValue(context, String.class);
 	}
 
@@ -147,16 +148,12 @@ public class PagerdutyNotifier extends AbstractStatusChangeNotifier {
 		return details;
 	}
 
-	public void setUrl(URI url) {
-		this.url = url;
-	}
-
 	public URI getUrl() {
 		return url;
 	}
 
-	public void setClient(@Nullable String client) {
-		this.client = client;
+	public void setUrl(URI url) {
+		this.url = url;
 	}
 
 	@Nullable
@@ -164,8 +161,8 @@ public class PagerdutyNotifier extends AbstractStatusChangeNotifier {
 		return client;
 	}
 
-	public void setClientUrl(@Nullable URI clientUrl) {
-		this.clientUrl = clientUrl;
+	public void setClient(@Nullable String client) {
+		this.client = client;
 	}
 
 	@Nullable
@@ -173,8 +170,8 @@ public class PagerdutyNotifier extends AbstractStatusChangeNotifier {
 		return clientUrl;
 	}
 
-	public void setServiceKey(@Nullable String serviceKey) {
-		this.serviceKey = serviceKey;
+	public void setClientUrl(@Nullable URI clientUrl) {
+		this.clientUrl = clientUrl;
 	}
 
 	@Nullable
@@ -182,14 +179,18 @@ public class PagerdutyNotifier extends AbstractStatusChangeNotifier {
 		return serviceKey;
 	}
 
-	public void setDescription(String description) {
-		this.description = parser.parseExpression(description, ParserContext.TEMPLATE_EXPRESSION);
+	public void setServiceKey(@Nullable String serviceKey) {
+		this.serviceKey = serviceKey;
 	}
 
 	public String getDescription() {
 		return description.getExpressionString();
 	}
 
+	public void setDescription(String description) {
+		this.description = parser.parseExpression(description, ParserContext.TEMPLATE_EXPRESSION);
+	}
+
 	public void setRestTemplate(RestTemplate restTemplate) {
 		this.restTemplate = restTemplate;
 	}

programming_language: Java
filename: SlackNotifier.java
change_type: ModificationType.MODIFY
diff: @@ -27,7 +27,8 @@ import org.springframework.context.expression.MapAccessor;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
+import org.springframework.expression.spel.support.SimpleEvaluationContext;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
@@ -128,8 +129,10 @@ public class SlackNotifier extends AbstractStatusChangeNotifier {
 		root.put("event", event);
 		root.put("instance", instance);
 		root.put("lastStatus", getLastStatus(event.getInstance()));
-		StandardEvaluationContext context = new StandardEvaluationContext(root);
-		context.addPropertyAccessor(new MapAccessor());
+		SimpleEvaluationContext context = SimpleEvaluationContext
+				.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess(), new MapAccessor())
+				.withRootObject(root).build();
+
 		return message.getValue(context, String.class);
 	}
 

programming_language: Java
filename: TelegramNotifier.java
change_type: ModificationType.MODIFY
diff: @@ -25,7 +25,8 @@ import org.springframework.context.expression.MapAccessor;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.expression.spel.support.DataBindingPropertyAccessor;
+import org.springframework.expression.spel.support.SimpleEvaluationContext;
 import org.springframework.web.client.RestTemplate;
 import reactor.core.publisher.Mono;
 
@@ -107,8 +108,10 @@ public class TelegramNotifier extends AbstractStatusChangeNotifier {
 		root.put("event", event);
 		root.put("instance", instance);
 		root.put("lastStatus", getLastStatus(event.getInstance()));
-		StandardEvaluationContext context = new StandardEvaluationContext(root);
-		context.addPropertyAccessor(new MapAccessor());
+		SimpleEvaluationContext context = SimpleEvaluationContext
+				.forPropertyAccessors(DataBindingPropertyAccessor.forReadOnlyAccess(), new MapAccessor())
+				.withRootObject(root).build();
+
 		return message.getValue(context, String.class);
 	}
 

programming_language: Java