diff --git a/app/src/main/java/org/apache/roller/weblogger/util/Utilities.java b/app/src/main/java/org/apache/roller/weblogger/util/Utilities.java index e35efb030..7caf9a682 100644 --- a/app/src/main/java/org/apache/roller/weblogger/util/Utilities.java +++ b/app/src/main/java/org/apache/roller/weblogger/util/Utilities.java @@ -80,7 +80,12 @@ public class Utilities { private static final Pattern CLOSING_A_TAG_PATTERN = Pattern.compile( "</a>", Pattern.CASE_INSENSITIVE); private static final Pattern OPENING_A_TAG_PATTERN = Pattern.compile( - "<a href=.*?>", Pattern.CASE_INSENSITIVE); + "<a\\s+href\\s*=.*?>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); + private static final Pattern A_HREF_PATTERN = Pattern.compile( + "<a\\s+href\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'=<>`]+))\\s*>", + Pattern.CASE_INSENSITIVE); + private static final Pattern SUBSET_LINK_PATTERN = Pattern.compile( + "(?:https?://|mailto:)[^\\x00-\\x20\\x7f]+", Pattern.CASE_INSENSITIVE); private static final Pattern QUOTE_PATTERN = Pattern.compile(""", Pattern.CASE_INSENSITIVE); @@ -975,23 +980,39 @@ public static String transformToHTMLSubset(String s) { s = replace(s, CLOSING_LI_TAG_PATTERN, ""); s = replace(s, QUOTE_PATTERN, "\""); - // HTTP links + // Normalize supported links while retaining the surrounding text. s = replace(s, CLOSING_A_TAG_PATTERN, ""); Matcher m = OPENING_A_TAG_PATTERN.matcher(s); + StringBuilder result = new StringBuilder(s.length()); + int end = 0; while (m.find()) { - int start = m.start(); - int end = m.end(); - String link = s.substring(start, end); - link = "<" + link.substring(4, link.length() - 4) + ">"; - s = s.substring(0, start) + link + s.substring(end, s.length()); - m = OPENING_A_TAG_PATTERN.matcher(s); + result.append(restoreSubsetEntities(s.substring(end, m.start()))); + Matcher hrefMatcher = A_HREF_PATTERN.matcher(m.group()); + String link = ""; + if (hrefMatcher.matches()) { + String href = hrefMatcher.group(1); + if (href == null) { + href = hrefMatcher.group(2); + } + if (href == null) { + href = hrefMatcher.group(3); + } + href = StringEscapeUtils.unescapeHtml4(restoreSubsetEntities(href)); + if (SUBSET_LINK_PATTERN.matcher(href).matches()) { + link = ""; + } + } + result.append(link); + end = m.end(); } + result.append(restoreSubsetEntities(s.substring(end))); + return result.toString(); + } - // escaped angle brackets + private static String restoreSubsetEntities(String s) { s = s.replace("<", "<"); s = s.replace(">", ">"); s = s.replace("&#", ""); - return s; } diff --git a/app/src/test/java/org/apache/roller/weblogger/util/UtilitiesTest.java b/app/src/test/java/org/apache/roller/weblogger/util/UtilitiesTest.java index 2b97d0c4a..7e008aeb2 100644 --- a/app/src/test/java/org/apache/roller/weblogger/util/UtilitiesTest.java +++ b/app/src/test/java/org/apache/roller/weblogger/util/UtilitiesTest.java @@ -19,14 +19,70 @@ package org.apache.roller.weblogger.util; import org.junit.jupiter.api.Test; +import org.apache.roller.weblogger.business.plugins.comment.HTMLSubsetPlugin; +import org.apache.roller.weblogger.pojos.WeblogEntryComment; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; /** * Test utilities. */ public class UtilitiesTest { + @Test + public void testHtmlSubsetLinkFormats() { + assertEquals("site", + htmlSubset("site")); + assertEquals("site", + htmlSubset("site")); + assertEquals("mail", + htmlSubset("mail")); + assertEquals("query", + htmlSubset("query")); + assertEquals("site", + htmlSubset("site")); + assertEquals("site", + htmlSubset("site")); + } + + @Test + public void testHtmlSubsetLinkSubset() { + assertEquals("file", htmlSubset("file")); + assertEquals("local", htmlSubset("local")); + assertEquals("site", + htmlSubset("site")); + assertEquals("empty", htmlSubset("empty")); + } + + @Test + public void testHtmlSubsetRetainsTextAndFormatting() { + assertNull(Utilities.transformToHTMLSubset(null)); + assertEquals("", htmlSubset("")); + String text = "
Bold and italic
line
Bold and italic
line