/* * Copyright 2014 Attila Szegedi, Daniel Dekany, Jonathan Revusky * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package freemarker.template.utility; import static org.junit.Assert.*; import java.io.IOException; import java.io.StringWriter; import java.util.regex.Pattern; import org.hamcrest.Matchers; import org.junit.Test; public class StringUtilTest { @Test public void testV2319() { assertEquals("\\n\\r\\f\\b\\t\\x00\\x19", StringUtil.javaScriptStringEnc("\n\r\f\b\t\u0000\u0019")); } @Test public void testControlChars() { assertEsc( "\n\r\f\b\t \u0000\u0019\u001F \u007F\u0080\u009F \u2028\u2029", "\\n\\r\\f\\b\\t \\x00\\x19\\x1F \\x7F\\x80\\x9F \\u2028\\u2029", "\\n\\r\\f\\b\\t \\u0000\\u0019\\u001F \\u007F\\u0080\\u009F \\u2028\\u2029"); } @Test public void testHtmlChars() { assertEsc( "<safe>/>->]> </foo> <!-- --> <![CDATA[ ]]> <?php?>", "<safe>/>->]> <\\/foo> \\x3C!-- --\\> \\x3C![CDATA[ ]]\\> \\x3C?php?>", "<safe>/>->]> <\\/foo> \\u003C!-- --\\u003E \\u003C![CDATA[ ]]\\u003E \\u003C?php?>"); assertEsc("<!c", "\\x3C!c", "\\u003C!c"); assertEsc("c<!", "c\\x3C!", "c\\u003C!"); assertEsc("c<", "c\\x3C", "c\\u003C"); assertEsc("c<c", "c<c", "c<c"); assertEsc("<c", "<c", "<c"); assertEsc(">", "\\>", "\\u003E"); assertEsc("->", "-\\>", "-\\u003E"); assertEsc("-->", "--\\>", "--\\u003E"); assertEsc("c-->", "c--\\>", "c--\\u003E"); assertEsc("-->c", "--\\>c", "--\\u003Ec"); assertEsc("]>", "]\\>", "]\\u003E"); assertEsc("]]>", "]]\\>", "]]\\u003E"); assertEsc("c]]>", "c]]\\>", "c]]\\u003E"); assertEsc("]]>c", "]]\\>c", "]]\\u003Ec"); assertEsc("c->", "c->", "c->"); assertEsc("c>", "c>", "c>"); assertEsc("-->", "--\\>", "--\\u003E"); assertEsc("/", "\\/", "\\/"); assertEsc("/c", "\\/c", "\\/c"); assertEsc("</", "<\\/", "<\\/"); assertEsc("</c", "<\\/c", "<\\/c"); assertEsc("c/", "c/", "c/"); } @Test public void testJSChars() { assertEsc("\"", "\\\"", "\\\""); assertEsc("'", "\\'", "'"); assertEsc("\\", "\\\\", "\\\\"); } @Test public void testSameStringsReturned() { String s = "==> I/m <safe>!"; assertTrue(s == StringUtil.jsStringEnc(s, false)); // "==" because is must return the same object assertTrue(s == StringUtil.jsStringEnc(s, true)); s = ""; assertTrue(s == StringUtil.jsStringEnc(s, false)); assertTrue(s == StringUtil.jsStringEnc(s, true)); s = "\u00E1rv\u00EDzt\u0171r\u0151 \u3020"; assertEquals(s, StringUtil.jsStringEnc(s, false)); assertTrue(s == StringUtil.jsStringEnc(s, false)); assertTrue(s == StringUtil.jsStringEnc(s, true)); } @Test public void testOneOffs() { assertEsc("c\"c\"cc\"\"c", "c\\\"c\\\"cc\\\"\\\"c", "c\\\"c\\\"cc\\\"\\\"c"); assertEsc("\"c\"cc\"", "\\\"c\\\"cc\\\"", "\\\"c\\\"cc\\\""); assertEsc("c/c/cc//c", "c/c/cc//c", "c/c/cc//c"); assertEsc("c<c<cc<<c", "c<c<cc<<c", "c<c<cc<<c"); assertEsc("/<", "\\/\\x3C", "\\/\\u003C"); assertEsc(">", "\\>", "\\u003E"); assertEsc("]>", "]\\>", "]\\u003E"); assertEsc("->", "-\\>", "-\\u003E"); } @Test public void testFTLEscaping() { assertFTLEsc("", "", "", "", "\"\""); assertFTLEsc("\'", "\\'", "'", "\\'", "\"'\""); assertFTLEsc("\"", "\\\"", "\\\"", "\"", "'\"'"); assertFTLEsc("\"", "\\\"", "\\\"", "\"", "'\"'"); assertFTLEsc("foo", "foo", "foo", "foo", "\"foo\""); assertFTLEsc("foo's", "foo\\'s", "foo's", "foo\\'s", "\"foo's\""); assertFTLEsc("foo \"", "foo \\\"", "foo \\\"", "foo \"", "'foo \"'"); assertFTLEsc("foo's \"", "foo\\'s \\\"", "foo's \\\"", "foo\\'s \"", "\"foo's \\\"\""); assertFTLEsc("foo\nb\u0000c", "foo\\nb\\x0000c", "foo\\nb\\x0000c", "foo\\nb\\x0000c", "\"foo\\nb\\x0000c\""); } private void assertEsc(String s, String javaScript, String json) { assertEquals(javaScript, StringUtil.jsStringEnc(s, false)); assertEquals(json, StringUtil.jsStringEnc(s, true)); } private void assertFTLEsc(String s, String partAny, String partQuot, String partApos, String quoted) { assertEquals(partAny, StringUtil.FTLStringLiteralEnc(s)); assertEquals(partQuot, StringUtil.FTLStringLiteralEnc(s, '\"')); assertEquals(partApos, StringUtil.FTLStringLiteralEnc(s, '\'')); assertEquals(quoted, StringUtil.ftlQuote(s)); } @Test public void testTrim() { assertSame(CollectionUtils.EMPTY_CHAR_ARRAY, StringUtil.trim(CollectionUtils.EMPTY_CHAR_ARRAY)); assertSame(CollectionUtils.EMPTY_CHAR_ARRAY, StringUtil.trim(" \t\u0001 ".toCharArray())); { char[] cs = "foo".toCharArray(); assertSame(cs, cs); } assertArrayEquals("foo".toCharArray(), StringUtil.trim("foo ".toCharArray())); assertArrayEquals("foo".toCharArray(), StringUtil.trim(" foo".toCharArray())); assertArrayEquals("foo".toCharArray(), StringUtil.trim(" foo ".toCharArray())); assertArrayEquals("foo".toCharArray(), StringUtil.trim("\t\tfoo \r\n".toCharArray())); assertArrayEquals("x".toCharArray(), StringUtil.trim(" x ".toCharArray())); assertArrayEquals("x y z".toCharArray(), StringUtil.trim(" x y z ".toCharArray())); } @Test public void testIsTrimmedToEmpty() { assertTrue(StringUtil.isTrimmableToEmpty("".toCharArray())); assertTrue(StringUtil.isTrimmableToEmpty("\r\r\n\u0001".toCharArray())); assertFalse(StringUtil.isTrimmableToEmpty("x".toCharArray())); assertFalse(StringUtil.isTrimmableToEmpty(" x ".toCharArray())); } @Test public void testJQuote() { assertEquals("null", StringUtil.jQuote(null)); assertEquals("\"foo\"", StringUtil.jQuote("foo")); assertEquals("\"123\"", StringUtil.jQuote(Integer.valueOf(123))); assertEquals("\"foo's \\\"bar\\\"\"", StringUtil.jQuote("foo's \"bar\"")); assertEquals("\"\\n\\r\\t\\u0001\"", StringUtil.jQuote("\n\r\t\u0001")); assertEquals("\"<\\nb\\rc\\td\\u0001>\"", StringUtil.jQuote("<\nb\rc\td\u0001>")); } @Test public void testJQuoteNoXSS() { assertEquals("null", StringUtil.jQuoteNoXSS(null)); assertEquals("\"foo\"", StringUtil.jQuoteNoXSS("foo")); assertEquals("\"123\"", StringUtil.jQuoteNoXSS(Integer.valueOf(123))); assertEquals("\"foo's \\\"bar\\\"\"", StringUtil.jQuoteNoXSS("foo's \"bar\"")); assertEquals("\"\\n\\r\\t\\u0001\"", StringUtil.jQuoteNoXSS("\n\r\t\u0001")); assertEquals("\"\\u003C\\nb\\rc\\td\\u0001>\"", StringUtil.jQuoteNoXSS("<\nb\rc\td\u0001>")); assertEquals("\"\\u003C\\nb\\rc\\td\\u0001>\"", StringUtil.jQuoteNoXSS((Object) "<\nb\rc\td\u0001>")); } @Test public void testFTLStringLiteralEnc() { assertEquals("", StringUtil.FTLStringLiteralEnc("")); assertEquals("abc", StringUtil.FTLStringLiteralEnc("abc")); assertEquals("{", StringUtil.FTLStringLiteralEnc("{")); assertEquals("a{b}c", StringUtil.FTLStringLiteralEnc("a{b}c")); assertEquals("a#b", StringUtil.FTLStringLiteralEnc("a#b")); assertEquals("a$b", StringUtil.FTLStringLiteralEnc("a$b")); assertEquals("a#\\{b}c", StringUtil.FTLStringLiteralEnc("a#{b}c")); assertEquals("a$\\{b}c", StringUtil.FTLStringLiteralEnc("a${b}c")); assertEquals("a'c\\\"d", StringUtil.FTLStringLiteralEnc("a'c\"d", '"')); assertEquals("a\\'c\"d", StringUtil.FTLStringLiteralEnc("a'c\"d", '\'')); assertEquals("a\\'c\"d", StringUtil.FTLStringLiteralEnc("a'c\"d", '\'')); assertEquals("\\n\\r\\t\\f\\x0002\\\\", StringUtil.FTLStringLiteralEnc("\n\r\t\f\u0002\\")); assertEquals("\\l\\g\\a", StringUtil.FTLStringLiteralEnc("<>&")); } @Test public void testGlobToRegularExpression() { assertGlobMatches("a/b/c.ftl", "a/b/c.ftl"); assertGlobDoesNotMatch("/a/b/cxftl", "/a/b/c.ftl", "a/b/C.ftl"); assertGlobMatches("a/b/*.ftl", "a/b/.ftl", "a/b/x.ftl", "a/b/xx.ftl"); assertGlobDoesNotMatch("a/b/*.ftl", "a/c/x.ftl", "a/b/c/x.ftl", "/a/b/x.ftl", "a/b/xxftl"); assertGlobMatches("a/b/?.ftl", "a/b/x.ftl"); assertGlobDoesNotMatch("a/b/?.ftl", "a/c/x.ftl", "a/b/.ftl", "a/b/xx.ftl", "a/b/xxftl"); assertGlobMatches("a/**/c.ftl", "a/b/c.ftl", "a/c.ftl", "a/b/b2/b3/c.ftl", "a//c.ftl"); assertGlobDoesNotMatch("a/**/c.ftl", "x/b/c.ftl", "a/b/x.ftl"); assertGlobMatches("**/c.ftl", "a/b/c.ftl", "c.ftl", "/c.ftl", "///c.ftl"); assertGlobDoesNotMatch("**/c.ftl", "a/b/x.ftl"); assertGlobMatches("a/b/**", "a/b/c.ftl", "a/b/c2/c.ftl", "a/b/", "a/b/c/"); assertGlobDoesNotMatch("a/b.ftl"); assertGlobMatches("**", "a/b/c.ftl", ""); assertGlobMatches("\\[\\{\\*\\?\\}\\]\\\\", "[{*?}]\\"); assertGlobDoesNotMatch("\\[\\{\\*\\?\\}\\]\\\\", "[{xx}]\\"); assertGlobMatches("a/b/\\?.ftl", "a/b/?.ftl"); assertGlobDoesNotMatch("a/b/\\?.ftl", "a/b/x.ftl"); assertGlobMatches("\\?\\?.ftl", "??.ftl"); assertGlobMatches("\\\\\\\\", "\\\\"); assertGlobMatches("\\\\\\\\?", "\\\\x"); assertGlobMatches("x\\", "x"); assertGlobMatches("???*", "123", "1234", "12345"); assertGlobDoesNotMatch("???*", "12", "1", ""); assertGlobMatches("**/a??/b*.ftl", "a11/b1.ftl", "x/a11/b123.ftl", "x/y/a11/b.ftl"); assertGlobDoesNotMatch("**/a??/b*.ftl", "a1/b1.ftl", "x/a11/c123.ftl"); assertFalse(StringUtil.globToRegularExpression("ab*").matcher("aBc").matches()); assertTrue(StringUtil.globToRegularExpression("ab*", true).matcher("aBc").matches()); assertTrue(StringUtil.globToRegularExpression("ab", true).matcher("aB").matches()); assertTrue(StringUtil.globToRegularExpression("\u00E1b*", true).matcher("\u00C1bc").matches()); try { StringUtil.globToRegularExpression("x**/y"); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), Matchers.containsString("**")); } try { StringUtil.globToRegularExpression("**y"); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), Matchers.containsString("**")); } try { StringUtil.globToRegularExpression("[ab]c"); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), Matchers.containsString("unsupported")); } try { StringUtil.globToRegularExpression("{aa,bb}c"); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), Matchers.containsString("unsupported")); } } private void assertGlobMatches(String glob, String... ss) { Pattern pattern = StringUtil.globToRegularExpression(glob); for (String s : ss) { if (!pattern.matcher(s).matches()) { fail("Glob " + glob + " (regexp: " + pattern + ") doesn't match " + s); } } } private void assertGlobDoesNotMatch(String glob, String... ss) { Pattern pattern = StringUtil.globToRegularExpression(glob); for (String s : ss) { if (pattern.matcher(s).matches()) { fail("Glob " + glob + " (regexp: " + pattern + ") matches " + s); } } } @Test public void testHTMLEnc() { String s = ""; assertSame(s, StringUtil.HTMLEnc(s)); s = "asd"; assertSame(s, StringUtil.HTMLEnc(s)); assertEquals("a&b<c>d"e'f", StringUtil.HTMLEnc("a&b<c>d\"e'f")); assertEquals("<", StringUtil.HTMLEnc("<")); assertEquals("<a", StringUtil.HTMLEnc("<a")); assertEquals("<a>", StringUtil.HTMLEnc("<a>")); assertEquals("a>", StringUtil.HTMLEnc("a>")); assertEquals("<>", StringUtil.HTMLEnc("<>")); assertEquals("a<>b", StringUtil.HTMLEnc("a<>b")); } @Test public void testXHTMLEnc() throws IOException { String s = ""; assertSame(s, StringUtil.XHTMLEnc(s)); s = "asd"; assertSame(s, StringUtil.XHTMLEnc(s)); testXHTMLEnc("a&b<c>d"e'f", "a&b<c>d\"e'f"); testXHTMLEnc("<", "<"); testXHTMLEnc("<a", "<a"); testXHTMLEnc("<a>", "<a>"); testXHTMLEnc("a>", "a>"); testXHTMLEnc("<>", "<>"); testXHTMLEnc("a<>b", "a<>b"); } private void testXHTMLEnc(String expected, String in) throws IOException { assertEquals(expected, StringUtil.XHTMLEnc(in)); StringWriter sw = new StringWriter(); StringUtil.XHTMLEnc(in, sw); assertEquals(expected, sw.toString()); } @Test public void testXMLEnc() throws IOException { String s = ""; assertSame(s, StringUtil.XMLEnc(s)); s = "asd"; assertSame(s, StringUtil.XMLEnc(s)); testXMLEnc("a&b<c>d"e'f", "a&b<c>d\"e'f"); testXMLEnc("<", "<"); testXMLEnc("<a", "<a"); testXMLEnc("<a>", "<a>"); testXMLEnc("a>", "a>"); testXMLEnc("<>", "<>"); testXMLEnc("a<>b", "a<>b"); } private void testXMLEnc(String expected, String in) throws IOException { assertEquals(expected, StringUtil.XMLEnc(in)); StringWriter sw = new StringWriter(); StringUtil.XMLEnc(in, sw); assertEquals(expected, sw.toString()); } @Test public void testXMLEncQAttr() throws IOException { String s = ""; assertSame(s, StringUtil.XMLEncQAttr(s)); s = "asd"; assertSame(s, StringUtil.XMLEncQAttr(s)); assertEquals("a&b<c>d"e'f", StringUtil.XMLEncQAttr("a&b<c>d\"e'f")); assertEquals("<", StringUtil.XMLEncQAttr("<")); assertEquals("<a", StringUtil.XMLEncQAttr("<a")); assertEquals("<a>", StringUtil.XMLEncQAttr("<a>")); assertEquals("a>", StringUtil.XMLEncQAttr("a>")); assertEquals("<>", StringUtil.XMLEncQAttr("<>")); assertEquals("a<>b", StringUtil.XMLEncQAttr("a<>b")); } @Test public void testXMLEncNQG() throws IOException { String s = ""; assertSame(s, StringUtil.XMLEncNQG(s)); s = "asd"; assertSame(s, StringUtil.XMLEncNQG(s)); assertEquals("a&b<c>d\"e'f", StringUtil.XMLEncNQG("a&b<c>d\"e'f")); assertEquals("<", StringUtil.XMLEncNQG("<")); assertEquals("<a", StringUtil.XMLEncNQG("<a")); assertEquals("<a>", StringUtil.XMLEncNQG("<a>")); assertEquals("a>", StringUtil.XMLEncNQG("a>")); assertEquals("<>", StringUtil.XMLEncNQG("<>")); assertEquals("a<>b", StringUtil.XMLEncNQG("a<>b")); assertEquals(">", StringUtil.XMLEncNQG(">")); assertEquals("]>", StringUtil.XMLEncNQG("]>")); assertEquals("]]>", StringUtil.XMLEncNQG("]]>")); assertEquals("x]]>", StringUtil.XMLEncNQG("x]]>")); assertEquals("x]>", StringUtil.XMLEncNQG("x]>")); assertEquals("]x>", StringUtil.XMLEncNQG("]x>")); } @Test public void testRTFEnc() throws IOException { String s = ""; assertSame(s, StringUtil.RTFEnc(s)); s = "asd"; assertSame(s, StringUtil.RTFEnc(s)); testRTFEnc("a\\{b\\}c\\\\d", "a{b}c\\d"); testRTFEnc("\\{", "{"); testRTFEnc("\\{a", "{a"); testRTFEnc("\\{a\\}", "{a}"); testRTFEnc("a\\}", "a}"); testRTFEnc("\\{\\}", "{}"); testRTFEnc("a\\{\\}b", "a{}b"); } private void testRTFEnc(String expected, String in) throws IOException { assertEquals(expected, StringUtil.RTFEnc(in)); StringWriter sw = new StringWriter(); StringUtil.RTFEnc(in, sw); assertEquals(expected, sw.toString()); } }