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