diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/constant/LogMaskingRegex.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/constant/LogMaskingRegex.kt index e34be9506..7745719f3 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/constant/LogMaskingRegex.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/constant/LogMaskingRegex.kt @@ -22,4 +22,22 @@ import com.expediagroup.sdk.core.constant.HeaderKey.AUTHORIZATION internal object LogMaskingRegex { val AUTHORIZATION_REGEX: Regex = "(?<=$AUTHORIZATION: ($EAN|$BASIC|$BEARER|)\\s)\\S+".toRegex() + + private val paymentKeys = + arrayListOf( + "security_code", + "card_number", + "card_cvv_response", + "card_avs_response", + "pin", + "card_cvv", + "account_number", + "card_cvv2", + "card_cvv2_response", + "cvv" + ) + + val PAYMENT_REGEX = "(?<=[\"']?(${paymentKeys.joinToString("|")})[\"']?:\\s?[\"'])(\\s*[^\"']+\\s*)(?=[\"'])".toRegex() + + val NUMBER_FIELD_REGEX = "(?<=[\"']?number[\"']?:\\s?[\"'])(\\s*\\d{15,16}\\s*)(?=[\"'])".toRegex() } diff --git a/core/src/main/kotlin/com/expediagroup/sdk/core/plugin/logging/LogMasker.kt b/core/src/main/kotlin/com/expediagroup/sdk/core/plugin/logging/LogMasker.kt index 594b633a9..e82ac306f 100644 --- a/core/src/main/kotlin/com/expediagroup/sdk/core/plugin/logging/LogMasker.kt +++ b/core/src/main/kotlin/com/expediagroup/sdk/core/plugin/logging/LogMasker.kt @@ -16,24 +16,32 @@ package com.expediagroup.sdk.core.plugin.logging import com.expediagroup.sdk.core.constant.LogMaskingRegex.AUTHORIZATION_REGEX +import com.expediagroup.sdk.core.constant.LogMaskingRegex.NUMBER_FIELD_REGEX +import com.expediagroup.sdk.core.constant.LogMaskingRegex.PAYMENT_REGEX import com.expediagroup.sdk.core.constant.LoggingMessage.OMITTED internal fun mask(message: String): String = MaskProvider.masks.fold(message) { acc, mask -> mask.mask(acc) } +internal interface Mask { + val regex: Regex + + fun mask(string: String): String = string.replace(regex) { maskSubstring(it.value) } + + fun maskSubstring(string: String) = OMITTED +} + internal object MaskProvider { - val masks = listOf(AuthMask) + val masks = listOf(AuthMask, PaymentMask, PaymentNumberMask) object AuthMask : Mask { override val regex: Regex = AUTHORIZATION_REGEX - - override fun maskSubstring(string: String) = OMITTED } -} - -internal interface Mask { - val regex: Regex - fun mask(string: String): String = string.replace(regex) { maskSubstring(it.value) } + object PaymentMask : Mask { + override val regex: Regex = PAYMENT_REGEX + } - fun maskSubstring(string: String): String + object PaymentNumberMask : Mask { + override val regex: Regex = NUMBER_FIELD_REGEX + } } diff --git a/core/src/test/kotlin/com/expediagroup/sdk/core/plugin/logging/LogMaskerTest.kt b/core/src/test/kotlin/com/expediagroup/sdk/core/plugin/logging/LogMaskerTest.kt index b3b732556..4bb03b595 100644 --- a/core/src/test/kotlin/com/expediagroup/sdk/core/plugin/logging/LogMaskerTest.kt +++ b/core/src/test/kotlin/com/expediagroup/sdk/core/plugin/logging/LogMaskerTest.kt @@ -21,10 +21,13 @@ import com.expediagroup.sdk.core.constant.Authentication.EAN import com.expediagroup.sdk.core.constant.HeaderKey.AUTHORIZATION import com.expediagroup.sdk.core.constant.LoggingMessage.OMITTED import com.expediagroup.sdk.core.plugin.logging.MaskProvider.AuthMask +import com.expediagroup.sdk.core.plugin.logging.MaskProvider.PaymentMask +import com.expediagroup.sdk.core.plugin.logging.MaskProvider.PaymentNumberMask import io.mockk.mockkObject import io.mockk.verify import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource @@ -32,12 +35,12 @@ internal class LogMaskerTest { @ParameterizedTest @ValueSource(strings = [BASIC, BEARER, EAN]) fun `given text apply all masks available`(authType: String) { - val text = "$AUTHORIZATION: $authType token" + val text = "$AUTHORIZATION: $authType token \"number\":\"4111111111111111\",'security_code':'123',\"something else\"" mockkObject(AuthMask) val maskedText = mask(text) - assertThat(maskedText).isEqualTo("$AUTHORIZATION: $authType $OMITTED") + assertThat(maskedText).isEqualTo("$AUTHORIZATION: $authType $OMITTED \"number\":\"$OMITTED\",'security_code':'$OMITTED',\"something else\"") verify(exactly = 1) { AuthMask.mask(text) } } @@ -53,4 +56,57 @@ internal class LogMaskerTest { assertThat(maskedText).isEqualTo("$AUTHORIZATION: $authType $OMITTED") } } + + @Nested + inner class PaymentMaskTest { + @ParameterizedTest + @ValueSource( + strings = [ + "security_code", + "card_number", + "card_cvv_response", + "card_avs_response", + "pin", + "card_cvv", + "account_number", + "card_cvv2", + "card_cvv2_response", + "cvv" + ] + ) + fun `given a text with payment info then omit it`(key: String) { + assertThat(PaymentMask.mask("$key:\"123456\" something else")).isEqualTo("$key:\"$OMITTED\" something else") + assertThat(PaymentMask.mask("\"$key\":\"123456\" something else")).isEqualTo("\"$key\":\"$OMITTED\" something else") + assertThat(PaymentMask.mask("$key: \"123456\" something else")).isEqualTo("$key: \"$OMITTED\" something else") + assertThat(PaymentMask.mask("$key:'123456' something else")).isEqualTo("$key:'$OMITTED' something else") + assertThat(PaymentMask.mask("$key: '123456' something else")).isEqualTo("$key: '$OMITTED' something else") + } + } + + @Nested + inner class PaymentNumberTest { + @ParameterizedTest + @ValueSource( + strings = [ + "012345678901234", + "0123456789012345", + "4111111111111111" + ] + ) + fun `given a text with number of 15 or 16 digits then omit it`(number: String) { + assertThat(PaymentNumberMask.mask("number:\"$number\" something else")).isEqualTo("number:\"$OMITTED\" something else") + assertThat(PaymentNumberMask.mask("number: \"$number\" something else")).isEqualTo("number: \"$OMITTED\" something else") + assertThat(PaymentNumberMask.mask("number:'$number' something else")).isEqualTo("number:'$OMITTED' something else") + assertThat(PaymentNumberMask.mask("number: '$number' something else")).isEqualTo("number: '$OMITTED' something else") + } + + @Test + fun `given a text with number of 14 digits then do not omit it`() { + val number = "01234567890123" + assertThat(PaymentNumberMask.mask("number:\"$number\" something else")).isEqualTo("number:\"$number\" something else") + assertThat(PaymentNumberMask.mask("number: \"$number\" something else")).isEqualTo("number: \"$number\" something else") + assertThat(PaymentNumberMask.mask("number:'$number' something else")).isEqualTo("number:'$number' something else") + assertThat(PaymentNumberMask.mask("number: '$number' something else")).isEqualTo("number: '$number' something else") + } + } }