Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public Answer execute(final UpdateHostPasswordCommand command, final LibvirtComp
final String newPassword = command.getNewPassword();

final Script script = libvirtUtilitiesHelper.buildScript(libvirtComputingResource.getUpdateHostPasswdPath());
script.add(username, newPassword);
script.add(username);
script.addSensitive(newPassword);
final String result = script.execute();

if (result != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ public Answer execute(final UpdateHostPasswordCommand command, final CitrixResou

Pair<Boolean, String> result;
try {
logger.debug("Executing command in Host: " + cmdLine);
logger.debug("Executing command in Host: " + xenServerUtilitiesHelper.buildCommandLine(SCRIPT_CMD_PATH,
VRScripts.UPDATE_HOST_PASSWD, username, "******"));
final String hostPassword = citrixResourceBase.getPwdFromQueue();
result = xenServerUtilitiesHelper.executeSshWrapper(hostIp, 22, username, null, hostPassword, cmdLine.toString());
result = xenServerUtilitiesHelper.executeSshWrapper(hostIp, 22, username, null, hostPassword, cmdLine);
} catch (final Exception e) {
return new Answer(command, false, e.getMessage());
}
Expand Down
99 changes: 60 additions & 39 deletions utils/src/main/java/com/cloud/utils/script/Script.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
Expand All @@ -43,7 +45,6 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import org.apache.cloudstack.utils.security.KeyStoreUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
Expand All @@ -66,8 +67,8 @@ public class Script implements Callable<String> {
private static final int DEFAULT_TIMEOUT = 3600 * 1000; /* 1 hour */
private volatile boolean _isTimeOut = false;

private boolean _passwordCommand = false;
private boolean avoidLoggingCommand = false;
private final Set<Integer> sensitiveArgIndices = new HashSet<>();

private static final ScheduledExecutorService s_executors = Executors.newScheduledThreadPool(10, new NamedThreadFactory("Script"));

Expand Down Expand Up @@ -145,6 +146,11 @@ public void add(String param) {
_command.add(param);
}

public void addSensitive(String param) {
Copy link
Contributor

@sureshanaparti sureshanaparti Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about defining this as add(String param, boolean sensitive).

add(String param) {
    add(param, false)
}

add(String param, boolean sensitive) {
    _command.add(param);
    if (sensitive) {
        sensitiveArgIndices.add(_command.size() - 1);
    }
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like your solution but let’s do it in a new PR and merge this one when verified.

_command.add(param);
sensitiveArgIndices.add(_command.size() - 1);
}

public Script set(String name, String value) {
_command.add(name);
_command.add(value);
Expand All @@ -163,7 +169,7 @@ protected String buildCommandLine(String[] command) {
if (sanitizeViCmdParameter(cmd, builder) || sanitizeRbdFileFormatCmdParameter(cmd, builder)) {
continue;
}
if (obscureParam) {
if (obscureParam || sensitiveArgIndices.contains(i)) {
builder.append("******").append(" ");
obscureParam = false;
} else {
Expand All @@ -172,7 +178,6 @@ protected String buildCommandLine(String[] command) {

if ("-y".equals(cmd) || "-z".equals(cmd)) {
obscureParam = true;
_passwordCommand = true;
}
}
return builder.toString();
Expand Down Expand Up @@ -240,8 +245,8 @@ static String stackTraceAsString(Throwable throwable) {
public String execute(OutputInterpreter interpreter) {
String[] command = _command.toArray(new String[_command.size()]);
String commandLine = buildCommandLine(command);
if (_logger.isDebugEnabled() && !avoidLoggingCommand) {
_logger.debug(String.format("Executing command [%s].", commandLine.split(KeyStoreUtils.KS_FILENAME)[0]));
if (_logger.isDebugEnabled() ) {
_logger.debug(String.format("Executing command [%s].", commandLine));
}

try {
Expand All @@ -263,48 +268,62 @@ public String execute(OutputInterpreter interpreter) {
_thread = Thread.currentThread();
ScheduledFuture<String> future = null;
if (_timeout > 0) {
_logger.trace(String.format("Scheduling the execution of command [%s] with a timeout of [%s] milliseconds.", commandLine, _timeout));
_logger.trace(String.format(
"Scheduling the execution of command [%s] with a timeout of [%s] milliseconds.",
commandLine, _timeout));
future = s_executors.schedule(this, _timeout, TimeUnit.MILLISECONDS);
}

long processPid = _process.pid();
Task task = null;
if (interpreter != null && interpreter.drain()) {
_logger.trace(String.format("Executing interpreting task of process [%s] for command [%s].", processPid, commandLine));
_logger.trace(String.format("Executing interpreting task of process [%s] for command [%s].",
processPid, commandLine));
task = new Task(interpreter, ir);
s_executors.execute(task);
}

while (true) {
_logger.trace(String.format("Attempting process [%s] execution for command [%s] with timeout [%s].", processPid, commandLine, _timeout));
_logger.trace(String.format("Attempting process [%s] execution for command [%s] with timeout [%s].",
processPid, commandLine, _timeout));
try {
if (_process.waitFor(_timeout, TimeUnit.MILLISECONDS)) {
_logger.trace(String.format("Process [%s] execution for command [%s] completed within timeout period [%s].", processPid, commandLine,
_logger.trace(String.format(
"Process [%s] execution for command [%s] completed within timeout period [%s].",
processPid, commandLine,
_timeout));
if (_process.exitValue() == 0) {
_logger.debug(String.format("Successfully executed process [%s] for command [%s].", processPid, commandLine));
_logger.debug(String.format("Successfully executed process [%s] for command [%s].",
processPid, commandLine));
if (interpreter != null) {
if (interpreter.drain()) {
_logger.trace(String.format("Returning task result of process [%s] for command [%s].", processPid, commandLine));
_logger.trace(
String.format("Returning task result of process [%s] for command [%s].",
processPid, commandLine));
return task.getResult();
}
_logger.trace(String.format("Returning interpretation of process [%s] for command [%s].", processPid, commandLine));
_logger.trace(
String.format("Returning interpretation of process [%s] for command [%s].",
processPid, commandLine));
return interpreter.interpret(ir);
} else {
// null return exitValue apparently
_logger.trace(String.format("Process [%s] for command [%s] exited with value [%s].", processPid, commandLine,
_logger.trace(String.format("Process [%s] for command [%s] exited with value [%s].",
processPid, commandLine,
_process.exitValue()));
return String.valueOf(_process.exitValue());
}
} else {
_logger.warn(String.format("Execution of process [%s] for command [%s] failed.", processPid, commandLine));
_logger.warn(String.format("Execution of process [%s] for command [%s] failed.",
processPid, commandLine));
break;
}
}
} catch (InterruptedException e) {
if (!_isTimeOut) {
_logger.debug(String.format("Exception [%s] occurred; however, it was not a timeout. Therefore, proceeding with the execution of process [%s] for command "
+ "[%s].", e.getMessage(), processPid, commandLine), e);
_logger.debug(String.format(
"Exception [%s] occurred; however, it was not a timeout. Therefore, proceeding with the execution of process [%s] for command [%s].",
e.getMessage(), processPid, commandLine), e);
continue;
}
} finally {
Expand All @@ -317,18 +336,17 @@ public String execute(OutputInterpreter interpreter) {
TimedOutLogger log = new TimedOutLogger(_process);
Task timedoutTask = new Task(log, ir);

_logger.trace(String.format("Running timed out task of process [%s] for command [%s].", processPid, commandLine));
_logger.trace(String.format("Running timed out task of process [%s] for command [%s].", processPid,
commandLine));
timedoutTask.run();
if (!_passwordCommand) {
_logger.warn(String.format("Process [%s] for command [%s] timed out. Output is [%s].", processPid, commandLine, timedoutTask.getResult()));
} else {
_logger.warn(String.format("Process [%s] for command [%s] timed out.", processPid, commandLine));
}
_logger.warn(String.format("Process [%s] for command [%s] timed out. Output is [%s].", processPid,
commandLine, timedoutTask.getResult()));

return ERR_TIMEOUT;
}

_logger.debug(String.format("Exit value of process [%s] for command [%s] is [%s].", processPid, commandLine, _process.exitValue()));
_logger.debug(String.format("Exit value of process [%s] for command [%s] is [%s].", processPid,
commandLine, _process.exitValue()));

BufferedReader reader = new BufferedReader(new InputStreamReader(_process.getInputStream()), 128);

Expand All @@ -339,19 +357,24 @@ public String execute(OutputInterpreter interpreter) {
error = String.valueOf(_process.exitValue());
}

_logger.warn(String.format("Process [%s] for command [%s] encountered the error: [%s].", processPid, commandLine, error));
_logger.warn(String.format("Process [%s] for command [%s] encountered the error: [%s].", processPid,
commandLine, error));

return error;
} catch (SecurityException ex) {
_logger.warn(String.format("Exception [%s] occurred. This may be due to an attempt of executing command [%s] as non root.", ex.getMessage(), commandLine),
_logger.warn(String.format(
"Exception [%s] occurred. This may be due to an attempt of executing command [%s] as non root.",
ex.getMessage(), commandLine),
ex);
return stackTraceAsString(ex);
} catch (Exception ex) {
_logger.warn(String.format("Exception [%s] occurred when attempting to run command [%s].", ex.getMessage(), commandLine), ex);
_logger.warn(String.format("Exception [%s] occurred when attempting to run command [%s].",
ex.getMessage(), commandLine), ex);
return stackTraceAsString(ex);
} finally {
if (_process != null) {
_logger.trace(String.format("Destroying process [%s] for command [%s].", _process.pid(), commandLine));
_logger.trace(
String.format("Destroying process [%s] for command [%s].", _process.pid(), commandLine));
IOUtils.closeQuietly(_process.getErrorStream());
IOUtils.closeQuietly(_process.getOutputStream());
IOUtils.closeQuietly(_process.getInputStream());
Expand All @@ -362,9 +385,10 @@ public String execute(OutputInterpreter interpreter) {

public String executeIgnoreExitValue(OutputInterpreter interpreter, int exitValue) {
String[] command = _command.toArray(new String[_command.size()]);
String commandLine = buildCommandLine(command);

if (_logger.isDebugEnabled()) {
_logger.debug(String.format("Executing: %s", buildCommandLine(command).split(KeyStoreUtils.KS_FILENAME)[0]));
_logger.debug(String.format("Executing: %s", commandLine));
}

try {
Expand All @@ -375,7 +399,7 @@ public String executeIgnoreExitValue(OutputInterpreter interpreter, int exitValu

_process = pb.start();
if (_process == null) {
_logger.warn(String.format("Unable to execute: %s", buildCommandLine(command)));
_logger.warn(String.format("Unable to execute: %s", commandLine));
return String.format("Unable to execute the command: %s", command[0]);
}

Expand Down Expand Up @@ -439,11 +463,8 @@ public String executeIgnoreExitValue(OutputInterpreter interpreter, int exitValu
Task timedoutTask = new Task(log, ir);

timedoutTask.run();
if (!_passwordCommand) {
_logger.warn(String.format("Timed out: %s. Output is: %s", buildCommandLine(command), timedoutTask.getResult()));
} else {
_logger.warn(String.format("Timed out: %s", buildCommandLine(command)));
}
_logger.warn(String.format("Timed out: %s. Output is: %s", commandLine,
timedoutTask.getResult()));

return ERR_TIMEOUT;
}
Expand All @@ -467,7 +488,7 @@ public String executeIgnoreExitValue(OutputInterpreter interpreter, int exitValu
_logger.warn("Security Exception....not running as root?", ex);
return stackTraceAsString(ex);
} catch (Exception ex) {
_logger.warn(String.format("Exception: %s", buildCommandLine(command)), ex);
_logger.warn(String.format("Exception: %s", commandLine), ex);
return stackTraceAsString(ex);
} finally {
if (_process != null) {
Expand Down Expand Up @@ -516,9 +537,9 @@ public void run() {
} catch (Exception ex) {
result = stackTraceAsString(ex);
} finally {
done = true;
notifyAll();
IOUtils.closeQuietly(reader);
done = true;
notifyAll();
IOUtils.closeQuietly(reader);
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions utils/src/test/java/com/cloud/utils/script/ScriptTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,34 @@ public void testGetExecutableAbsolutePath() {
String result = Script.getExecutableAbsolutePath("ls");
Assert.assertTrue(List.of("/usr/bin/ls", "/bin/ls").contains(result));
}

@Test
public void testBuildCommandLineWithSensitiveData() {
Script script = new Script("test.sh");
script.add("normal-arg");
script.addSensitive("sensitive-arg");
String commandLine = script.toString();
Assert.assertEquals("test.sh normal-arg ****** ", commandLine);
}

@Test
public void testBuildCommandLineWithMultipleSensitiveData() {
Script script = new Script("test.sh");
script.add("normal-arg");
script.addSensitive("sensitive-arg1");
script.add("another-normal-arg");
script.addSensitive("sensitive-arg2");
String commandLine = script.toString();
Assert.assertEquals("test.sh normal-arg ****** another-normal-arg ****** ", commandLine);
}

@Test
public void testBuildCommandLineWithLegacyPasswordOption() {
Script script = new Script("test.sh");
script.add("-y");
script.add("legacy-password");
String commandLine = script.toString();
Assert.assertEquals("test.sh -y ****** ", commandLine);
}

}
Loading