From 3c8c40c76a699ffe9840c4c9b0faccd54fe413ea Mon Sep 17 00:00:00 2001
From: Thomas Florio <thomas.florio@suse.com>
Date: Tue, 30 Jan 2024 11:41:37 +0100
Subject: [PATCH 1/1] Added debug info

---
 .../suse/manager/gatherer/GathererRunner.java | 16 +++++-
 java/code/src/com/suse/utils/Json.java        | 56 +++++++++++++++++++
 .../src/com/suse/utils/test/JsonTest.java     | 53 ++++++++++++++++++
 3 files changed, 123 insertions(+), 2 deletions(-)
 create mode 100644 java/code/src/com/suse/utils/test/JsonTest.java
#L3PD: bsc#1218724

diff --git a/java/code/src/com/suse/manager/gatherer/GathererRunner.java b/java/code/src/com/suse/manager/gatherer/GathererRunner.java
index 563f5d239f5..228df149de0 100644
--- a/java/code/src/com/suse/manager/gatherer/GathererRunner.java
+++ b/java/code/src/com/suse/manager/gatherer/GathererRunner.java
@@ -23,6 +23,9 @@ import com.redhat.rhn.manager.satellite.Executor;
 import com.redhat.rhn.manager.satellite.SystemCommandExecutor;
 
 import com.suse.manager.model.gatherer.GathererModule;
+import com.suse.utils.Json;
+
+import com.google.gson.GsonBuilder;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.client.utils.URIBuilder;
@@ -38,6 +41,7 @@ import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 
 /**
@@ -134,8 +138,14 @@ public class GathererRunner {
         Runtime r = Runtime.getRuntime();
         try {
             Process p = r.exec(args.toArray(new String[0]), envp);
+            String jsonInput = new GathererJsonIO().toJson(vhms);
+            if (logger.isDebugEnabled()) {
+                logger.debug("virtual-host-gatherer INPUT: {}",
+                    Json.maskFields(jsonInput, Set.of("password"), new GsonBuilder().create()));
+            }
+
             PrintWriter stdin = new PrintWriter(p.getOutputStream());
-            stdin.println(new GathererJsonIO().toJson(vhms));
+            stdin.println(jsonInput);
             stdin.flush();
             stdin.close();
 
@@ -163,7 +173,9 @@ public class GathererRunner {
             while ((line = br.readLine()) != null) {
                 sb.append(line);
             }
-            hosts = new GathererJsonIO().readHosts(sb.toString());
+            String outputJson = sb.toString();
+            logger.debug("virtual-host-gatherer OUTPUT: {}", outputJson);
+            hosts = new GathererJsonIO().readHosts(outputJson);
 
             int exitCode = p.waitFor();
             if (exitCode != 0) {
diff --git a/java/code/src/com/suse/utils/Json.java b/java/code/src/com/suse/utils/Json.java
index 60d6d58fdb1..4cb4ecb4ff3 100644
--- a/java/code/src/com/suse/utils/Json.java
+++ b/java/code/src/com/suse/utils/Json.java
@@ -25,13 +25,19 @@ import com.suse.salt.netapi.parser.XorTypeAdapterFactory;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
 
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
 import java.time.LocalDateTime;
 import java.time.ZonedDateTime;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Function;
 
 /**
@@ -40,6 +46,8 @@ import java.util.function.Function;
  */
 public class Json {
 
+   private static final Logger LOGGER = LogManager.getLogger(Json.class);
+
    private Json() {
    }
 
@@ -86,4 +94,52 @@ public class Json {
    }
 
 
+   /**
+    * Masks the specified fields in the given text using the specified {@link Gson} processor object to perform the
+    * text -> Map -> text conversion. Supported types are:
+    *
+    * - String: replace with string ********
+    * - Numbers: replace with 1234567890
+    *
+    * @param jsonText the json text
+    * @param fields the fields to mask
+    * @return the same json given in input, but with the specified fields masked
+    */
+   public static String maskFields(String jsonText, Set<String> fields, Gson gson) {
+      JsonElement rootElement = gson.fromJson(jsonText, JsonElement.class);
+      maskFieldsInElement(rootElement, fields);
+      return gson.toJson(rootElement);
+   }
+
+   private static void maskFieldsInElement(JsonElement jsonElement, Set<String> fields) {
+      if (jsonElement.isJsonObject()) {
+         maskFieldsInObject(jsonElement.getAsJsonObject(), fields);
+      } else if (jsonElement.isJsonArray()) {
+         maskFieldsInArray(fields, jsonElement.getAsJsonArray());
+      }
+   }
+
+   private static void maskFieldsInObject(JsonObject jsonObject, Set<String> fields) {
+      for (var entry : jsonObject.entrySet()) {
+         if (entry.getValue().isJsonPrimitive() && fields.contains(entry.getKey())) {
+            JsonPrimitive jsonPrimitive = entry.getValue().getAsJsonPrimitive();
+            if (jsonPrimitive.isString()) {
+               entry.setValue(new JsonPrimitive(StringUtils.repeat('*', 8)));
+            }
+            else if (jsonPrimitive.isNumber()) {
+               entry.setValue(new JsonPrimitive(1234567890L));
+            } else {
+               LOGGER.warn("Field {} should be masked, but its type is not supported", entry.getKey());
+            }
+         }
+
+         maskFieldsInElement(entry.getValue(), fields);
+      }
+   }
+
+   private static void maskFieldsInArray(Set<String> fields, JsonArray jsonArray) {
+      for (JsonElement entry : jsonArray) {
+         maskFieldsInElement(entry, fields);
+      }
+   }
 }
