This is an automated email from the ASF dual-hosted git repository. ffang pushed a commit to branch CXF-9164 in repository https://gitbox.apache.org/repos/asf/cxf.git
commit e9df8356b4c3edcb30cadc20dfc5d380a82446d3 Author: Freeman Fang <[email protected]> AuthorDate: Mon Jan 19 13:24:28 2026 -0500 [CXF-9164]Logging Feature - Sensitive attributes (not elements) are not masked --- .../ext/logging/AbstractLoggingInterceptor.java | 8 ++ .../cxf/ext/logging/AttributeMaskingHelper.java | 116 --------------------- .../cxf/ext/logging/MaskSensitiveHelper.java | 46 +++++++- .../logging/AttributeMaskSensitiveHelperTest.java | 27 ++--- 4 files changed, 58 insertions(+), 139 deletions(-) diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AbstractLoggingInterceptor.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AbstractLoggingInterceptor.java index 67e27633d3..4f7c7c015a 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AbstractLoggingInterceptor.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AbstractLoggingInterceptor.java @@ -94,6 +94,14 @@ public abstract class AbstractLoggingInterceptor extends AbstractPhaseIntercepto public void addSensitiveElementNames(final Set<String> sensitiveElementNames) { maskSensitiveHelper.addSensitiveElementNames(sensitiveElementNames); } + + public void setSensitiveAttributeNames(final Set<String> sensitiveAttributeNames) { + maskSensitiveHelper.setSensitiveAttributeNames(sensitiveAttributeNames); + } + + public void addSensitiveAttributeNames(final Set<String> sensitiveAttributeNames) { + maskSensitiveHelper.addSensitiveAttributeNames(sensitiveAttributeNames); + } public void setSensitiveProtocolHeaderNames(final Set<String> protocolHeaderNames) { this.sensitiveProtocolHeaderNames.clear(); diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AttributeMaskingHelper.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AttributeMaskingHelper.java deleted file mode 100644 index 742659f9b8..0000000000 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AttributeMaskingHelper.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF 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.apache.cxf.ext.logging; - -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; - -import org.apache.cxf.message.Message; - - -/** - * Adds XML/HTML attribute value masking on top of the parent MaskSensitiveHelper. - * - */ -public class AttributeMaskingHelper extends MaskSensitiveHelper { - - private static final String ATTR_NAME_TEMPLATE = "-ATTR_NAME-"; - - // Re-declare namespace prefix class per Namespaces in XML (private in parent; reproduce here) - private static final String PATTERN_XML_NAMESPACE_PREFIX = "[\\w.\\-\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6" - + "\\u00F8-\\u02FF\\u0300-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F-\\u2040\\u2070-\\u218F" - + "\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD]+"; - - // Case-sensitive attribute pattern; supports optional namespace prefix; preserves original quotes - // Groups: 1=full attr name (w/ optional prefix), 2=open quote, 3=value, 4=close quote (backref to 2) - private static final String MATCH_PATTERN_XML_ATTR_TEMPLATE = - "(\\b(?:" + PATTERN_XML_NAMESPACE_PREFIX + ":)?" + ATTR_NAME_TEMPLATE + ")\\s*=\\s*(\"|')(.*?)(\\2)"; - private static final String REPLACEMENT_XML_ATTR_TEMPLATE = "$1=$2XXX$4"; - - private static final String XML_CONTENT = "xml"; - private static final String HTML_CONTENT = "html"; - - private static class ReplacementPair { - private final Pattern matchPattern; - private final String replacement; - ReplacementPair(String matchPattern, String replacement) { - - this.matchPattern = Pattern.compile(matchPattern, Pattern.DOTALL); - this.replacement = replacement; - } - } - - private final Set<ReplacementPair> replacementsXMLAttributes = new HashSet<>(); - - /** Adds attribute names to be masked in XML/HTML logs (values replaced with "XXX"). */ - public void addSensitiveAttributeNames(final Set<String> inSensitiveAttirbuteNames) { - if (inSensitiveAttirbuteNames == null || inSensitiveAttirbuteNames.isEmpty()) { - return; - } - for (final String attr : inSensitiveAttirbuteNames) { - final String match = MATCH_PATTERN_XML_ATTR_TEMPLATE.replace(ATTR_NAME_TEMPLATE, Pattern.quote(attr)); - final String repl = REPLACEMENT_XML_ATTR_TEMPLATE.replace(ATTR_NAME_TEMPLATE, escapeForReplacement(attr)); - replacementsXMLAttributes.add(new ReplacementPair(match, repl)); - } - } - - /** Optional convenience resetter if you want it. */ - public void setSensitiveAttributeNames(final Set<String> inSensitiveAttributeNames) { - replacementsXMLAttributes.clear(); - addSensitiveAttributeNames(inSensitiveAttributeNames); - } - - @Override - public String maskSensitiveElements(final Message message, final String originalLogString) { - // First, do all base-class masking (elements/JSON/headers) - String masked = super.maskSensitiveElements(message, originalLogString); - if (masked == null || message == null) { - return masked; - } - final String contentType = (String) message.get(Message.CONTENT_TYPE); - if (contentType == null) { - return masked; - } - final String lower = contentType.toLowerCase(); - if (lower.contains(XML_CONTENT) || lower.contains(HTML_CONTENT)) { - // Then apply attribute-value masking - return applyMasks(masked, replacementsXMLAttributes); - } - return masked; - } - - // --- helpers (local copy; parent versions are private) --- - - private static String escapeForReplacement(String s) { - if (s == null || s.isEmpty()) { - return s; - } - return s.replace("\\", "\\\\").replace("$", "\\$"); - } - - private String applyMasks(String input, Set<ReplacementPair> pairs) { - String out = input; - for (final ReplacementPair rp : pairs) { - out = rp.matchPattern.matcher(out).replaceAll(rp.replacement); - } - return out; - } -} diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/MaskSensitiveHelper.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/MaskSensitiveHelper.java index 0795038c62..f55fea7e28 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/MaskSensitiveHelper.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/MaskSensitiveHelper.java @@ -26,6 +26,7 @@ import java.util.regex.Pattern; import org.apache.cxf.message.Message; public class MaskSensitiveHelper { + private static final String ATTR_NAME_TEMPLATE = "-ATTR_NAME-"; private static final String ELEMENT_NAME_TEMPLATE = "-ELEMENT_NAME-"; // see https://www.w3.org/TR/REC-xml-names/#NT-NCName for allowed chars in namespace prefix private static final String PATTERN_XML_NAMESPACE_PREFIX = "[\\w.\\-\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6" @@ -42,6 +43,12 @@ public class MaskSensitiveHelper { private static final String MATCH_PATTERN_JSON_TEMPLATE = "\"-ELEMENT_NAME-\"[ \\t]*:[ \\t]*\"(.*?)\""; private static final String REPLACEMENT_JSON_TEMPLATE = "\"-ELEMENT_NAME-\": \"XXX\""; private static final String MASKED_HEADER_VALUE = "XXX"; + + // Case-sensitive attribute pattern; supports optional namespace prefix; preserves original quotes + // Groups: 1=full attr name (w/ optional prefix), 2=open quote, 3=value, 4=close quote (backref to 2) + private static final String MATCH_PATTERN_XML_ATTR_TEMPLATE = + "(\\b(?:" + PATTERN_XML_NAMESPACE_PREFIX + ":)?" + ATTR_NAME_TEMPLATE + ")\\s*=\\s*(\"|')(.*?)(\\2)"; + private static final String REPLACEMENT_XML_ATTR_TEMPLATE = "$1=$2XXX$4"; private static final String XML_CONTENT = "xml"; private static final String HTML_CONTENT = "html"; @@ -57,11 +64,12 @@ public class MaskSensitiveHelper { } } - private final Set<ReplacementPair> replacementsXML = new HashSet<>(); + private final Set<ReplacementPair> replacementsElements = new HashSet<>(); private final Set<ReplacementPair> replacementsJSON = new HashSet<>(); + private final Set<ReplacementPair> replacementsXMLAttributes = new HashSet<>(); public void setSensitiveElementNames(final Set<String> inSensitiveElementNames) { - replacementsXML.clear(); + replacementsElements.clear(); replacementsJSON.clear(); addSensitiveElementNames(inSensitiveElementNames); } @@ -69,13 +77,31 @@ public class MaskSensitiveHelper { public void addSensitiveElementNames(final Set<String> inSensitiveElementNames) { for (final String sensitiveName : inSensitiveElementNames) { addReplacementPair(MATCH_PATTERN_XML_TEMPLATE, REPLACEMENT_XML_TEMPLATE, - sensitiveName, replacementsXML); + sensitiveName, replacementsElements); addReplacementPair(MATCH_PATTERN_JSON_TEMPLATE_ARRAY, REPLACEMENT_JSON_TEMPLATE_ARRAY, sensitiveName, replacementsJSON); addReplacementPair(MATCH_PATTERN_JSON_TEMPLATE, REPLACEMENT_JSON_TEMPLATE, sensitiveName, replacementsJSON); } } + + /** Adds attribute names to be masked in XML/HTML logs (values replaced with "XXX"). */ + public void addSensitiveAttributeNames(final Set<String> inSensitiveAttirbuteNames) { + if (inSensitiveAttirbuteNames == null || inSensitiveAttirbuteNames.isEmpty()) { + return; + } + for (final String attr : inSensitiveAttirbuteNames) { + final String match = MATCH_PATTERN_XML_ATTR_TEMPLATE.replace(ATTR_NAME_TEMPLATE, Pattern.quote(attr)); + final String repl = REPLACEMENT_XML_ATTR_TEMPLATE.replace(ATTR_NAME_TEMPLATE, escapeForReplacement(attr)); + replacementsXMLAttributes.add(new ReplacementPair(match, repl)); + } + } + + /** Optional convenience resetter if you want it. */ + public void setSensitiveAttributeNames(final Set<String> inSensitiveAttributeNames) { + replacementsXMLAttributes.clear(); + addSensitiveAttributeNames(inSensitiveAttributeNames); + } private void addReplacementPair(final String matchPatternTemplate, final String replacementTemplate, @@ -89,7 +115,8 @@ public class MaskSensitiveHelper { public String maskSensitiveElements( final Message message, final String originalLogString) { - if (replacementsXML.isEmpty() && replacementsJSON.isEmpty() + if (replacementsElements.isEmpty() && replacementsJSON.isEmpty() + && replacementsXMLAttributes.isEmpty() || originalLogString == null || message == null) { return originalLogString; } @@ -100,7 +127,9 @@ public class MaskSensitiveHelper { final String lowerCaseContentType = contentType.toLowerCase(); if (lowerCaseContentType.contains(XML_CONTENT) || lowerCaseContentType.contains(HTML_CONTENT)) { - return applyMasks(originalLogString, replacementsXML); + String replacedElement = applyMasks(originalLogString, replacementsElements); + return replacedElement == null ? replacedElement + : applyMasks(replacedElement, replacementsXMLAttributes); } else if (lowerCaseContentType.contains(JSON_CONTENT)) { return applyMasks(originalLogString, replacementsJSON); } @@ -123,4 +152,11 @@ public class MaskSensitiveHelper { } return resultString; } + + private static String escapeForReplacement(String s) { + if (s == null || s.isEmpty()) { + return s; + } + return s.replace("\\", "\\\\").replace("$", "\\$"); + } } diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/AttributeMaskSensitiveHelperTest.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/AttributeMaskSensitiveHelperTest.java index 1ff7db1242..925cc9b515 100644 --- a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/AttributeMaskSensitiveHelperTest.java +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/AttributeMaskSensitiveHelperTest.java @@ -122,11 +122,9 @@ public class AttributeMaskSensitiveHelperTest { public void shouldReplaceSensitiveDataInWithAdd() { // Arrange final LoggingInInterceptor inInterceptor = new LoggingInInterceptor(logEventSender); - AttributeMaskingHelper attrMaskHelper = new AttributeMaskingHelper(); - attrMaskHelper.setSensitiveAttributeNames(SENSITIVE_ATTRIBUTES); - attrMaskHelper.setSensitiveElementNames(SENSITIVE_ELEMENTS); - inInterceptor.setSensitiveDataHelper(attrMaskHelper); - + inInterceptor.setSensitiveAttributeNames(SENSITIVE_ATTRIBUTES); + inInterceptor.setSensitiveElementNames(SENSITIVE_ELEMENTS); + final Message message = prepareInMessage(); // Act @@ -146,11 +144,8 @@ public class AttributeMaskSensitiveHelperTest { public void shouldReplaceSensitiveDataInWithSet() { // Arrange final LoggingInInterceptor inInterceptor = new LoggingInInterceptor(logEventSender); - - AttributeMaskingHelper attrMaskHelper = new AttributeMaskingHelper(); - attrMaskHelper.setSensitiveAttributeNames(SENSITIVE_ATTRIBUTES); - attrMaskHelper.setSensitiveElementNames(SENSITIVE_ELEMENTS); - inInterceptor.setSensitiveDataHelper(attrMaskHelper); + inInterceptor.setSensitiveAttributeNames(SENSITIVE_ATTRIBUTES); + inInterceptor.setSensitiveElementNames(SENSITIVE_ELEMENTS); final Message message = prepareInMessage(); // Act @@ -170,10 +165,8 @@ public class AttributeMaskSensitiveHelperTest { public void shouldReplaceSensitiveDataOutWithAdd() throws IOException { // Arrange final LoggingOutInterceptor outInterceptor = new LoggingOutInterceptor(logEventSender); - AttributeMaskingHelper attrMaskHelper = new AttributeMaskingHelper(); - attrMaskHelper.setSensitiveAttributeNames(SENSITIVE_ATTRIBUTES); - attrMaskHelper.setSensitiveElementNames(SENSITIVE_ELEMENTS); - outInterceptor.setSensitiveDataHelper(attrMaskHelper); + outInterceptor.setSensitiveAttributeNames(SENSITIVE_ATTRIBUTES); + outInterceptor.setSensitiveElementNames(SENSITIVE_ELEMENTS); final Message message = prepareOutMessage(); @@ -194,10 +187,8 @@ public class AttributeMaskSensitiveHelperTest { public void shouldReplaceSensitiveDataOutWithSet() throws IOException { // Arrange final LoggingOutInterceptor outInterceptor = new LoggingOutInterceptor(logEventSender); - AttributeMaskingHelper attrMaskHelper = new AttributeMaskingHelper(); - attrMaskHelper.setSensitiveAttributeNames(SENSITIVE_ATTRIBUTES); - attrMaskHelper.setSensitiveElementNames(SENSITIVE_ELEMENTS); - outInterceptor.setSensitiveDataHelper(attrMaskHelper); + outInterceptor.setSensitiveAttributeNames(SENSITIVE_ATTRIBUTES); + outInterceptor.setSensitiveElementNames(SENSITIVE_ELEMENTS); final Message message = prepareOutMessage();
