diff --git a/activemq-web/pom.xml b/activemq-web/pom.xml index 6c1f85d28a8..11f556b220f 100644 --- a/activemq-web/pom.xml +++ b/activemq-web/pom.xml @@ -87,6 +87,13 @@ websocket-jetty-server + + + org.apache.commons + commons-text + test + + com.rometools diff --git a/activemq-web/src/main/java/org/apache/activemq/web/util/ViewUtils.java b/activemq-web/src/main/java/org/apache/activemq/web/util/ViewUtils.java new file mode 100644 index 00000000000..378461f74d0 --- /dev/null +++ b/activemq-web/src/main/java/org/apache/activemq/web/util/ViewUtils.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.activemq.web.util; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class ViewUtils { + + public static final String AMP = "&"; + public static final String QUOTE = "\""; + public static final String LT = "<"; + public static final String GT = ">"; + public static final String APOS = "'"; + + public static final String XML_ESCAPED_AMP = "&"; + public static final String XML_ESCAPED_QUOTE = """; + public static final String XML_ESCAPED_LT = "<"; + public static final String XML_ESCAPED_GT = ">"; + public static final String XML_ESCAPED_APOS = "'"; + + public static final Map XML_ESCAPE_MAPPINGS; + + static { + // order matters for processing so use a linked map + Map mappings = new LinkedHashMap<>(); + mappings.put(AMP, XML_ESCAPED_AMP); + mappings.put(LT, XML_ESCAPED_LT); + mappings.put(GT, XMl_ESCAPED_GT); + mappings.put(QUOTE, XML_ESCAPED_QUOTE); + mappings.put(APOS, XML_ESCAPED_APOS); + + XML_ESCAPE_MAPPINGS = Collections.unmodifiableMap(mappings); + } + + + public static String escapeXml(String input) { + if (input == null) { + return null; + } + + String escaped = input; + for (Entry entry : XML_ESCAPE_MAPPINGS.entrySet()) { + escaped = escaped.replace(entry.getKey(), entry.getValue()); + } + + return escaped; + } + +} diff --git a/activemq-web/src/main/java/org/apache/activemq/web/view/RssMessageRenderer.java b/activemq-web/src/main/java/org/apache/activemq/web/view/RssMessageRenderer.java index 088a4c1926b..f05d0467833 100644 --- a/activemq-web/src/main/java/org/apache/activemq/web/view/RssMessageRenderer.java +++ b/activemq-web/src/main/java/org/apache/activemq/web/view/RssMessageRenderer.java @@ -49,7 +49,7 @@ public class RssMessageRenderer extends SimpleMessageRenderer { private String feedType = "rss_2.0"; private SyndFeed feed; private String description = "This feed is auto-generated by Apache ActiveMQ"; - private String entryContentType = "text/plain"; + private static final String ENTRY_CONTENT_TYPE = "text/plain"; public void renderMessage(PrintWriter writer, HttpServletRequest request, HttpServletResponse response, QueueBrowser browser, Message message) throws JMSException { SyndFeed feed = getFeed(browser, request); @@ -79,11 +79,7 @@ public void setFeedType(String feedType) { } public String getEntryContentType() { - return entryContentType; - } - - public void setEntryContentType(String entryContentType) { - this.entryContentType = entryContentType; + return ENTRY_CONTENT_TYPE; } // Implementation methods @@ -122,7 +118,7 @@ protected SyndEntry createEntry(QueueBrowser browser, Message message, HttpServl protected SyndContent createEntryContent(QueueBrowser browser, Message message, HttpServletRequest request) throws JMSException { SyndContent description = new SyndContentImpl(); - description.setType(entryContentType); + description.setType(getEntryContentType()); if (message instanceof TextMessage) { String text = ((TextMessage)message).getText(); diff --git a/activemq-web/src/main/java/org/apache/activemq/web/view/SimpleMessageRenderer.java b/activemq-web/src/main/java/org/apache/activemq/web/view/SimpleMessageRenderer.java index f8165d8849a..7c14248743b 100644 --- a/activemq-web/src/main/java/org/apache/activemq/web/view/SimpleMessageRenderer.java +++ b/activemq-web/src/main/java/org/apache/activemq/web/view/SimpleMessageRenderer.java @@ -26,6 +26,7 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.apache.activemq.web.util.ViewUtils; /** * A simple rendering of the contents of a queue appear as a list of message @@ -35,11 +36,12 @@ */ public class SimpleMessageRenderer implements MessageRenderer { - private String contentType = "text/xml"; + protected static final String DEFAULT_CONTENT_TYPE = "text/xml"; + private int maxMessages; public void renderMessages(HttpServletRequest request, HttpServletResponse response, QueueBrowser browser) throws IOException, JMSException, ServletException { - // lets use XML by default + // XML is used by default unless a child class overrides this method response.setContentType(getContentType()); PrintWriter writer = response.getWriter(); printHeader(writer, browser, request); @@ -53,10 +55,10 @@ public void renderMessages(HttpServletRequest request, HttpServletResponse respo printFooter(writer, browser, request); } - public void renderMessage(PrintWriter writer, HttpServletRequest request, HttpServletResponse response, QueueBrowser browser, Message message) throws JMSException, ServletException { + public void renderMessage(PrintWriter writer, HttpServletRequest request, HttpServletResponse response, QueueBrowser browser, Message message) throws JMSException { // lets just write the message IDs for now writer.print(""); } @@ -71,25 +73,21 @@ public void setMaxMessages(int maxMessages) { } public String getContentType() { - return contentType; - } - - public void setContentType(String contentType) { - this.contentType = contentType; + return DEFAULT_CONTENT_TYPE; } // Implementation methods // ------------------------------------------------------------------------- - protected void printHeader(PrintWriter writer, QueueBrowser browser, HttpServletRequest request) throws IOException, JMSException, ServletException { + protected void printHeader(PrintWriter writer, QueueBrowser browser, HttpServletRequest request) throws IOException, JMSException { writer.println(""); writer.print(""); diff --git a/activemq-web/src/test/java/org/apache/activemq/web/util/ViewUtilsTest.java b/activemq-web/src/test/java/org/apache/activemq/web/util/ViewUtilsTest.java new file mode 100644 index 00000000000..668edb2fce9 --- /dev/null +++ b/activemq-web/src/test/java/org/apache/activemq/web/util/ViewUtilsTest.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.activemq.web.util; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.apache.commons.text.StringEscapeUtils; +import org.junit.Test; + +public class ViewUtilsTest { + + @Test + public void testXmlEscape() throws IOException { + final String original = Files.readString(Path.of("src/test/resources/activemq.xml")); + final String escaped = ViewUtils.escapeXml(original); + + // Verify that our escape method matches StringEscapeUtils + assertEquals(StringEscapeUtils.escapeXml11(original), ViewUtils.escapeXml(original)); + // Verify if we unescape we get back the original + assertEquals(original, StringEscapeUtils.unescapeXml(escaped)); + } +} diff --git a/activemq-web/src/test/resources/activemq.xml b/activemq-web/src/test/resources/activemq.xml new file mode 100644 index 00000000000..9affdddf3e8 --- /dev/null +++ b/activemq-web/src/test/resources/activemq.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 260f4342fa2..4e3943b0ced 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,7 @@ 2.21.0 1.3.6 2.13.1 + 1.15.0 2.0.0.AM25 3.45.0 1.11 @@ -901,6 +902,12 @@ ${commons-io-version} + + org.apache.commons + commons-text + ${commons-text-version} + + com.rometools