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 0b7021a184749da8c2839057638eb4c2e17127ca..adef078e3b23ad57069dc625cfece3b864770f21 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,9 @@
 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,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.
@@ -37,8 +34,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 +45,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 +100,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 0000000000000000000000000000000000000000..988fc6b190010e1caeb0930585b6e9f692a85c51
--- /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));
+    }
+}