From 50df912595badf0b8531c73d8d39e09e3ab23943 Mon Sep 17 00:00:00 2001 From: Kavinda Rajapakse Date: Fri, 17 Jul 2026 12:12:22 +0530 Subject: [PATCH 1/2] Fix NPE in LoggerUtils.getSanitizedErrorMessage on null username --- .../carbon/identity/central/log/mgt/utils/LoggerUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java index 9ebffdbacad2..fe567e5db5a4 100644 --- a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java +++ b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/main/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtils.java @@ -260,7 +260,8 @@ public static String[] getMaskedArraysOfValues(String[] values) { */ public static String getSanitizedErrorMessage(String errorMessage, String userName) { - if (LoggerUtils.isLogMaskingEnable && errorMessage.contains(userName)) { + if (LoggerUtils.isLogMaskingEnable && StringUtils.isNotBlank(errorMessage) + && StringUtils.isNotBlank(userName) && errorMessage.contains(userName)) { return errorMessage.replace(userName, LoggerUtils.getMaskedContent(userName)); } return errorMessage; From dfdae4add757451f2c37546d3d5e003ed8580c01 Mon Sep 17 00:00:00 2001 From: Kavinda Rajapakse Date: Mon, 20 Jul 2026 09:51:01 +0530 Subject: [PATCH 2/2] Add unit tests --- .../pom.xml | 15 +++ .../log/mgt/utils/LoggerUtilsTest.java | 99 +++++++++++++++++++ .../src/test/resources/testng.xml | 26 +++++ 3 files changed, 140 insertions(+) create mode 100644 components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/test/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtilsTest.java create mode 100644 components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/test/resources/testng.xml diff --git a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/pom.xml b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/pom.xml index 921d57bc7c8a..c15c8e60ac60 100644 --- a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/pom.xml +++ b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/pom.xml @@ -74,6 +74,16 @@ true + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.plugin.version} + + + src/test/resources/testng.xml + + + @@ -82,6 +92,11 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.event + + org.testng + testng + test + diff --git a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/test/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtilsTest.java b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/test/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtilsTest.java new file mode 100644 index 000000000000..bc45069af21c --- /dev/null +++ b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/test/java/org/wso2/carbon/identity/central/log/mgt/utils/LoggerUtilsTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.central.log.mgt.utils; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; + +/** + * Unit tests for {@link LoggerUtils#getSanitizedErrorMessage(String, String)}. + * + */ +public class LoggerUtilsTest { + + @AfterMethod + public void resetMaskingFlag() { + + LoggerUtils.isLogMaskingEnable = false; + } + + /** + * Core regression: masking enabled + null/blank/empty userName must NOT throw and must return + * the message unchanged. Before the fix this threw NPE ("s is null") inside String.contains. + */ + @Test(dataProvider = "blankUserNames") + public void testBlankUserNameReturnsMessageUnchanged(String userName) { + + LoggerUtils.isLogMaskingEnable = true; + String errorMessage = "Some error for admin."; + assertEquals(LoggerUtils.getSanitizedErrorMessage(errorMessage, userName), errorMessage, + "Blank userName must return the original error message unchanged."); + } + + @DataProvider(name = "blankUserNames") + public Object[][] blankUserNames() { + + return new Object[][]{{""}, {" "}, {null}}; + } + + /** + * Masking disabled: null userName must still not throw and the message is returned as-is. + */ + @Test + public void testNullUserNameWithMaskingDisabledDoesNotThrow() { + + LoggerUtils.isLogMaskingEnable = false; + String errorMessage = "Persisted access token data not found."; + assertEquals(LoggerUtils.getSanitizedErrorMessage(errorMessage, null), errorMessage, + "Masking disabled must return the original message unchanged."); + } + + /** + * Existing behavior preserved: masking enabled and the userName present in the message -> + * the username is masked. Pattern (?<=.).(?=.) masks all but the first and last char. + */ + @Test + public void testUserNamePresentIsMaskedWhenMaskingEnabled() { + + LoggerUtils.isLogMaskingEnable = true; + String userName = "admin"; + String errorMessage = "Login failed for user admin in tenant carbon.super"; + String sanitized = LoggerUtils.getSanitizedErrorMessage(errorMessage, userName); + assertFalse(sanitized.contains(userName), + "Raw username must not remain in the sanitized message."); + assertEquals(sanitized, "Login failed for user a***n in tenant carbon.super", + "Username must be masked with the first/last char retained."); + } + + /** + * Masking enabled but the userName is not contained in the message -> message returned as-is. + */ + @Test + public void testUserNameNotPresentReturnsMessageUnchanged() { + + LoggerUtils.isLogMaskingEnable = true; + String errorMessage = "Persisted access token data not found."; + assertEquals(LoggerUtils.getSanitizedErrorMessage(errorMessage, "admin"), errorMessage, + "When the username is absent the message must be unchanged."); + } +} diff --git a/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/test/resources/testng.xml b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/test/resources/testng.xml new file mode 100644 index 000000000000..c4ba0670195c --- /dev/null +++ b/components/central-logger/org.wso2.carbon.identity.central.log.mgt/src/test/resources/testng.xml @@ -0,0 +1,26 @@ + + + + + + + + + +