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);

Reply via email to