diff --git a/legal-core/src/main/java/org/opengroup/osdu/legal/middleware/GlobalErrorHandler.java b/legal-core/src/main/java/org/opengroup/osdu/legal/middleware/GlobalErrorHandler.java index eee17cfa44c1707102ed66eb7d147e45b4e7be8a..a086f4020ac730bb09ce81d150a89400d6ac01ce 100644 --- a/legal-core/src/main/java/org/opengroup/osdu/legal/middleware/GlobalErrorHandler.java +++ b/legal-core/src/main/java/org/opengroup/osdu/legal/middleware/GlobalErrorHandler.java @@ -7,6 +7,7 @@ import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.apache.commons.text.StringEscapeUtils; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -27,6 +28,8 @@ public class GlobalErrorHandler implements ErrorController { if(exception instanceof AppException){ AppException appException = (AppException)exception; String message = appException.getError().getMessage(); + // need sanitize the user's input because inputs may contain EL expressions like ${expression} + sanitization(appException); return new ResponseEntity<Object>(appException.getError(), HttpStatus.resolve(appException.getError().getCode())); } else if (statusCode != null) { @@ -36,4 +39,38 @@ public class GlobalErrorHandler implements ErrorController { throw new AppException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Server error", "An unknown error has occurred."); } -} \ No newline at end of file + /** + * Need to sanitize only two fields: message and reason. No need to sanitize other AppError object's fields since + * they are annotated with @JsonIgnore thus they never will reach a client. + */ + private static void sanitization(AppException appException) { + sanitizeMessage(appException); + sanitizeReason(appException); + } + + private static void sanitizeMessage(AppException appException) { + String message = appException.getError().getMessage(); + message = sanitize(message); + appException.getError().setMessage(message); + } + + private static void sanitizeReason(AppException appException) { + String reason = appException.getError().getReason(); + reason = sanitize(reason); + appException.getError().setReason(reason); + } + + private static String sanitize(String message) { + if (message != null) { + message = StringEscapeUtils.escapeHtml4(message); + message = message.replace("#{", "") + .replace("${", "") + .replace("}", "") + .replace("'", "") + .replace("\"", ""); + } else { + message = "No message given in AppException"; + } + return message; + } +} diff --git a/legal-core/src/test/java/org/opengroup/osdu/legal/middleware/GlobalErrorHandlerTest.java b/legal-core/src/test/java/org/opengroup/osdu/legal/middleware/GlobalErrorHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..98852194f11c82d6b95e365c4adc7abee9a769ee --- /dev/null +++ b/legal-core/src/test/java/org/opengroup/osdu/legal/middleware/GlobalErrorHandlerTest.java @@ -0,0 +1,49 @@ +package org.opengroup.osdu.legal.middleware; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opengroup.osdu.core.common.model.http.AppException; + +@ExtendWith(MockitoExtension.class) +class GlobalErrorHandlerTest { + + @Mock + HttpServletRequest request; + + @Test + void givenMessageWhenExceptionIsThrownThenMessageSanitiseTriggered() { + // given + String originalMessage = "original${}Message"; + String expectedMessage = "originalMessage"; + AppException appException = new AppException(200, "reason", originalMessage); + appException.getError().setMessage(originalMessage); + when(request.getAttribute("jakarta.servlet.error.exception")).thenReturn(appException); + // when + GlobalErrorHandler globalErrorHandler = new GlobalErrorHandler(); + globalErrorHandler.handleError(request, null); + // then + assertEquals(expectedMessage, appException.getError().getMessage()); + } + + @Test + void givenReasonWhenExceptionIsThrownThenReasonSanitiseTriggered() { + // given + String originalReason = "original${}Reason"; + String expectedReason = "originalReason"; + AppException appException = new AppException(200, originalReason, "message"); + appException.getError().setReason(originalReason); + when(request.getAttribute("jakarta.servlet.error.exception")).thenReturn(appException); + // when + GlobalErrorHandler globalErrorHandler = new GlobalErrorHandler(); + globalErrorHandler.handleError(request, null); + // then + assertEquals(expectedReason, appException.getError().getReason()); + } + +}