From bc8f1f550e59379c8bc290cdc491bee0b5b5e08f Mon Sep 17 00:00:00 2001
From: Martin Lowe <martin.lowe@eclipse-foundation.org>
Date: Thu, 16 Mar 2023 10:34:43 -0400
Subject: [PATCH 1/2] Iss #113 - Update obfuscation logic to use new somewhat
 readable format

The new format transforms `sample@somecorp.co` into `sample@some*orp DOT
co`, which is more human readable but is still difficult for machines to
parse and determine.

Resolves #113
---
 .../config/EclipseQuteTemplateExtensions.java | 72 +++++++++++++------
 .../EclipseQuteTemplateExtensionsTest.java    | 67 +++++++++++++++++
 2 files changed, 117 insertions(+), 22 deletions(-)
 create mode 100644 src/test/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensionsTest.java

diff --git a/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java b/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java
index 0b7021a1..7ad9ab9f 100644
--- a/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java
+++ b/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java
@@ -12,10 +12,10 @@
 package org.eclipsefoundation.git.eca.config;
 
 import java.util.List;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+import org.apache.commons.lang3.StringUtils;
 import org.eclipsefoundation.git.eca.dto.CommitValidationMessage;
 import org.eclipsefoundation.git.eca.dto.CommitValidationStatus;
 
