-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added sanitization filter for request sanitization #78
Changes from 14 commits
4094d3e
9c4b7bc
36b5fdb
7ff2e5d
3caac81
fc6a6f8
0f05622
f9b5c06
808643a
30128bb
d93d616
6fe53b7
458e989
179ec79
2d2dec8
e6541a2
2806314
0f1a202
573ace1
bfe04e8
9c9d440
4af29a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
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.jsoup.Jsoup; | ||
import org.jsoup.nodes.Document; | ||
import org.jsoup.safety.Safelist; | ||
import org.jsoup.nodes.Entities.EscapeMode; | ||
|
||
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; | ||
|
||
@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 { | ||
if (!(servletRequest instanceof HttpServletRequest)) { | ||
throw new ServletException("Can only process HttpServletRequest"); | ||
} | ||
HttpServletRequest request = (HttpServletRequest) servletRequest; | ||
sanitizeRequestBody(request); | ||
sanitizeRequestParams(request); | ||
sanitizeRequestHeaders(request); | ||
|
||
chain.doFilter(sanitizedRequest, servletResponse); | ||
} | ||
kreloaded marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* 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); | ||
} | ||
kreloaded marked this conversation as resolved.
Show resolved
Hide resolved
kreloaded marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* Method to sanitize the request params | ||
* @return void | ||
*/ | ||
private void sanitizeRequestParams(HttpServletRequest request) { | ||
request.getParameterMap().forEach((key, values) -> { | ||
for (int index = 0; index < values.length; index++) { | ||
String sanitizedValue = sanitizeHtml(values[index]); | ||
this.sanitizedRequest.setParameter(key, sanitizedValue); | ||
} | ||
}); | ||
kreloaded marked this conversation as resolved.
Show resolved
Hide resolved
kreloaded marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
* Method to sanitize the request headers | ||
* @return void | ||
*/ | ||
private void sanitizeRequestHeaders(HttpServletRequest request) { | ||
Enumeration<String> headerNames = request.getHeaderNames(); | ||
|
||
while (headerNames != null && headerNames.hasMoreElements()) { | ||
String headerName = headerNames.nextElement(); | ||
Enumeration<String> headerValues = request.getHeaders(headerName); | ||
|
||
while (headerValues != null && headerValues.hasMoreElements()) { | ||
String headerValue = headerValues.nextElement(); | ||
String sanitizedValue = sanitizeHtml(headerValue); | ||
this.sanitizedRequest.setHeader(headerName, sanitizedValue); | ||
} | ||
} | ||
kreloaded marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
* 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; | ||
// TODO: Consider using a more efficient way to read the request body, such as | ||
// streaming | ||
StringBuilder requestBody = new StringBuilder(); | ||
while ((line = reader.readLine()) != null) { | ||
requestBody.append(line); | ||
} | ||
return requestBody.toString(); | ||
} | ||
catch (IOException e) { | ||
throw new RuntimeException("Error reading request body: ", e.getCause()); | ||
} | ||
Comment on lines
+104
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The BufferedReader is not closed after use which can lead to resource leaks. Always close resources in a finally block or use try-with-resources statement. - BufferedReader reader = request.getReader();
- String line;
- StringBuilder requestBody = new StringBuilder();
- while ((line = reader.readLine()) != null) {
- requestBody.append(line);
- }
- return requestBody.toString();
+ 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) {
+ throw new RuntimeException("Error reading request body: ", e.getCause());
+ } |
||
} | ||
Comment on lines
+104
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The method |
||
|
||
/** | ||
* Sanitizes a given HTML string by removing all HTML tags and attributes, leaving | ||
* only the text content. Also handles basic HTML entity escaping using the 'base' | ||
* escape mode. | ||
* | ||
* This function uses the Jsoup library's 'clean' method with a 'none' safelist, which | ||
* means no HTML tags are allowed and will be removed. | ||
* | ||
* The function also disables pretty-printing to ensure the resulting HTML is not | ||
* reformatted, and sets the escape mode to 'base' to handle basic HTML entities like | ||
* & and <. | ||
* | ||
* If the input is null or empty, the function returns an empty string. | ||
* @param input The original HTML string that needs to be sanitized. | ||
* @return A sanitized version of the input string, safe for display as text. | ||
*/ | ||
private String sanitizeHtml(String input) { | ||
if (input == null || input.isEmpty()) { | ||
return ""; | ||
} | ||
|
||
Safelist safelist = Safelist.none(); | ||
|
||
Document.OutputSettings outputSettings = new Document.OutputSettings(); | ||
outputSettings.prettyPrint(false); | ||
outputSettings.escapeMode(EscapeMode.base); | ||
|
||
String sanitizedInput = Jsoup.clean(input, "", safelist, outputSettings); | ||
|
||
return sanitizedInput; | ||
} | ||
Comment on lines
+136
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The method |
||
|
||
/** | ||
* Method to destroy the filter | ||
*/ | ||
@Override | ||
public void destroy() { | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,7 @@ public class CreateSalesforceNote implements CreateNoteInterface { | |
public CreateNoteFormatterDto createNote(SalesforceUser user, String accountId, NoteDto note) { | ||
String salesforceUserId = user.getExternalUserId(); | ||
|
||
logger.info("createNote note text: ", note.getText()); | ||
String noteTitle = getNoteTitleFromContent(note); | ||
String encodedNoteContent = base64Helper.base64Encode(note.getText()); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logger.info statement at line 57 is not correctly formatted. It should include a placeholder - logger.info("createNote note text: ", note.getText());
+ logger.info("createNote note text: {}", note.getText()); |
||
|
@@ -127,11 +128,16 @@ private CreateNoteFormatterDto parseResponse(String createNoteResponse) { | |
* @return String | ||
*/ | ||
private String getNoteTitleFromContent(NoteDto note) { | ||
if (note.getText().length() < 50) { | ||
return note.getText(); | ||
String noteText = note.getText(); | ||
|
||
Util util = new Util(); | ||
noteText = util.unEscapeSpecialCharactersForPlainText(noteText); | ||
|
||
if (noteText.length() < 50) { | ||
return noteText; | ||
} | ||
|
||
return note.getText().substring(0, 50); | ||
return noteText.substring(0, 50); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creating a new instance of - Util util = new Util();
- noteText = util.unEscapeSpecialCharactersForPlainText(noteText);
+ noteText = Util.unEscapeSpecialCharactersForPlainText(noteText); |
||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The filter should be able to handle all types of
ServletRequest
. Instead of throwing an exception when the request is not an instance ofHttpServletRequest
, consider skipping the sanitization process and just pass the request to the next filter in the chain.