This is an automated email from the ASF dual-hosted git repository.

vavrtom pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git


The following commit(s) were added to refs/heads/main by this push:
     new f962e7f94f QPID-8675 - [Broker-J] XSS vulnerability in path (#245)
f962e7f94f is described below

commit f962e7f94f2dbc7eafaf7a799869d1566088556c
Author: Daniil Kirilyuk <[email protected]>
AuthorDate: Mon Sep 30 09:55:21 2024 +0200

    QPID-8675 - [Broker-J] XSS vulnerability in path (#245)
---
 .../org/apache/qpid/server/util/StringUtil.java    | 105 +++++++++++-
 .../apache/qpid/server/util/StringUtilTest.java    |  16 ++
 .../plugin/servlet/DefinedFileServlet.java         | 113 ------------
 .../management/plugin/servlet/RootServlet.java     |  52 +++---
 .../management/plugin/servlet/RootServletTest.java | 190 +++++++++++++++++++++
 .../src/test/resources/resources/file.txt          |  17 ++
 6 files changed, 347 insertions(+), 146 deletions(-)

diff --git 
a/broker-core/src/main/java/org/apache/qpid/server/util/StringUtil.java 
b/broker-core/src/main/java/org/apache/qpid/server/util/StringUtil.java
index 0ac09da802..159178eafd 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/util/StringUtil.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/util/StringUtil.java
@@ -20,9 +20,15 @@
  */
 package org.apache.qpid.server.util;
 
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.BitSet;
+import java.util.Comparator;
+import java.util.Map;
 import java.util.Random;
 import java.security.SecureRandom;
 
@@ -33,9 +39,22 @@ public class StringUtil
     private static final String OTHERS = "_-";
     private static final char[] CHARACTERS = (NUMBERS + LETTERS + 
LETTERS.toUpperCase() + OTHERS).toCharArray();
     private static final char[] HEX = "0123456789ABCDEF".toCharArray();
+    private static final Map<CharSequence, CharSequence> HTML_ESCAPE = 
Map.of("\"", "&quot;",
+            "&", "&amp;",
+            "<", "&lt;",
+            ">", "&gt;",
+            "'", "&#x27;");
+    private static final BitSet HTML_ESCAPE_PREFIX_SET = new BitSet();
+    private static final int LONGEST_HTML_ESCAPE_ENTRY = 
HTML_ESCAPE.values().stream()
+            .max(Comparator.comparingInt(CharSequence::length))
+            .get().length();
+    private static final Random RANDOM = new SecureRandom();
 
-   
-    private final Random _random = new SecureRandom();
+    static
+    {
+        HTML_ESCAPE.keySet().forEach(key -> 
HTML_ESCAPE_PREFIX_SET.set(key.charAt(0)));
+
+    }
 
     public static String elideDataUrl(final String path)
     {
@@ -57,7 +76,7 @@ public class StringUtil
         char[] result = new char[maxLength];
         for (int i = 0; i < maxLength; i++)
         {
-            result[i] = (char) CHARACTERS[_random.nextInt(CHARACTERS.length)];
+            result[i] = CHARACTERS[RANDOM.nextInt(CHARACTERS.length)];
         }
         return new String(result);
     }
@@ -69,9 +88,9 @@ public class StringUtil
      * @param managerName
      * @return unique java name
      */
-    public String createUniqueJavaName(String managerName)
+    public String createUniqueJavaName(final String managerName)
     {
-        StringBuilder builder = new StringBuilder();
+        final StringBuilder builder = new StringBuilder();
         boolean initialChar = true;
         for (int i = 0; i < managerName.length(); i++)
         {
@@ -89,7 +108,7 @@ public class StringUtil
         }
         try
         {
-            byte[] digest = 
MessageDigest.getInstance("MD5").digest(managerName.getBytes(StandardCharsets.UTF_8));
+            final byte[] digest = 
MessageDigest.getInstance("MD5").digest(managerName.getBytes(StandardCharsets.UTF_8));
             builder.append(toHex(digest).toLowerCase());
         }
         catch (NoSuchAlgorithmException e)
@@ -99,4 +118,78 @@ public class StringUtil
         return builder.toString();
     }
 
+    public static String escapeHtml4(final String input)
+    {
+        if (input == null)
+        {
+            return null;
+        }
+        try
+        {
+            final StringWriter writer = new StringWriter(input.length() * 2);
+            translate(input, writer);
+            return writer.toString();
+        }
+        catch (IOException e)
+        {
+            throw new ConnectionScopedRuntimeException(e);
+        }
+    }
+
+    private static void translate(final CharSequence input, final Writer 
writer) throws IOException
+    {
+        int pos = 0;
+        int len = input.length();
+
+        while (pos < len)
+        {
+            int consumed = translate(input, pos, writer);
+            if (consumed == 0)
+            {
+                char c1 = input.charAt(pos);
+                writer.write(c1);
+                ++pos;
+                if (Character.isHighSurrogate(c1) && pos < len)
+                {
+                    char c2 = input.charAt(pos);
+                    if (Character.isLowSurrogate(c2))
+                    {
+                        writer.write(c2);
+                        ++pos;
+                    }
+                }
+            }
+            else
+            {
+                for (int pt = 0; pt < consumed; ++pt)
+                {
+                    pos += Character.charCount(Character.codePointAt(input, 
pos));
+                }
+            }
+        }
+    }
+
+    private static int translate(final CharSequence input, final int index, 
final Writer writer) throws IOException
+    {
+        if (HTML_ESCAPE_PREFIX_SET.get(input.charAt(index)))
+        {
+            int max = LONGEST_HTML_ESCAPE_ENTRY;
+            if (index + LONGEST_HTML_ESCAPE_ENTRY > input.length())
+            {
+                max = input.length() - index;
+            }
+
+            for (int i = max; i >= 1; --i)
+            {
+                final CharSequence subSeq = input.subSequence(index, index + 
i);
+                final String result = (String) 
HTML_ESCAPE.get(subSeq.toString());
+                if (result != null)
+                {
+                    writer.write(result);
+                    return Character.codePointCount(subSeq, 0, 
subSeq.length());
+                }
+            }
+        }
+        return 0;
+    }
 }
diff --git 
a/broker-core/src/test/java/org/apache/qpid/server/util/StringUtilTest.java 
b/broker-core/src/test/java/org/apache/qpid/server/util/StringUtilTest.java
index 1c32d6823c..4136855d62 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/util/StringUtilTest.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/util/StringUtilTest.java
@@ -21,6 +21,7 @@
 package org.apache.qpid.server.util;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.junit.jupiter.api.BeforeEach;
@@ -70,4 +71,19 @@ public class StringUtilTest extends UnitTestBase
         assertEquals("97b247ba19ff869340d3797cc73ca065", 
_util.createUniqueJavaName("1++++----"));
         assertEquals("d41d8cd98f00b204e9800998ecf8427e", 
_util.createUniqueJavaName(""));
     }
