/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.utils.common.security;
import static de.rcenvironment.core.utils.common.security.StringSubstitutionSecurityUtils.isSafeForSubstitutionInsideDoubleQuotes;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.common.security.StringSubstitutionSecurityUtils.SubstitutionContext;
/**
* Unit test for {@link StringSubstitutionSecurityUtils}.
*
* @author Robert Mischke
*/
public class StringSubstitutionSecurityUtilsTest {
private static final String SIMPLE_TEST_STRING = "test";
private static final String FANCY_TEST_STRING = " te_34 st (23) + ";
private static final String SINGLE_QUOTE = "'";
/**
* Test cleanup.
*/
@After
public void tearDown() {
// reset test flag
StringSubstitutionSecurityUtils.setSuppressLogMessageOnDeniedSubstitution(false);
}
/**
* Test proper rejection of null parameters.
*/
@Test
public void testNullParameters() {
// null string
for (SubstitutionContext context : SubstitutionContext.values()) {
try {
isSafeForSubstitutionInsideDoubleQuotes(null, context);
fail("Exception expected for null string");
} catch (NullPointerException e) {
assertTrue(true);
}
}
// null context
try {
isSafeForSubstitutionInsideDoubleQuotes(SIMPLE_TEST_STRING, null);
fail("Exception expected for null context");
} catch (NullPointerException e) {
assertTrue(true);
}
}
/**
* Test context-independent cases.
*/
@Test
public void testSharedCases() {
for (SubstitutionContext context : SubstitutionContext.values()) {
testIsSafeForSubstitution(true, "", context);
testIsSafeForSubstitution(true, SIMPLE_TEST_STRING, context);
// test method sanity checks
testSubstringInStartMiddleAndEnd(true, "x", context);
testSubstringInStartMiddleAndEnd(true, " ", context);
testSubstringInStartMiddleAndEnd(true, "0", context);
testSubstringInStartMiddleAndEnd(false, "\"", context);
testSubstringInStartMiddleAndEnd(false, "\\", context);
testSubstringInStartMiddleAndEnd(false, "/", context);
testSubstringInStartMiddleAndEnd(false, "*", context);
testSubstringInStartMiddleAndEnd(false, "?", context);
testSubstringInStartMiddleAndEnd(false, "\n", context);
testSubstringInStartMiddleAndEnd(false, "\r", context);
testSubstringInStartMiddleAndEnd(false, "\rn", context);
testSubstringInStartMiddleAndEnd(false, "\t", context);
testSubstringInStartMiddleAndEnd(false, "\0", context);
testSubstringInStartMiddleAndEnd(false, "" + '\u0000', context); // using char instead of string to rule out typos
testSubstringInStartMiddleAndEnd(false, "" + '\u001f', context); // using char instead of string to rule out typos
}
}
/**
* Test patterns that are only forbidden in Windows batch files.
*/
@Test
public void testWindowsBatchSpecificCases() {
testSubstringInStartMiddleAndEnd(false, "%", SubstitutionContext.WINDOWS_BATCH);
// single quote is only forbidden in Jython context
testSubstringInStartMiddleAndEnd(true, SINGLE_QUOTE, SubstitutionContext.WINDOWS_BATCH);
}
/**
* Test patterns that are only forbidden in Linux bash scripts.
*/
@Test
public void testLinuxBashSpecificCases() {
// backtick character
testSubstringInStartMiddleAndEnd(false, "" + '\u0060', SubstitutionContext.LINUX_BASH); // char instead of string to rule out typos
testSubstringInStartMiddleAndEnd(false, "$", SubstitutionContext.LINUX_BASH);
testSubstringInStartMiddleAndEnd(false, "${", SubstitutionContext.LINUX_BASH);
testSubstringInStartMiddleAndEnd(true, "{", SubstitutionContext.LINUX_BASH);
testSubstringInStartMiddleAndEnd(true, "}", SubstitutionContext.LINUX_BASH);
testSubstringInStartMiddleAndEnd(false, "$(", SubstitutionContext.LINUX_BASH);
// single quote is only forbidden in Jython context
testSubstringInStartMiddleAndEnd(true, SINGLE_QUOTE, SubstitutionContext.LINUX_BASH);
}
/**
* Test patterns that are only forbidden in Jython scripts.
*/
@Test
public void testJythonSpecificCases() {
testSubstringInStartMiddleAndEnd(false, SINGLE_QUOTE, SubstitutionContext.JYTHON);
}
private void testSubstringInStartMiddleAndEnd(boolean expected, String substring, SubstitutionContext testContext) {
testIsSafeForSubstitution(expected, substring + SIMPLE_TEST_STRING, testContext);
testIsSafeForSubstitution(expected, substring + FANCY_TEST_STRING, testContext);
testIsSafeForSubstitution(expected, "pre" + substring + "post", testContext);
testIsSafeForSubstitution(expected, FANCY_TEST_STRING + substring + FANCY_TEST_STRING, testContext);
testIsSafeForSubstitution(expected, SIMPLE_TEST_STRING + substring, testContext);
testIsSafeForSubstitution(expected, FANCY_TEST_STRING + substring, testContext);
}
private void testIsSafeForSubstitution(boolean expected, String testString, SubstitutionContext testContext) {
// suppress "denied substitution" message if this is the correct and expected result
StringSubstitutionSecurityUtils.setSuppressLogMessageOnDeniedSubstitution(!expected);
boolean result = isSafeForSubstitutionInsideDoubleQuotes(testString, testContext);
StringSubstitutionSecurityUtils.setSuppressLogMessageOnDeniedSubstitution(false);
if (expected != result) {
// only build message on failure
Assert.fail(StringUtils.format("Test case \"%s\" failed in %s context: expected '%s', received '%s'",
testString, testContext, expected, result));
}
}
}