You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2016/12/20 22:23:34 UTC

geode git commit: GEODE-2119: gfsh user and password visible in clear text

Repository: geode
Updated Branches:
  refs/heads/feature/GEODE-2119 [created] 3fad7a38b


GEODE-2119: gfsh user and password visible in clear text

Made changes to suppress the password from being displayed in the log files and on the console.

* this closes #311 [klund]


Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/3fad7a38
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/3fad7a38
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/3fad7a38

Branch: refs/heads/feature/GEODE-2119
Commit: 3fad7a38bfa90fbf9f931d1ada01da2f00e83c31
Parents: 4067ddc
Author: Kevin J. Duling <kd...@pivotal.io>
Authored: Wed Dec 7 13:23:38 2016 -0800
Committer: Kirk Lund <kl...@apache.org>
Committed: Tue Dec 20 10:00:53 2016 -0800

----------------------------------------------------------------------
 .../geode/distributed/AbstractLauncher.java     | 101 ++++++------
 .../java/org/apache/geode/internal/Banner.java  |  44 ++----
 .../geode/internal/util/ArgumentRedactor.java   | 154 +++++++++++++++++++
 .../management/internal/cli/shell/Gfsh.java     |  95 ++++++------
 .../util/ArgumentRedactorJUnitTest.java         | 125 +++++++++++++++
 5 files changed, 398 insertions(+), 121 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/3fad7a38/geode-core/src/main/java/org/apache/geode/distributed/AbstractLauncher.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/AbstractLauncher.java b/geode-core/src/main/java/org/apache/geode/distributed/AbstractLauncher.java
index 0e40785..d5626dd 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/AbstractLauncher.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/AbstractLauncher.java
@@ -15,6 +15,8 @@
 
 package org.apache.geode.distributed;
 
+import static org.apache.geode.distributed.ConfigurationProperties.NAME;
+
 import org.apache.geode.distributed.internal.DistributionConfig;
 import org.apache.geode.distributed.internal.InternalDistributedSystem;
 import org.apache.geode.distributed.internal.unsafe.RegisterSignalHandlerSupport;
@@ -28,28 +30,37 @@ import org.apache.geode.internal.lang.StringUtils;
 import org.apache.geode.internal.lang.SystemUtils;
 import org.apache.geode.internal.process.PidUnavailableException;
 import org.apache.geode.internal.process.ProcessUtils;
+import org.apache.geode.internal.util.ArgumentRedactor;
 import org.apache.geode.internal.util.SunAPINotFoundException;
 import org.apache.geode.management.internal.cli.json.GfJsonObject;
 
-import java.io.*;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.net.BindException;
 import java.net.InetAddress;
 import java.net.URL;
 import java.sql.Timestamp;
 import java.text.SimpleDateFormat;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.FileHandler;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import static org.apache.geode.distributed.ConfigurationProperties.*;
