-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added sanitization filter for request sanitization
- Loading branch information
Showing
7 changed files
with
419 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
151 changes: 151 additions & 0 deletions
151
src/main/java/com/salessparrow/api/filter/SanitizationFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package com.salessparrow.api.filter; | ||
|
||
import jakarta.servlet.Filter; | ||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.FilterConfig; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.ServletRequest; | ||
import jakarta.servlet.ServletResponse; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
|
||
import org.owasp.html.HtmlPolicyBuilder; | ||
import org.owasp.html.PolicyFactory; | ||
|
||
import com.salessparrow.api.lib.wrappers.SanitizedRequestWrapper; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.util.Enumeration; | ||
|
||
/** | ||
* Class to sanitize the request | ||
*/ | ||
public class SanitizationFilter implements Filter { | ||
private SanitizedRequestWrapper sanitizedRequest; | ||
|
||
private final PolicyFactory policy = new HtmlPolicyBuilder() | ||
.allowElements( | ||
"a", "label", "h1", "h2", "h3", "h4", "h5", "h6", | ||
"p", "i", "b", "u", "strong", "em", "strike", "code", "hr", "br", "div", | ||
"table", "thead", "caption", "tbody", "tr", "th", "td", "pre") | ||
.allowUrlProtocols("https") | ||
.allowAttributes("href").onElements("a") | ||
.allowAttributes("target").onElements("a") | ||
.allowAttributes("class").globally() | ||
.allowAttributes("id").globally() | ||
.disallowElements( | ||
"script", "iframe", "object", "embed", "form", "input", "button", "select", | ||
"textarea", "style", "link", "meta", "base" | ||
) | ||
.toFactory(); | ||
|
||
@Override | ||
public void init(FilterConfig filterConfig) { | ||
} | ||
|
||
/** | ||
* Method to sanitize the request | ||
* | ||
* @param servletRequest - Servlet request object | ||
* @param servletResponse - Servlet response object | ||
* @param chain - Filter chain - Filter chain | ||
* | ||
* @throws IOException - IOException | ||
* @throws ServletException - ServletException | ||
* | ||
* @return void | ||
*/ | ||
@Override | ||
public void doFilter( | ||
ServletRequest servletRequest, | ||
ServletResponse servletResponse, | ||
FilterChain chain | ||
) throws IOException, ServletException { | ||
HttpServletRequest request = (HttpServletRequest) servletRequest; | ||
sanitizeRequestBody(request); | ||
sanitizeRequestParams(); | ||
sanitizeRequestHeaders(); | ||
|
||
chain.doFilter(sanitizedRequest, servletResponse); | ||
} | ||
|
||
/** | ||
* Method to sanitize the request body | ||
* | ||
* @param request - Servlet request object | ||
* | ||
* @return void | ||
*/ | ||
private void sanitizeRequestBody(HttpServletRequest request) { | ||
String originalBody = getRequestBody(request); | ||
String sanitizedBody = sanitizeHtml(originalBody); | ||
this.sanitizedRequest = new SanitizedRequestWrapper(request, sanitizedBody); | ||
} | ||
|
||
/** | ||
* Method to sanitize the request params | ||
* | ||
* @return void | ||
*/ | ||
private void sanitizeRequestParams() { | ||
this.sanitizedRequest.getParameterMap().forEach((key, value) -> { | ||
String sanitizedValue = sanitizeHtml(value[0]); | ||
this.sanitizedRequest.setParameter(key, sanitizedValue); | ||
}); | ||
} | ||
|
||
/** | ||
* Method to sanitize the request headers | ||
* | ||
* @return void | ||
*/ | ||
private void sanitizeRequestHeaders() { | ||
Enumeration<String> headerNames = this.sanitizedRequest.getHeaderNames(); | ||
|
||
if (headerNames != null && headerNames.hasMoreElements()) { | ||
this.sanitizedRequest.getHeaderNames().asIterator().forEachRemaining(headerName -> { | ||
String sanitizedValue = sanitizeHtml(this.sanitizedRequest.getHeader(headerName)); | ||
this.sanitizedRequest.setHeader(headerName, sanitizedValue); | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Method to get the request body | ||
* | ||
* @param request - Servlet request object | ||
* @return String - Request body | ||
*/ | ||
private String getRequestBody(HttpServletRequest request) { | ||
try { | ||
BufferedReader reader = request.getReader(); | ||
String line; | ||
StringBuilder requestBody = new StringBuilder(); | ||
while ((line = reader.readLine()) != null) { | ||
requestBody.append(line); | ||
} | ||
return requestBody.toString(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
return ""; | ||
} | ||
} | ||
|
||
/** | ||
* Method to sanitize the html | ||
* | ||
* @param input - Input string | ||
* @return String - Sanitized string | ||
*/ | ||
public String sanitizeHtml(String input) { | ||
String sanitizedInput = policy.sanitize(input); | ||
return sanitizedInput; | ||
} | ||
|
||
/** | ||
* Method to destroy the filter | ||
*/ | ||
@Override | ||
public void destroy() { | ||
} | ||
} |
117 changes: 117 additions & 0 deletions
117
src/main/java/com/salessparrow/api/lib/wrappers/SanitizedRequestWrapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package com.salessparrow.api.lib.wrappers; | ||
|
||
import jakarta.servlet.ReadListener; | ||
import jakarta.servlet.ServletInputStream; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletRequestWrapper; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.ByteArrayInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import org.apache.commons.text.StringEscapeUtils; | ||
|
||
/** | ||
* Custom request wrapper to sanitize the request body | ||
*/ | ||
public class SanitizedRequestWrapper extends HttpServletRequestWrapper { | ||
private final String sanitizedBody; | ||
|
||
private Map<String, String> sanitizedParams; | ||
|
||
private Map<String, String> sanitizedHeaders; | ||
|
||
public SanitizedRequestWrapper(HttpServletRequest request, String sanitizedBody) { | ||
super(request); | ||
this.sanitizedBody = StringEscapeUtils.unescapeHtml4(sanitizedBody); | ||
this.sanitizedParams = new HashMap<>(); | ||
this.sanitizedHeaders = new HashMap<>(); | ||
} | ||
|
||
/** | ||
* Method to get the request body as buffered reader | ||
*/ | ||
@Override | ||
public BufferedReader getReader() throws IOException { | ||
return new BufferedReader( | ||
new InputStreamReader( | ||
new ByteArrayInputStream(sanitizedBody.getBytes()))); | ||
} | ||
|
||
/** | ||
* Method to get the request body as input stream | ||
*/ | ||
@Override | ||
public ServletInputStream getInputStream() throws IOException { | ||
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(sanitizedBody.getBytes()); | ||
|
||
return new ServletInputStream() { | ||
@Override | ||
public int read() throws IOException { | ||
return byteArrayInputStream.read(); | ||
} | ||
|
||
@Override | ||
public boolean isFinished() { | ||
return byteArrayInputStream.available() == 0; | ||
} | ||
|
||
@Override | ||
public boolean isReady() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public void setReadListener(ReadListener readListener) { | ||
throw new UnsupportedOperationException("Not implemented"); | ||
} | ||
}; | ||
} | ||
|
||
/** | ||
* Method to set the request parameter value by key | ||
* | ||
* @param key | ||
* @param value | ||
* | ||
* @return void | ||
*/ | ||
public void setParameter(String key, String value) { | ||
this.sanitizedParams.put(key, value); | ||
} | ||
|
||
/** | ||
* Method to get the request parameter value by | ||
* key from the sanitized params map if present | ||
* else from the super class | ||
* | ||
* @param key - Request parameter key | ||
* | ||
* @return String - Request parameter value | ||
*/ | ||
@Override | ||
public String getParameter(String key) { | ||
return this.sanitizedParams.getOrDefault(key, super.getParameter(key)); | ||
} | ||
|
||
/** | ||
* Method to get the request parameter map | ||
* | ||
* @param key - header key | ||
* @param value - header value | ||
* | ||
* @return void | ||
*/ | ||
public void setHeader(String key, String value) { | ||
this.sanitizedHeaders.put(key, value); | ||
} | ||
|
||
@Override | ||
public String getHeader(String key) { | ||
return this.sanitizedHeaders.getOrDefault(key, super.getHeader(key)); | ||
} | ||
} | ||
|
Oops, something went wrong.