@@ -23,7 +23,7 @@ import io.quarkus.qute.TemplateExtension;
 
 @TemplateExtension
 public class EclipseQuteTemplateExtensions {
-    public static final Pattern EMAIL_OBFUSCATION_PATTERN = Pattern.compile("^(.).*?(@.*)$");
+    public static final Pattern EMAIL_OBFUSCATION_PATTERN = Pattern.compile("^(.*?)(@.*)$");
     public static final String EMAIL_OBFUSCATION_REPLACEMENT = "$1*****$2";
 
     /**
@@ -37,8 +37,7 @@ public class EclipseQuteTemplateExtensions {
     }
 
     /**
-     * Formats and flattens a list of statuses to a list of errors encountered while
-     * validation a set of commits.
+     * Formats and flattens a list of statuses to a list of errors encountered while validation a set of commits.
      * 
      * @param statuses a list of commit validation statuses to retrieve errors from
      * @return a list of flattened errors, or an empty list if (none are found.
@@ -49,29 +48,54 @@ public class EclipseQuteTemplateExtensions {
 
     /**
      * <p>
-     * Obfuscates an email for public consumption, showing the first letter of the
-     * email address, and the domain of the address for identification, and
-     * stripping out the rest of the address.
+     * Obfuscates an email for public consumption, showing the first letter of the email address, and the domain of the
+     * address for identification, and stripping out the rest of the address.
      * </p>
      * 
      * <p>
      * <strong>Example:</strong> <br />
      * Source: sample.address+123@eclipse.org <br />
-     * Output: s*****@eclipse.org
+     * Output: sample.address+123@ecl*pse DOT org
      * </p>
      * 
      * @param email the address to obfuscate
-     * @return the obfuscated address, or empty string if (the string could not be
-     *         parsed as an email address.
+     * @return the obfuscated address, or empty string if (the string could not be parsed as an email address.
      */
     static String obfuscateEmail(String email) {
-        Matcher m = EMAIL_OBFUSCATION_PATTERN.matcher(email);
-        return m.matches() ? m.replaceAll(EMAIL_OBFUSCATION_REPLACEMENT) : "";
+        if (StringUtils.isBlank(email)) {
+            return "";
+        }
+        // split on the @ symbol
+        String[] emailParts = email.split("\\@");
+        if (emailParts.length != 2) {
+            // valid emails will have 2 sections when split this way
+            return "";
+        }
+
+        String[] domainParts = emailParts[1].split("\\.");
+        if (domainParts.length < 2) {
+            // emails should have at least 2 parts here
+            return "";
+        }
+        // the index in the middle of the first domain part of the email address
+        int middleIndexDomain = Math.floorDiv(domainParts[0].length(), 2);
+
+        // build the obfuscated email address in the pattern defined in the block comment
+        StringBuilder sb = new StringBuilder();
+        sb.append(emailParts[0]);
+        sb.append("@");
+        sb.append(domainParts[0].substring(0, middleIndexDomain));
+        sb.append('*');
+        sb.append(domainParts[0].substring(middleIndexDomain + 1));
+        for (int i = 1; i < domainParts.length; i++) {
+            sb.append(" DOT ");
+            sb.append(domainParts[i]);
+        }
+        return sb.toString();
     }
 
     /**
-     * Standardized conversion of error codes into messages to be consumed
-     * downstream.
+     * Standardized conversion of error codes into messages to be consumed downstream.
      * 
      * @param message the validation error to convert into a meaningful message
      * @return the message string for the given validation error.
@@ -79,17 +103,21 @@ public class EclipseQuteTemplateExtensions {
     static String getMessageForError(CommitValidationMessage message) {
         String out = "";
         if (message.getStatusCode() == -406) {
-            out = String.format("Committer does not have permission to push on behalf of another user (Legacy). (%s)",
-                    EclipseQuteTemplateExtensions.obfuscateEmail(message.getCommitterEmail()));
+            out = String
+                    .format("Committer does not have permission to push on behalf of another user (Legacy). (%s)",
+                            EclipseQuteTemplateExtensions.obfuscateEmail(message.getCommitterEmail()));
         } else if (message.getStatusCode() == -405) {
-            out = String.format("Committer did not have a signed ECA on file. (%s)",
-                    EclipseQuteTemplateExtensions.obfuscateEmail(message.getCommitterEmail()));
+            out = String
+                    .format("Committer did not have a signed ECA on file. (%s)",
+                            EclipseQuteTemplateExtensions.obfuscateEmail(message.getCommitterEmail()));
         } else if (message.getStatusCode() == -404) {
-            out = String.format("Author did not have a signed ECA on file. (%s)",
-                    EclipseQuteTemplateExtensions.obfuscateEmail(message.getAuthorEmail()));
+            out = String
+                    .format("Author did not have a signed ECA on file. (%s)",
+                            EclipseQuteTemplateExtensions.obfuscateEmail(message.getAuthorEmail()));
         } else if (message.getStatusCode() == -403) {
-            out = String.format("Committer does not have permission to commit on specification projects. (%s)",
-                    EclipseQuteTemplateExtensions.obfuscateEmail(message.getCommitterEmail()));
+            out = String
+                    .format("Committer does not have permission to commit on specification projects. (%s)",
+                            EclipseQuteTemplateExtensions.obfuscateEmail(message.getCommitterEmail()));
         } else if (message.getStatusCode() == -402) {
             out = "Sign-off not detected in the commit message (Legacy).";
         } else if (message.getStatusCode() == -401) {
diff --git a/src/test/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensionsTest.java b/src/test/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensionsTest.java
new file mode 100644
index 00000000..988fc6b1
--- /dev/null
+++ b/src/test/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensionsTest.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2023 Eclipse Foundation
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * Author: Martin Lowe <martin.lowe@eclipse-foundation.org>
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipsefoundation.git.eca.config;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+/**
+ * Tests for Qute template extensions
+ * 
+ * @author Martin Lowe
+ *
+ */
+@QuarkusTest
+class EclipseQuteTemplateExtensionsTest {
+
+    /**
+     * Success cases where emails can be obfuscated and their expected output
+     * 
+     * 
+     * <ul>
+     * <li>Case 1: Standard address, no edge cases
+     * <li>Case 2: Address with email alias (+123)
+     * <li>Case 3: Multiple top-level domain parts
+     * </ul>
+     * 
+     * @param input the value to be obfuscated
+     * @param expected the value that is expected out
+     */
+    @ParameterizedTest
+    @CsvSource(value = { "sample@co.co,sample@c* DOT co", "sample.address+123@eclipse.org,sample.address+123@ecl*pse DOT org",
+            "sample@somecorp.co.co, sample@some*orp DOT co DOT co" })
+    void obfuscateEmail_success(String input, String expected) {
+        Assertions.assertEquals(expected, EclipseQuteTemplateExtensions.obfuscateEmail(input));
+    }
+
+    /**
+     * Cases where a value can't be obfuscated and an empty string is returned.
+     * 
+     * <ul>
+     * <li>Case 1: No @ symbol
+     * <li>Case 2: Null value
+     * <li>Case 3: Empty string
+     * <li>Case 4: No qualified domain (something.com)
+     * </ul>
+     * 
+     * @param input the value to be obfuscated
+     * @param expected the value that is expected out
+     */
+    @ParameterizedTest
+    @CsvSource(value = { "no-at-symbol.com,''", ",''", "'',''", "no-qualified-domain@wowee,''" })
+    void obfuscateEmail_failure_invalidAddress(String input, String expected) {
+        Assertions.assertEquals(expected, EclipseQuteTemplateExtensions.obfuscateEmail(input));
+    }
+}
-- 
GitLab


From ff4eeb0da000c6b5f7b8e61811a4a4229558e7be Mon Sep 17 00:00:00 2001
From: Martin Lowe <martin.lowe@eclipse-foundation.org>
Date: Fri, 17 Mar 2023 09:23:14 -0400
Subject: [PATCH 2/2] Remove the unused pattern in the Qute templates

---
 .../git/eca/config/EclipseQuteTemplateExtensions.java          | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java b/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java
index 7ad9ab9f..adef078e 100644
--- a/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java
+++ b/src/main/java/org/eclipsefoundation/git/eca/config/EclipseQuteTemplateExtensions.java
@@ -12,7 +12,6 @@
 package org.eclipsefoundation.git.eca.config;
 
 import java.util.List;
-import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.StringUtils;
@@ -23,8 +22,6 @@ import io.quarkus.qute.TemplateExtension;
 
 @TemplateExtension
 public class EclipseQuteTemplateExtensions {
-    public static final Pattern EMAIL_OBFUSCATION_PATTERN = Pattern.compile("^(.*?)(@.*)$");
-    public static final String EMAIL_OBFUSCATION_REPLACEMENT = "$1*****$2";
 
     /**
      * Made to count nested errors in a list of commit status objects.
-- 
GitLab