+
+    @Test
+    public void escapeHtml4()
+    {
+        assertNull(StringUtil.escapeHtml4(null));
+        assertEquals("", StringUtil.escapeHtml4(""));
+        assertEquals("test", StringUtil.escapeHtml4("test"));
+        assertEquals("&lt;test&gt;", StringUtil.escapeHtml4("<test>"));
+        assertEquals("&quot;test&quot;", StringUtil.escapeHtml4("\"test\""));
+        assertEquals("&amp;", StringUtil.escapeHtml4("&"));
+        assertEquals("&amp; &amp; &lt; &quot; &gt; &amp; &amp;", 
StringUtil.escapeHtml4("& & < \" > & &"));
+        assertEquals("&gt;&gt;&gt;", StringUtil.escapeHtml4(">>>"));
+        assertEquals("&lt;&lt;&lt;", StringUtil.escapeHtml4("<<<"));
+        assertEquals("&#x27;&#x27;&#x27;", StringUtil.escapeHtml4("'''"));
+    }
 }
diff --git 
a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java
 
b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java
deleted file mode 100644
index c23c5c392c..0000000000
--- 
a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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.qpid.server.management.plugin.servlet;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-
-import jakarta.servlet.ServletConfig;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServlet;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-import org.apache.qpid.server.management.plugin.HttpManagementUtil;
-
-public class DefinedFileServlet extends HttpServlet
-{
-    private static final long serialVersionUID = 1L;
-
-    private static final String FILENAME_INIT_PARAMETER = "filename";
-    private final String _expectedPath;
-    private final String _apiDocsPath;
-
-    private String _filename;
-
-    public DefinedFileServlet()
-    {
-        this(null);
-    }
-
-    public DefinedFileServlet(String filename)
-    {
-        this(filename, null, null);
-    }
-
-
-    public DefinedFileServlet(String filename, String expectedPath, String 
apiDocsPath)
-    {
-        _filename = filename;
-        _expectedPath = expectedPath;
-        _apiDocsPath = apiDocsPath;
-    }
-
-    @Override
-    public void init() throws ServletException
-    {
-        ServletConfig config = getServletConfig();
-        String fileName = config.getInitParameter(FILENAME_INIT_PARAMETER);
-        if (fileName != null && !"".equals(fileName))
-        {
-            _filename = fileName;
-        }
-    }
-
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse 
response) throws ServletException, IOException
-    {
-        final String path = request.getServletPath();
-        if(_expectedPath == null || _expectedPath.equals(path == null ? "" : 
path))
-        {
-            try (OutputStream output = 
HttpManagementUtil.getOutputStream(request, response))
-            {
-                try (InputStream fileInput = 
getClass().getResourceAsStream("/resources/" + _filename))
-                {
-                    if (fileInput != null)
-                    {
-                        byte[] buffer = new byte[1024];
-                        response.setStatus(HttpServletResponse.SC_OK);
-                        int read = 0;
-
-                        while ((read = fileInput.read(buffer)) > 0)
-                        {
-                            output.write(buffer, 0, read);
-                        }
-                    }
-                    else
-                    {
-                        response.sendError(HttpServletResponse.SC_NOT_FOUND, 
"unknown file: " + _filename);
-                    }
-                }
-            }
-        }
-        else
-        {
-            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
-            try (OutputStream output = 
HttpManagementUtil.getOutputStream(request, response))
-            {
-                final String notFoundMessage = "Unknown path '"
-                                 + request.getServletPath()
-                                 + "'. Please read the api docs at "
-                                 + request.getScheme()
-                                 + "://" + request.getServerName() + ":" + 
request.getServerPort() + _apiDocsPath + "\n";
-                output.write(notFoundMessage.getBytes(StandardCharsets.UTF_8));
-            }
-        }
-    }
-}
diff --git 
a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/RootServlet.java
 
