You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by sl...@apache.org on 2026/10/18 18:23:28 UTC

(maven-mvnd) branch mvnd-1.x updated: Mask password-like values and user input in the daemon log

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

slachiewicz pushed a commit to branch mvnd-1.x
in repository https://gitbox.apache.org/repos/asf/maven-mvnd.git


The following commit(s) were added to refs/heads/mvnd-1.x by this push:
     new 25443bc0 Mask password-like values and user input in the daemon log
25443bc0 is described below

commit 25443bc092bcdbf9e24509dd8aae087f00bffcd0
Author: Sylwester Lachiewicz <sl...@apache.org>
AuthorDate: Sun Oct 18 15:49:31 2026 +0200

    Mask password-like values and user input in the daemon log
---
 .../java/org/mvndaemon/mvnd/common/LogMasking.java | 84 ++++++++++++++++++++++
 .../java/org/mvndaemon/mvnd/common/Message.java    |  8 +--
 .../org/mvndaemon/mvnd/common/LogMaskingTest.java  | 77 ++++++++++++++++++++
 .../java/org/mvndaemon/mvnd/cli/EnvHelper.java     |  8 ++-
 4 files changed, 170 insertions(+), 7 deletions(-)

diff --git a/common/src/main/java/org/mvndaemon/mvnd/common/LogMasking.java b/common/src/main/java/org/mvndaemon/mvnd/common/LogMasking.java
new file mode 100644
index 00000000..6216a8c1
--- /dev/null
+++ b/common/src/main/java/org/mvndaemon/mvnd/common/LogMasking.java
@@ -0,0 +1,84 @@
+/*
+ * 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.mvndaemon.mvnd.common;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Masks values that should not appear in the daemon log: property and environment values whose key names a
+ * password or similar, as they come in on the command line or in the client environment.
+ */
+public final class LogMasking {
+
+    private static final Pattern SENSITIVE_KEY =
+            Pattern.compile("(?i).*(password|passwd|passphrase|secret|token|credential).*");
+
+    private static final String MASK = "****";
+
+    private LogMasking() {}
+
+    public static boolean isSensitive(String key) {
+        return key != null && SENSITIVE_KEY.matcher(key).matches();
+    }
+
+    /**
+     * The value to log for {@code key}: the value itself, or a mask when the key looks sensitive.
+     */
+    public static String value(String key, String value) {
+        return value != null && isSensitive(key) ? MASK : value;
+    }
+
+    /**
+     * Command-line arguments with the values of sensitive {@code -Dkey=value} (also {@code -D key=value} and
+     * {@code --define key=value}) definitions masked.
+     */
+    public static List<String> args(List<String> args) {
+        if (args == null) {
+            return null;
+        }
+        List<String> result = new ArrayList<>(args.size());
+        boolean defineValue = false;
+        for (String arg : args) {
+            if (defineValue) {
+                result.add(definition(arg));
+                defineValue = false;
+            } else if (arg != null && arg.startsWith("-D") && arg.length() > 2) {
+                result.add("-D" + definition(arg.substring(2)));
+            } else {
+                result.add(arg);
+                defineValue = "-D".equals(arg) || "--define".equals(arg);
+            }
+        }
+        return result;
+    }
+
+    private static String definition(String keyValue) {
+        if (keyValue == null) {
+            return null;
+        }
+        int eq = keyValue.indexOf('=');
+        if (eq < 0) {
+            return keyValue;
+        }
+        String key = keyValue.substring(0, eq);
+        return isSensitive(key) ? key + '=' + MASK : keyValue;
+    }
+}
diff --git a/common/src/main/java/org/mvndaemon/mvnd/common/Message.java b/common/src/main/java/org/mvndaemon/mvnd/common/Message.java
index 8d826124..a679bf3d 100644
--- a/common/src/main/java/org/mvndaemon/mvnd/common/Message.java
+++ b/common/src/main/java/org/mvndaemon/mvnd/common/Message.java
@@ -384,7 +384,7 @@ public abstract class Message {
         @Override
         public String toString() {
             return "BuildRequest{" + "args="
-                    + args + ", workingDir='"
+                    + LogMasking.args(args) + ", workingDir='"
                     + workingDir + '\'' + ", projectDir='"
                     + projectDir + '\'' + '}';
         }
@@ -1108,8 +1108,8 @@ public abstract class Message {
         public String toString() {
             return "PromptResponse{" + "projectId='"
                     + projectId + '\'' + ", uid='"
-                    + uid + '\'' + ", message='"
-                    + message + '\'' + '}';
+                    + uid + '\'' + ", message="
+                    + (message == null ? "null" : "[" + message.length() + " chars]") + '}';
         }
 
         @Override
@@ -1362,7 +1362,7 @@ public abstract class Message {
 
         @Override
         public String toString() {
-            return "InputResponse{" + "data='" + data + "\'" + '}';
+            return "InputResponse{" + (data == null ? "eof" : "data=[" + data.length() + " chars]") + '}';
         }
 
         @Override
diff --git a/common/src/test/java/org/mvndaemon/mvnd/common/LogMaskingTest.java b/common/src/test/java/org/mvndaemon/mvnd/common/LogMaskingTest.java
new file mode 100644
index 00000000..1e363718
--- /dev/null
+++ b/common/src/test/java/org/mvndaemon/mvnd/common/LogMaskingTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.mvndaemon.mvnd.common;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class LogMaskingTest {
+
+    @Test
+    void masksSensitiveDefinitions() {
+        assertEquals(
+                Arrays.asList(
+                        "verify",
+                        "-Dgpg.passphrase=****",
+                        "-DskipTests=true",
+                        "-D",
+                        "repo.password=****",
+                        "--define",
+                        "api.TOKEN=****",
+                        "-Dflag"),
+                LogMasking.args(Arrays.asList(
+                        "verify",
+                        "-Dgpg.passphrase=hunter2",
+                        "-DskipTests=true",
+                        "-D",
+                        "repo.password=hunter2",
+                        "--define",
+                        "api.TOKEN=abc",
+                        "-Dflag")));
+    }
+
+    @Test
+    void masksSensitiveEnvironmentValues() {
+        assertEquals("****", LogMasking.value("NPM_TOKEN", "abc"));
+        assertEquals("/usr/bin", LogMasking.value("PATH", "/usr/bin"));
+        assertEquals(null, LogMasking.value("DB_PASSWORD", null));
+    }
+
+    @Test
+    void messagesDoNotPrintUserInput() {
+        Message.Prompt prompt = new Message.Prompt("p", "u1", "Password:", true);
+        String response = prompt.response("hunter2").toString();
+        assertFalse(response.contains("hunter2"), response);
+        assertTrue(response.contains("[7 chars]"), response);
+
+        String input = Message.inputResponse("hunter2").toString();
+        assertFalse(input.contains("hunter2"), input);
+
+        String request = new Message.BuildRequest(
+                        Arrays.asList("-Dgpg.passphrase=hunter2", "verify"), "/w", "/w", Collections.emptyMap())
+                .toString();
+        assertFalse(request.contains("hunter2"), request);
+    }
+}
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/cli/EnvHelper.java b/daemon/src/main/java/org/mvndaemon/mvnd/cli/EnvHelper.java
index cfb83d81..e0e6aa24 100644
--- a/daemon/src/main/java/org/mvndaemon/mvnd/cli/EnvHelper.java
+++ b/daemon/src/main/java/org/mvndaemon/mvnd/cli/EnvHelper.java
@@ -35,6 +35,7 @@ import java.util.stream.Stream;
 
 import org.jline.utils.ExecHelper;
 import org.mvndaemon.mvnd.common.JavaVersion;
+import org.mvndaemon.mvnd.common.LogMasking;
 import org.mvndaemon.mvnd.common.Os;
 import org.mvndaemon.mvnd.nativ.CLibrary;
 import org.slf4j.Logger;
@@ -65,7 +66,8 @@ public class EnvHelper {
                 String vr = requested.get(key);
                 int rc = CLibrary.setenv(key, vr);
                 if (Os.current() == Os.WINDOWS ^ rc != 0) {
-                    log.accept(String.format("Error setting environment value %s = %s", key, vr));
+                    log.accept(
+                            String.format("Error setting environment value %s = %s", key, LogMasking.value(key, vr)));
                 }
             }
             setEnv(requested);
@@ -83,8 +85,8 @@ public class EnvHelper {
         if (!diffs.isEmpty()) {
             log.accept("A few environment mismatches have been detected between the client and the daemon.");
             diffs.forEach(key -> {
-                String vr = requested.get(key);
-                String va = nactual.get(key);
+                String vr = LogMasking.value(key, requested.get(key));
+                String va = LogMasking.value(key, nactual.get(key));
                 log.accept(String.format(
                         "   %s -> %s instead of %s",
                         key, va != null ? "'" + va + "'" : "<null>", vr != null ? "'" + vr + "'" : "<null>"));