[cxf] branch feature/CXF-8099_mask_sensitive_logging_elements created (now 6870b04)

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

[cxf] branch feature/CXF-8099_mask_sensitive_logging_elements created (now 6870b04)

ashakirin
This is an automated email from the ASF dual-hosted git repository.

ashakirin pushed a change to branch feature/CXF-8099_mask_sensitive_logging_elements
in repository https://gitbox.apache.org/repos/asf/cxf.git.


      at 6870b04  CXF-8099: mask sensitive logging elements

This branch includes the following new commits:

     new 6870b04  CXF-8099: mask sensitive logging elements

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Reply | Threaded
Open this post in threaded view
|

[cxf] 01/01: CXF-8099: mask sensitive logging elements

ashakirin
This is an automated email from the ASF dual-hosted git repository.

ashakirin pushed a commit to branch feature/CXF-8099_mask_sensitive_logging_elements
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit 6870b040452032c557ec6a93af717139737801fd
Author: ashakirin <[hidden email]>
AuthorDate: Sat Jun 27 21:25:18 2020 +0200

    CXF-8099: mask sensitive logging elements
---
 rt/features/logging/pom.xml                        |   4 +
 .../ext/logging/AbstractLoggingInterceptor.java    |  16 +-
 .../org/apache/cxf/ext/logging/LoggingFeature.java |  89 ++++++-----
 .../cxf/ext/logging/LoggingInInterceptor.java      |   6 +-
 .../cxf/ext/logging/LoggingOutInterceptor.java     |   6 +-
 .../cxf/ext/logging/MaskSensitiveHelper.java       |  74 ++++++++++
 .../apache/cxf/ext/logging/MaskSensitiveTest.java  | 162 +++++++++++++++++++++
 .../org/apache/cxf/ext/logging/TransformTest.java  |   8 +-
 8 files changed, 323 insertions(+), 42 deletions(-)

diff --git a/rt/features/logging/pom.xml b/rt/features/logging/pom.xml
index b8396a0..aa515f4 100644
--- a/rt/features/logging/pom.xml
+++ b/rt/features/logging/pom.xml
@@ -38,6 +38,10 @@
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.slf4j</groupId>
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 9302a43..9a452b8 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
@@ -18,6 +18,7 @@
  */
 package org.apache.cxf.ext.logging;
 
+import java.util.List;
 import java.util.UUID;
 
 import org.apache.cxf.common.util.PropertyUtils;
@@ -34,7 +35,8 @@ public abstract class AbstractLoggingInterceptor extends AbstractPhaseIntercepto
     public static final int DEFAULT_LIMIT = 48 * 1024;
     public static final int DEFAULT_THRESHOLD = -1;
     public static final String CONTENT_SUPPRESSED = "--- Content suppressed ---";
-    private static final String  LIVE_LOGGING_PROP = "org.apache.cxf.logging.enable";
+    private static final String  LIVE_LOGGING_PROP = "org.apache.cxf.logging.enable";
+
     protected int limit = DEFAULT_LIMIT;
     protected long threshold = DEFAULT_THRESHOLD;
     protected boolean logBinary;
@@ -43,6 +45,8 @@ public abstract class AbstractLoggingInterceptor extends AbstractPhaseIntercepto
     protected LogEventSender sender;
     protected final DefaultLogEventMapper eventMapper = new DefaultLogEventMapper();
 
+    private List<String> sensitiveElementNames;
+
     public AbstractLoggingInterceptor(String phase, LogEventSender sender) {
         super(phase);
         this.sender = sender;
@@ -73,6 +77,10 @@ public abstract class AbstractLoggingInterceptor extends AbstractPhaseIntercepto
         return threshold;
     }
 
+    public void setSensitiveElementNames(final List<String> sensitiveElementNames) {
+        this.sensitiveElementNames = sensitiveElementNames;
+    }
+
     public void setPrettyLogging(boolean prettyLogging) {
         if (sender instanceof PrettyLoggingFilter) {
             ((PrettyLoggingFilter)this.sender).setPrettyLogging(prettyLogging);
@@ -102,8 +110,12 @@ public abstract class AbstractLoggingInterceptor extends AbstractPhaseIntercepto
         }
     }
 
