/* * The MIT License * * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.util; import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.junit.Ignore; import org.junit.Test; import org.jvnet.hudson.test.Issue; public class ArgumentListBuilderTest { @Test public void assertEmptyMask() { ArgumentListBuilder builder = new ArgumentListBuilder(); builder.add("arg"); builder.add("other", "arguments"); assertFalse("There should not be any masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertThat("The mask array was incorrect", array, is(new boolean[] { false, false, false })); } @Test public void assertLastArgumentIsMasked() { ArgumentListBuilder builder = new ArgumentListBuilder(); builder.add("arg"); builder.addMasked("ismasked"); assertTrue("There should be masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertThat("The mask array was incorrect", array, is(new boolean[] { false, true })); } @Test public void assertSeveralMaskedArguments() { ArgumentListBuilder builder = new ArgumentListBuilder(); builder.add("arg"); builder.addMasked("ismasked"); builder.add("non masked arg"); builder.addMasked("ismasked2"); assertTrue("There should be masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertThat("The mask array was incorrect", array, is(new boolean[] { false, true, false, true })); } @Test public void assertPrependAfterAddingMasked() { ArgumentListBuilder builder = new ArgumentListBuilder(); builder.addMasked("ismasked"); builder.add("arg"); builder.prepend("first", "second"); assertTrue("There should be masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertThat("The mask array was incorrect", array, is(new boolean[] { false, false, true, false })); } @Test public void assertPrependBeforeAddingMasked() { ArgumentListBuilder builder = new ArgumentListBuilder(); builder.prepend("first", "second"); builder.addMasked("ismasked"); builder.add("arg"); assertTrue("There should be masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertThat("The mask array was incorrect", array, is(new boolean[] { false, false, true, false })); } @Test public void testToWindowsCommand() { ArgumentListBuilder builder = new ArgumentListBuilder(). add("ant.bat").add("-Dfoo1=abc"). // nothing special, no quotes add("-Dfoo2=foo bar").add("-Dfoo3=/u*r").add("-Dfoo4=/us?"). // add quotes add("-Dfoo10=bar,baz"). add("-Dfoo5=foo;bar^baz").add("-Dfoo6=<xml>&here;</xml>"). // add quotes add("-Dfoo7=foo|bar\"baz"). // add quotes and "" for " add("-Dfoo8=% %QED% %comspec% %-%(%.%"). // add quotes, and extra quotes for %Q and %c add("-Dfoo9=%'''%%@%"); // no quotes as none of the % are followed by a letter // By default, does not escape %VAR% assertThat(builder.toWindowsCommand().toCommandArray(), is(new String[] { "cmd.exe", "/C", "\"ant.bat", "-Dfoo1=abc", "\"-Dfoo2=foo bar\"", "\"-Dfoo3=/u*r\"", "\"-Dfoo4=/us?\"", "\"-Dfoo10=bar,baz\"", "\"-Dfoo5=foo;bar^baz\"", "\"-Dfoo6=<xml>&here;</xml>\"", "\"-Dfoo7=foo|bar\"\"baz\"", "\"-Dfoo8=% %QED% %comspec% %-%(%.%\"", "-Dfoo9=%'''%%@%", "&&", "exit", "%%ERRORLEVEL%%\"" })); // Pass flag to escape %VAR% assertThat(builder.toWindowsCommand(true).toCommandArray(), is(new String[] { "cmd.exe", "/C", "\"ant.bat", "-Dfoo1=abc", "\"-Dfoo2=foo bar\"", "\"-Dfoo3=/u*r\"", "\"-Dfoo4=/us?\"", "\"-Dfoo10=bar,baz\"", "\"-Dfoo5=foo;bar^baz\"", "\"-Dfoo6=<xml>&here;</xml>\"", "\"-Dfoo7=foo|bar\"\"baz\"", "\"-Dfoo8=% %\"Q\"ED% %\"c\"omspec% %-%(%.%\"", "-Dfoo9=%'''%%@%", "&&", "exit", "%%ERRORLEVEL%%\"" })); // Try to hide password builder.add("-Dpassword=hidden", true); // By default, does not escape %VAR% assertThat(builder.toWindowsCommand().toString(), is("cmd.exe /C \"ant.bat -Dfoo1=abc \"\"-Dfoo2=foo bar\"\" \"-Dfoo3=/u*r\" \"-Dfoo4=/us?\" \"-Dfoo10=bar,baz\" \"-Dfoo5=foo;bar^baz\" \"-Dfoo6=<xml>&here;</xml>\" \"-Dfoo7=foo|bar\"\"baz\" \"\"-Dfoo8=% %QED% %comspec% %-%(%.%\"\" -Dfoo9=%'''%%@% ****** && exit %%ERRORLEVEL%%\"" )); // Pass flag to escape %VAR% assertThat(builder.toWindowsCommand(true).toString(), is("cmd.exe /C \"ant.bat -Dfoo1=abc \"\"-Dfoo2=foo bar\"\" \"-Dfoo3=/u*r\" \"-Dfoo4=/us?\" \"-Dfoo10=bar,baz\" \"-Dfoo5=foo;bar^baz\" \"-Dfoo6=<xml>&here;</xml>\" \"-Dfoo7=foo|bar\"\"baz\" \"\"-Dfoo8=% %\"Q\"ED% %\"c\"omspec% %-%(%.%\"\" -Dfoo9=%'''%%@% ****** && exit %%ERRORLEVEL%%\"")); } @Test @Ignore("It's only for reproduce JENKINS-28790 issue. It's added to testToWindowsCommand") @Issue("JENKINS-28790") public void testToWindowsCommandMasked() { ArgumentListBuilder builder = new ArgumentListBuilder(). add("ant.bat").add("-Dfoo1=abc"). // nothing special, no quotes add("-Dfoo2=foo bar").add("-Dfoo3=/u*r").add("-Dfoo4=/us?"). // add quotes add("-Dfoo10=bar,baz"). add("-Dfoo5=foo;bar^baz").add("-Dfoo6=<xml>&here;</xml>"). // add quotes add("-Dfoo7=foo|bar\"baz"). // add quotes and "" for " add("-Dfoo8=% %QED% %comspec% %-%(%.%"). // add quotes, and extra quotes for %Q and %c add("-Dfoo9=%'''%%@%"). // no quotes as none of the % are followed by a letter add("-Dpassword=hidden", true); // By default, does not escape %VAR% assertThat(builder.toWindowsCommand().toString(), is("cmd.exe /C \"ant.bat -Dfoo1=abc \"\"-Dfoo2=foo bar\"\" \"-Dfoo3=/u*r\" \"-Dfoo4=/us?\" \"-Dfoo10=bar,baz\" \"-Dfoo5=foo;bar^baz\" \"-Dfoo6=<xml>&here;</xml>\" \"-Dfoo7=foo|bar\"\"baz\" \"\"-Dfoo8=% %QED% %comspec% %-%(%.%\"\" -Dfoo9=%'''%%@% ****** && exit %%ERRORLEVEL%%\"" )); // Pass flag to escape %VAR% assertThat(builder.toWindowsCommand(true).toString(), is("cmd.exe /C \"ant.bat -Dfoo1=abc \"\"-Dfoo2=foo bar\"\" \"-Dfoo3=/u*r\" \"-Dfoo4=/us?\" \"-Dfoo10=bar,baz\" \"-Dfoo5=foo;bar^baz\" \"-Dfoo6=<xml>&here;</xml>\" \"-Dfoo7=foo|bar\"\"baz\" \"\"-Dfoo8=% %\"Q\"ED% %\"c\"omspec% %-%(%.%\"\" -Dfoo9=%'''%%@% ****** && exit %%ERRORLEVEL%%\"")); } @Test public void assertMaskOnClone() { ArgumentListBuilder builder = new ArgumentListBuilder(); builder.add("arg1"); builder.addMasked("masked1"); builder.add("arg2"); ArgumentListBuilder clone = builder.clone(); assertTrue("There should be masked arguments", clone.hasMaskedArguments()); boolean[] array = clone.toMaskArray(); assertNotNull("The mask array should not be null", array); assertThat("The mask array was incorrect", array, is(builder.toMaskArray())); } private static final Map<String, String> KEY_VALUES = new HashMap<String, String>() {{ put("key1", "value1"); put("key2", "value2"); put("key3", "value3"); }}; private static final Set<String> MASKS = new HashSet<String>() {{ add("key2"); }}; @Test public void assertKeyValuePairsWithMask() { ArgumentListBuilder builder = new ArgumentListBuilder(); builder.addKeyValuePairs(null, KEY_VALUES, MASKS); assertTrue("There should be masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertThat("The mask array was incorrect", array, is(new boolean[] { false, true, false })); } @Test public void assertKeyValuePairs() { ArgumentListBuilder builder = new ArgumentListBuilder(); builder.addKeyValuePairs(null, KEY_VALUES); assertFalse("There should not be any masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertThat("The mask array was incorrect", array, is(new boolean[] { false, false, false })); } @Test public void addKeyValuePairsFromPropertyString() throws IOException { final Map<String, String> map = new HashMap<String, String>(); map.put("PATH", "C:\\Windows"); final VariableResolver<String> resolver = new VariableResolver.ByMap<String>(map); final String properties = "my.path=$PATH"; ArgumentListBuilder builder = new ArgumentListBuilder(); builder.addKeyValuePairsFromPropertyString("", properties, resolver); assertEquals("my.path=C:\\Windows", builder.toString()); builder = new ArgumentListBuilder(); builder.addKeyValuePairsFromPropertyString("", properties, resolver, null); assertEquals("my.path=C:\\Windows", builder.toString()); } @Test public void numberOfBackslashesInPropertiesShouldBePreservedAfterMacroExpansion() throws IOException { final Map<String, String> map = new HashMap<String, String>(); map.put("ONE", "one\\backslash"); map.put("TWO", "two\\\\backslashes"); map.put("FOUR", "four\\\\\\\\backslashes"); final String properties = new StringBuilder() .append("one=$ONE\n") .append("two=$TWO\n") .append("four=$FOUR\n") .toString() ; final String args = new ArgumentListBuilder() .addKeyValuePairsFromPropertyString("", properties, new VariableResolver.ByMap<String>(map)) .toString() ; assertThat(args, containsString("one=one\\backslash")); assertThat(args, containsString("two=two\\\\backslashes")); assertThat(args, containsString("four=four\\\\\\\\backslashes")); } }