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) {
63                  SimpleEntry<String, String> stringValue = new SimpleEntry<>(xpath, (String) value);
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) {
87                  
88                  SimpleEntry<String, String> stringValue = new SimpleEntry<>(xpath, (String) value);
89                  attributesXPath.add(stringValue);
90                  
91                  if (parentXPath == null) {
92                      jsonObjectEntity.put(key, value.toString().replaceAll(Pattern.quote(InjectionModel.STAR) +"$", StringUtils.EMPTY));
93                  } else if (stringValue.equals(parentXPath)) {
94                      jsonObjectEntity.put(key, value + InjectionModel.STAR);
95                  }
96              }
97          }
98      }
99      
100     public boolean testJsonParam(AbstractMethodInjection methodInjection, SimpleEntry<String, String> paramStar) {
101         var hasFoundInjection = false;
102         
103         // Remove STAR at the end of parameter, STAR will be added inside json data instead
104         paramStar.setValue(paramStar.getValue().replace(InjectionModel.STAR, StringUtils.EMPTY));
105         
106         // Will test if current value is a JSON entity
107         Object jsonEntity = JsonUtil.getJson(paramStar.getValue());
108         
109         // Define a tree of JSON attributes with path as the key: root.a => value of a
110         List<SimpleEntry<String, String>> attributesJson = JsonUtil.createEntries(jsonEntity, "root", null);
111         
112         // Loop through each JSON values
113         for (SimpleEntry<String, String> parentXPath: attributesJson) {
114             JsonUtil.createEntries(jsonEntity, "root", null);  // Erase previously defined *
115             JsonUtil.createEntries(jsonEntity, "root", parentXPath);  // Add * to current parameter's value
116 
117             paramStar.setValue(jsonEntity.toString());  // Replace param value by marked one
118             
119             try {
120                 LOGGER.log(
121                     LogLevelUtil.CONSOLE_INFORM,
122                     "{} JSON {}={} with {}",
123                     () -> I18nUtil.valueByKey("LOG_CHECKING"),
124                     parentXPath::getKey,
125                     () -> parentXPath.getValue().replace(InjectionModel.STAR, StringUtils.EMPTY),
126                     methodInjection::name
127                 );
128                 
129                 // Test current JSON value marked with * for injection
130                 // Keep original param
131                 hasFoundInjection = this.injectionModel.getMediatorStrategy().testStrategies(paramStar);
132                 
133                 // Injection successful
134                 break;
135             } catch (JSqlException e) {
136                 // Injection failure
137                 LOGGER.log(
138                     LogLevelUtil.CONSOLE_ERROR,
139                     String.format(
140                         "No injection found for JSON %s parameter %s=%s",
141                         methodInjection.name(),
142                         parentXPath.getKey(),
143                         parentXPath.getValue().replace(InjectionModel.STAR, StringUtils.EMPTY)
144                     )
145                 );
146             } finally {
147                 // Erase * at the end of each params
148                 // TODO useless
149                 methodInjection.getParams()
150                     .forEach(e -> e.setValue(
151                         e.getValue().replaceAll(Pattern.quote(InjectionModel.STAR) +"$", StringUtils.EMPTY)
152                     ));
153                 
154                 // Erase * from JSON if failure
155                 if (!hasFoundInjection) {
156                     paramStar.setValue(
157                         paramStar.getValue().replace(InjectionModel.STAR, StringUtils.EMPTY)
158                     );
159                 }
160             }
161         }
162         return hasFoundInjection;
163     }
164 }