Issue #9309 - More comprehensive escaping of command line options

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
Joakim Erdfelt 2023-02-03 10:20:46 -06:00
parent 016de2faeb
commit 45fdebc0fb
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
2 changed files with 46 additions and 34 deletions

View File

@ -19,6 +19,17 @@ import java.util.List;
public class CommandLineBuilder
{
// Matrix of 7-bit characters that needs escaping on command line.
private static final boolean[] NEEDS_ESCAPING = new boolean[127];
static
{
for (char c : " %$\\{}[]()\"|<>&;`!".toCharArray())
{
NEEDS_ESCAPING[c] = true;
}
}
public static File findExecutable(File root, String path)
{
String npath = path.replace('/', File.separatorChar);
@ -59,21 +70,28 @@ public class CommandLineBuilder
*
* @param arg the argument to quote
* @return the quoted and escaped argument
* @deprecated use {@link #escape(String) instead}
*/
@Deprecated
public static String quote(String arg)
{
boolean needsQuoting = (arg.indexOf(' ') >= 0) || (arg.indexOf('"') >= 0);
if (!needsQuoting)
{
return arg;
}
return escape(arg);
}
/**
* Escape the raw string to make it suitable for use on a command line.
*
* @param arg the argument to escape
* @return the escaped argument
*/
public static String escape(String arg)
{
StringBuilder buf = new StringBuilder();
// buf.append('"');
boolean escaped = false;
boolean quoted = false;
for (char c : arg.toCharArray())
{
if (!quoted && !escaped && ((c == '"') || (c == ' ')))
if (!quoted && !escaped && NEEDS_ESCAPING[c])
{
buf.append("\\");
}
@ -85,7 +103,6 @@ public class CommandLineBuilder
escaped = (c == '\\');
buf.append(c);
}
// buf.append('"');
return buf.toString();
}
@ -113,7 +130,7 @@ public class CommandLineBuilder
{
if (arg != null)
{
args.add(quote(arg));
args.add(escape(arg));
}
}
@ -136,11 +153,11 @@ public class CommandLineBuilder
{
if ((value != null) && (value.length() > 0))
{
args.add(quote(name + "=" + value));
args.add(escape(name + "=" + value));
}
else
{
args.add(quote(name));
args.add(escape(name));
}
}
@ -180,7 +197,7 @@ public class CommandLineBuilder
{
buf.append(delim);
}
buf.append(quote(arg));
buf.append(arg); // we assume escaping has occurred during addArg
}
return buf.toString();

View File

@ -21,54 +21,49 @@ import static org.hamcrest.Matchers.is;
public class CommandLineBuilderTest
{
private CommandLineBuilder cmd = new CommandLineBuilder("java");
@BeforeEach
public void setUp()
{
cmd.addEqualsArg("-Djava.io.tmpdir", "/home/java/temp dir/");
cmd.addArg("--version");
}
@Test
public void testSimpleCommandline()
{
CommandLineBuilder cmd = new CommandLineBuilder("java");
cmd.addEqualsArg("-Djava.io.tmpdir", "/home/java/temp dir/");
cmd.addArg("--version");
assertThat(cmd.toString(), is("java -Djava.io.tmpdir=/home/java/temp\\ dir/ --version"));
}
@Test
public void testQuotingSimple()
public void testEscapedSimple()
{
assertQuoting("/opt/jetty", "/opt/jetty");
assertEscaping("/opt/jetty", "/opt/jetty");
}
@Test
public void testQuotingSpaceInPath()
public void testEscapedSpaceInPath()
{
assertQuoting("/opt/jetty 7/home", "/opt/jetty\\ 7/home");
assertEscaping("/opt/jetty 7/home", "/opt/jetty\\ 7/home");
}
@Test
public void testQuotingSpaceAndQuotesInPath()
public void testEscapedSpaceAndQuotesInPath()
{
assertQuoting("/opt/jetty 7 \"special\"/home", "/opt/jetty\\ 7\\ \\\"special\\\"/home");
assertEscaping("/opt/jetty 7 \"special\"/home", "/opt/jetty\\ 7\\ \\\"special\\\"/home");
}
@Test
public void testToStringIsQuotedEvenIfArgsAreNotQuotedForProcessBuilder()
public void testEscapedFormattingString()
{
System.out.println(cmd.toString());
assertEscaping("%{client}a - %u %{dd/MMM/yyyy:HH:mm:ss ZZZ|GMT}t \"%r\" %s %O \"%{Referer}i\" \"%{User-Agent}i\"",
"\\%\\{client\\}a\\ -\\ \\%u\\ \\%\\{dd/MMM/yyyy:HH:mm:ss\\ ZZZ\\|GMT\\}t\\ \\\"\\%r\\\"\\ \\%s\\ \\%O\\ \\\"\\%\\{Referer\\}i\\\"\\ \\\"\\%\\{User-Agent\\}i\\\"");
}
@Test
public void testQuoteQuotationMarks()
public void testEscapeQuotationMarks()
{
assertQuoting("-XX:OnOutOfMemoryError='kill -9 %p'", "-XX:OnOutOfMemoryError='kill -9 %p'");
assertEscaping("-XX:OnOutOfMemoryError='kill -9 %p'", "-XX:OnOutOfMemoryError='kill -9 %p'");
}
private void assertQuoting(String raw, String expected)
private void assertEscaping(String raw, String expected)
{
String actual = CommandLineBuilder.quote(raw);
assertThat("Quoted version of [" + raw + "]", actual, is(expected));
String actual = CommandLineBuilder.escape(raw);
assertThat("Escaped version of [" + raw + "]", actual, is(expected));
}
}