diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/UsernameAnonymizer.java b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/UsernameAnonymizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b00ece4ab43db397d307602ff1cf7b3ef20c6ae
--- /dev/null
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/malwarescan/UsernameAnonymizer.java
@@ -0,0 +1,155 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2023 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ *
+ * Licensed 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 com.basistech.df.cybertriage.autopsy.malwarescan;
+
+import com.google.common.net.InetAddresses;
+import java.net.InetAddress;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.commons.lang3.StringUtils;
+import org.sleuthkit.autopsy.coreutils.Logger;
+
+/**
+ * Utility class to anonymize username in paths also anonymizes hostname / ip
+ * from UNC paths
+ */
+public class UsernameAnonymizer {
+
+    private static final Logger LOGGER = Logger.getLogger(UsernameAnonymizer.class.getName());
+
+    private final String USER_PATH_FORWARD_SLASH_REGEX = "(?<!all )([/]{0,1}\\Qusers\\E/)(?!(public|Default|defaultAccount|All Users))([^/]+)(/){0,1}";
+    private final String USER_PATH_BACK_SLASH_REGEX = "(?<!all )([\\\\]{0,1}\\Qusers\\E\\\\)(?!(public|Default|defaultAccount|All Users))([^\\\\]+)([\\\\]){0,1}";
+
+    private final double WINDOWS_VERSION;
+    private final double DEFAULT_WINDOWS_VERSION = 10.0;
+    private final String USER_PATH_FORWARD_SLASH_REGEX_XP = "([/]{0,1}\\Qdocuments and settings\\E/)(?!(Default User|All Users))([^/]+)(/){0,1}";
+    private final String USER_PATH_BACK_SLASH_REGEX_XP = "([\\\\]{0,1}\\Qdocuments and settings\\E\\\\)(?!(Default User|All Users))([^\\\\]+)(\\\\){0,1}";
+
+    private final Pattern UNC_PATH_FORWARD_SLASH_PATTERN = Pattern.compile("(//)([^/]+)(/){0,1}");
+    private final Pattern UNC_PATH_BACK_SLASH_PATTERN = Pattern.compile("(\\\\\\\\)([^\\\\]+)(\\\\){0,1}");
+
+    public UsernameAnonymizer() {
+        // This constructor was added for the unit tests
+        // For most purposes, the other constructor should be used so we get the collection info such as users and windows version
+
+        WINDOWS_VERSION = DEFAULT_WINDOWS_VERSION;
+    }
+
+    public String anonymousUsername(String inputString) {
+        if (StringUtils.isBlank(inputString)) {
+            return "";
+        }
+
+        String anonymousString = anonymizeUserFromPathsWithForwardSlashes(inputString);
+        anonymousString = anonymizeUserFromPathsWithBackSlashes(anonymousString);
+        anonymousString = anonymizeServerFromUNCPath(anonymousString);
+
+        return anonymousString;
+    }
+
+    private String anonymizeUserFromPathsWithForwardSlashes(String stringWithUsername) {
+        Pattern pattern = WINDOWS_VERSION < 6 ? Pattern.compile(USER_PATH_FORWARD_SLASH_REGEX_XP, Pattern.CASE_INSENSITIVE) : Pattern.compile(USER_PATH_FORWARD_SLASH_REGEX, Pattern.CASE_INSENSITIVE);
+        Matcher matcher = pattern.matcher(stringWithUsername.toLowerCase(Locale.ENGLISH));
+        String replacement = "";
+        while (matcher.find()) {
+            replacement = String.format("$1%s$4", "<user>");
+        }
+        String anonymousString = matcher.replaceAll(replacement);
+
+        return anonymousString;
+    }
+
+    // Most paths in CyberTriage are normalized with forward slashes
+    // but there can still be strings containing paths that are not normalized such paths contained in arguments or event log payloads
+    private String anonymizeUserFromPathsWithBackSlashes(String stringWithUsername) {
+        Pattern pattern = WINDOWS_VERSION < 6 ? Pattern.compile(USER_PATH_BACK_SLASH_REGEX_XP, Pattern.CASE_INSENSITIVE) : Pattern.compile(USER_PATH_BACK_SLASH_REGEX, Pattern.CASE_INSENSITIVE);
+        Matcher matcher = pattern.matcher(stringWithUsername.toLowerCase(Locale.ENGLISH));
+        String replacement = "";
+        while (matcher.find()) {
+            replacement = String.format("$1%s$4", "<user>");
+        }
+        String anonymousString = matcher.replaceAll(replacement);
+
+        return anonymousString;
+    }
+
+    private String anonymizeServerFromUNCPath(String inputString) {
+
+        Set<String> serverNames = new HashSet<>();
+        String anonymousString = inputString.toLowerCase(Locale.ENGLISH);
+
+        Matcher forwardSlashMatcher = UNC_PATH_FORWARD_SLASH_PATTERN.matcher(anonymousString);
+        while (forwardSlashMatcher.find()) {
+            String serverName = forwardSlashMatcher.group(2);
+            serverNames.add(serverName);
+        }
+
+        Matcher backSlashMatcher = UNC_PATH_BACK_SLASH_PATTERN.matcher(anonymousString);
+        while (backSlashMatcher.find()) {
+            String serverName = backSlashMatcher.group(2);
+            serverNames.add(serverName);
+        }
+
+        for (String serverName : serverNames) {
+
+            if (StringUtils.isBlank(serverName)) {
+                continue;
+            }
+
+            if (InetAddresses.isInetAddress(serverName)) {
+                if (isLocalIP(serverName)) {
+                    anonymousString = StringUtils.replace(anonymousString, "\\" + serverName + "\\", "\\<private_ip>\\");
+                    anonymousString = StringUtils.replace(anonymousString, "/" + serverName + "/", "/<private_ip>/");
+                }
+            } else {
+                anonymousString = StringUtils.replace(anonymousString, "\\" + serverName + "\\", "\\<hostname>\\");
+                anonymousString = StringUtils.replace(anonymousString, "/" + serverName + "/", "/<hostname>/");
+            }
+
+        }
+
+        return anonymousString;
+    }
+
+    /**
+     * Returns true if IP Address is Any Local / Site Local / Link Local / Loop
+     * back local. Sample list "0.0.0.0", wildcard addres
+     * "10.1.1.1","10.10.10.10", site local address "127.0.0.0","127.2.2.2",
+     * loopback address "169.254.0.0","169.254.10.10", Link local address
+     * "172.16.0.0","172.31.245.245", site local address
+     *
+     * @param ipAddress
+     * @return
+     */
+    public static boolean isLocalIP(String ipAddress) {
+        try {
+            InetAddress a = InetAddresses.forString(ipAddress);
+            return a.isAnyLocalAddress() || a.isSiteLocalAddress()
+                    || a.isLoopbackAddress() || a.isLinkLocalAddress();
+        } catch (IllegalArgumentException ex) {
+            LOGGER.log(Level.WARNING, "Invalid IP string", ex);
+            return false;
+        }
+    }
+
+}