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("\"", """,
+ "&", "&",
+ "<", "<",
+ ">", ">",
+ "'", "'");
+ 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("<test>", StringUtil.escapeHtml4("<test>"));
+ assertEquals(""test"", StringUtil.escapeHtml4("\"test\""));
+ assertEquals("&", StringUtil.escapeHtml4("&"));
+ assertEquals("& & < " > & &",
StringUtil.escapeHtml4("& & < \" > & &"));
+ assertEquals(">>>", StringUtil.escapeHtml4(">>>"));
+ assertEquals("<<<", StringUtil.escapeHtml4("<<<"));
+ assertEquals("'''", 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 \"<unknown> &
"test" 'test' \". " +
+ "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: <unknown> & "test"
'test'.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]