-    protected String transform(final String originalLogString) {
+    protected String transform(final Message message, final String originalLogString) {
         return originalLogString;
     }
 
+    protected String maskSensitiveElements(final Message message, String originalLogString) {
+        return (new MaskSensitiveHelper(sensitiveElementNames))
+                .maskSensitiveElements(message, originalLogString);
+    }
 }
diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java
index 0646ff6..afd905f 100644
--- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java
+++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java
@@ -18,6 +18,8 @@
  */
 package org.apache.cxf.ext.logging;
 
+import java.util.List;
+
 import org.apache.cxf.Bus;
 import org.apache.cxf.annotations.Provider;
 import org.apache.cxf.annotations.Provider.Type;
@@ -88,18 +90,67 @@ public class LoggingFeature extends DelegatingFeature<LoggingFeature.Portable> {
         delegate.setVerbose(verbose);
     }
 
+    /**
+     * Add additional binary media types to the default values in the LoggingInInterceptor.
+     * Content for these types will not be logged.
+     * For example:
+     * <pre>
+     * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
+     *   &lt;property name="addInBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
+     * &lt;/bean&gt;
+     * </pre>
+     * @param mediaTypes list of mediaTypes. symbol ; - delimeter
+     */
     public void addInBinaryContentMediaTypes(String mediaTypes) {
         delegate.addInBinaryContentMediaTypes(mediaTypes);
     }
 
+    /**
+     * Add additional binary media types to the default values in the LoggingOutInterceptor.
+     * Content for these types will not be logged.
+     * For example:
+     * <pre>
+     * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
+     *   &lt;property name="addOutBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
+     * &lt;/bean&gt;
+     * </pre>
+     * @param mediaTypes list of mediaTypes. symbol ; - delimeter
+     */
     public void addOutBinaryContentMediaTypes(String mediaTypes) {
         delegate.addOutBinaryContentMediaTypes(mediaTypes);
     }
 
+    /**
+     * Add additional binary media types to the default values for both logging interceptors
+     * Content for these types will not be logged.
+     * For example:
+     * <pre>
+     * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
+     *   &lt;property name="addBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
+     * &lt;/bean&gt;
+     * </pre>
+     * @param mediaTypes list of mediaTypes. symbol ; - delimeter
+     */
     public void addBinaryContentMediaTypes(String mediaTypes) {
         delegate.addBinaryContentMediaTypes(mediaTypes);
     }
 
+    /**
+     * Sets list of XML or JSON elements containing sensitive information to be masked.
+     * Corresponded data will be replaced with configured mask
+     * For example:
+     * <pre>
+     * sensitiveElementNames: {password}
+     *
+     * Initial logging statement: <user>my user</user><password>my secret password</password>
+     * Result logging statement: <user>my user</user><password>XXXX</password>
+     * </pre>
+     * @param sensitiveElementNames list of sensitive element names to be replaced
+     */
+    public void setSensitiveElementNames(final List<String> sensitiveElementNames) {
+        delegate.setSensitiveElementNames(sensitiveElementNames);
+    }
+
     public static class Portable implements AbstractPortableFeature {
         private LoggingInInterceptor in;
         private LoggingOutInterceptor out;
@@ -172,50 +223,22 @@ public class LoggingFeature extends DelegatingFeature<LoggingFeature.Portable> {
             setSender(verbose ? new Slf4jVerboseEventSender() : new Slf4jEventSender());
         }
 
-        /**
-         * Add additional binary media types to the default values in the LoggingInInterceptor.
-         * Content for these types will not be logged.
-         * For example:
-         * <pre>
-         * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
-         *   &lt;property name="addInBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
-         * &lt;/bean&gt;
-         * </pre>
-         * @param mediaTypes list of mediaTypes. symbol ; - delimeter
-         */
         public void addInBinaryContentMediaTypes(String mediaTypes) {
             in.addBinaryContentMediaTypes(mediaTypes);
         }
 
-        /**
-         * Add additional binary media types to the default values in the LoggingOutInterceptor.
-         * Content for these types will not be logged.
-         * For example:
-         * <pre>
-         * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
-         *   &lt;property name="addOutBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
-         * &lt;/bean&gt;
-         * </pre>
-         * @param mediaTypes list of mediaTypes. symbol ; - delimeter
-         */
         public void addOutBinaryContentMediaTypes(String mediaTypes) {
             out.addBinaryContentMediaTypes(mediaTypes);
         }
 
-        /**
-         * Add additional binary media types to the default values for both logging interceptors
-         * Content for these types will not be logged.
-         * For example:
-         * <pre>
-         * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
-         *   &lt;property name="addBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
-         * &lt;/bean&gt;
-         * </pre>
-         * @param mediaTypes list of mediaTypes. symbol ; - delimeter
-         */
         public void addBinaryContentMediaTypes(String mediaTypes) {
             addInBinaryContentMediaTypes(mediaTypes);
             addOutBinaryContentMediaTypes(mediaTypes);
         }
+
+        public void setSensitiveElementNames(final List<String> sensitiveElementNames) {
+            in.setSensitiveElementNames(sensitiveElementNames);
+            out.setSensitiveElementNames(sensitiveElementNames);
+        }
     }
 }
diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java
index db677f0..e703bf7 100644
--- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java
+++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java
@@ -72,6 +72,8 @@ public class LoggingInInterceptor extends AbstractLoggingInterceptor {
         } else {
             event.setPayload(AbstractLoggingInterceptor.CONTENT_SUPPRESSED);
         }
+        final String maskedContent = maskSensitiveElements(message, event.getPayload());
+        event.setPayload(transform(message, maskedContent));
         sender.send(event);
     }
 
@@ -99,7 +101,7 @@ public class LoggingInInterceptor extends AbstractLoggingInterceptor {
         StringBuilder payload = new StringBuilder();
         cos.writeCacheTo(payload, encoding, limit);
         cos.close();
-        event.setPayload(transform(payload.toString()));
+        event.setPayload(payload.toString());
         boolean isTruncated = cos.size() > limit && limit != -1;
         event.setTruncated(isTruncated);
         event.setFullContentFile(cos.getTempFile());
@@ -110,7 +112,7 @@ public class LoggingInInterceptor extends AbstractLoggingInterceptor {
         StringBuilder payload = new StringBuilder();
         writer.writeCacheTo(payload, limit);
         writer.close();
-        event.setPayload(transform(payload.toString()));
+        event.setPayload(payload.toString());
         event.setTruncated(isTruncated);
         event.setFullContentFile(writer.getTempFile());
     }
diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingOutInterceptor.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingOutInterceptor.java
index 2580649..0d53aa0 100644
--- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingOutInterceptor.java
+++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingOutInterceptor.java
@@ -146,7 +146,8 @@ public class LoggingOutInterceptor extends AbstractLoggingInterceptor {
             }
 
             String payload = shouldLogContent(event) ? getPayload(event, w2) : CONTENT_SUPPRESSED;
-            event.setPayload(transform(payload));
+            final String maskedContent = maskSensitiveElements(message, payload);
+            event.setPayload(transform(message, maskedContent));
             sender.send(event);
             message.setContent(Writer.class, out);
             super.close();
@@ -212,7 +213,8 @@ public class LoggingOutInterceptor extends AbstractLoggingInterceptor {
                 String encoding = (String) message.get(Message.ENCODING);
                 StringBuilder payload = new StringBuilder();
                 writePayload(payload, cos, encoding, event.getContentType());
-                event.setPayload(transform(payload.toString()));
+                final String maskedContent = maskSensitiveElements(message, payload.toString());
+                event.setPayload(transform(message, maskedContent));
                 boolean isTruncated = cos.size() > limit && limit != -1;
                 event.setTruncated(isTruncated);
             } catch (Exception ex) {
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
new file mode 100644
index 0000000..3130680
--- /dev/null
+++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/MaskSensitiveHelper.java
@@ -0,0 +1,74 @@
+/**
+ * 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.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.message.Message;
+
+import static org.apache.commons.lang3.ObjectUtils.isEmpty;
+
+public class MaskSensitiveHelper {
+    private static final String ELEMENT_NAME_TEMPLATE = "-ELEMENT_NAME-";
+    private static final String MATCH_PATTERN_XML = "<-ELEMENT_NAME->(.*?)</-ELEMENT_NAME->";
+    private static final String MATCH_PATTERN_JSON = "\"-ELEMENT_NAME-\"[ \\t]*:[ \\t]*\"(.*?)\"";
+    private static final String REPLACEMENT_PATTERN_XML = "<-ELEMENT_NAME->XXX</-ELEMENT_NAME->";
+    private static final String REPLACEMENT_PATTERN_JSON = "\"-ELEMENT_NAME-\": \"XXX\"";
+
+    private static final String XML_CONTENT = "xml";
+    private static final String HTML_CONTENT = "html";
+    private static final String JSON_CONTENT = "json";
+
+    final List<String> sensitiveElementNames;
+
+    public MaskSensitiveHelper(final List<String> sensitiveElementNames) {
+        this.sensitiveElementNames = sensitiveElementNames;
+    }
+
+    public String maskSensitiveElements(
+            final Message message,
+            final String originalLogString) {
+        if (isEmpty(sensitiveElementNames)) {
+            return originalLogString;
+        }
+        String contentType = (String) message.get(Message.CONTENT_TYPE);
+        if (StringUtils.containsIgnoreCase(contentType, XML_CONTENT)
+                || StringUtils.containsIgnoreCase(contentType, HTML_CONTENT)) {
+            return applyExpression(originalLogString, MATCH_PATTERN_XML, REPLACEMENT_PATTERN_XML);
+        } else if (StringUtils.containsIgnoreCase(contentType, JSON_CONTENT)) {
+            return applyExpression(originalLogString, MATCH_PATTERN_JSON, REPLACEMENT_PATTERN_JSON);
+        } else {
+            return originalLogString;
+        }
+    }
+
+    private String applyExpression(
+            final String originalLogString,
+            final String matchPatternTemplate,
+            final String replacementTemplate) {
+        String resultString = originalLogString;
+        for (final String elementName : sensitiveElementNames) {
+            final String matchPattern = matchPatternTemplate.replaceAll(ELEMENT_NAME_TEMPLATE, elementName);
+            final String replacement = replacementTemplate.replaceAll(ELEMENT_NAME_TEMPLATE, elementName);
+            resultString = resultString.replaceAll(matchPattern, replacement);
+        }
+        return resultString;
+    }
+}
diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/MaskSensitiveTest.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/MaskSensitiveTest.java
new file mode 100644
index 0000000..997dcb9
--- /dev/null
+++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/MaskSensitiveTest.java
@@ -0,0 +1,162 @@
+/**
+ * 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.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.cxf.ext.logging.event.LogEvent;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.ExchangeImpl;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.phase.PhaseInterceptor;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(Parameterized.class)
+public class MaskSensitiveTest {
+
+    private static final String SENSITIVE_LOGGING_CONTENT_XML =
+            "<user>testUser</user><password>my secret password</password>";
+    private static final String MASKED_LOGGING_CONTENT_XML =
+            "<user>testUser</user><password>XXX</password>";
+
+    private static final String SENSITIVE_LOGGING_CONTENT_JSON =
+            "\"user\":\"testUser\", \"password\": \"my secret password\"";
+    private static final String MASKED_LOGGING_CONTENT_JSON =
+            "\"user\":\"testUser\", \"password\": \"XXX\"";
+
+    private static final List<String> SENSITIVE_ELEMENTS = Arrays.asList("password");
+    private static final String APPLICATION_XML = "application/xml";
+    private static final String APPLICATION_JSON = "application/json";
+
+    private final String loggingContent;
+    private final String maskedContent;
+    private final String contentType;
+    private LogEventSenderMock logEventSender = new LogEventSenderMock();
+    public MaskSensitiveTest(String loggingContent, String maskedContent, String contentType) {
+        this.loggingContent = loggingContent;
+        this.maskedContent = maskedContent;
+        this.contentType = contentType;
+    }
+
+    @Parameterized.Parameters
+    public static Collection primeNumbers() {
+        return Arrays.asList(new Object[][]{
+            {SENSITIVE_LOGGING_CONTENT_XML, MASKED_LOGGING_CONTENT_XML, APPLICATION_XML},
+            {SENSITIVE_LOGGING_CONTENT_JSON, MASKED_LOGGING_CONTENT_JSON, APPLICATION_JSON}
+        });
+    }
+
+    @Test
+    public void shouldReplaceSensitiveDataIn() throws IOException {
+        // Arrange
+        final LoggingInInterceptor inInterceptor = new LoggingInInterceptor(logEventSender);
+        inInterceptor.setSensitiveElementNames(SENSITIVE_ELEMENTS);
+
+        final Message message = prepareInMessage();
+
+        // Act
+        Collection<PhaseInterceptor<? extends Message>> interceptors = inInterceptor.getAdditionalInterceptors();
+        for (PhaseInterceptor intercept : interceptors) {
+            intercept.handleMessage(message);
+        }
+        inInterceptor.handleMessage(message);
+
+        // Verify
+        LogEvent event = logEventSender.getLogEvent();
+        assertNotNull(event);
+        assertEquals(maskedContent, event.getPayload());
+    }
+
+    @Test
+    public void shouldReplaceSensitiveDataOut() throws IOException {
+        // Arrange
+        final LoggingOutInterceptor outInterceptor = new LoggingOutInterceptor(logEventSender);
+        outInterceptor.setSensitiveElementNames(SENSITIVE_ELEMENTS);
+
+        final Message message = prepareOutMessage();
+
+        // Act
+        outInterceptor.handleMessage(message);
+        byte[] payload = loggingContent.getBytes(StandardCharsets.UTF_8);
+        OutputStream out = message.getContent(OutputStream.class);
+        out.write(payload);
+        out.close();
+
+        // Verify
+        LogEvent event = logEventSender.getLogEvent();
+        assertNotNull(event);
+        assertEquals(maskedContent, event.getPayload());
+    }
+
+    @Test
+    public void shouldNotReplaceSensitiveDataEmptyExpression() throws IOException {
+        // Arrange
+        final LoggingOutInterceptor outInterceptor = new LoggingOutInterceptor(logEventSender);
+        final Message message = prepareOutMessage();
+
+        // Act
+        outInterceptor.handleMessage(message);
+        byte[] payload = loggingContent.getBytes(StandardCharsets.UTF_8);
+        OutputStream out = message.getContent(OutputStream.class);
+        out.write(payload);
+        out.close();
+
+        // Verify
+        LogEvent event = logEventSender.getLogEvent();
+        assertNotNull(event);
+        assertEquals(loggingContent, event.getPayload());
+    }
+
+    private Message prepareInMessage() {
+        Message message = new MessageImpl();
+        ByteArrayInputStream inputStream =
+                new ByteArrayInputStream(loggingContent.getBytes(StandardCharsets.UTF_8));
+        message.put(Message.CONTENT_TYPE, contentType);
+        message.setContent(InputStream.class, inputStream);
+        Exchange exchange = new ExchangeImpl();
+        message.setExchange(exchange);
+        return message;
+    }
+
+    private Message prepareOutMessage() {
+        Message message = new MessageImpl();
+        message.put(Message.CONTENT_TYPE, contentType);
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        message.setContent(OutputStream.class, outputStream);
+        Exchange exchange = new ExchangeImpl();
+        message.setExchange(exchange);
+        return message;
+    }
+
+}
diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TransformTest.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TransformTest.java
index 2adcb10..48d53b0 100644
--- a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TransformTest.java
+++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TransformTest.java
@@ -53,7 +53,8 @@ public class TransformTest {
             super(sender);
         }
 
-        protected String transform(String content) {
+        @Override
+        protected String transform(final Message message, final String content) {
             return content.replace(ORIG_LOGGING_CONTENT, TRANSFORMED_LOGGING_CONTENT);
         }
     }
@@ -63,7 +64,8 @@ public class TransformTest {
             super(sender);
         }
 
-        protected String transform(String content) {
+        @Override
+        protected String transform(final Message message, final String content) {
             return content.replace(ORIG_LOGGING_CONTENT, TRANSFORMED_LOGGING_CONTENT);
         }
     }
@@ -89,7 +91,7 @@ public class TransformTest {
         // Verify
         LogEvent event = logEventSender.getLogEvent();
         assertNotNull(event);
-        assertEquals(TRANSFORMED_LOGGING_CONTENT, event.getPayload()); // only the first byte is read!
+        assertEquals(TRANSFORMED_LOGGING_CONTENT, event.getPayload());
     }
 
     @Test