diff --git a/pom.xml b/pom.xml index 58462d45..d9b3c91f 100644 --- a/pom.xml +++ b/pom.xml @@ -107,6 +107,11 @@ micrometer-registry-prometheus ${micrometer.version} + + ch.qos.logback + logback-classic + 1.3.5 + com.github.ben-manes.caffeine caffeine diff --git a/src/main/java/com/uid2/shared/util/MaskingPatternLayout.java b/src/main/java/com/uid2/shared/util/MaskingPatternLayout.java new file mode 100644 index 00000000..809cbdf0 --- /dev/null +++ b/src/main/java/com/uid2/shared/util/MaskingPatternLayout.java @@ -0,0 +1,31 @@ +package com.uid2.shared.util; + +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.ILoggingEvent; + +import java.util.Map; + +public class MaskingPatternLayout extends PatternLayout { + private static final Map MASKING_PATTERNS = Map.of( + "[^\\s]+s3\\.amazonaws\\.com\\/.*X-Amz-Security-Token=[^\\s]+", "REDACTED - S3" + ); + + @Override + public String doLayout(ILoggingEvent event) { + return mask(super.doLayout(event)); + } + + private String mask(String message) { + if (message == null) { + return null; + } + + String maskedMessage = message; + for (Map.Entry entry : MASKING_PATTERNS.entrySet()) { + String regex = entry.getKey(); + String mask = entry.getValue(); + maskedMessage = maskedMessage.replaceAll(regex, mask); + } + return maskedMessage; + } +} diff --git a/src/test/java/com/uid2/shared/util/MaskingPatternLayoutTest.java b/src/test/java/com/uid2/shared/util/MaskingPatternLayoutTest.java new file mode 100644 index 00000000..fa287157 --- /dev/null +++ b/src/test/java/com/uid2/shared/util/MaskingPatternLayoutTest.java @@ -0,0 +1,69 @@ +package com.uid2.shared.util; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.pattern.FormattingConverter; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@ExtendWith(MockitoExtension.class) +public class MaskingPatternLayoutTest { + private static final Logger LOGGER = (Logger) LoggerFactory.getLogger(MaskingPatternLayoutTest.class); + private static final MaskingPatternLayout MASKING_PATTERN_LAYOUT = new MaskingPatternLayout(); + + @BeforeAll + public static void setupAll() { + LoggerContext loggerContext = LOGGER.getLoggerContext(); + MASKING_PATTERN_LAYOUT.setPattern("%msg %ex"); + MASKING_PATTERN_LAYOUT.setContext(loggerContext); + MASKING_PATTERN_LAYOUT.start(); + } + + @ParameterizedTest + @MethodSource("maskedMessagesWithS3") + public void testMaskedMessagesWithS3(String message, String maskedMessage) { + String log = MASKING_PATTERN_LAYOUT.doLayout(getLoggingEvent(message)); + + assertAll( + "testMaskingMessageWithS3", + () -> assertEquals(maskedMessage, log.trim()) + ); + } + + private static Set maskedMessagesWithS3() { + String urlWithoutProtocol = "myservice.s3.amazonaws.com/some/path?param1=value1&X-Amz-Security-Token=mysecurityToken¶m3=value3"; + Map maskedMessages = Map.of( + "Error: " + urlWithoutProtocol + " and something else", "Error: REDACTED - S3 and something else", + "https://" + urlWithoutProtocol, "REDACTED - S3", + "http://" + urlWithoutProtocol, "REDACTED - S3", + urlWithoutProtocol, "REDACTED - S3" + ); + + return maskedMessages.entrySet().stream() + .map(entry -> Arguments.of(entry.getKey(), entry.getValue())) + .collect(Collectors.toSet()); + } + + private static ILoggingEvent getLoggingEvent(String msg, Exception ex) { + return new LoggingEvent(FormattingConverter.class.getName(), LOGGER, Level.ERROR, msg, ex, null); + } + + private static ILoggingEvent getLoggingEvent(String msg) { + return getLoggingEvent(msg, null); + } +}