1 package com.jsql.util;
2
3 import com.jsql.model.InjectionModel;
4 import com.jsql.model.bean.util.Interaction;
5 import com.jsql.model.bean.util.Request;
6 import com.jsql.model.exception.InjectionFailureException;
7 import com.jsql.model.injection.method.AbstractMethodInjection;
8 import org.apache.commons.lang3.StringUtils;
9 import org.apache.logging.log4j.LogManager;
10 import org.apache.logging.log4j.Logger;
11
12 import java.net.IDN;
13 import java.net.MalformedURLException;
14 import java.net.URI;
15 import java.net.URISyntaxException;
16 import java.util.AbstractMap.SimpleEntry;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.List;
20 import java.util.Objects;
21 import java.util.concurrent.CopyOnWriteArrayList;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24 import java.util.stream.Collectors;
25
26 public class ParameterUtil {
27
28
29
30
31 private static final Logger LOGGER = LogManager.getRootLogger();
32
33
34
35
36 private List<SimpleEntry<String, String>> listQueryString = new CopyOnWriteArrayList<>();
37
38
39
40
41 private List<SimpleEntry<String, String>> listRequest = new CopyOnWriteArrayList<>();
42
43
44
45
46 private List<SimpleEntry<String, String>> listHeader = new CopyOnWriteArrayList<>();
47
48 private String rawRequest = StringUtils.EMPTY;
49 private String rawHeader = StringUtils.EMPTY;
50 private boolean isMultipartRequest = false;
51 private static final String FORMAT_KEY_VALUE = "%s=%s";
52
53 private final InjectionModel injectionModel;
54
55 public ParameterUtil(InjectionModel injectionModel) {
56 this.injectionModel = injectionModel;
57 }
58
59
60
61
62
63
64 public void controlInput(
65 String urlQuery,
66 String rawRequest,
67 String rawHeader,
68 AbstractMethodInjection methodInjection,
69 String typeRequest,
70 boolean isScanning
71 ) {
72 try {
73 String urlQueryFixed = urlQuery;
74
75
76 if (urlQueryFixed.isEmpty()) {
77 throw new MalformedURLException("empty URL");
78 } else if (!urlQueryFixed.matches("(?i)^https?://.*")) {
79 if (!urlQueryFixed.matches("(?i)^\\w+://.*")) {
80
81 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Undefined URL protocol, forcing to [http://]");
82 urlQueryFixed = "http://"+ urlQueryFixed;
83
84 } else {
85 throw new MalformedURLException("unknown URL protocol");
86 }
87 }
88
89 String authority = URI.create(urlQueryFixed).getAuthority();
90 if (authority == null) {
91 throw new MalformedURLException("incorrect domain authority");
92 }
93 String authorityPunycode = IDN.toASCII(authority);
94 if (!authority.equals(authorityPunycode)) {
95 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Punycode domain detected, using [{}] instead of [{}]", authorityPunycode, authority);
96 urlQueryFixed = urlQueryFixed.replace(authority, authorityPunycode);
97 }
98
99 this.initializeQueryString(urlQueryFixed);
100 this.initializeHeader(rawHeader);
101 this.initializeRequest(rawRequest);
102
103 this.injectionModel.getMediatorUtils().getConnectionUtil().setMethodInjection(methodInjection);
104 this.injectionModel.getMediatorUtils().getConnectionUtil().setTypeRequest(typeRequest);
105
106 if (isScanning) {
107 this.injectionModel.beginInjection();
108 } else {
109
110 new Thread(
111 this.injectionModel::beginInjection,
112 "ThreadBeginInjection"
113 )
114 .start();
115 }
116 } catch (IllegalArgumentException | MalformedURLException | URISyntaxException e) {
117
118 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Incorrect Url: {}", e.getMessage());
119
120
121 var request = new Request();
122 request.setMessage(Interaction.END_PREPARATION);
123 this.injectionModel.sendToViews(request);
124 }
125 }
126
127
128
129
130
131 public void checkParametersFormat() throws InjectionFailureException {
132
133 this.checkOneOrLessStar();
134 this.checkStarMatchMethod();
135 this.checkMethodNotEmpty();
136 this.checkMultipart();
137 }
138
139 private void checkMultipart() throws InjectionFailureException {
140
141 isMultipartRequest = false;
142
143 if (
144 this.getListHeader()
145 .stream()
146 .filter(entry -> "Content-Type".equals(entry.getKey()))
147 .anyMatch(entry ->
148 entry.getValue() != null
149 && entry.getValue().contains("multipart/form-data")
150 && entry.getValue().contains("boundary=")
151 )
152 ) {
153 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Multipart boundary found in header");
154 Matcher matcherBoundary = Pattern.compile("boundary=([^;]*)").matcher(this.getHeaderFromEntries());
155 if (matcherBoundary.find()) {
156 String boundary = matcherBoundary.group(1);
157 if (!this.rawRequest.contains(boundary)) {
158 throw new InjectionFailureException(
159 String.format("Incorrect multipart data, boundary not found in body: %s", boundary)
160 );
161 } else {
162 isMultipartRequest = true;
163 }
164 }
165 }
166 }
167
168 private void checkOneOrLessStar() throws InjectionFailureException {
169
170 var nbStarInParameter = 0;
171
172 if (this.getQueryStringFromEntries().contains(InjectionModel.STAR)) {
173 nbStarInParameter++;
174 }
175 if (this.getRequestFromEntries().contains(InjectionModel.STAR)) {
176 nbStarInParameter++;
177 }
178 if (this.getHeaderFromEntries().contains(InjectionModel.STAR)) {
179 nbStarInParameter++;
180 }
181
182
183 if (
184 nbStarInParameter > 1
185 || StringUtils.countMatches(this.getQueryStringFromEntries(), "*") > 1
186 || StringUtils.countMatches(this.getRequestFromEntries(), "*") > 1
187 || StringUtils.countMatches(this.getHeaderFromEntries(), "*") > 1
188 ) {
189 throw new InjectionFailureException("Character insertion [*] must be used once in Query String, Request or Header parameters");
190 }
191 }
192
193 public void checkStarMatchMethod() throws InjectionFailureException {
194
195 AbstractMethodInjection methodInjection = this.injectionModel.getMediatorUtils().getConnectionUtil().getMethodInjection();
196 boolean isCheckingAllParam = this.injectionModel.getMediatorUtils().getPreferencesUtil().isCheckingAllParam();
197
198 if (
199 this.getQueryStringFromEntries().contains(InjectionModel.STAR)
200 && methodInjection != this.injectionModel.getMediatorMethod().getQuery()
201 && !isCheckingAllParam
202 ) {
203 throw new InjectionFailureException("Select method GET to use character [*] or remove [*] from GET parameters");
204 } else if (
205 this.getRequestFromEntries().contains(InjectionModel.STAR)
206 && methodInjection != this.injectionModel.getMediatorMethod().getRequest()
207 && !isCheckingAllParam
208 ) {
209 throw new InjectionFailureException("Select a Request method (like POST) to use [*], or remove [*] from Request parameters");
210 } else if (
211 this.getHeaderFromEntries().contains(InjectionModel.STAR)
212 && methodInjection != this.injectionModel.getMediatorMethod().getHeader()
213 && !isCheckingAllParam
214 ) {
215 throw new InjectionFailureException("Select method Header to use character [*] or remove [*] from Header parameters");
216 }
217 }
218
219 public void checkMethodNotEmpty() throws InjectionFailureException {
220
221 AbstractMethodInjection methodInjection = this.injectionModel.getMediatorUtils().getConnectionUtil().getMethodInjection();
222 boolean isCheckingAllParam = this.injectionModel.getMediatorUtils().getPreferencesUtil().isCheckingAllParam();
223
224 if (
225 methodInjection == this.injectionModel.getMediatorMethod().getQuery()
226 && !isCheckingAllParam
227 && this.getListQueryString().isEmpty()
228 && !this.injectionModel.getMediatorUtils().getConnectionUtil().getUrlBase().contains(InjectionModel.STAR)
229 ) {
230 throw new InjectionFailureException("No query string");
231 } else if (
232 methodInjection == this.injectionModel.getMediatorMethod().getRequest()
233 && this.getListRequest().isEmpty()
234 ) {
235 throw new InjectionFailureException("Incorrect Request format");
236 } else if (
237 methodInjection == this.injectionModel.getMediatorMethod().getHeader()
238 && this.getListHeader().isEmpty()
239 ) {
240 throw new InjectionFailureException("Incorrect Header format");
241 }
242 }
243
244 public String initializeStar(SimpleEntry<String, String> parameterToInject) {
245
246 String characterInsertionByUser;
247
248 if (parameterToInject == null) {
249 characterInsertionByUser = InjectionModel.STAR;
250 } else {
251
252 characterInsertionByUser = parameterToInject.getValue();
253 parameterToInject.setValue(InjectionModel.STAR);
254 }
255
256 return characterInsertionByUser;
257 }
258
259 public void initializeQueryString(String urlQuery) throws MalformedURLException, URISyntaxException {
260
261
262 var url = new URI(urlQuery).toURL();
263
264 if (
265 StringUtils.isEmpty(urlQuery)
266 || StringUtils.isEmpty(url.getHost())
267 ) {
268 throw new MalformedURLException("empty URL");
269 }
270
271 this.injectionModel.getMediatorUtils().getConnectionUtil().setUrlByUser(urlQuery);
272 this.injectionModel.getMediatorUtils().getConnectionUtil().setUrlBase(urlQuery);
273
274 this.listQueryString.clear();
275
276
277 var regexQueryString = Pattern.compile("(.*\\?)(.*)").matcher(urlQuery);
278
279 if (!regexQueryString.find()) {
280 return;
281 }
282
283 this.injectionModel.getMediatorUtils().getConnectionUtil().setUrlBase(regexQueryString.group(1));
284
285 if (StringUtils.isNotEmpty(url.getQuery())) {
286 this.listQueryString = Pattern.compile("&")
287 .splitAsStream(url.getQuery())
288 .map(keyValue -> Arrays.copyOf(keyValue.split("="), 2))
289 .map(keyValue -> new SimpleEntry<>(
290 keyValue[0],
291 keyValue[1] == null ? StringUtils.EMPTY : keyValue[1]
292 ))
293 .collect(Collectors.toList());
294 }
295 }
296
297 public void initializeRequest(String rawRequest) {
298
299 this.rawRequest = rawRequest;
300 this.listRequest.clear();
301
302 if (StringUtils.isNotEmpty(rawRequest)) {
303 if (isMultipartRequest()) {
304
305 this.listRequest = new ArrayList<>(List.of(new SimpleEntry<>(
306 rawRequest,
307 ""
308 )));
309 } else {
310 this.listRequest = Pattern.compile("&")
311 .splitAsStream(rawRequest)
312 .map(keyValue -> Arrays.copyOf(keyValue.split("="), 2))
313 .map(keyValue -> new SimpleEntry<>(
314 keyValue[0],
315 keyValue[1] == null ? StringUtils.EMPTY : keyValue[1]
316 ))
317 .collect(Collectors.toList());
318 }
319 }
320 }
321
322 public void initializeHeader(String rawHeader) {
323
324 this.rawHeader = rawHeader;
325 this.listHeader.clear();
326
327 if (StringUtils.isNotEmpty(rawHeader)) {
328 this.listHeader = Pattern.compile("\\\\r\\\\n")
329 .splitAsStream(rawHeader)
330 .map(keyValue -> Arrays.copyOf(keyValue.split(":"), 2))
331 .map(keyValue -> new SimpleEntry<>(
332 keyValue[0],
333 keyValue[1] == null ? StringUtils.EMPTY : keyValue[1]
334 ))
335 .collect(Collectors.toList());
336 }
337 }
338
339 public String getQueryStringFromEntries() {
340 return this.listQueryString.stream()
341 .filter(Objects::nonNull)
342 .map(entry -> {
343 if (
344 this.injectionModel.getMediatorStrategy().getStrategy() == this.injectionModel.getMediatorStrategy().getMultibit()
345 && entry.getValue() != null
346 && entry.getValue().contains(InjectionModel.STAR)
347 ) {
348 return String.format(FORMAT_KEY_VALUE, entry.getKey(), InjectionModel.STAR);
349 } else {
350 return String.format(FORMAT_KEY_VALUE, entry.getKey(), entry.getValue());
351 }
352 })
353 .collect(Collectors.joining("&"));
354 }
355
356 public String getRequestFromEntries() {
357 return this.listRequest.stream()
358 .filter(Objects::nonNull)
359 .map(entry -> String.format(
360 FORMAT_KEY_VALUE,
361 entry.getKey(),
362 StringUtils.isEmpty(entry.getValue()) ? "" : entry.getValue()
363 ))
364 .collect(Collectors.joining("&"));
365 }
366
367 public String getHeaderFromEntries() {
368 return this.listHeader.stream()
369 .filter(Objects::nonNull)
370 .map(entry -> String.format(
371 "%s:%s",
372 entry.getKey(),
373 entry.getValue()
374 ))
375 .collect(Collectors.joining("\\r\\n"));
376 }
377
378 public boolean isRequestSoap() {
379 return this.rawRequest
380 .trim()
381 .matches("^(<soapenv:|<\\?xml).*");
382 }
383
384
385
386
387 public String getRawRequest() {
388 return this.rawRequest;
389 }
390
391 public String getRawHeader() {
392 return this.rawHeader;
393 }
394
395 public List<SimpleEntry<String, String>> getListRequest() {
396 return this.listRequest;
397 }
398
399 public void setListRequest(List<SimpleEntry<String, String>> listRequest) {
400 this.listRequest = listRequest;
401 }
402
403 public List<SimpleEntry<String, String>> getListHeader() {
404 return this.listHeader;
405 }
406
407 public void setListHeader(List<SimpleEntry<String, String>> listHeader) {
408 this.listHeader = listHeader;
409 }
410
411 public List<SimpleEntry<String, String>> getListQueryString() {
412 return this.listQueryString;
413 }
414
415 public void setListQueryString(List<SimpleEntry<String, String>> listQueryString) {
416 this.listQueryString = listQueryString;
417 }
418
419 public boolean isMultipartRequest() {
420 return isMultipartRequest;
421 }
422 }