From 2ae49b99ad9a56bb94a9ddd6c8ffc5efaab1d460 Mon Sep 17 00:00:00 2001 From: Greg Gibeling Date: Wed, 11 Feb 2026 17:16:29 -0800 Subject: [PATCH] G2-1857 Use command format for quoting in SSHRunner --- gb-ssh/.gitignore | 2 +- .../com/g2forge/gearbox/ssh/SSHRunner.java | 4 +++- .../g2forge/gearbox/ssh/TestSSHRunner.java | 22 +++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/gb-ssh/.gitignore b/gb-ssh/.gitignore index 1e09185..66210f0 100644 --- a/gb-ssh/.gitignore +++ b/gb-ssh/.gitignore @@ -3,4 +3,4 @@ /.project /.classpath /.factorypath -/test.properties +/*.properties diff --git a/gb-ssh/src/main/java/com/g2forge/gearbox/ssh/SSHRunner.java b/gb-ssh/src/main/java/com/g2forge/gearbox/ssh/SSHRunner.java index 092b4f6..ccc2166 100644 --- a/gb-ssh/src/main/java/com/g2forge/gearbox/ssh/SSHRunner.java +++ b/gb-ssh/src/main/java/com/g2forge/gearbox/ssh/SSHRunner.java @@ -17,6 +17,7 @@ import com.g2forge.alexandria.annotations.note.NoteType; import com.g2forge.alexandria.command.invocation.CommandInvocation; import com.g2forge.alexandria.command.invocation.environment.SystemEnvironment; +import com.g2forge.alexandria.command.invocation.format.ICommandFormat; import com.g2forge.alexandria.java.close.ICloseable; import com.g2forge.alexandria.java.core.error.NotYetImplementedError; import com.g2forge.alexandria.java.io.RuntimeIOException; @@ -69,7 +70,8 @@ public IProcess apply(CommandInvocation commandInvocation) ChannelExec _channel = null; Throwable _launchException = null; try { - final String command = commandInvocation.getArguments().stream().collect(Collectors.joining(" ")); + final ICommandFormat format = commandInvocation.getFormat(); + final String command = commandInvocation.getArguments().stream().map(format::quote).collect(Collectors.joining(" ")); _channel = session.createExecChannel(command); if (!_channel.open().await()) throw new RuntimeIOException(); } catch (Throwable throwable) { diff --git a/gb-ssh/src/test/java/com/g2forge/gearbox/ssh/TestSSHRunner.java b/gb-ssh/src/test/java/com/g2forge/gearbox/ssh/TestSSHRunner.java index 2d5a379..1e686c6 100644 --- a/gb-ssh/src/test/java/com/g2forge/gearbox/ssh/TestSSHRunner.java +++ b/gb-ssh/src/test/java/com/g2forge/gearbox/ssh/TestSSHRunner.java @@ -2,6 +2,10 @@ import java.nio.file.Paths; import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.After; import org.junit.Test; @@ -14,9 +18,11 @@ import com.g2forge.alexandria.wizard.PropertyStringInput; import com.g2forge.alexandria.wizard.UserStringInput; import com.g2forge.gearbox.command.converter.ICommandConverterR_; +import com.g2forge.gearbox.command.converter.dumb.Command; import com.g2forge.gearbox.command.converter.dumb.DumbCommandConverter; import com.g2forge.gearbox.command.process.IProcess; import com.g2forge.gearbox.command.process.redirect.IRedirect; +import com.g2forge.gearbox.command.proxy.method.ICommandInterface; import com.g2forge.gearbox.command.test.ATestCommand; public class TestSSHRunner extends ATestCommand { @@ -55,6 +61,22 @@ public void hostname() { HAssert.assertEquals(hostname, getUtils().echo(false, "${HOSTNAME}").trim()); } + @Test + public void quoting() { + HAssume.assumeNotNull(TestSSH.getConfig()); + final String executable = HAssume.assumeNoException(new PropertyStringInput("sshtest.clireport").fallback(new UserStringInput("SSH Test CLIReport executable", false))); + final ICLIReport clireport = getFactory().apply(ICLIReport.class); + + final List lines = new ArrayList<>(clireport.clireport(executable, "A", "B C").map(String::strip).collect(Collectors.toList())); + lines.remove(1); // Remove the executable itself from the output, since it might be in a weird path during testing + HAssert.assertEquals("CLIReport: 3 arguments\n0001: A\n0003: B C", lines.stream().collect(Collectors.joining("\n"))); + } + + public interface ICLIReport extends ICommandInterface { + @Command({}) + public Stream clireport(String executable, String... args); + } + protected boolean isValid() { return TestSSH.getConfig() != null; }