You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@roller.apache.org by sn...@apache.org on 2026/09/14 23:06:49 UTC
(roller) branch codex/html-subset-links created (now 0e4c3db6c)
This is an automated email from the ASF dual-hosted git repository.
snoopdave pushed a change to branch codex/html-subset-links
in repository https://gitbox.apache.org/repos/asf/roller.git
at 0e4c3db6c Normalize links in HTML subset formatting
This branch includes the following new commits:
new 0e4c3db6c Normalize links in HTML subset formatting
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(roller) 01/01: Normalize links in HTML subset formatting
Posted by sn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
snoopdave pushed a commit to branch codex/html-subset-links
in repository https://gitbox.apache.org/repos/asf/roller.git
commit 0e4c3db6c3eec896f84b55e3e5b53961c4949346
Author: David M. Johnson <sn...@apache.org>
AuthorDate: Mon Sep 14 19:06:14 2026 -0400
Normalize links in HTML subset formatting
---
.../apache/roller/weblogger/util/Utilities.java | 41 ++++++++++++----
.../roller/weblogger/util/UtilitiesTest.java | 56 ++++++++++++++++++++++
2 files changed, 87 insertions(+), 10 deletions(-)
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 class Utilities {
s = replace(s, CLOSING_LI_TAG_PATTERN, "</li>");
s = replace(s, QUOTE_PATTERN, "\"");
- // HTTP links
+ // Normalize supported links while retaining the surrounding text.
s = replace(s, CLOSING_A_TAG_PATTERN, "</a>");
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 = "<a>";
+ 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 = "<a href=\"" + StringEscapeUtils.escapeHtml4(href) + "\">";
+ }
+ }
+ 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("&lt;", "<");
s = s.replace("&gt;", ">");
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("<a href=\"https://example.com/\">site</a>",
+ htmlSubset("<a href='https://example.com/'>site</a>"));
+ assertEquals("<a href=\"HTTP://example.com/\">site</a>",
+ htmlSubset("<A HREF=HTTP://example.com/>site</A>"));
+ assertEquals("<a href=\"mailto:reader@example.com\">mail</a>",
+ htmlSubset("<a href=\"mailto:reader@example.com\">mail</a>"));
+ assertEquals("<a href=\"https://example.com/?a=1&b=2\">query</a>",
+ htmlSubset("<a href=\"https://example.com/?a=1&b=2\">query</a>"));
+ assertEquals("<a href=\"https://example.com/\">site</a>",
+ htmlSubset("<a\n href = 'https://example.com/' >site</a>"));
+ assertEquals("<a href=\"https://example.com/~reader\">site</a>",
+ htmlSubset("<a href=\"https://example.com/~reader\">site</a>"));
+ }
+
+ @Test
+ public void testHtmlSubsetLinkSubset() {
+ assertEquals("<a>file</a>", htmlSubset("<a href=\"ftp://example.com/file\">file</a>"));
+ assertEquals("<a>local</a>", htmlSubset("<a href=\"../page\">local</a>"));
+ assertEquals("<a>site</a>",
+ htmlSubset("<a href=\"https://example.com/\" title=\"site\">site</a>"));
+ assertEquals("<a>empty</a>", htmlSubset("<a href=\"\">empty</a>"));
+ }
+
+ @Test
+ public void testHtmlSubsetRetainsTextAndFormatting() {
+ assertNull(Utilities.transformToHTMLSubset(null));
+ assertEquals("", htmlSubset(""));
+ String text = "<p><b>Bold</b> and <i>italic</i><br>line</p>";
+ assertEquals("<p><b>Bold</b> and <i>italic</i><br />line</p>", htmlSubset(text));
+ assertEquals("A <example>", htmlSubset("A <example>"));
+ assertEquals("<a href=\"https://example.com/\">site</a>",
+ htmlSubset("<a href=\"https://example.com/\">site</a>"));
+ assertEquals("<a href=\"https://example.com/$1\">one</a> <a>two</a>",
+ htmlSubset("<a href=\"https://example.com/$1\">one</a> <a href=\"ftp://example.com/\">two</a>"));
+ }
+
+ @Test
+ public void testHtmlSubsetPluginUsesLinkFormatting() {
+ WeblogEntryComment comment = new WeblogEntryComment();
+ comment.setContentType("text/html");
+ HTMLSubsetPlugin plugin = new HTMLSubsetPlugin();
+ String text = "<a href=\"ftp://example.com/\">file</a>";
+ assertEquals("<a>file</a>", plugin.render(comment, text));
+ comment.setContentType("text/plain");
+ assertEquals(text, plugin.render(comment, text));
+ }
+
+ private String htmlSubset(String text) {
+ return Utilities.transformToHTMLSubset(Utilities.escapeHTML(text));
+ }
+
@Test
public void testExtractHTML() {
String test = "<a>keep me</a>";