/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/kernel/trunk/kernel-util/src/main/java/org/sakaiproject/util/FormattedText.java $
* $Id: FormattedText.java 97738 2011-08-31 17:30:03Z ottenhoff@longsight.com $
***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 Sakai Foundation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ECL-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 org.sakaiproject.util.impl;
import java.util.regex.Pattern;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.component.impl.BasicConfigurationService;
import org.sakaiproject.id.api.IdManager;
import org.sakaiproject.id.impl.UuidV4IdComponent;
import org.sakaiproject.thread_local.api.ThreadLocalManager;
import org.sakaiproject.thread_local.impl.ThreadLocalComponent;
import org.sakaiproject.tool.api.SessionManager;
import org.sakaiproject.tool.api.ToolManager;
import org.sakaiproject.tool.impl.SessionComponent;
import org.sakaiproject.util.api.FormattedText.Level;
import org.sakaiproject.util.BasicConfigItem;
import junit.framework.TestCase;
public class FormattedTextTest extends TestCase {
FormattedTextImpl formattedText;
private SessionManager sessionManager;
private ServerConfigurationService serverConfigurationService;
@Override
protected void setUp() throws Exception {
// instantiate the services we need for our test
final IdManager idManager = new UuidV4IdComponent();
final ThreadLocalManager threadLocalManager = new ThreadLocalComponent();
serverConfigurationService = new BasicConfigurationService(); // cannot use home or server methods
sessionManager = new SessionComponent() {
@Override
protected ToolManager toolManager() {
return null; // not needed for this test
}
@Override
protected ThreadLocalManager threadLocalManager() {
return threadLocalManager;
}
@Override
protected IdManager idManager() {
return idManager;
}
};
// add in the config so we can test it
serverConfigurationService.registerConfigItem(BasicConfigItem.makeDefaultedConfigItem("content.cleaner.errors.handling", "return", "FormattedTextTest"));
ComponentManager.testingMode = true;
// instantiate what we are testing
formattedText = new FormattedTextImpl();
formattedText.setServerConfigurationService(serverConfigurationService);
formattedText.setSessionManager(sessionManager);
formattedText.init();
}
@Override
protected void tearDown() throws Exception {
ComponentManager.shutdown();
}
public static String TEST1 = "<a href=\"blah.html\" style=\"font-weight:bold;\">blah</a><div>hello there</div>";
public static String TEST2 = "<span>this is my span</span><script>alert('oh noes, a XSS attack!');</script><div>hello there from a div</div>";
// TESTS
public void testProcessAnchor() {
// Check we add the target attribute
assertEquals("<a href=\"http://sakaiproject.org/\" target=\"_blank\">", formattedText
.processAnchor("<a href=\"http://sakaiproject.org/\">"));
}
public void testProcessAnchorRelative() {
// Check we add the target attribute
assertEquals("<a href=\"other.html\" target=\"_blank\">", formattedText
.processAnchor("<a href=\"other.html\">"));
}
public void testProcessAnchorMailto() {
assertEquals("<a href=\"mailto:someone@example.com\" target=\"_blank\">", formattedText
.processAnchor("<a href=\"mailto:someone@example.com\">"));
}
public void testProcessAnchorName() {
assertEquals("<a href=\"#anchor\" target=\"_blank\">", formattedText
.processAnchor("<a href=\"#anchor\">"));
}
public void testRegexTargetMatch() {
Pattern patternAnchorTagWithOutTarget = formattedText.M_patternAnchorTagWithOutTarget;
/* Pattern.compile("([<]a\\s)(?![^>]*target=)([^>]*?)[>]",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL); */
assertTrue(patternAnchorTagWithOutTarget.matcher("<a href=\"other.html\">link</a>").find());
assertFalse(patternAnchorTagWithOutTarget.matcher("<a target=\"AZ\" href=\"other.html\">link</a>").find());
assertTrue(patternAnchorTagWithOutTarget.matcher("<a href=\"other.html\" class=\"AZ\">link</a>").find());
assertFalse(patternAnchorTagWithOutTarget.matcher("<a target=\"AZ\" href=\"other.html\">link</a>").find());
assertFalse(patternAnchorTagWithOutTarget.matcher("<a href=\"other.html\" target=\"AZ\">link</a>").find());
}
// KNL-526 - testing that targets are not destroyed or replaced
public void testTargetNotOverridden() {
// method 1 - processAnchor (kills all A attribs and only works on the first part of the tag
assertEquals("<a href=\"other.html\" target=\"_blank\">",
formattedText.processAnchor("<a href=\"other.html\">") );
assertEquals("<a href=\"other.html\" target=\"_blank\">",
formattedText.processAnchor("<a target=\"_blank\" href=\"other.html\">") );
assertEquals("<a href=\"other.html\" target=\"_AZ\">",
formattedText.processAnchor("<a href=\"other.html\" target=\"_AZ\">"));
// destroys other attributes though...
assertEquals("<a href=\"other.html\" target=\"_AZ\">",
formattedText.processAnchor("<a href=\"other.html\" target=\"_AZ\" class=\"azeckoski\">"));
// method 2 - escapeHtmlFormattedText (saves other A attribs)
assertEquals("<a href=\"other.html\" target=\"_blank\">link</a>",
formattedText.escapeHtmlFormattedText("<a href=\"other.html\">link</a>") );
assertEquals("<a href=\"other.html\" class=\"azeckoski\" target=\"_blank\">link</a>",
formattedText.escapeHtmlFormattedText("<a href=\"other.html\" class=\"azeckoski\">link</a>") );
assertEquals("<b>simple</b><b class=\"AZ\">bold</b>",
formattedText.escapeHtmlFormattedText("<b>simple</b><b class=\"AZ\">bold</b>") );
assertEquals("<a href=\"other.html\" target=\"_AZ\">link</a>",
formattedText.escapeHtmlFormattedText("<a href=\"other.html\" target=\"_AZ\">link</a>") );
assertEquals("<a href=\"other.html\" target=\"_AZ\" class=\"azeckoski\">link</a>",
formattedText.escapeHtmlFormattedText("<a href=\"other.html\" target=\"_AZ\" class=\"azeckoski\">link</a>") );
assertEquals("<a href=\"other.html\" class=\"azeckoski\" target=\"_blank\">link</a><a href=\"other.html\" target=\"_AZ\" class=\"azeckoski\">link</a>",
formattedText.escapeHtmlFormattedText("<a href=\"other.html\" class=\"azeckoski\">link</a><a href=\"other.html\" target=\"_AZ\" class=\"azeckoski\">link</a>") );
assertEquals("<b>simple</b><b class=\"AZ\">bold</b><a href=\"other.html\" class=\"azeckoski\" target=\"_blank\">link</a><a href=\"other.html\" target=\"_AZ\" class=\"azeckoski\">link</a>",
formattedText.escapeHtmlFormattedText("<b>simple</b><b class=\"AZ\">bold</b><a href=\"other.html\" class=\"azeckoski\">link</a><a href=\"other.html\" target=\"_AZ\" class=\"azeckoski\">link</a>") );
}
public void testAntisamyProcessFormattedText() {
// TESTS using the antiSamy library
String strFromBrowser = null;
String result = null;
StringBuilder errorMessages = null;
strFromBrowser = TEST1;
errorMessages = new StringBuilder();
formattedText.setDefaultAddBlankTargetToLinks(false);
result = formattedText.processFormattedText(strFromBrowser, errorMessages, false);
assertNotNull(result);
assertTrue( result.contains("href=\"blah.html\""));
//assertFalse( result.contains("style=\"font-weight:bold;\"")); // strips this out
//assertTrue( result.contains("target=\"_blank\"")); // adds target in
assertTrue( result.contains("<div>hello there</div>"));
assertEquals("<a href=\"blah.html\" style=\"font-weight: bold;\">blah</a>\n<div>hello there</div>", result);
strFromBrowser = TEST1;
errorMessages = new StringBuilder();
formattedText.setDefaultAddBlankTargetToLinks(true);
result = formattedText.processFormattedText(strFromBrowser, errorMessages, false);
assertNotNull(result);
assertTrue( result.contains("href=\"blah.html\""));
//assertFalse( result.contains("style=\"font-weight:bold;\"")); // strips this out
//assertTrue( result.contains("target=\"_blank\"")); // adds target in
assertTrue( result.contains("<div>hello there</div>"));
assertEquals("<a href=\"blah.html\" style=\"font-weight: bold;\" target=\"_blank\">blah</a>\n<div>hello there</div>", result);
strFromBrowser = TEST2;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, false);
assertNotNull(result);
assertEquals("<span>this is my span</span>\n<div>hello there from a div</div>", result);
String SVG_BAD = "<div>hello</div><embed allowscriptaccess=\"always\" type=\"image/svg+xml\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==\"></embed>";
strFromBrowser = SVG_BAD;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, false);
assertNotNull(result);
assertEquals("<div>hello</div>", result);
}
public void testSAK_18269() {
// http://jira.sakaiproject.org/browse/SAK-18269
String strFromBrowser = null;
String result = null;
StringBuilder errorMessages = null;
String SVG_GOOD = "<div>hello</div><embed allowscriptaccess=\"always\" type=\"image/svg+xml\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAwIiBpZD0ieHNzIj5pbWFnZTwvc3ZnPg==\"></embed>";
String SVG_BAD = "<div>hello</div><embed allowscriptaccess=\"always\" type=\"image/svg+xml\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==\"></embed>";
strFromBrowser = SVG_GOOD;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() > 50 );
assertTrue( result.contains("<div"));
assertFalse( result.contains("<embed"));
strFromBrowser = SVG_BAD;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() > 50 );
assertTrue( result.contains("<div"));
assertFalse( result.contains("<embed"));
/* CHANGED BEHAVIOR
* Antisamy strips the entire embed tag
strFromBrowser = SVG_GOOD;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() == 0 );
assertTrue( result.contains("<div"));
assertTrue( result.contains("<embed"));
assertTrue( result.contains("src="));
assertTrue( result.contains("data:image/svg+xml;base64"));
assertFalse( result.contains("<script"));
strFromBrowser = SVG_BAD;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div"));
assertTrue( result.contains("<embed"));
assertFalse( result.contains("src="));
assertFalse( result.contains("data:image/svg+xml;base64"));
assertFalse( result.contains("<script"));
*/
}
public void testDataAttributes() {
String[] passTests;
String[] failTests;
String[] failResults;
String result;
StringBuilder errors;
String oneKV = "<span class=\"one\"></span>";
String twoKV = "<span class=\"one\" id=\"two\"></span>";
String selfClose = "<hr class=\"section\" title=\"Contents\" />";
String subAttr = "<span id=\"name\" title=\"http://example.com/data-src\"></span>";
String subAttrs = "<span class=\"data-name\" id=\"name\" title=\"http://example.com/\"></span>";
String repeatK = "<span class class></span>";
String repeatKV = "<span class=\"one\" class=\"two\"></span>";
String badK = "<span class=\"foo\" class-></span>";
String badK2 = "<span class=\"foo\" data-></span>";
String badK3 = "<span class=\"foo\" data--></span>";
String badKV = "<span class=\"foo\" class-=\"one\"></span>";
String resultRepeatK = "<span></span>";
String resultRepeatKV = "<span class=\"two\"></span>";
String resultBadK = "<span class=\"foo\"></span>";
String resultBadKV = "<span class=\"foo\"></span>";
// antisamy will not allow empty attributes OR unknown attributes
passTests = new String[] { oneKV, twoKV, selfClose, subAttr, subAttrs };
failTests = new String[] { repeatK, badK, badK2, badK3, badKV };
failResults = new String[] { resultRepeatK, resultBadK, resultBadK, resultBadK, resultBadKV };
result = formattedText.processFormattedText(repeatKV, new StringBuilder());
assertEquals(resultRepeatKV, result);
for (String passTest : passTests) {
errors = new StringBuilder();
result = formattedText.processFormattedText(passTest, errors);
assertEquals(passTest+" != "+result, 0, errors.length());
assertEquals(passTest+" != "+result, passTest, result);
}
for (int i = 0; i < failTests.length; i++) {
errors = new StringBuilder();
result = formattedText.processFormattedText(failTests[i], errors);
assertEquals(failResults[i]+" != "+result, failResults[i], result);
assertTrue(failTests[i]+": "+failResults[i]+" != "+result, errors.length() > 10);
}
// LEGACY tests
/*
passTests = new String[] { oneK, oneKV, twoK, twoKV, mixed, selfCloseL, subAttr, subAttrs };
failTests = new String[] { repeatK, repeatKV, badK, badK2, badK3, badKV };
failResults = new String[] { resultRepeatKL, resultRepeatKVL, resultBadK, resultBadK, resultBadK, resultBadKV };
for (String passTest : passTests) {
errors = new StringBuilder();
result = formattedText.processFormattedText(passTest, errors, true);
assertEquals(passTest, result);
assertTrue(errors.length() == 0);
}
for (int i = 0; i < failTests.length; i++) {
errors = new StringBuilder();
result = formattedText.processFormattedText(failTests[i], errors, true);
assertEquals(failResults[i], result);
assertTrue(result, errors.length() > 10);
}
*/
}
public void testKNL_528() {
// http://jira.sakaiproject.org/browse/KNL-528
String SVG_BAD_CAPS = "<div>hello</div><EMBED ALLOWSCRIPTACCESS=\"always\" type=\"image/svg+xml\" SRC=\"data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==\"></EMBED>";
String SVG_BAD = "<div>hello</div><embed allowscriptaccess=\"always\" type=\"image/svg+xml\" src=\"data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==\"></embed>";
String strFromBrowser = null;
String result = null;
StringBuilder errorMessages = null;
strFromBrowser = SVG_BAD;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, true);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div"));
assertFalse( result.contains("<embed"));
assertFalse( result.contains("src="));
assertFalse( result.contains("data:image/svg+xml;base64"));
assertFalse( result.contains("<script"));
strFromBrowser = SVG_BAD_CAPS;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, true);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div"));
assertFalse( result.contains("<EMBED"));
assertFalse( result.contains("SRC="));
assertFalse( result.contains("data:image/svg+xml;base64"));
assertFalse( result.contains("<script"));
/* CDATA is ignored so it will not be cleaned
String TRICKY = "<div><![CDATA[<EMBED SRC=\"data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==\" type=\"image/svg+xml\" AllowScriptAccess=\"always\"></EMBED>]]></div>";
String CDATA_TRICKY = "<div><![CDATA[<embed src=\"data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==\" type=\"image/svg+xml\" AllowScriptAccess=\"always\"></embed>]]></div>";
strFromBrowser = CDATA_TRICKY;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, true);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div"));
assertTrue( result.contains("<embed"));
assertFalse( result.contains("src="));
assertFalse( result.contains("data:image/svg+xml;base64"));
assertFalse( result.contains("<script"));
strFromBrowser = TRICKY;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, true);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div"));
assertTrue( result.contains("<EMBED"));
assertFalse( result.contains("SRC="));
assertFalse( result.contains("data:image/svg+xml;base64"));
assertFalse( result.contains("<script"));
*/
}
public void testUnbalancedMarkup() {
StringBuilder errorMessages = new StringBuilder();
String strFromBrowser = "A<B Test";
String result = formattedText.processFormattedText(strFromBrowser, errorMessages, true);
assertNotNull(result);
assertEquals("A", result);
}
public void testKNL_579() {
// http://jira.sakaiproject.org/browse/KNL-579
String SCRIPT1 = "<div>testing</div><SCRIPT>alert(\"XSS\");//</SCRIPT>";
String SCRIPT2 = "<div>testing</div><SCRIPT>alert(\"XSS\");//<</SCRIPT>";
String SCRIPT3 = "<div>testing</div><<SCRIPT>alert(\"XSS\");//<</SCRIPT>";
String SCRIPT4 = "<div>testing</div><<SCRIPT>>alert(\"XSS\");//<</SCRIPT>";
String strFromBrowser = null;
String result = null;
StringBuilder errorMessages = null;
strFromBrowser = SCRIPT1;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, true);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div>testing</div>"));
assertFalse( result.contains("XSS"));
assertFalse( result.contains("<SCRIPT"));
strFromBrowser = SCRIPT2;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, true);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div>testing</div>"));
assertFalse( result.contains("XSS"));
assertFalse( result.contains("<SCRIPT"));
strFromBrowser = SCRIPT3;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, true);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div>testing</div>"));
assertFalse( result.contains("XSS"));
assertFalse( result.contains("<SCRIPT"));
strFromBrowser = SCRIPT4;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, true);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div>testing</div>"));
assertFalse( result.contains("XSS"));
assertFalse( result.contains("<SCRIPT"));
}
public void testHighLowNoneScanning() {
// KNL-1048 KNL-1009
String strFromBrowser = null;
String result = null;
StringBuilder errorMessages = null;
String SCRIPT1 = "<div>testing</div><embed src=\"DANGER.swf\"><SCRIPT>alert(\"XSS\");//</SCRIPT>";
String SCRIPT2 = "<div>testing</div><script>alert(\"XSS\");<BR>";
// Test KNL-1009
strFromBrowser = SCRIPT2;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, Level.HIGH);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div>testing</div>"));
assertFalse( result.contains("XSS"));
assertFalse( result.contains("<script"));
// check the options
strFromBrowser = SCRIPT1;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, null); // default: high
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div>testing</div>"));
assertFalse( result.contains("XSS"));
assertFalse( result.contains("<SCRIPT"));
assertFalse( result.contains("DANGER"));
assertFalse( result.contains("<embed"));
strFromBrowser = SCRIPT1;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, Level.HIGH);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div>testing</div>"));
assertFalse( result.contains("XSS"));
assertFalse( result.contains("<SCRIPT"));
assertFalse( result.contains("DANGER"));
assertFalse( result.contains("<embed"));
strFromBrowser = SCRIPT1;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, Level.LOW);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("<div>testing</div>"));
assertFalse( result.contains("XSS"));
assertFalse( result.contains("<SCRIPT"));
assertTrue( result.contains("DANGER"));
assertTrue( result.contains("<embed"));
strFromBrowser = SCRIPT1;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages, Level.NONE);
assertNotNull(result);
assertTrue( errorMessages.length() == 0 );
assertTrue( result.contains("<div>testing</div>"));
assertTrue( result.contains("XSS"));
assertTrue( result.contains("<SCRIPT"));
assertTrue( result.contains("DANGER"));
assertTrue( result.contains("<embed"));
}
public void testKNL_1019() {
// https://jira.sakaiproject.org/browse/KNL-1029
String strFromBrowser = null;
String result = null;
StringBuilder errorMessages = null;
String youTubeObject = "<object width=\"560\" height=\"315\"><param name=\"movie\" value=\"http://www.youtube.com/v/1yqVD0swvWU?hl=en_US&version=3&rel=0\"></param><param name=\"allowFullScreen\" value=\"true\"></param><param name=\"allowscriptaccess\" value=\"always\"></param><embed src=\"http://www.youtube.com/v/1yqVD0swvWU?hl=en_US&version=3&rel=0\" type=\"application/x-shockwave-flash\" width=\"560\" height=\"315\" allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed></object>";
String youTubeIframe = "<iframe width=\"560\" height=\"315\" src=\"http://www.youtube.com/embed/1yqVD0swvWU?rel=0\" frameborder=\"0\" allowfullscreen></iframe>";
String youTubeIframeOptions = "<iframe width=\"560\" height=\"315\" src=\"https://www.youtube-nocookie.com/embed/3pAnRKD4raY?rel=0\" frameborder=\"0\" allowfullscreen></iframe>";
String youTubeCK = "<object data=\"/library/editor/ckextraplugins/movieplayer/StrobeMediaPlayback.swf\" height=\"240\" id=\"movie941276\" type=\"application/x-shockwave-flash\" width=\"320\"><param name=\"movie\" value=\"http://www.youtube.com/v/1yqVD0swvWU\" /><param name=\"FlashVars\" value=\"src=http://www.youtube.com/v/1yqVD0swvWU&showplayer=always&width=320&height=240&showiconplay=true&autoplay=0&plugin_YouTubePlugin=/library/editor/ckextraplugins/movieplayer/YouTubePlugin.swf\" /><param name=\"allowFullScreen\" value=\"true\" /></object>";
String youTubeSpecialCK = "<object data=\"http://youtu.be/1yqVD0swvWU\" height=\"240\" id=\"movie791812\" type=\"video/x-ms-wmv\" width=\"320\"><param name=\"src\" value=\"http://youtu.be/1yqVD0swvWU\" /><param name=\"autostart\" value=\"0\" /><param name=\"controller\" value=\"true\" /></object>";
String dangerEmbed = "<div>SAFE</div><object data=\"/access/library/hacked/DANGER.swf\" height=\"240\" id=\"movie941276\" type=\"application/x-shockwave-flash\" width=\"320\"><param name=\"movie\" value=\"http://www.youtube.com/v/1yqVD0swvWU\" /><param name=\"FlashVars\" value=\"src=http://www.youtube.com/v/1yqVD0swvWU&showplayer=always&width=320&height=240&showiconplay=true&autoplay=0&plugin_YouTubePlugin=/library/editor/ckextraplugins/movieplayer/YouTubePlugin.swf\" /><param name=\"allowFullScreen\" value=\"true\" /></object>";
String dangerLibraryPath = "<div>SAFE</div><object data=\"/library/../access/content/user/myUser/DANGER.swf\" type=\"application/x-shockwave-flash\"><param name=\"FlashVars\" value=\"hacked=true\" /></object>";
String dangerLibraryPath2 = "<div>SAFE</div><object data=\"/library/happy/../../access/content/user/myUser/DANGER.swf\" type=\"application/x-shockwave-flash\"><param name=\"FlashVars\" value=\"hacked=true\" /></object>";
String dangerLibraryPath3 = "<div>SAFE</div><object data=\"/access/content/user/myUser/library/test/DANGER.swf\" type=\"application/x-shockwave-flash\"><param name=\"FlashVars\" value=\"hacked=true\" /></object>";
String dangerLibraryPath4 = "<div>SAFE</div><object data=\"/library\\../access/content/user/myUser/DANGER..swf\" type=\"application/x-shockwave-flash\"><param name=\"FlashVars\" value=\"hacked=true\" /></object>";
String dangerLibraryPath5 = "<div>SAFE</div><object data=\"/libraryAnyString/path/DANGER.swf\" type=\"application/x-shockwave-flash\"><param name=\"FlashVars\" value=\"hacked=true\" /></object>";
String dangerLibraryPath6 = "<div>SAFE</div><object data=\"/library/aaa\\..\\..\\access/content/user//myUser/DANGER..swf\" type=\"application/x-shockwave-flash\"><param name=\"FlashVars\" value=\"hacked=true\" /></object>";
strFromBrowser = youTubeObject;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() == 0 );
assertTrue( result.contains("<object"));
assertTrue( result.contains("<embed"));
assertTrue( result.contains("www.youtube.com/v/1yqVD0swvWU"));
strFromBrowser = youTubeIframe;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() == 0 );
assertTrue( result.contains("<iframe"));
assertTrue( result.contains("allowfullscreen"));
assertTrue( result.contains("www.youtube.com/embed/1yqVD0swvWU"));
strFromBrowser = youTubeIframeOptions;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() == 0 );
assertTrue( result.contains("<iframe"));
assertTrue( result.contains("allowfullscreen"));
assertTrue( result.contains("www.youtube-nocookie.com/embed/3pAnRKD4raY"));
strFromBrowser = youTubeCK;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() == 0 );
assertTrue( result.contains("<object"));
assertTrue( result.contains("/library/editor/ckextraplugins"));
assertTrue( result.contains("www.youtube.com/v/1yqVD0swvWU"));
strFromBrowser = youTubeSpecialCK;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() == 0 );
assertTrue( result.contains("<object"));
assertTrue( result.contains("<param"));
assertTrue( result.contains("youtu.be/1yqVD0swvWU"));
// test bad stuff
strFromBrowser = dangerEmbed;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("SAFE"));
assertFalse( result.contains("<object"));
assertFalse( result.contains("DANGER"));
strFromBrowser = dangerLibraryPath;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("SAFE"));
assertFalse( result.contains("<object"));
assertFalse( result.contains("DANGER"));
strFromBrowser = dangerLibraryPath2;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("SAFE"));
assertFalse( result.contains("<object"));
assertFalse( result.contains("DANGER"));
strFromBrowser = dangerLibraryPath3;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("SAFE"));
assertFalse( result.contains("<object"));
assertFalse( result.contains("DANGER"));
strFromBrowser = dangerLibraryPath4;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("SAFE"));
assertFalse( result.contains("<object"));
assertFalse( result.contains("DANGER"));
strFromBrowser = dangerLibraryPath5;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("SAFE"));
assertFalse( result.contains("<object"));
assertFalse( result.contains("DANGER"));
strFromBrowser = dangerLibraryPath6;
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertTrue( errorMessages.length() > 10 );
assertTrue( result.contains("SAFE"));
assertFalse( result.contains("<object"));
assertFalse( result.contains("DANGER"));
}
public void testNullParams() {
//KNL-862 test we don't NPE if a null string is passed with Newlines == true - DH
try {
formattedText.escapeHtml(null, true);
} catch (Exception e) {
fail();
}
}
public void testKNL_1065() {
// https://jira.sakaiproject.org/browse/KNL-1065
String strFromBrowser = null;
String result = null;
StringBuilder errorMessages = null;
strFromBrowser = "<span class=\"kaltura-media classValue\" rel=\"relValue::video\"><img src=\"https://cdnsecakmi.kaltura.com/p/999999/imgValue\" /></span>";
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertFalse( errorMessages.length() > 10 );
assertTrue( result.contains("classValue"));
assertTrue( result.contains("relValue"));
assertTrue( result.contains("imgValue"));
strFromBrowser = "<div class=\"classValue\">divValue<ins>insValue</ins><ins datetime=\"2013-10-29\" cite=\"/url/to/cite.html\">insComplexValue</ins><del>delValue</del></div>";
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertFalse( errorMessages.length() > 10 );
assertTrue( result.contains("<div "));
assertTrue( result.contains("<ins>"));
assertTrue( result.contains("<ins "));
assertTrue( result.contains("<del>"));
assertTrue( result.contains("divValue"));
assertTrue( result.contains("insValue"));
assertTrue( result.contains("delValue"));
assertTrue( result.contains("insComplexValue"));
assertTrue( result.contains("2013-10-29"));
assertTrue( result.contains("/url/to/cite.html"));
}
public void testKNL_1061() {
// https://jira.sakaiproject.org/browse/KNL-1061
String strFromBrowser = null;
String result = null;
StringBuilder errorMessages = null;
strFromBrowser = "<div class=\"classValue\">divValue</div><img border=\"0\" data-mathml=\"%3Cmrow%3E%0A%20%20%20%20%20%20%20%20%3Cmo%20selected%3D%22true%22%3E%26frac23%3B%3C/mo%3E%0A%3C/mrow%3E\" id=\"MathMLEq1\" src=\"http://nightly2.sakaiproject.org:8085/access/content/group/mercury/fmath-equation-94BDA89D-E911-283D-53C1-32D6CCE53EB0.png\" />";
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertFalse( errorMessages.length() > 10 );
assertTrue( result.contains("<div "));
assertTrue( result.contains("divValue"));
assertTrue( result.contains("<img"));
assertTrue( result.contains("data-mathml"));
assertTrue( result.contains("%20selected%3D"));
}
public void testKNL_1071() {
// https://jira.sakaiproject.org/browse/KNL-1071
String strFromBrowser = null;
String result = null;
StringBuilder errorMessages = null;
strFromBrowser = "<p><span style=\"background-color:yellow;\">aaa </span><tt>bbb </tt><code>ccc </code><kbd>ddd </kbd><del>eee </del><span dir=\"rtl\">fff </span><cite>ggg</cite></p>";
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertFalse( errorMessages.length() > 0 );
assertTrue( result.contains("<tt>"));
assertTrue( result.contains("ddd"));
assertTrue( result.contains("<cite>"));
assertTrue( result.contains("<kbd>"));
assertTrue( result.contains("<span dir=\"rtl\""));
}
public void testKNL_1096() {
// https://jira.sakaiproject.org/browse/KNL-1096
String strFromBrowser = null;
String result = null;
StringBuilder errorMessages = null;
strFromBrowser = "<object width=\"560\" height=\"315\"><param name=\"movie\" value=\"//www.youtube.com/v/JNSK0647wJI?version=3&hl=en_US\"></param><param name=\"allowFullScreen\" value=\"true\"></param><param name=\"allowscriptaccess\" value=\"always\"></param><embed src=\"//www.youtube.com/v/JNSK0647wJI?version=3&hl=en_US\" type=\"application/x-shockwave-flash\" width=\"560\" height=\"315\" allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed></object>";
errorMessages = new StringBuilder();
result = formattedText.processFormattedText(strFromBrowser, errorMessages);
assertNotNull(result);
assertFalse( errorMessages.length() > 0 );
assertTrue( result.contains("<object"));
assertTrue( result.contains("<param"));
assertTrue( result.contains("value=\"//www.youtube.com/v/JNSK0647wJI"));
assertTrue( result.contains("src=\"//www.youtube.com/v/JNSK0647wJI"));
}
public void testValidateURL() {
// https://jira.sakaiproject.org/browse/KNL-1100
boolean result = false;
result = formattedText.validateURL("http://www.vt.edu/");
assertTrue(result);
result = formattedText.validateURL("http://localhost:8080/access/site/634cce7b-96da-4997-90b1-f99ea3c3973d");
assertTrue(result);
result = formattedText.validateURL("XXXXXXXX");
assertFalse(result);
}
public void testBasicUrlMatch() {
assertEquals("I like <a href=\"http://www.apple.com\">http://www.apple.com</a> and stuff", formattedText.encodeUrlsAsHtml(formattedText.escapeHtml("I like http://www.apple.com and stuff")));
}
public void testCanDoSsl() {
assertEquals("<a href=\"https://sakaiproject.org\">https://sakaiproject.org</a>", formattedText.encodeUrlsAsHtml("https://sakaiproject.org"));
}
public void testCanIgnoreTrailingExclamation() {
assertEquals("Hey, it's <a href=\"http://sakaiproject.org\">http://sakaiproject.org</a>!", formattedText.encodeUrlsAsHtml("Hey, it's http://sakaiproject.org!"));
}
public void testCanIgnoreTrailingQuestion() {
assertEquals("Have you ever seen <a href=\"http://sakaiproject.org\">http://sakaiproject.org</a>? Just wondering.", formattedText.encodeUrlsAsHtml("Have you ever seen http://sakaiproject.org? Just wondering."));
}
public void testCanEncodeQueryString() {
assertEquals("See <a href=\"http://sakaiproject.org/index.php?task=blogcategory&id=181\">http://sakaiproject.org/index.php?task=blogcategory&id=181</a> for more info.", formattedText.encodeUrlsAsHtml(formattedText.escapeHtml("See http://sakaiproject.org/index.php?task=blogcategory&id=181 for more info.")));
}
public void testCanTakePortNumber() {
assertEquals("<a href=\"http://localhost:8080/portal\">http://localhost:8080/portal</a>", formattedText.encodeUrlsAsHtml("http://localhost:8080/portal"));
}
public void testCanTakePortNumberAndQueryString() {
assertEquals("<a href=\"http://www.loco.com:3000/portal?person=224\">http://www.loco.com:3000/portal?person=224</a>", formattedText.encodeUrlsAsHtml("http://www.loco.com:3000/portal?person=224"));
}
public void testCanIgnoreExistingHref() {
assertEquals("<a href=\"http://sakaiproject.org\">Sakai Project</a>", formattedText.encodeUrlsAsHtml("<a href=\"http://sakaiproject.org\">Sakai Project</a>"));
}
public void testALongUrlFromNyTimes() {
assertEquals("<a href=\"http://www.nytimes.com/mem/MWredirect.html?MW=http://custom.marketwatch.com/custom/nyt-com/html-companyprofile.asp&symb=LLNW\">http://www.nytimes.com/mem/MWredirect.html?MW=http://custom.marketwatch.com/custom/nyt-com/html-companyprofile.asp&symb=LLNW</a>",
formattedText.encodeUrlsAsHtml(formattedText.escapeHtml("http://www.nytimes.com/mem/MWredirect.html?MW=http://custom.marketwatch.com/custom/nyt-com/html-companyprofile.asp&symb=LLNW")));
}
}