-
 /**
  * The AbstractLauncher class is a base class for implementing various launchers to construct and
  * run different GemFire processes, like Cache Servers, Locators, Managers, HTTP servers and so on.
- * 
+ *
  * @see java.lang.Comparable
  * @see java.lang.Runnable
  * @see org.apache.geode.lang.Identifiable
@@ -85,7 +96,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
   protected final transient AtomicBoolean running = new AtomicBoolean(false);
 
   protected Logger logger = Logger.getLogger(getClass().getName()); // TODO:KIRK: does this need
-                                                                    // log4j2?
+  // log4j2?
 
   public AbstractLauncher() {
     try {
@@ -135,7 +146,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
    * Properties. The property is considered "set" if the String value of the property is not
    * non-null, non-empty and non-blank. Therefore, the Properties may "have" a property with name,
    * but having no value as determined by this method.
-   * 
+   *
    * @param properties the Properties used in determining whether the given property is set.
    * @param propertyName a String indicating the name of the property to check if set.
    * @return a boolean indicating whether the specified property with name has been given a value in
@@ -148,7 +159,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
   /**
    * Loads the GemFire properties at the specified URL.
-   * 
+   *
    * @param url the URL to the gemfire.properties to load.
    * @return a Properties instance populated with the gemfire.properties.
    * @see java.net.URL
@@ -188,7 +199,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
   /**
    * This method attempts to make a best effort determination for whether the Attach API classes are
    * on the classpath.
-   * 
+   *
    * @param t the Throwable being evaluated for missing Attach API classes.
    * @return a boolean indicating whether the Exception or Error condition is a result of the Attach
    *         API missing from the classpath.
@@ -223,7 +234,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
   /**
    * Determines if the Attach API is on the classpath.
-   * 
+   *
    * @return a boolean value indicating if the Attach API is on the classpath.
    */
   boolean isAttachAPIOnClasspath() {
@@ -233,7 +244,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
   /**
    * Determines whether the Locator launcher is in debug mode.
-   * 
+   *
    * @return a boolean to indicate whether the Locator launcher is in debug mode.
    * @see #setDebug(boolean)
    */
@@ -244,7 +255,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
   /**
    * Sets the debug mode of the GemFire launcher class. This mutable property of the launcher
    * enables the user to turn the debug mode on and off programmatically.
-   * 
+   *
    * @param debug a boolean used to enable or disable debug mode.
    * @see #isDebugging()
    */
@@ -254,7 +265,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
   /**
    * Determines whether the Locator referenced by this launcher is running.
-   * 
+   *
    * @return a boolean valued indicating if the referenced Locator is running.
    */
   public boolean isRunning() {
@@ -319,7 +330,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
   /**
    * Gets the name of the log file used to log information about this GemFire service.
-   * 
+   *
    * @return a String value indicating the name of this GemFire service's log file.
    */
   public abstract String getLogFileName();
@@ -328,7 +339,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
    * Gets the name or ID of the member in the GemFire distributed system. This method prefers name
    * if specified, otherwise the ID is returned. If name was not specified to the Builder that
    * created this Launcher and this call is not in-process, then null is returned.
-   * 
+   *
    * @return a String value indicating the member's name if specified, otherwise the member's ID is
    *         returned if this call is made in-process, or finally, null is returned if neither name
    *         name was specified or the call is out-of-process.
@@ -367,21 +378,21 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
   /**
    * Gets the user-specified process ID (PID) of the running GemFire service that AbstractLauncher
    * implementations can use to determine status, or stop the service.
-   * 
+   *
    * @return an Integer value indicating the process ID (PID) of the running GemFire service.
    */
   public abstract Integer getPid();
 
   /**
    * Gets the name of the GemFire service.
-   * 
+   *
    * @return a String indicating the name of the GemFire service.
    */
   public abstract String getServiceName();
 
   /**
    * Gets the working directory pathname in which the process will be run.
-   * 
+   *
    * @return a String value indicating the pathname of the Server's working directory.
    */
   public String getWorkingDirectory() {
@@ -391,7 +402,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
   /**
    * Prints the specified debug message to standard err, replacing any placeholder values with the
    * specified arguments on output, if debugging has been enabled.
-   * 
+   *
    * @param message the String value written to standard err.
    * @param args an Object array containing arguments to replace the placeholder values in the
    *        message.
@@ -412,7 +423,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
   /**
    * Prints the stack trace of the given Throwable to standard err if debugging has been enabled.
-   * 
+   *
    * @param t the Throwable who's stack trace is printed to standard err.
    * @see java.lang.System#err
    * @see #isDebugging()
@@ -427,7 +438,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
   /**
    * Prints the specified informational message to standard err, replacing any placeholder values
    * with the specified arguments on output.
-   * 
+   *
    * @param message the String value written to standard err.
    * @param args an Object array containing arguments to replace the placeholder values in the
    *        message.
@@ -445,7 +456,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
   /**
    * Redirects the standard out and standard err to the configured log file as specified in the
    * GemFire distributed system properties.
-   * 
+   *
    * @param distributedSystem the GemFire model for a distributed system.
    * @throws IOException if the standard out and err redirection was unsuccessful.
    */
@@ -458,7 +469,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
   /**
    * Gets the version of GemFire currently running.
-   * 
+   *
    * @return a String representation of GemFire's version.
    */
   public String version() {
@@ -635,7 +646,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the Java classpath used when launching the GemFire service.
-     * 
+     *
      * @return a String value indicating the Java classpath used when launching the GemFire service.
      * @see java.lang.System#getProperty(String) with 'java.class.path'
      */
@@ -645,7 +656,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the version of GemFire used to launch and run the GemFire service.
-     * 
+     *
      * @return a String indicating the version of GemFire used in the running GemFire service.
      */
     public String getGemFireVersion() {
@@ -654,7 +665,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the version of Java used to launch and run the GemFire service.
-     * 
+     *
      * @return a String indicating the version of the Java runtime used in the running GemFire
      *         service.
      * @see java.lang.System#getProperty(String) with 'java.verson'
@@ -665,7 +676,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the arguments passed to the JVM process that is running the GemFire service.
-     * 
+     *
      * @return a List of String value each representing an argument passed to the JVM of the GemFire
      *         service.
      * @see java.lang.management.RuntimeMXBean#getInputArguments()
@@ -676,7 +687,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets GemFire member's name for the process.
-     * 
+     *
      * @return a String indicating the GemFire member's name for the process.
      */
     public String getMemberName() {
@@ -685,7 +696,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the process ID of the running GemFire service if known, otherwise returns null.
-     * 
+     *
      * @return a integer value indicating the process ID (PID) of the running GemFire service, or
      *         null if the PID cannot be determined.
      */
@@ -695,7 +706,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the location of the GemFire service (usually the host in combination with the port).
-     * 
+     *
      * @return a String indication the location (such as host/port) of the GemFire service.
      */
     public String getServiceLocation() {
@@ -704,14 +715,14 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the name of the GemFire service.
-     * 
+     *
      * @return a String indicating the name of the GemFire service.
      */
     protected abstract String getServiceName();
 
     /**
      * Gets the state of the GemFire service.
-     * 
+     *
      * @return a Status enumerated type representing the state of the GemFire service.
      * @see org.apache.geode.distributed.AbstractLauncher.Status
      */
@@ -721,7 +732,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets description of the the service's current state.
-     * 
+     *
      * @return a String describing the service's current state.
      */
     public String getStatusMessage() {
@@ -730,7 +741,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * The date and time the GemFire service was last in this state.
-     * 
+     *
      * @return a Timestamp signifying the last date and time the GemFire service was in this state.
      * @see java.sql.Timestamp
      */
@@ -741,7 +752,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
     /**
      * Gets the amount of time in milliseconds that the JVM process with the GemFire service has
      * been running.
-     * 
+     *
      * @return a long value indicating the number of milliseconds that the GemFire service JVM has
      *         been running.
      * @see java.lang.management.RuntimeMXBean#getUptime()
@@ -753,7 +764,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
     /**
      * Gets the directory in which the GemFire service is running. This is also the location where
      * all GemFire service files (log files, the PID file, and so on) are written.
-     * 
+     *
      * @return a String value indicating the GemFire service's working (running) directory.
      */
     public String getWorkingDirectory() {
@@ -762,7 +773,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the path of the log file for the process.
-     * 
+     *
      * @return a String value indicating the path of the log file for the process.
      */
     public String getLogFile() {
@@ -771,7 +782,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the host or IP address for the process and its service.
-     * 
+     *
      * @return a String value representing the host or IP address for the process and its service.
      */
     public String getHost() {
@@ -780,7 +791,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the port for the process and its service.
-     * 
+     *
      * @return an Integer value indicating the port for the process and its service.
      */
     public String getPort() {
@@ -789,7 +800,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets a String describing the state of the GemFire service.
-     * 
+     *
      * @return a String describing the state of the GemFire service.
      */
     @Override
@@ -799,14 +810,14 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
           return LocalizedStrings.Launcher_ServiceStatus_STARTING_MESSAGE.toLocalizedString(
               getServiceName(), getWorkingDirectory(), getServiceLocation(), getMemberName(),
               toString(getTimestamp()), toString(getPid()), toString(getGemFireVersion()),
-              toString(getJavaVersion()), getLogFile(), toString(getJvmArguments().toArray()),
+              toString(getJavaVersion()), getLogFile(), ArgumentRedactor.redact(getJvmArguments()),
               toString(getClasspath()));
         case ONLINE:
           return LocalizedStrings.Launcher_ServiceStatus_RUNNING_MESSAGE.toLocalizedString(
               getServiceName(), getWorkingDirectory(), getServiceLocation(), getMemberName(),
               getStatus(), toString(getPid()), toDaysHoursMinutesSeconds(getUptime()),
               toString(getGemFireVersion()), toString(getJavaVersion()), getLogFile(),
-              toString(getJvmArguments().toArray()), toString(getClasspath()));
+              ArgumentRedactor.redact(getJvmArguments()), toString(getClasspath()));
         case STOPPED:
           return LocalizedStrings.Launcher_ServiceStatus_STOPPED_MESSAGE
               .toLocalizedString(getServiceName(), getWorkingDirectory(), getServiceLocation());
@@ -856,7 +867,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Looks up the Status enum type by description. The lookup operation is case-insensitive.
-     * 
+     *
      * @param description a String value describing the Locator's status.
      * @return a Status enumerated type matching the description.
      */
@@ -872,7 +883,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets the description of the Status enum type.
-     * 
+     *
      * @return a String describing the Status enum type.
      */
     public String getDescription() {
@@ -881,7 +892,7 @@ public abstract class AbstractLauncher<T extends Comparable<T>> implements Runna
 
     /**
      * Gets a String representation of the Status enum type.
-     * 
+     *
      * @return a String representing the Status enum type.
      * @see #getDescription()
      */

http://git-wip-us.apache.org/repos/asf/geode/blob/3fad7a38/geode-core/src/main/java/org/apache/geode/internal/Banner.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/Banner.java b/geode-core/src/main/java/org/apache/geode/internal/Banner.java
index 5ad70b8..4cb7dd0 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/Banner.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/Banner.java
@@ -16,14 +16,20 @@ package org.apache.geode.internal;
 
 import org.apache.geode.SystemFailure;
 import org.apache.geode.distributed.internal.DistributionConfig;
-import org.apache.geode.distributed.internal.DistributionConfigImpl;
 import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.util.ArgumentRedactor;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
 
 /**
  * Utility class to print banner information at manager startup.
@@ -45,10 +51,10 @@ public class Banner {
 
   /**
    * Print information about this process to the specified stream.
-   * 
+   *
    * @param args possibly null list of command line arguments
    */
-  private static void print(PrintWriter out, String args[]) {
+  static void print(PrintWriter out, String args[]) {
     Map sp = new TreeMap((Properties) System.getProperties().clone()); // fix for 46822
     int processId = -1;
     final String SEPERATOR =
@@ -105,7 +111,7 @@ public class Banner {
     sp.remove("user.dir");
     out.println("Home dir: " + sp.get("user.home"));
     sp.remove("user.home");
-    List<String> allArgs = new ArrayList<String>();
+    List<String> allArgs = new ArrayList<>();
     {
       RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
       if (runtimeBean != null) {
@@ -114,16 +120,13 @@ public class Banner {
     }
 
     if (args != null && args.length != 0) {
-      for (int i = 0; i < args.length; i++) {
-        allArgs.add(args[i]);
-      }
+      Collections.addAll(allArgs, args);
     }
     if (!allArgs.isEmpty()) {
       out.println("Command Line Parameters:");
-      for (String arg : allArgs) {
-        out.println("  " + arg);
-      }
+      out.println(ArgumentRedactor.redact(allArgs));
     }
+
     out.println("Class Path:");
     prettyPrintPath((String) sp.get("java.class.path"), out);
     sp.remove("java.class.path");
@@ -135,22 +138,7 @@ public class Banner {
       out.println("System property logging disabled.");
     } else {
       out.println("System Properties:");
-      Iterator it = sp.entrySet().iterator();
-      while (it.hasNext()) {
-        Map.Entry me = (Map.Entry) it.next();
-        String key = me.getKey().toString();
-        // SW: Filter out the security properties since they may contain
-        // sensitive information.
-        if (!key
-            .startsWith(DistributionConfig.GEMFIRE_PREFIX + DistributionConfig.SECURITY_PREFIX_NAME)
-            && !key.startsWith(DistributionConfigImpl.SECURITY_SYSTEM_PREFIX
-                + DistributionConfig.SECURITY_PREFIX_NAME)
-            && !key.toLowerCase().contains("password") /* bug 45381 */) {
-          out.println("    " + key + " = " + me.getValue());
-        } else {
-          out.println("    " + key + " = " + "********");
-        }
-      }
+      out.println(ArgumentRedactor.redact(sp));
       out.println("Log4J 2 Configuration:");
       out.println("    " + LogService.getConfigInformation());
     }
@@ -159,7 +147,7 @@ public class Banner {
 
   /**
    * Return a string containing the banner information.
-   * 
+   *
    * @param args possibly null list of command line arguments
    */
   public static String getString(String args[]) {

http://git-wip-us.apache.org/repos/asf/geode/blob/3fad7a38/geode-core/src/main/java/org/apache/geode/internal/util/ArgumentRedactor.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/util/ArgumentRedactor.java b/geode-core/src/main/java/org/apache/geode/internal/util/ArgumentRedactor.java
new file mode 100644
index 0000000..419f3f9
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/util/ArgumentRedactor.java
@@ -0,0 +1,154 @@
+/*
+ * 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.apache.geode.internal.util;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class ArgumentRedactor {
+
+  private ArgumentRedactor() {}
+
+  public static String redact(final List<String> args) {
+    StringBuilder redacted = new StringBuilder();
+    for (String arg : args) {
+      redacted.append(redact(arg)).append(" ");
+    }
+    return redacted.toString().trim();
+  }
+
+  /**
+   * Accept a map of key/value pairs and produce a printable string, redacting any necessary values.
+   * 
+   * @param map A {@link Map} of key/value pairs such as a collection of properties
+   *
+   * @return A printable string with redacted fields. E.g., "username=jdoe password=********"
+   */
+  public static String redact(final Map<String, String> map) {
+    StringBuilder redacted = new StringBuilder();
+    for (Entry<String, String> entry : map.entrySet()) {
+      redacted.append(entry.getKey());
+      redacted.append("=");
+      redacted.append(redact(entry));
+      redacted.append(" ");
+    }
+    return redacted.toString().trim();
+  }
+
+  /**
+   * Returns the redacted value of the {@link Entry} if the key indicates redaction is necessary.
+   * Otherwise, value is returned, unchanged.
+   * 
+   * @param entry A key/value pair
+   *
+   * @return The redacted string for value.
+   */
+  public static String redact(Entry<String, String> entry) {
+    return redact(entry.getKey(), entry.getValue());
+  }
+
+  /**
+   * Parse a string to find key=value pairs and redact the values if necessary. If more than one
+   * key=value pair exists in the input, each pair must be preceeded by a hyphen '-' to delineate
+   * the pairs. <br>
+   * Example:<br>
+   * Single value: "password=secret" or "--password=secret" Mulitple values: "-Dflag -Dkey=value
+   * --classpath=."
+   * 
+   * @param line The input to be parsed
+   * @return A redacted string that has sensitive information obscured.
+   */
+  public static String redact(String line) {
+    StringBuilder redacted = new StringBuilder();
+    if (line.startsWith("-")) {
+      line = " " + line;
+      String[] args = line.split(" -");
+      StringBuilder param = new StringBuilder();
+      for (String arg : args) {
+        if (arg.isEmpty()) {
+          param.append("-");
+        } else {
+          String[] pair = arg.split("=", 2);
+          param.append(pair[0].trim());
+          if (pair.length == 1) {
+            redacted.append(param);
+          } else {
+            redacted.append(param).append("=").append(redact(param.toString(), pair[1].trim()));
+          }
+          redacted.append(" ");
+        }
+        param.setLength(0);
+        param.append("-");
+      }
+    } else {
+      String[] args = line.split("=", 2);
+      if (args.length == 1) {
+        redacted.append(line);
+      } else {
+        redacted.append(args[0].trim()).append("=").append(redact(args[0], args[1]));
+      }
+      redacted.append(" ");
+    }
+    return redacted.toString().trim();
+  }
+
+  /**
+   * Return a redacted value if the key indicates redaction is necessary. Otherwise, return the
+   * value unchanged.
+   *
+   * @param key A string such as a system property, jvm parameter or similar in a key=value
+   *        situation.
+   * @param value A string that is the value assigned to the key.
+   *
+   * @return A redacted string if the key indicates it should be redacted, otherwise the string is
+   *         unchanged.
+   */
+  public static String redact(String key, String value) {
+    if (shouldBeRedacted(key)) {
+      return "********";
+    }
+    return value.trim();
+  }
+
+  /**
+   * Determine whether a key's value should be redacted.
+   * 
+   * @param key The key in question.
+   *
+   * @return true if the value should be redacted, otherwise false.
+   */
+  private static boolean shouldBeRedacted(String key) {
+    if (key == null) {
+      return false;
+    }
+
+    // Clean off any flags such as -J and -D to get to the actual start of the parameter
+    String compareKey = key;
+    if (key.startsWith("-J")) {
+      compareKey = key.substring(2);
+    }
+    if (compareKey.startsWith("-D")) {
+      compareKey = compareKey.substring(2);
+    }
+    return compareKey.toLowerCase().contains("password");
+    // return compareKey
+    // .startsWith(DistributionConfig.GEMFIRE_PREFIX + DistributionConfig.SECURITY_PREFIX_NAME)
+    // || compareKey.startsWith(
+    // DistributionConfigImpl.SECURITY_SYSTEM_PREFIX + DistributionConfig.SECURITY_PREFIX_NAME)
+    // || compareKey.toLowerCase().contains("password");
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/3fad7a38/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
index cc5882a..bcf1b41 100755
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
@@ -14,42 +14,13 @@
  */
 package org.apache.geode.management.internal.cli.shell;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.net.URL;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
 import jline.Terminal;
 import jline.console.ConsoleReader;
-import org.springframework.shell.core.AbstractShell;
-import org.springframework.shell.core.CommandMarker;
-import org.springframework.shell.core.Converter;
-import org.springframework.shell.core.ExecutionStrategy;
-import org.springframework.shell.core.ExitShellRequest;
-import org.springframework.shell.core.JLineLogHandler;
-import org.springframework.shell.core.JLineShell;
-import org.springframework.shell.core.Parser;
-import org.springframework.shell.event.ShellStatus.Status;
-
 import org.apache.geode.internal.Banner;
 import org.apache.geode.internal.GemFireVersion;
 import org.apache.geode.internal.lang.ClassUtils;
 import org.apache.geode.internal.process.signal.AbstractSignalNotificationHandler;
+import org.apache.geode.internal.util.ArgumentRedactor;
 import org.apache.geode.internal.util.HostName;
 import org.apache.geode.internal.util.SunAPINotFoundException;
 import org.apache.geode.management.cli.CommandProcessingException;
@@ -70,6 +41,35 @@ import org.apache.geode.management.internal.cli.shell.jline.GfshHistory;
 import org.apache.geode.management.internal.cli.shell.jline.GfshUnsupportedTerminal;
 import org.apache.geode.management.internal.cli.shell.unsafe.GfshSignalHandler;
 import org.apache.geode.management.internal.cli.util.CommentSkipHelper;
+import org.springframework.shell.core.AbstractShell;
+import org.springframework.shell.core.CommandMarker;
+import org.springframework.shell.core.Converter;
+import org.springframework.shell.core.ExecutionStrategy;
+import org.springframework.shell.core.ExitShellRequest;
+import org.springframework.shell.core.JLineLogHandler;
+import org.springframework.shell.core.JLineShell;
+import org.springframework.shell.core.Parser;
+import org.springframework.shell.event.ShellStatus.Status;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
 
 /**
  * Extends an interactive shell provided by
@@ -84,7 +84,7 @@ import org.apache.geode.management.internal.cli.util.CommentSkipHelper;
  * <p />
  * Additionally, this class is used to maintain GemFire SHell (gfsh) specific information like:
  * environment TODO
- *
+ * 
  *
  * @since GemFire 7.0
  */
@@ -180,7 +180,7 @@ public class Gfsh extends JLineShell {
 
   /**
    * Create a GemFire shell with console using the specified arguments.
-   *
+   * 
    * @param args arguments to be used to create a GemFire shell instance
    * @throws IOException
    * @throws ClassNotFoundException
@@ -192,7 +192,7 @@ public class Gfsh extends JLineShell {
   /**
    * Create a GemFire shell using the specified arguments. Console for user inputs is made available
    * if <code>launchShell</code> is set to <code>true</code>.
-   *
+   * 
    * @param launchShell whether to make Console available
    * @param args arguments to be used to create a GemFire shell instance or execute command
    * @throws IOException
@@ -208,10 +208,10 @@ public class Gfsh extends JLineShell {
     this.gfshFileLogger = LogWrapper.getInstance();
     this.gfshFileLogger.configure(this.gfshConfig);
     this.ansiHandler = ANSIHandler.getInstance(this.gfshConfig.isANSISupported()); // TODO -
-                                                                                   // Abhishek :
-                                                                                   // should take it
-                                                                                   // from
-                                                                                   // ConsoleReader.terminal??
+    // Abhishek :
+    // should take it
+    // from
+    // ConsoleReader.terminal??
 
     /* 3. log system properties & gfsh environment */
     this.gfshFileLogger.info(Banner.getString(args));
@@ -397,7 +397,7 @@ public class Gfsh extends JLineShell {
   /**
    * Returns the {@link ExecutionStrategy} implementation used by this implementation of
    * {@link AbstractShell}. {@link Gfsh} uses {@link GfshExecutionStrategy}.
-   *
+   * 
    * @return ExecutionStrategy used by Gfsh
    */
   @Override
@@ -408,7 +408,7 @@ public class Gfsh extends JLineShell {
   /**
    * Returns the {@link Parser} implementation used by this implementation of
    * {@link AbstractShell}.{@link Gfsh} uses {@link GfshParser}.
-   *
+   * 
    * @return Parser used by Gfsh
    */
   @Override
@@ -420,7 +420,7 @@ public class Gfsh extends JLineShell {
   /**
    * Executes the given command string. We have over-ridden the behavior to extend the original
    * implementation to store the 'last command execution status'.
-   *
+   * 
    * @param line command string to be executed
    * @return true if execution is successful; false otherwise
    */
@@ -684,7 +684,7 @@ public class Gfsh extends JLineShell {
 
   /**
    * Set the last command execution status
-   *
+   * 
    * @param lastExecutionStatus last command execution status
    */
   public void setLastExecutionStatus(int lastExecutionStatus) {
@@ -782,7 +782,7 @@ public class Gfsh extends JLineShell {
         String lineRead = "";
         StringBuilder linesBuffer = new StringBuilder();
         String linesBufferString = ""; // used to check whether the string in a buffer contains a
-                                       // ";".
+        // ";".
         int commandSrNum = 0;
         CommentSkipHelper commentSkipper = new CommentSkipHelper();
 
@@ -804,13 +804,12 @@ public class Gfsh extends JLineShell {
           if (!linesBufferString.endsWith(SyntaxConstants.CONTINUATION_CHARACTER)) { // see 45893
             // String command = null;
 
-
             List<String> commandList = MultiCommandHelper.getMultipleCommands(linesBufferString);
             for (String cmdLet : commandList) {
-              String trimmedCommand = cmdLet.trim();
-              if (!trimmedCommand.isEmpty()) {
+              if (!cmdLet.isEmpty()) {
+                String redactedCmdLet = ArgumentRedactor.redact(cmdLet);
                 ++commandSrNum;
-                Gfsh.println(commandSrNum + ". Executing - " + cmdLet);
+                Gfsh.println(commandSrNum + ". Executing - " + redactedCmdLet);
                 Gfsh.println();
                 boolean executeSuccess = executeScriptLine(cmdLet);
                 if (!executeSuccess) {
@@ -1138,14 +1137,14 @@ public class Gfsh extends JLineShell {
    *
    * For example: if the terminal width were 5 and the string "123 456789 01234" were passed in with
    * an indentation level of 2, then the returned string would be:
-   * 
+   *
    * <pre>
    *         123
    *         45678
    *         9
    *         01234
    * </pre>
-   * 
+   *
    * @param string String to wrap (add breakpoints and indent)
    * @param indentationLevel The number of indentation levels to use.
    * @return The wrapped string.

http://git-wip-us.apache.org/repos/asf/geode/blob/3fad7a38/geode-core/src/test/java/org/apache/geode/internal/util/ArgumentRedactorJUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/util/ArgumentRedactorJUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/util/ArgumentRedactorJUnitTest.java
new file mode 100644
index 0000000..b40d485
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/internal/util/ArgumentRedactorJUnitTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.apache.geode.internal.util;
+
+import static org.apache.geode.internal.util.ArgumentRedactor.redact;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.geode.test.junit.categories.UnitTest;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ArgumentRedactor Tester.
+ */
+@Category(UnitTest.class)
+public class ArgumentRedactorJUnitTest {
+  @Test
+  public void testRedactArgList() throws Exception {
+    List<String> argList = new ArrayList<>();
+    argList.add("gemfire.security-password=secret");
+    argList.add("gemfire.security-properties=./security.properties");
+    argList.add("gemfire.sys.security-value=someValue");
+    argList.add("gemfire.use-cluster-configuration=true");
+    argList.add("someotherstringvalue");
+    argList.add("login-password=secret");
+    argList.add("login-name=admin");
+    argList.add("gemfire-password = super-secret");
+    argList.add("geode-password= confidential");
+    argList.add("some-other-password =shhhh");
+    String redacted = redact(argList);
+    assertTrue(redacted.contains("gemfire.security-password=********"));
+    assertTrue(redacted.contains("gemfire.security-properties=./security.properties"));
+    assertTrue(redacted.contains("gemfire.sys.security-value=someValue"));
+    assertTrue(redacted.contains("gemfire.use-cluster-configuration=true"));
+    assertTrue(redacted.contains("someotherstringvalue"));
+    assertTrue(redacted.contains("login-password=********"));
+    assertTrue(redacted.contains("login-name=admin"));
+    assertTrue(redacted.contains("gemfire-password=********"));
+    assertTrue(redacted.contains("geode-password=********"));
+    assertTrue(redacted.contains("some-other-password=********"));
+  }
+
+  @Test
+  public void testRedactMap() throws Exception {
+    Map<String, String> argMap = new HashMap<>();
+    argMap.put("gemfire.security-password", "secret");
+    argMap.put("gemfire.security-properties", "./security.properties");
+    argMap.put("gemfire.sys.security-value", "someValue");
+    argMap.put("gemfire.use-cluster-configuration", "true");
+    argMap.put("login-password", "secret");
+    argMap.put("login-name", "admin");
+    String redacted = redact(argMap);
+    assertTrue(redacted.contains("gemfire.security-password=********"));
+    assertTrue(redacted.contains("gemfire.security-properties=./security.properties"));
+    assertTrue(redacted.contains("gemfire.sys.security-value=someValue"));
+    assertTrue(redacted.contains("gemfire.use-cluster-configuration=true"));
+    assertTrue(redacted.contains("login-password=********"));
+    assertTrue(redacted.contains("login-name=admin"));
+  }
+
+  @Test
+  public void testRedactArg() throws Exception {
+    String arg = "-Dgemfire.security-password=secret";
+    assertTrue(redact(arg).endsWith("password=********"));
+
+    arg = "-Dgemfire.security-properties=./security-properties";
+    assertEquals(arg, (redact(arg)));
+
+    arg = "-J-Dgemfire.sys.security-value=someValue";
+    assertEquals(arg, (redact(arg)));
+
+    arg = "-Dgemfire.sys.value=printable";
+    assertEquals(arg, redact(arg));
+
+    arg = "-Dgemfire.use-cluster-configuration=true";
+    assertEquals(arg, redact(arg));
+
+    arg = "someotherstringvalue";
+    assertEquals(arg, redact(arg));
+
+    arg = "--password=foo";
+    assertEquals("--password=********", redact(arg));
+
+    arg = "--classpath=.";
+    assertEquals(arg, redact(arg));
+
+    arg = "-DmyArg -Duser-password=foo --classpath=.";
+    assertEquals("-DmyArg -Duser-password=******** --classpath=.", redact(arg));
+
+    arg = "-DmyArg -Duser-password=foo -DOtherArg -Dsystem-password=bar";
+    assertEquals("-DmyArg -Duser-password=******** -DOtherArg -Dsystem-password=********",
+        redact(arg));
+
+    arg =
+        "-Dlogin-password=secret -Dlogin-name=admin -Dgemfire-password = super-secret --geode-password= confidential -J-Dsome-other-password =shhhh";
+    String redacted = redact(arg);
+    assertTrue(redacted.contains("login-password=********"));
+    assertTrue(redacted.contains("login-name=admin"));
+    assertTrue(redacted.contains("gemfire-password=********"));
+    assertTrue(redacted.contains("geode-password=********"));
+    assertTrue(redacted.contains("some-other-password=********"));
+
+    arg = "-Dgemfire.security-properties=\"c:\\Program Files (x86)\\My Folder\"";
+    assertEquals(arg, (redact(arg)));
+  }
+}