View Javadoc
1   package com.jsql.util;
2   
3   import com.jsql.model.InjectionModel;
4   import com.jsql.model.exception.JSqlException;
5   import com.jsql.model.injection.method.AbstractMethodInjection;
6   import org.apache.commons.lang3.StringUtils;
7   import org.apache.logging.log4j.LogManager;
8   import org.apache.logging.log4j.Logger;
9   import org.json.JSONArray;
10  import org.json.JSONException;
11  import org.json.JSONObject;
12  
13  import java.util.AbstractMap.SimpleEntry;
14  import java.util.ArrayList;
15  import java.util.Iterator;
16  import java.util.List;
17  import java.util.regex.Pattern;
18  
19  public class JsonUtil {
20      
21      private static final Logger LOGGER = LogManager.getRootLogger();
22  
23      private final InjectionModel injectionModel;
24      
25      public JsonUtil(InjectionModel injectionModel) {
26          this.injectionModel = injectionModel;
27      }
28  
29      public static Object getJson(String param) {
30          Object jsonEntity;  // Will test if current value is a JSON entity
31          try {
32              jsonEntity = new JSONObject(param);  // Test for JSON Object
33          } catch (JSONException exceptionJSONObject) {
34              try {
35                  jsonEntity = new JSONArray(param);  // Test for JSON Array
36              } catch (JSONException exceptionJSONArray) {
37                  jsonEntity = new Object();  // Not a JSON entity
38              }
39          }
40          return jsonEntity;
41      }
42  
43      public static List<SimpleEntry<String, String>> createEntries(Object jsonEntity, String parentName, SimpleEntry<String, String> parentXPath) {
44          List<SimpleEntry<String, String>> attributesXPath = new ArrayList<>();
45          if (jsonEntity instanceof JSONObject) {
46              JsonUtil.scanJsonObject(jsonEntity, parentName, parentXPath, attributesXPath);
47          } else if (jsonEntity instanceof JSONArray) {
48              JsonUtil.scanJsonArray(jsonEntity, parentName, parentXPath, attributesXPath);
49          }
50          return attributesXPath;
51      }
52  
53      private static void scanJsonArray(Object jsonEntity, String parentName, SimpleEntry<String, String> parentXPath, List<SimpleEntry<String, String>> attributesXPath) {
54          var jsonArrayEntity = (JSONArray) jsonEntity;
55          for (var i = 0 ; i < jsonArrayEntity.length() ; i++) {
56              Object value = jsonArrayEntity.get(i);
57              String xpath = parentName +"["+ i +"]";
58              
59              // Not possible to make generic with scanJsonObject() because of JSONArray.put(int) != JSONObject.put(String)
60              if (value instanceof JSONArray || value instanceof JSONObject) {
61                  attributesXPath.addAll(JsonUtil.createEntries(value, xpath, parentXPath));
62              } else if (value instanceof String string) {
63                  SimpleEntry<String, String> stringValue = new SimpleEntry<>(xpath, string);
64                  attributesXPath.add(stringValue);
65                  
66                  if (parentXPath == null) {
67                      jsonArrayEntity.put(i, value.toString().replaceAll(Pattern.quote(InjectionModel.STAR) +"$", StringUtils.EMPTY));
68                  } else if (stringValue.equals(parentXPath)) {
69                      jsonArrayEntity.put(i, value + InjectionModel.STAR);
70                  }
71              }
72          }
73      }
74  
75      private static void scanJsonObject(Object jsonEntity, String parentName, SimpleEntry<String, String> parentXPath, List<SimpleEntry<String, String>> attributesXPath) {
76          var jsonObjectEntity = (JSONObject) jsonEntity;
77          Iterator<?> keys = jsonObjectEntity.keys();
78          while (keys.hasNext()) {
79              String key = (String) keys.next();
80              var value = jsonObjectEntity.get(key);
81              String xpath = parentName +"."+ key;
82              
83              // Not possible to make generic with scanJsonObject() because of JSONArray.put(int) != JSONObject.put(String)
84              if (value instanceof JSONArray || value instanceof JSONObject) {
85                  attributesXPath.addAll(JsonUtil.createEntries(value, xpath, parentXPath));
86              } else if (value instanceof String string) {
87                  SimpleEntry<String, String> stringValue = new SimpleEntry<>(xpath, string);
88                  attributesXPath.add(stringValue);
89                  
90                  if (parentXPath == null) {
91                      jsonObjectEntity.put(key, value.toString().replaceAll(Pattern.quote(InjectionModel.STAR) +"$", StringUtils.EMPTY));
92                  } else if (stringValue.equals(parentXPath)) {
93                      jsonObjectEntity.put(key, value + InjectionModel.STAR);
94                  }
95              }
96          }
97      }
98      
99      public boolean testJsonParam(AbstractMethodInjection methodInjection, SimpleEntry<String, String> paramStar) {
100         var hasFoundInjection = false;
101         
102         // Remove STAR at the end of parameter, STAR will be added inside json data instead
103         paramStar.setValue(paramStar.getValue().replace(InjectionModel.STAR, StringUtils.EMPTY));
104         
105         // Will test if current value is a JSON entity
106         Object jsonEntity = JsonUtil.getJson(paramStar.getValue());
107         
108         // Define a tree of JSON attributes with path as the key: root.a => value of a
109         List<SimpleEntry<String, String>> attributesJson = JsonUtil.createEntries(jsonEntity, "root", null);
110         
111         // Loop through each JSON values
112         for (SimpleEntry<String, String> parentXPath: attributesJson) {
113             JsonUtil.createEntries(jsonEntity, "root", null);  // Erase previously defined *
114             JsonUtil.createEntries(jsonEntity, "root", parentXPath);  // Add * to current parameter's value
115 
116             paramStar.setValue(jsonEntity.toString());  // Replace param value by marked one
117             
118             try {
119                 LOGGER.log(
120                     LogLevelUtil.CONSOLE_INFORM,
121                     "{} JSON {}={} with {}",
122                     () -> I18nUtil.valueByKey("LOG_CHECKING"),
123                     parentXPath::getKey,
124                     () -> parentXPath.getValue().replace(InjectionModel.STAR, StringUtils.EMPTY),
125                     methodInjection::name
126                 );
127                 
128                 // Test current JSON value marked with * for injection
129                 // Keep original param
130                 hasFoundInjection = this.injectionModel.getMediatorStrategy().testStrategies(paramStar);
131                 
132                 // Injection successful
133                 break;
134             } catch (JSqlException e) {
135                 // Injection failure
136                 LOGGER.log(
137                     LogLevelUtil.CONSOLE_ERROR,
138                     String.format(
139                         "No injection found for JSON %s parameter %s=%s",
140                         methodInjection.name(),
141                         parentXPath.getKey(),
142                         parentXPath.getValue().replace(InjectionModel.STAR, StringUtils.EMPTY)
143                     )
144                 );
145             } finally {
146                 // Erase * at the end of each params
147                 // TODO useless
148                 methodInjection.getParams().forEach(e -> e.setValue(
149                     e.getValue().replaceAll(Pattern.quote(InjectionModel.STAR) +"$", StringUtils.EMPTY)
150                 ));
151                 
152                 // Erase * from JSON if failure
153                 if (!hasFoundInjection) {
154                     paramStar.setValue(
155                         paramStar.getValue().replace(InjectionModel.STAR, StringUtils.EMPTY)
156                     );
157                 }
158             }
159         }
160         return hasFoundInjection;
161     }
162 }