b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/RootServlet.java
index 00e726fcdb..9ec7ecd728 100644
--- 
a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/RootServlet.java
+++ 
b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/RootServlet.java
@@ -27,6 +27,7 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.qpid.server.management.plugin.HttpManagementUtil;
+import org.apache.qpid.server.util.StringUtil;
 
 public class RootServlet extends HttpServlet
 {
@@ -37,7 +38,7 @@ public class RootServlet extends HttpServlet
     private final String _filename;
 
 
-    public RootServlet(String expectedPath, String apiDocsPath, String 
filename)
+    public RootServlet(final String expectedPath, final String apiDocsPath, 
final String filename)
     {
         _expectedPath = expectedPath;
         _apiDocsPath = apiDocsPath;
@@ -47,48 +48,45 @@ public class RootServlet extends HttpServlet
     @Override
     public void init() throws ServletException
     {
-
+        // currently no initialization logic needed
     }
 
     @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse 
response) throws ServletException, IOException
+    protected void doGet(final HttpServletRequest request, final 
HttpServletResponse response)
+            throws ServletException, IOException
     {
         final String path = request.getServletPath();
-        if(_expectedPath == null || _expectedPath.equals(path == null ? "" : 
path))
+        if (_expectedPath == null || _expectedPath.equals(path == null ? "" : 
path))
         {
-            try (OutputStream output = 
HttpManagementUtil.getOutputStream(request, response))
+            try (final OutputStream output = 
HttpManagementUtil.getOutputStream(request, response);
+                 final InputStream fileInput = 
getClass().getResourceAsStream("/resources/" + _filename))
             {
-                try (InputStream fileInput = 
getClass().getResourceAsStream("/resources/" + _filename))
+                if (fileInput == null)
                 {
-                    if (fileInput != null)
-                    {
-                        byte[] buffer = new byte[1024];
-                        response.setStatus(HttpServletResponse.SC_OK);
-                        int read = 0;
+                    final String fileName = StringUtil.escapeHtml4(_filename);
+                    response.sendError(HttpServletResponse.SC_NOT_FOUND, 
"unknown file: " + fileName);
+                    return;
+                }
+
+                final byte[] buffer = new byte[1024];
+                response.setStatus(HttpServletResponse.SC_OK);
+                int read;
 
-                        while ((read = fileInput.read(buffer)) > 0)
-                        {
-                            output.write(buffer, 0, read);
-                        }
-                    }
-                    else
-                    {
-                        response.sendError(HttpServletResponse.SC_NOT_FOUND, 
"unknown file: " + _filename);
-                    }
+                while ((read = fileInput.read(buffer)) > 0)
+                {
+                    output.write(buffer, 0, read);
                 }
             }
         }
         else
         {
-
             response.setStatus(HttpServletResponse.SC_NOT_FOUND);
-            try (OutputStream output = 
HttpManagementUtil.getOutputStream(request, response))
+            try (final OutputStream output = 
HttpManagementUtil.getOutputStream(request, response))
             {
-                final String notFoundMessage = "Unknown path '"
-                                 + request.getServletPath()
-                                 + "'. Please read the api docs at "
-                                 + request.getScheme()
-                                 + "://" + request.getServerName() + ":" + 
request.getServerPort() + _apiDocsPath + "\n";
+                final String servletPath = 
StringUtil.escapeHtml4(request.getServletPath());
+                final String notFoundMessage = "Unknown path \"" + servletPath 
+
+                        "\". Please read the api docs at " + 
request.getScheme() + "://" + request.getServerName() +
+                        ":" + request.getServerPort() + "/" + _apiDocsPath + 
"\n";
                 output.write(notFoundMessage.getBytes(StandardCharsets.UTF_8));
             }
         }
diff --git 
a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/RootServletTest.java
 
b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/RootServletTest.java
new file mode 100644
index 0000000000..c2e15f6ed2
--- /dev/null
+++ 
b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/RootServletTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.qpid.server.management.plugin.servlet;
+
+import static 
org.apache.qpid.server.management.plugin.HttpManagementUtil.ATTR_MANAGEMENT_CONFIGURATION;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.io.output.WriterOutputStream;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
+
+class RootServletTest
+{
+    private final HttpManagementConfiguration<?> httpManagementConfiguration = 
mock(HttpManagementConfiguration.class);
+    private final ServletContext servletContext = mock(ServletContext.class);
+    private final HttpServletRequest request = mock(HttpServletRequest.class);
+    private final HttpServletResponse response = 
mock(HttpServletResponse.class);
+
+    private final RootServlet _rootServlet = new RootServlet("expectedPath", 
"apiDocsPath", "file.txt");
+
+    @BeforeEach
+    void beforeEach()
+    {
+        
when(servletContext.getAttribute(ATTR_MANAGEMENT_CONFIGURATION)).thenReturn(httpManagementConfiguration);
+        when(request.getScheme()).thenReturn("http");
+        when(request.getServerName()).thenReturn("localhost");
+        when(request.getServerPort()).thenReturn(8080);
+        when(request.getServletContext()).thenReturn(servletContext);
+    }
+
+    @Test
+    void expectedPathUnknownFile() throws Exception
+    {
+        when(request.getServletPath()).thenReturn("expectedPath");
+
+        try (final StringWriter stringWriter = new StringWriter();
+             final OutputStream outputStream = new 
WriterOutputStream(stringWriter, StandardCharsets.UTF_8))
+        {
+            final ServletOutputStream servletOutputStream = 
mock(ServletOutputStream.class);
+            doAnswer(invocationOnMock ->
+            {
+                outputStream.write(invocationOnMock.getArgument(0));
+                return null;
+            }).when(servletOutputStream).write(any(byte[].class));
+            when(response.getOutputStream()).thenReturn(servletOutputStream);
+
+            doAnswer(invocationOnMock ->
+            {
+                final String arg = invocationOnMock.getArgument(1);
+                outputStream.write(arg.getBytes(StandardCharsets.UTF_8));
+                return null;
+            }).when(response).sendError(any(int.class), any(String.class));
+
+            new RootServlet("expectedPath", "apiDocsPath", 
"unknown-file.txt").doGet(request, response);
+
+            outputStream.flush();
+            assertEquals("unknown file: unknown-file.txt", 
stringWriter.toString());
+        }
+    }
+
+    @Test
+    void expectedPathKnownFile() throws Exception
+    {
+        when(request.getServletPath()).thenReturn("expectedPath");
+
+        try (final StringWriter stringWriter = new StringWriter();
+             final OutputStream outputStream = new 
WriterOutputStream(stringWriter, StandardCharsets.UTF_8))
+        {
+            final ServletOutputStream servletOutputStream = 
mock(ServletOutputStream.class);
+            doAnswer(invocationOnMock ->
+            {
+                outputStream.write(invocationOnMock.getArgument(0), 
invocationOnMock.getArgument(1), invocationOnMock.getArgument(2));
+                return null;
+            }).when(servletOutputStream).write(any(byte[].class), 
any(int.class), any(int.class));
+            when(response.getOutputStream()).thenReturn(servletOutputStream);
+
+            _rootServlet.doGet(request, response);
+
+            outputStream.flush();
+            assertTrue(stringWriter.toString().contains("result"));
+        }
+    }
+
+    @Test
+    void unknownPath () throws Exception
+    {
+        when(request.getServletPath()).thenReturn("unknown");
+
+        try (final StringWriter stringWriter = new StringWriter();
+             final OutputStream outputStream = new 
WriterOutputStream(stringWriter, StandardCharsets.UTF_8))
+        {
+            final ServletOutputStream servletOutputStream = 
mock(ServletOutputStream.class);
+            doAnswer(invocationOnMock ->
+            {
+                outputStream.write(invocationOnMock.getArgument(0));
+                return null;
+            }).when(servletOutputStream).write(any(byte[].class));
+            when(response.getOutputStream()).thenReturn(servletOutputStream);
+
+            _rootServlet.doGet(request, response);
+
+            outputStream.flush();
+            assertEquals("Unknown path \"unknown\". Please read the api docs 
at "  +
+                    "http://localhost:8080/apiDocsPath\n";, 
stringWriter.toString());
+        }
+    }
+
+    @Test
+    void escapedUnknownPath () throws Exception
+    {
+        when(request.getServletPath()).thenReturn("<unknown> & \"test\" 'test' 
");
+
+        try (final StringWriter stringWriter = new StringWriter();
+             final OutputStream outputStream = new 
WriterOutputStream(stringWriter, StandardCharsets.UTF_8))
+        {
+            final ServletOutputStream servletOutputStream = 
mock(ServletOutputStream.class);
+            doAnswer(invocationOnMock ->
+            {
+                outputStream.write(invocationOnMock.getArgument(0));
+                return null;
+            }).when(servletOutputStream).write(any(byte[].class));
+            when(response.getOutputStream()).thenReturn(servletOutputStream);
+
+            _rootServlet.doGet(request, response);
+
+            outputStream.flush();
+            assertEquals("Unknown path \"&lt;unknown&gt; &amp; 
&quot;test&quot; &#x27;test&#x27; \". " +
+                    "Please read the api docs at 
http://localhost:8080/apiDocsPath\n";, stringWriter.toString());
+        }
+    }
+
+    @Test
+    void escapedUnknownFile () throws Exception
+    {
+        when(request.getServletPath()).thenReturn("expectedPath");
+
+        try (final StringWriter stringWriter = new StringWriter();
+             final OutputStream outputStream = new 
WriterOutputStream(stringWriter, StandardCharsets.UTF_8))
+        {
+            final ServletOutputStream servletOutputStream = 
mock(ServletOutputStream.class);
+            doAnswer(invocationOnMock ->
+            {
+                outputStream.write(invocationOnMock.getArgument(0));
+                return null;
+            }).when(servletOutputStream).write(any(byte[].class));
+            when(response.getOutputStream()).thenReturn(servletOutputStream);
+
+            doAnswer(invocationOnMock ->
+            {
+                final String arg = invocationOnMock.getArgument(1);
+                outputStream.write(arg.getBytes(StandardCharsets.UTF_8));
+                return null;
+            }).when(response).sendError(any(int.class), any(String.class));
+
+            new RootServlet("expectedPath", "apiDocsPath", "<unknown> & 
\"test\" 'test'.txt").doGet(request, response);
+
+            outputStream.flush();
+            assertEquals("unknown file: &lt;unknown&gt; &amp; &quot;test&quot; 
&#x27;test&#x27;.txt", stringWriter.toString());
+        }
+    }
+}
diff --git 
a/broker-plugins/management-http/src/test/resources/resources/file.txt 
b/broker-plugins/management-http/src/test/resources/resources/file.txt
new file mode 100644
index 0000000000..fbeadb9d49
--- /dev/null
+++ b/broker-plugins/management-http/src/test/resources/resources/file.txt
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+result
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to