This is an automated email from the ASF dual-hosted git repository.
harikrishna-patnala pushed a commit to branch 4.20
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.20 by this push:
new 2eb9820f3cb framework-jobs,server: fix password obfuscation for job
result and password with display=false (#13388)
2eb9820f3cb is described below
commit 2eb9820f3cb42e232ab01a1c340cd90b62844499
Author: Abhishek Kumar <[email protected]>
AuthorDate: Tue Jun 23 15:37:51 2026 +0530
framework-jobs,server: fix password obfuscation for job result and password
with display=false (#13388)
* framework-jobs: fix password obfuscation for job result
Fixes #13387
Signed-off-by: Abhishek Kumar <[email protected]>
* add password as hidden
Signed-off-by: Abhishek Kumar <[email protected]>
---------
Signed-off-by: Abhishek Kumar <[email protected]>
---
.../framework/jobs/impl/AsyncJobManagerImpl.java | 38 +++++++++++++---------
.../framework/jobs/AsyncJobManagerTest.java | 20 ++++++++++++
.../network/element/VirtualRouterElement.java | 24 +++++++-------
3 files changed, 54 insertions(+), 28 deletions(-)
diff --git
a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
index 80140b0d950..4c1e44c5e50 100644
---
a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
+++
b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
@@ -31,6 +31,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
@@ -114,6 +116,8 @@ import com.cloud.vm.dao.VMInstanceDao;
import org.apache.logging.log4j.ThreadContext;
public class AsyncJobManagerImpl extends ManagerBase implements
AsyncJobManager, ClusterManagerListener, Configurable {
+ private static final Pattern PASSWORD_FIELD_PATTERN =
Pattern.compile("\\\"password\\\":\\\"([^\\\"]*)\\\"+");
+
// Advanced
public static final ConfigKey<Long> JobExpireMinutes = new
ConfigKey<Long>("Advanced", Long.class, "job.expire.minutes", "1440",
"Time (in minutes) for async-jobs to be kept in system", true,
ConfigKey.Scope.Global);
@@ -517,22 +521,26 @@ public class AsyncJobManagerImpl extends ManagerBase
implements AsyncJobManager,
}
public String obfuscatePassword(String result, boolean hidePassword) {
- if (hidePassword) {
- String pattern = "\"password\":";
- if (result != null) {
- if (result.contains(pattern)) {
- String[] resp = result.split(pattern);
- String psswd = resp[1].toString().split(",")[0];
- if (psswd.endsWith("}")) {
- psswd = psswd.substring(0, psswd.length() - 1);
- result = resp[0] + pattern +
psswd.replace(psswd.substring(2, psswd.length() - 1), "*****") + "}," +
resp[1].split(",", 2)[1];
- } else {
- result = resp[0] + pattern +
psswd.replace(psswd.substring(2, psswd.length() - 1), "*****") + "," +
resp[1].split(",", 2)[1];
- }
- }
- }
+ if (!hidePassword || StringUtils.isBlank(result)) {
+ return result;
+ }
+
+ Matcher matcher = PASSWORD_FIELD_PATTERN.matcher(result);
+ StringBuilder obfuscatedResult = new StringBuilder();
+ while (matcher.find()) {
+ String password = matcher.group(1);
+ String replacement = "\"password\":\"" +
obfuscatePasswordValue(password) + "\"";
+ matcher.appendReplacement(obfuscatedResult,
Matcher.quoteReplacement(replacement));
+ }
+ matcher.appendTail(obfuscatedResult);
+ return obfuscatedResult.toString();
+ }
+
+ private String obfuscatePasswordValue(String password) {
+ if (StringUtils.isEmpty(password)) {
+ return password;
}
- return result;
+ return password.charAt(0) + "*****";
}
private void scheduleExecution(final AsyncJobVO job) {
diff --git
a/framework/jobs/src/test/java/org/apache/cloudstack/framework/jobs/AsyncJobManagerTest.java
b/framework/jobs/src/test/java/org/apache/cloudstack/framework/jobs/AsyncJobManagerTest.java
index 7130873e4ee..f3cd3718845 100644
---
a/framework/jobs/src/test/java/org/apache/cloudstack/framework/jobs/AsyncJobManagerTest.java
+++
b/framework/jobs/src/test/java/org/apache/cloudstack/framework/jobs/AsyncJobManagerTest.java
@@ -17,12 +17,15 @@
package org.apache.cloudstack.framework.jobs;
import org.apache.cloudstack.framework.jobs.impl.AsyncJobManagerImpl;
+import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
+import com.cloud.utils.HumanReadableJson;
+
@RunWith (MockitoJUnitRunner.class)
public class AsyncJobManagerTest {
@@ -37,6 +40,12 @@ public class AsyncJobManagerTest {
String inputNoBraces = "\"password\":\"password\"\",\"action\":\"OFF\"";
String expectedNoBraces = "\"password\":\"p*****\",\"action\":\"OFF\"";
+ String realUserVmResponseWithPasswordInput =
"{\"id\":\"f75b0990-5801-4b78-bcb0-58a503afa49c\",\"name\":\"pw-vm\"," +
+
"\"displayname\":\"pw-vm\",\"account\":\"admin\",\"password\":\"67wSK5\",\"instancename\":\"i-2-17-VM\","
+
+
"\"details\":{\"password\":\"3WTVryPJZJwMZGcJJ+OOYf84+uixk/1FraomPG9N6/Uvng\\u003d\\u003d\","
+
+
"\"Message.ReservedCapacityFreed.Flag\":\"true\",\"rootDiskController\":\"osdefault\"},"
+
+
"\"arch\":\"x86_64\",\"jobid\":\"c13865d3-61ec-4269-979a-3d799181d5fe\",\"jobstatus\":0}";
+
@Test
public void obfuscatePasswordTest() {
String result = asyncJobManager.obfuscatePassword(input, true);
@@ -79,4 +88,15 @@ public class AsyncJobManagerTest {
Assert.assertEquals(noPassword, result);
}
+ @Test
+ public void obfuscatePasswordTestHidePasswordRealInput() {
+ String result =
asyncJobManager.obfuscatePassword(realUserVmResponseWithPasswordInput, true);
+
+ Assert.assertNotNull(result);
+
Assert.assertFalse(result.contains("\"password\":\"3WTVryPJZJwMZGcJJ+OOYf84+uixk\""));
+ String jsonObject =
HumanReadableJson.getHumanReadableBytesJson(result);
+ Assert.assertTrue(StringUtils.isNotEmpty(jsonObject));
+ Assert.assertTrue(jsonObject.contains("\"password\":\"3*****\""));
+ }
+
}
diff --git
a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
index 263ff523ab6..5938c1e4c56 100644
--- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
+++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java
@@ -24,28 +24,21 @@ import java.util.Set;
import javax.inject.Inject;
-import org.apache.cloudstack.network.BgpPeer;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang3.ObjectUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.vm.VirtualMachineProfileImpl;
-import com.cloud.vm.VmDetailConstants;
-import com.cloud.vm.dao.NicDao;
-import com.google.gson.Gson;
-
import org.apache.cloudstack.api.command.admin.router.ConfigureOvsElementCmd;
import
org.apache.cloudstack.api.command.admin.router.ConfigureVirtualRouterElementCmd;
import
org.apache.cloudstack.api.command.admin.router.CreateVirtualRouterElementCmd;
import org.apache.cloudstack.api.command.admin.router.ListOvsElementsCmd;
import
org.apache.cloudstack.api.command.admin.router.ListVirtualRouterElementsCmd;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.network.BgpPeer;
import
org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition;
import
org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinitionBuilder;
import org.apache.cloudstack.network.topology.NetworkTopology;
import org.apache.cloudstack.network.topology.NetworkTopologyContext;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import com.cloud.agent.api.to.LoadBalancerTO;
import com.cloud.configuration.ConfigurationManager;
@@ -101,6 +94,7 @@ import com.cloud.network.vpc.Vpc;
import com.cloud.offering.NetworkOffering;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.offerings.dao.NetworkOfferingDao;
+import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.utils.component.AdapterBase;
@@ -117,8 +111,12 @@ import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.VirtualMachineProfile;
+import com.cloud.vm.VirtualMachineProfileImpl;
+import com.cloud.vm.VmDetailConstants;
import com.cloud.vm.dao.DomainRouterDao;
+import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.UserVmDao;
+import com.google.gson.Gson;
public class VirtualRouterElement extends AdapterBase implements
VirtualRouterElementService, DhcpServiceProvider, UserDataServiceProvider,
SourceNatServiceProvider,
StaticNatServiceProvider, FirewallServiceProvider,
LoadBalancingServiceProvider, PortForwardingServiceProvider,
RemoteAccessVPNServiceProvider, IpDeployer,
@@ -736,7 +734,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor,
RedundantResource, DnsServ
_userVmDao.loadDetails(userVmVO);
userVmVO.setDetail(VmDetailConstants.PASSWORD, password_encrypted);
- _userVmDao.saveDetails(userVmVO);
+ _userVmDao.saveDetails(userVmVO, List.of(VmDetailConstants.PASSWORD));
userVmVO.setUpdateParameters(true);
_userVmDao.update(userVmVO.getId(), userVmVO);