diff --git a/java/code/src/com/suse/utils/test/JsonTest.java b/java/code/src/com/suse/utils/test/JsonTest.java
new file mode 100644
index 00000000000..ab847386993
--- /dev/null
+++ b/java/code/src/com/suse/utils/test/JsonTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024 SUSE LLC
+ *
+ * This software is licensed to you under the GNU General Public License,
+ * version 2 (GPLv2). There is NO WARRANTY for this software, express or
+ * implied, including the implied warranties of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
+ * along with this software; if not, see
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * Red Hat trademarks are not licensed under GPLv2. No permission is
+ * granted to use or replicate Red Hat trademarks that are incorporated
+ * in this software or its documentation.
+ */
+
+package com.suse.utils.test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.suse.utils.Json;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+
+public class JsonTest {
+
+    public static final String UNMASKED_JSON = "{\"username\":\"test\",\"password\":\"secretPassword\",\"age\":\"43\",\"bankingDetails\":{\"accountNumber\":98423723523845,\"creditCard\":\"fakeCreditCardNumber\"},\"devices\":{\"tablet\":{\"devicePin\":32597},\"phone\":{\"devicePin\":73872}}}";
+
+    public static final String MASKED_JSON = "{\"username\":\"test\",\"password\":\"********\",\"age\":\"43\",\"bankingDetails\":{\"accountNumber\":1234567890,\"creditCard\":\"********\"},\"devices\":{\"tablet\":{\"devicePin\":1234567890},\"phone\":{\"devicePin\":1234567890}}}";
+
+    @Test
+    public void canMaskJsonObject() {
+        String maskedJson = Json.maskFields(
+            UNMASKED_JSON,
+            Set.of("password", "accountNumber", "creditCard", "devicePin"),
+            Json.GSON
+        );
+
+        assertEquals(MASKED_JSON, maskedJson);
+    }
+
+    @Test
+    public void canMaskJsonArray() {
+        String maskedJson = Json.maskFields(
+            String.format("[%s,%s]", UNMASKED_JSON, UNMASKED_JSON),
+            Set.of("password", "accountNumber", "creditCard", "devicePin"),
+            Json.GSON
+        );
+
+        assertEquals(String.format("[%s,%s]", MASKED_JSON, MASKED_JSON), maskedJson);
+    }
+}
-- 
2.35.3
