From 7ffc7509acc203d167f4e4582226a20addb6c03d Mon Sep 17 00:00:00 2001 From: Manfred Date: Wed, 23 Aug 2023 00:09:58 +0100 Subject: [PATCH] BAEL-6848: Set a Parameter in a HttpServletRequest in Java --- web-modules/javax-servlets-2/pom.xml | 17 ++++++ .../baeldung/setparam/LanguageServlet.java | 21 +++++++ .../setparam/SanitizeParametersFilter.java | 17 ++++++ .../SanitizeParametersRequestWrapper.java | 46 ++++++++++++++ .../setparam/SetParameterRequestWrapper.java | 40 ++++++++++++ .../main/webapp/setparam/3rd_party_module.jsp | 13 ++++ .../main/webapp/setparam/with-sanitize.jsp | 10 +++ .../main/webapp/setparam/without-sanitize.jsp | 10 +++ .../LanguageServletIntegrationTest.java | 34 +++++++++++ ...itizeParametersRequestIntegrationTest.java | 40 ++++++++++++ ...itizeParametersRequestWrapperUnitTest.java | 60 ++++++++++++++++++ .../SetParameterRequestWrapperUnitTest.java | 61 +++++++++++++++++++ ...tizedParametersRequestIntegrationTest.java | 42 +++++++++++++ 13 files changed, 411 insertions(+) create mode 100644 web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/LanguageServlet.java create mode 100644 web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SanitizeParametersFilter.java create mode 100644 web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SanitizeParametersRequestWrapper.java create mode 100644 web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SetParameterRequestWrapper.java create mode 100644 web-modules/javax-servlets-2/src/main/webapp/setparam/3rd_party_module.jsp create mode 100644 web-modules/javax-servlets-2/src/main/webapp/setparam/with-sanitize.jsp create mode 100644 web-modules/javax-servlets-2/src/main/webapp/setparam/without-sanitize.jsp create mode 100644 web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/LanguageServletIntegrationTest.java create mode 100644 web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SanitizeParametersRequestIntegrationTest.java create mode 100644 web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SanitizeParametersRequestWrapperUnitTest.java create mode 100644 web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SetParameterRequestWrapperUnitTest.java create mode 100644 web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/UnsanitizedParametersRequestIntegrationTest.java diff --git a/web-modules/javax-servlets-2/pom.xml b/web-modules/javax-servlets-2/pom.xml index 81616e0055..9ba12352fd 100644 --- a/web-modules/javax-servlets-2/pom.xml +++ b/web-modules/javax-servlets-2/pom.xml @@ -22,6 +22,11 @@ commons-fileupload ${commons-fileupload.version} + + org.apache.commons + commons-text + ${commons-text.version} + javax.servlet @@ -76,6 +81,16 @@ + + org.eclipse.jetty + jetty-maven-plugin + ${jetty-maven-plugin.version} + + + / + + + @@ -84,6 +99,8 @@ 1.49 5.3.20 2.22.2 + 10.0.4 + 1.10.0 \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/LanguageServlet.java b/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/LanguageServlet.java new file mode 100644 index 0000000000..983066ec51 --- /dev/null +++ b/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/LanguageServlet.java @@ -0,0 +1,21 @@ +package com.baeldung.setparam; + +import java.io.IOException; +import java.util.Locale; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.*; + +@WebServlet(name = "LanguageServlet", urlPatterns = "/setparam/lang") +public class LanguageServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + + SetParameterRequestWrapper requestWrapper = new SetParameterRequestWrapper(request); + requestWrapper.setParameter("locale", Locale.getDefault().getLanguage()); + request.getRequestDispatcher("/setparam/3rd_party_module.jsp").forward(requestWrapper, response); + } + +} \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SanitizeParametersFilter.java b/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SanitizeParametersFilter.java new file mode 100644 index 0000000000..3f93591bd9 --- /dev/null +++ b/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SanitizeParametersFilter.java @@ -0,0 +1,17 @@ +package com.baeldung.setparam; + +import java.io.IOException; +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; + +@WebFilter(urlPatterns = { "/setparam/with-sanitize.jsp" }) +public class SanitizeParametersFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpReq = (HttpServletRequest) request; + chain.doFilter(new SanitizeParametersRequestWrapper(httpReq), response); + } + +} \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SanitizeParametersRequestWrapper.java b/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SanitizeParametersRequestWrapper.java new file mode 100644 index 0000000000..e4c2870ca6 --- /dev/null +++ b/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SanitizeParametersRequestWrapper.java @@ -0,0 +1,46 @@ +package com.baeldung.setparam; + +import java.util.*; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import org.apache.commons.text.StringEscapeUtils; + +public class SanitizeParametersRequestWrapper extends HttpServletRequestWrapper { + + private final Map sanitizedMap; + + public SanitizeParametersRequestWrapper(HttpServletRequest request) { + super(request); + sanitizedMap = Collections.unmodifiableMap( + request.getParameterMap().entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> Arrays.stream(entry.getValue()) + .map(StringEscapeUtils::escapeHtml4) + .toArray(String[]::new) + ))); + } + + @Override + public Map getParameterMap() { + return sanitizedMap; + } + + @Override + public String[] getParameterValues(String name) { + return Optional.ofNullable(getParameterMap().get(name)) + .map(values -> Arrays.copyOf(values, values.length)) + .orElse(null); + } + + @Override + public String getParameter(String name) { + return Optional.ofNullable(getParameterValues(name)) + .map(values -> values[0]) + .orElse(null); + } + +} \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SetParameterRequestWrapper.java b/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SetParameterRequestWrapper.java new file mode 100644 index 0000000000..7b2ab4597c --- /dev/null +++ b/web-modules/javax-servlets-2/src/main/java/com/baeldung/setparam/SetParameterRequestWrapper.java @@ -0,0 +1,40 @@ +package com.baeldung.setparam; + +import java.util.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +public class SetParameterRequestWrapper extends HttpServletRequestWrapper { + + private final Map paramMap; + + public SetParameterRequestWrapper(HttpServletRequest request) { + super(request); + paramMap = new HashMap<>(request.getParameterMap()); + } + + @Override + public Map getParameterMap() { + return Collections.unmodifiableMap(paramMap); + } + + @Override + public String[] getParameterValues(String name) { + return Optional.ofNullable(getParameterMap().get(name)) + .map(values -> Arrays.copyOf(values, values.length)) + .orElse(null); + } + + @Override + public String getParameter(String name) { + return Optional.ofNullable(getParameterValues(name)) + .map(values -> values[0]) + .orElse(null); + } + + public void setParameter(String name, String value) { + paramMap.put(name, new String[] {value}); + } + +} \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/main/webapp/setparam/3rd_party_module.jsp b/web-modules/javax-servlets-2/src/main/webapp/setparam/3rd_party_module.jsp new file mode 100644 index 0000000000..9ab4fd2c00 --- /dev/null +++ b/web-modules/javax-servlets-2/src/main/webapp/setparam/3rd_party_module.jsp @@ -0,0 +1,13 @@ +<%@ page import="java.util.*"%> + + + 3rd party Module + + + <% + String localeStr = request.getParameter("locale"); + Locale currentLocale = (localeStr != null ? new Locale(localeStr) : null); + %> + The language you have selected: <%=currentLocale != null ? currentLocale.getDisplayLanguage(currentLocale) : " None"%> + + \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/main/webapp/setparam/with-sanitize.jsp b/web-modules/javax-servlets-2/src/main/webapp/setparam/with-sanitize.jsp new file mode 100644 index 0000000000..68704d6133 --- /dev/null +++ b/web-modules/javax-servlets-2/src/main/webapp/setparam/with-sanitize.jsp @@ -0,0 +1,10 @@ + + + Sanitized request parameter + + + The text below comes from request parameter "input": +
+ <%=request.getParameter("input")%> + + \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/main/webapp/setparam/without-sanitize.jsp b/web-modules/javax-servlets-2/src/main/webapp/setparam/without-sanitize.jsp new file mode 100644 index 0000000000..f5437164ef --- /dev/null +++ b/web-modules/javax-servlets-2/src/main/webapp/setparam/without-sanitize.jsp @@ -0,0 +1,10 @@ + + + Non sanitized request parameter + + + The text below comes from request parameter "input": +
+ <%=request.getParameter("input")%> + + \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/LanguageServletIntegrationTest.java b/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/LanguageServletIntegrationTest.java new file mode 100644 index 0000000000..893ba06ce4 --- /dev/null +++ b/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/LanguageServletIntegrationTest.java @@ -0,0 +1,34 @@ +package com.baeldung.setparam; + +import static org.junit.Assert.assertTrue; + +import java.util.Locale; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.junit.Test; + +public class LanguageServletIntegrationTest { + + @Test + public void whenGetRequestUsingHttpClient_thenResponseBodyContainsDefaultLanguage() throws Exception { + + // When + HttpClient client = HttpClientBuilder.create().build(); + HttpGet method = new HttpGet("http://localhost:8080/setparam/lang"); + HttpResponse httpResponse = client.execute(method); + + // Then + Locale defaultLocale = Locale.getDefault(); + String expectedLanguage = defaultLocale.getDisplayLanguage(defaultLocale); + + HttpEntity entity = httpResponse.getEntity(); + String responseBody = EntityUtils.toString(entity, "UTF-8"); + assertTrue(responseBody.contains("The language you have selected: " + expectedLanguage)); + } + +} diff --git a/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SanitizeParametersRequestIntegrationTest.java b/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SanitizeParametersRequestIntegrationTest.java new file mode 100644 index 0000000000..047d5431f6 --- /dev/null +++ b/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SanitizeParametersRequestIntegrationTest.java @@ -0,0 +1,40 @@ +package com.baeldung.setparam; + +import static org.junit.Assert.assertTrue; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.junit.BeforeClass; +import org.junit.Test; + +public class SanitizeParametersRequestIntegrationTest { + + private static String PARAM_INPUT; + + @BeforeClass + public static void init() throws UnsupportedEncodingException { + PARAM_INPUT = URLEncoder.encode("", "UTF-8"); + } + + @Test + public void whenInputParameterContainsXss_thenResponseBodyContainsSanitizedValue() throws Exception { + + // When + HttpClient client = HttpClientBuilder.create().build(); + HttpGet method = new HttpGet(String.format("http://localhost:8080/setparam/with-sanitize.jsp?input=%s", PARAM_INPUT)); + HttpResponse httpResponse = client.execute(method); + + // Then + HttpEntity entity = httpResponse.getEntity(); + String responseBody = EntityUtils.toString(entity, "UTF-8"); + assertTrue(responseBody.contains("<script>alert('Hello');</script>")); + } + +} \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SanitizeParametersRequestWrapperUnitTest.java b/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SanitizeParametersRequestWrapperUnitTest.java new file mode 100644 index 0000000000..4f246e0953 --- /dev/null +++ b/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SanitizeParametersRequestWrapperUnitTest.java @@ -0,0 +1,60 @@ +package com.baeldung.setparam; + +import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class SanitizeParametersRequestWrapperUnitTest { + + private String NEW_VALUE = "NEW VALUE"; + + private Map parameterMap; + + @Mock + private HttpServletRequest request; + + @Before + public void initBeforeEachTest() { + parameterMap = new HashMap<>(); + parameterMap.put("input", new String[] {""}); + when(request.getParameterMap()).thenReturn(parameterMap); + } + + @Test + public void whenGetParameterViaWrapper_thenParameterReturnedIsSanitized() { + SanitizeParametersRequestWrapper wrapper = new SanitizeParametersRequestWrapper(request); + String actualValue = wrapper.getParameter("input"); + + assertEquals(actualValue, "<script>alert('Hello');</script>"); + } + + @Test(expected = UnsupportedOperationException.class) + public void whenPutValueToWrapperParameterMap_thenThrowsUnsupportedOperationException() { + SanitizeParametersRequestWrapper wrapper = new SanitizeParametersRequestWrapper(request); + Map wrapperParamMap = wrapper.getParameterMap(); + wrapperParamMap.put("input", new String[] {NEW_VALUE}); + } + + @Test + public void whenSetValueToWrapperParametersStringArray_thenThe2ndCallShouldNotEqualToNewValue() { + SanitizeParametersRequestWrapper wrapper = new SanitizeParametersRequestWrapper(request); + String[] firstCallValues = wrapper.getParameterValues("input"); + + firstCallValues[0] = NEW_VALUE; + String[] secondCallValues = wrapper.getParameterValues("input"); + assertNotEquals(firstCallValues, secondCallValues); + } + +} \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SetParameterRequestWrapperUnitTest.java b/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SetParameterRequestWrapperUnitTest.java new file mode 100644 index 0000000000..32343abb93 --- /dev/null +++ b/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/SetParameterRequestWrapperUnitTest.java @@ -0,0 +1,61 @@ +package com.baeldung.setparam; + +import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class SetParameterRequestWrapperUnitTest { + + private String NEW_VALUE = "NEW VALUE"; + + private Map parameterMap; + + @Mock + private HttpServletRequest request; + + @Before + public void initBeforeEachTest() { + parameterMap = new HashMap<>(); + parameterMap.put("input", new String[] {"inputValue"}); + when(request.getParameterMap()).thenReturn(parameterMap); + } + + @Test + public void whenSetParameterViaWrapper_thenGetParameterShouldReturnTheSameValue() { + SetParameterRequestWrapper wrapper = new SetParameterRequestWrapper(request); + wrapper.setParameter("newInput", "newInputValue"); + String actualValue = wrapper.getParameter("newInput"); + + assertEquals(actualValue, "newInputValue"); + } + + @Test(expected = UnsupportedOperationException.class) + public void whenPutValueToWrapperParameterMap_thenThrowsUnsupportedOperationException() { + SetParameterRequestWrapper wrapper = new SetParameterRequestWrapper(request); + Map wrapperParamMap = wrapper.getParameterMap(); + wrapperParamMap.put("input", new String[] {NEW_VALUE}); + } + + @Test + public void whenSetValueToWrapperParametersStringArray_thenThe2ndCallShouldNotEqualToNewValue() { + SetParameterRequestWrapper wrapper = new SetParameterRequestWrapper(request); + String[] firstCallValues = wrapper.getParameterValues("input"); + + firstCallValues[0] = NEW_VALUE; + String[] secondCallValues = wrapper.getParameterValues("input"); + assertNotEquals(firstCallValues, secondCallValues); + } + +} \ No newline at end of file diff --git a/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/UnsanitizedParametersRequestIntegrationTest.java b/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/UnsanitizedParametersRequestIntegrationTest.java new file mode 100644 index 0000000000..a00944b6a4 --- /dev/null +++ b/web-modules/javax-servlets-2/src/test/java/com/baeldung/setparam/UnsanitizedParametersRequestIntegrationTest.java @@ -0,0 +1,42 @@ +package com.baeldung.setparam; + +import static org.junit.Assert.assertTrue; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.junit.BeforeClass; +import org.junit.Test; + +public class UnsanitizedParametersRequestIntegrationTest { + + private static final String TAG_SCRIPT = ""; + + private static String PARAM_INPUT; + + @BeforeClass + public static void init() throws UnsupportedEncodingException { + PARAM_INPUT = URLEncoder.encode(TAG_SCRIPT, "UTF-8"); + } + + @Test + public void whenInputParameterContainsXss_thenResponseBodyContainsUnsanitizedValue() throws Exception { + + // When + HttpClient client = HttpClientBuilder.create().build(); + HttpGet method = new HttpGet(String.format("http://localhost:8080/setparam/without-sanitize.jsp?input=%s", PARAM_INPUT)); + HttpResponse httpResponse = client.execute(method); + + // Then + HttpEntity entity = httpResponse.getEntity(); + String responseBody = EntityUtils.toString(entity, "UTF-8"); + assertTrue(responseBody.contains(TAG_SCRIPT)); + } + +} \ No newline at end of file