wrap logger patterns in masking class 80/119380/1
authorBENJAMIN, MAX <max.benjamin@att.com>
Tue, 16 Mar 2021 15:20:24 +0000 (11:20 -0400)
committerAT&T Open Source <g22940@att.com>
Tue, 16 Mar 2021 15:20:25 +0000 (11:20 -0400)
wrap logger patterns in masking class

Issue-ID: SO-3589
Signed-off-by: AT&T Open Source <g22940@att.com>
Change-Id: Ic7893001b77a9791cd43c958b9bad7065d7a4e2a

common/src/main/java/org/onap/so/logger/MaskLogStatements.java [new file with mode: 0644]
common/src/test/java/org/onap/so/logging/MaskLogStatementsTest.java [new file with mode: 0644]
common/src/test/resources/__files/logging/openstack-payload.json [new file with mode: 0644]

diff --git a/common/src/main/java/org/onap/so/logger/MaskLogStatements.java b/common/src/main/java/org/onap/so/logger/MaskLogStatements.java
new file mode 100644 (file)
index 0000000..cadadcf
--- /dev/null
@@ -0,0 +1,71 @@
+package org.onap.so.logger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+
+public class MaskLogStatements extends PatternLayout {
+
+    private String patternsProperty;
+    private String maskChar = "*";
+    private Optional<Pattern> pattern = Optional.empty();
+    private static final Pattern authPattern =
+            Pattern.compile("Authorization(?:\\:|=)\\s?(?:\"|\\[)(?:Basic|Bearer) (.*?)(?:\"|\\])");
+    private static final Pattern openstackPattern = Pattern.compile("\"password\"\\s?:\\s?\"(.*?)\"");
+
+    public String getPatternsProperty() {
+        return patternsProperty;
+    }
+
+    public void setPatternsProperty(String patternsProperty) {
+        this.patternsProperty = patternsProperty;
+        if (this.patternsProperty != null) {
+            this.pattern = Optional.of(Pattern.compile(patternsProperty, Pattern.MULTILINE));
+        }
+    }
+
+    public String getMaskChar() {
+        return maskChar;
+    }
+
+    public void setMaskChar(String maskChar) {
+        this.maskChar = maskChar;
+    }
+
+
+    protected Collection<Pattern> getPatterns() {
+        return Arrays.asList(authPattern, openstackPattern);
+    }
+
+    @Override
+    public String doLayout(ILoggingEvent event) {
+
+        final StringBuilder message = new StringBuilder(super.doLayout(event));
+        List<Pattern> patterns = new ArrayList<>(getPatterns());
+        if (pattern.isPresent()) {
+            patterns.add(pattern.get());
+        }
+        patterns.forEach(p -> {
+            Matcher matcher = p.matcher(message);
+            while (matcher.find()) {
+                int group = 1;
+                while (group <= matcher.groupCount()) {
+                    if (matcher.group(group) != null) {
+                        for (int i = matcher.start(group); i < matcher.end(group); i++) {
+                            message.setCharAt(i, maskChar.charAt(0));
+                        }
+                    }
+                    group++;
+                }
+            }
+        });
+        return message.toString();
+    }
+
+}
diff --git a/common/src/test/java/org/onap/so/logging/MaskLogStatementsTest.java b/common/src/test/java/org/onap/so/logging/MaskLogStatementsTest.java
new file mode 100644 (file)
index 0000000..ba5aeb5
--- /dev/null
@@ -0,0 +1,90 @@
+package org.onap.so.logging;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import org.junit.Test;
+import org.onap.so.logger.MaskLogStatements;
+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;
+
+public class MaskLogStatementsTest {
+
+    private LoggerContext lc = new LoggerContext();
+    private Logger logger = lc.getLogger(MaskLogStatementsTest.class);
+
+    @Test
+    public void verifyOpenStackPayload() throws IOException {
+        String payload =
+                new String(Files.readAllBytes(Paths.get("src/test/resources/__files/logging/openstack-payload.json")));
+
+        ILoggingEvent event = makeLoggingEvent(payload);
+
+        MaskLogStatements mask = new MaskLogStatements();
+
+        mask.setContext(lc);
+        mask.setPattern("%m");
+        mask.start();
+        String result = mask.doLayout(event);
+
+        assertTrue(result.matches("(?s).*?\"password\"\\s?:\\s?\"\\*+\".*"));
+
+    }
+
+    @Test
+    public void maskAuthHeaderTest() {
+        String msg = "Headers     : [Accept:\"application/json\", Authorization:\"Basic dklfjeaklfjdkalf\","
+                + "Content-Type:\"application/json\", Content-Length:\"10\","
+                + "X-RequestID:\"db2a0462-69d0-499f-93ec-e2a064ef1f59\", X-TransactionID:\"db2a0462-69d0-499f-93ec-e2a064ef1f59\","
+                + "X-ECOMP-RequestID:\"db2a0462-69d0-499f-93ec-e2a064ef1f59\", X-ONAP-PartnerName:\"SO.APIH\","
+                + "X-InvocationID:\"885e4f99-6f24-4f17-ab1b-584b37715b49\"]";
+
+        String expected = "Headers     : [Accept:\"application/json\", Authorization:\"Basic ****************\","
+                + "Content-Type:\"application/json\", Content-Length:\"10\","
+                + "X-RequestID:\"db2a0462-69d0-499f-93ec-e2a064ef1f59\", X-TransactionID:\"db2a0462-69d0-499f-93ec-e2a064ef1f59\","
+                + "X-ECOMP-RequestID:\"db2a0462-69d0-499f-93ec-e2a064ef1f59\", X-ONAP-PartnerName:\"SO.APIH\","
+                + "X-InvocationID:\"885e4f99-6f24-4f17-ab1b-584b37715b49\"]";
+        ILoggingEvent event = makeLoggingEvent(msg);
+
+        MaskLogStatements mask = new MaskLogStatements();
+
+        mask.setContext(lc);
+        mask.setPattern("%m");
+        mask.start();
+        String result = mask.doLayout(event);
+
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void maskAuthHeaderObjectStringTest() {
+        String msg = "Headers: {Accept=[text/plain, application/json, application/*+json, */*],"
+                + "Authorization=[Basic aaaaa],"
+                + "connection=[keep-alive], Content-Length=[217], content-type=[application/xml],"
+                + "host=[mso-bpmn-infra-svc:9200], user-agent=[Java/11.0.6]}";
+        String expected = "Headers: {Accept=[text/plain, application/json, application/*+json, */*],"
+                + "Authorization=[Basic -----],"
+                + "connection=[keep-alive], Content-Length=[217], content-type=[application/xml],"
+                + "host=[mso-bpmn-infra-svc:9200], user-agent=[Java/11.0.6]}";
+        ILoggingEvent event = makeLoggingEvent(msg);
+
+        MaskLogStatements mask = new MaskLogStatements();
+
+        mask.setContext(lc);
+        mask.setPattern("%m");
+        mask.setMaskChar("-");
+        mask.start();
+        String result = mask.doLayout(event);
+
+        assertEquals(expected, result);
+    }
+
+    private ILoggingEvent makeLoggingEvent(String message) {
+        return new LoggingEvent(MaskLogStatementsTest.class.getName(), logger, Level.INFO, message, null, null);
+    }
+}
diff --git a/common/src/test/resources/__files/logging/openstack-payload.json b/common/src/test/resources/__files/logging/openstack-payload.json
new file mode 100644 (file)
index 0000000..ac4d163
--- /dev/null
@@ -0,0 +1,26 @@
+{
+    "auth": {
+        "identity": {
+            "password": {
+                "user": {
+                    "name": "j0000",
+                    "domain": {
+                        "name": "name"
+                    },
+                    "password": "my-password-wow"
+                }
+            },
+            "methods": [
+                "password"
+            ]
+        },
+        "scope": {
+            "project": {
+                "id": "ad299b37da30413391e9c28138f0b0dd",
+                "domain": {
+                    "name": "name"
+                }
+            }
+        }
+    }
+}