View Javadoc
1   /*******************************************************************************
2    * Copyhacked (H) 2012-2025.
3    * This program and the accompanying materials
4    * are made available under no term at all, use it like
5    * you want, but share and discuss it
6    * every time possible with every body.
7    * 
8    * Contributors:
9    *      ron190 at ymail dot com - initial implementation
10   ******************************************************************************/
11  package com.jsql.model;
12  
13  import com.jsql.model.accessible.DataAccess;
14  import com.jsql.model.accessible.ResourceAccess;
15  import com.jsql.view.subscriber.Seal;
16  import com.jsql.model.exception.JSqlException;
17  import com.jsql.model.exception.JSqlRuntimeException;
18  import com.jsql.model.injection.method.AbstractMethodInjection;
19  import com.jsql.model.injection.method.MediatorMethod;
20  import com.jsql.model.injection.strategy.MediatorStrategy;
21  import com.jsql.model.injection.strategy.blind.callable.AbstractCallableBit;
22  import com.jsql.model.injection.engine.MediatorEngine;
23  import com.jsql.model.injection.engine.model.EngineYaml;
24  import com.jsql.util.*;
25  import com.jsql.util.GitUtil.ShowOnConsole;
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.logging.log4j.LogManager;
28  import org.apache.logging.log4j.Logger;
29  
30  import javax.swing.*;
31  import java.awt.*;
32  import java.io.IOException;
33  import java.io.Serializable;
34  import java.net.*;
35  import java.net.http.HttpRequest;
36  import java.net.http.HttpRequest.BodyPublishers;
37  import java.net.http.HttpRequest.Builder;
38  import java.net.http.HttpResponse;
39  import java.net.http.HttpResponse.BodyHandlers;
40  import java.nio.charset.StandardCharsets;
41  import java.text.DecimalFormat;
42  import java.time.Duration;
43  import java.util.AbstractMap.SimpleEntry;
44  import java.util.Map;
45  import java.util.regex.Matcher;
46  import java.util.stream.Collectors;
47  import java.util.stream.Stream;
48  
49  /**
50   * Model class of MVC pattern for processing SQL injection automatically.<br>
51   * Different views can be attached to this observable, like Swing or command line, in order to separate
52   * the functional job from the graphical processing.<br>
53   * The Model has a specific database engine and strategy which run an automatic injection to get name of
54   * databases, tables, columns and values, and it can also retrieve resources like files and shell.<br>
55   * Tasks are run in multi-threads in general to speed the process.
56   */
57  public class InjectionModel extends AbstractModelObservable implements Serializable {
58      
59      private static final Logger LOGGER = LogManager.getRootLogger();
60      
61      private final transient MediatorEngine mediatorEngine = new MediatorEngine(this);
62      private final transient MediatorMethod mediatorMethod = new MediatorMethod(this);
63      private final transient DataAccess dataAccess = new DataAccess(this);
64      private final transient ResourceAccess resourceAccess = new ResourceAccess(this);
65      private final transient PropertiesUtil propertiesUtil = new PropertiesUtil();
66      private final transient MediatorUtils mediatorUtils;
67      private final transient MediatorStrategy mediatorStrategy;
68  
69      public static final String STAR = "*";
70      public static final String BR = "<br>&#10;";
71  
72      /**
73       * initialUrl transformed to a correct injection url.
74       */
75      private String analysisReport = StringUtils.EMPTY;
76  
77      /**
78       * Allow to directly start an injection after a failed one
79       * without asking the user 'Start a new injection?'.
80       */
81      private boolean shouldErasePreviousInjection = false;
82      private boolean isScanning = false;
83  
84      public InjectionModel() {
85          this.mediatorStrategy = new MediatorStrategy(this);
86          this.mediatorUtils = new MediatorUtils(
87              this.propertiesUtil,
88              new ConnectionUtil(this),
89              new AuthenticationUtil(),
90              new GitUtil(this),
91              new HeaderUtil(this),
92              new ParameterUtil(this),
93              new ExceptionUtil(this),
94              new SoapUtil(this),
95              new MultipartUtil(this),
96              new CookiesUtil(this),
97              new JsonUtil(this),
98              new PreferencesUtil(),
99              new ProxyUtil(),
100             new ThreadUtil(this),
101             new TamperingUtil(),
102             new UserAgentUtil(),
103             new CsrfUtil(this),
104             new DigestUtil(this),
105             new FormUtil(this),
106             new CertificateUtil()
107         );
108     }
109 
110     /**
111      * Reset each injection attributes: Database metadata, General Thread status, Strategy.
112      */
113     public void resetModel() {
114         this.mediatorStrategy.getTime().setApplicable(false);
115         this.mediatorStrategy.getBlindBin().setApplicable(false);
116         this.mediatorStrategy.getBlindBit().setApplicable(false);
117         this.mediatorStrategy.getMultibit().setApplicable(false);
118         this.mediatorStrategy.getDns().setApplicable(false);
119         this.mediatorStrategy.getError().setApplicable(false);
120         this.mediatorStrategy.getStack().setApplicable(false);
121         this.mediatorStrategy.getUnion().setApplicable(false);
122         this.mediatorStrategy.setStrategy(null);
123 
124         this.mediatorStrategy.getSpecificUnion().setVisibleIndex(null);
125         this.mediatorStrategy.getSpecificUnion().setIndexesInUrl(StringUtils.EMPTY);
126 
127         this.analysisReport = StringUtils.EMPTY;
128         this.isStoppedByUser = false;
129         this.shouldErasePreviousInjection = false;
130 
131         this.mediatorUtils.csrfUtil().setTokenCsrf(null);
132         this.mediatorUtils.digestUtil().setTokenDigest(null);
133         this.mediatorUtils.threadUtil().reset();
134     }
135 
136     /**
137      * Prepare the injection process, can be interrupted by the user (via shouldStopAll).
138      * Erase all attributes eventually defined in a previous injection.
139      * Run by Scan, Standard and TU.
140      */
141     public void beginInjection() {
142         this.resetModel();
143         try {
144             if (this.mediatorUtils.proxyUtil().isNotLive(ShowOnConsole.YES)) {
145                 return;
146             }
147             LOGGER.log(
148                 LogLevelUtil.CONSOLE_INFORM,
149                 "{}: {}",
150                 () -> I18nUtil.valueByKey("LOG_START_INJECTION"),
151                 () -> this.mediatorUtils.connectionUtil().getUrlByUser()
152             );
153             
154             // Check general integrity if user's parameters
155             this.mediatorUtils.parameterUtil().checkParametersFormat();
156             this.mediatorUtils.connectionUtil().testConnection();
157 
158             // TODO Check all path params URL segments
159             boolean hasFoundInjection = this.mediatorMethod.getQuery().testParameters(false);
160             hasFoundInjection = this.mediatorUtils.multipartUtil().testParameters(hasFoundInjection);
161             hasFoundInjection = this.mediatorUtils.soapUtil().testParameters(hasFoundInjection);
162             hasFoundInjection = this.mediatorMethod.getRequest().testParameters(hasFoundInjection);
163             hasFoundInjection = this.mediatorMethod.getHeader().testParameters(hasFoundInjection);
164             hasFoundInjection = this.mediatorUtils.cookiesUtil().testParameters(hasFoundInjection);
165 
166             if (hasFoundInjection && !this.isScanning) {
167                 if (!this.getMediatorUtils().preferencesUtil().isNotShowingVulnReport()) {
168                     this.sendToViews(new Seal.CreateAnalysisReport(this.analysisReport));
169                 }
170                 if (this.getMediatorUtils().preferencesUtil().isZipStrategy()) {
171                     LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Using Zip mode for reduced query size");
172                 } else if (this.getMediatorUtils().preferencesUtil().isDiosStrategy()) {
173                     LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Using Dump In One Shot strategy for single query dump");
174                 }
175                 if (!this.mediatorUtils.preferencesUtil().isNotInjectingMetadata()) {
176                     this.dataAccess.getDatabaseInfos();
177                 }
178                 this.dataAccess.listDatabases();
179             }
180             
181             LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, () -> I18nUtil.valueByKey("LOG_DONE"));
182             this.shouldErasePreviousInjection = true;
183         } catch (InterruptedException e) {
184             LOGGER.log(LogLevelUtil.IGNORE, e, e);
185             Thread.currentThread().interrupt();
186         } catch (JSqlRuntimeException | JSqlException | IOException e) {  // Catch expected exceptions only
187             LOGGER.log(
188                 LogLevelUtil.CONSOLE_ERROR,
189                 "Interruption: {}",
190                 e.getMessage() == null ? InjectionModel.getImplicitReason(e) : e.getMessage()
191             );
192         } finally {
193             this.sendToViews(new Seal.EndPreparation());
194         }
195     }
196     
197     public static String getImplicitReason(Throwable e) {
198         String message = e.getClass().getSimpleName();
199         if (e.getMessage() != null) {
200             message += ": "+ e.getMessage();
201         }
202         if (e.getCause() != null && !e.equals(e.getCause())) {
203             message += " > "+ InjectionModel.getImplicitReason(e.getCause());
204         }
205         return message;
206     }
207     
208     /**
209      * Run an HTTP connection to the web server.
210      * @param dataInjection SQL query
211      * @return source code of current page
212      */
213     @Override
214     public String inject(
215         String dataInjection,
216         boolean isUsingIndex,
217         String metadataInjectionProcess,
218         AbstractCallableBit<?> callableBoolean,
219         boolean isReport
220     ) {
221         // Temporary url, we go from "select 1,2,3,4..." to "select 1,([complex query]),2...", but keep initial url
222         String urlInjection = this.mediatorUtils.connectionUtil().getUrlBase();
223         urlInjection = this.mediatorStrategy.buildPath(urlInjection, isUsingIndex, dataInjection);
224         urlInjection = StringUtil.cleanSql(urlInjection.trim());
225 
226         URL urlObject;
227         String urlInjectionFixed;
228         try {
229             urlInjectionFixed = this.initQueryString(
230                 isUsingIndex,
231                 urlInjection,
232                 dataInjection
233             );
234             urlObject = new URI(urlInjectionFixed).toURL();
235         } catch (MalformedURLException | URISyntaxException e) {
236             LOGGER.log(LogLevelUtil.CONSOLE_ERROR, String.format("Incorrect Query Url: %s", e.getMessage()));
237             return StringUtils.EMPTY;
238         }
239 
240         String pageSource = StringUtils.EMPTY;
241         
242         // Define the connection
243         try {
244             var httpRequestBuilder = HttpRequest.newBuilder()
245                 .uri(URI.create(urlObject.toString()))
246                 .setHeader(HeaderUtil.CONTENT_TYPE_REQUEST, "text/plain")
247                 .timeout(Duration.ofSeconds(15));
248             
249             this.mediatorUtils.csrfUtil().addHeaderToken(httpRequestBuilder);
250             this.mediatorUtils.digestUtil().addHeaderToken(httpRequestBuilder);
251             this.mediatorUtils.connectionUtil().setCustomUserAgent(httpRequestBuilder);
252 
253             String body = this.initRequest(isUsingIndex, dataInjection, httpRequestBuilder);
254             this.initHeader(isUsingIndex, dataInjection, httpRequestBuilder);
255             
256             var httpRequest = httpRequestBuilder.build();
257             if (isReport) {
258                 Color colorReport = UIManager.getColor("TextArea.inactiveForeground");
259                 String report = InjectionModel.BR + StringUtil.formatReport(colorReport, "Method: ") + httpRequest.method();
260                 report += InjectionModel.BR + StringUtil.formatReport(colorReport, "Path: ") + httpRequest.uri().getPath();
261                 if (httpRequest.uri().getQuery() != null) {
262                     report += InjectionModel.BR + StringUtil.formatReport(colorReport, "Query: ") + httpRequest.uri().getQuery();
263                 }
264                 if (
265                     !(this.mediatorUtils.parameterUtil().getListRequest().isEmpty()
266                     && this.mediatorUtils.csrfUtil().getTokenCsrf() == null)
267                 ) {
268                     report += InjectionModel.BR + StringUtil.formatReport(colorReport, "Body: ") + body;
269                 }
270                 report += InjectionModel.BR 
271                     + StringUtil.formatReport(colorReport, "Header: ") 
272                     + httpRequest.headers().map().entrySet().stream()
273                     .map(entry -> 
274                         String.format("%s: %s", entry.getKey(), 
275                         String.join(StringUtils.EMPTY, entry.getValue()))
276                     )
277                     .collect(Collectors.joining(InjectionModel.BR));
278                 return report;
279             }
280             
281             HttpResponse<String> response = this.getMediatorUtils().connectionUtil().getHttpClient().build().send(
282                 httpRequestBuilder.build(),
283                 BodyHandlers.ofString()
284             );
285             if (this.mediatorUtils.parameterUtil().isRequestSoap()) {
286                 // Invalid XML control chars like \x04 requires urlencoding from server
287                 pageSource = URLDecoder.decode(response.body(), StandardCharsets.UTF_8);
288                 pageSource = StringUtil.fromHtml(pageSource);
289             } else {
290                 pageSource = response.body();
291             }
292 
293             Map<String, String> headersResponse = ConnectionUtil.getHeadersMap(response);
294             int sizeHeaders = headersResponse.keySet()
295                 .stream()
296                 .map(key -> headersResponse.get(key).length() + key.length())
297                 .mapToInt(Integer::intValue)
298                 .sum();
299             float size = (float) (pageSource.length() + sizeHeaders) / 1024;
300             var decimalFormat = new DecimalFormat("0.000");
301 
302             String pageSourceFixed = pageSource
303                 .replaceAll("("+ EngineYaml.CALIBRATOR_SQL +"){60,}", "$1...")  // Remove ranges of # created by calibration
304                 .replaceAll("(jIyM){60,}", "$1...");  // Remove batch of chars created by Dios
305 
306             // Send data to Views
307             this.sendToViews(new Seal.MessageHeader(
308                 urlInjectionFixed,
309                 body,
310                 ConnectionUtil.getHeadersMap(httpRequest.headers()),
311                 headersResponse,
312                 pageSourceFixed,
313                 decimalFormat.format(size),
314                 this.mediatorStrategy.getMeta(),
315                 metadataInjectionProcess,
316                 callableBoolean
317             ));
318         } catch (IOException e) {
319             LOGGER.log(
320                 LogLevelUtil.CONSOLE_ERROR,
321                 String.format("Error during connection: %s", e.getMessage())
322             );
323         } catch (InterruptedException e) {
324             LOGGER.log(LogLevelUtil.IGNORE, e, e);
325             Thread.currentThread().interrupt();
326         }
327 
328         return pageSource;
329     }
330 
331     private String initQueryString(boolean isUsingIndex, String urlInjection, String dataInjection) {
332         String urlInjectionFixed = urlInjection;
333         if (
334             this.mediatorUtils.parameterUtil().getListQueryString().isEmpty()
335             && !this.mediatorUtils.preferencesUtil().isProcessingCsrf()
336         ) {
337             return urlInjectionFixed;
338         }
339             
340         // URL without query string like Request and Header can receive
341         // new params from <form> parsing, in that case add the '?' to URL
342         if (!urlInjectionFixed.contains("?")) {
343             urlInjectionFixed += "?";
344         }
345         urlInjectionFixed += this.buildQuery(
346             this.mediatorMethod.getQuery(),
347             this.mediatorUtils.parameterUtil().getQueryStringFromEntries(),
348             isUsingIndex,
349             dataInjection
350         );
351         return this.mediatorUtils.csrfUtil().addQueryStringToken(urlInjectionFixed);
352     }
353 
354     private void initHeader(boolean isUsingIndex, String dataInjection, Builder httpRequest) {
355         if (!this.mediatorUtils.parameterUtil().getListHeader().isEmpty()) {
356             Stream.of(
357                 this.buildQuery(
358                     this.mediatorMethod.getHeader(),
359                     this.mediatorUtils.parameterUtil().getHeaderFromEntries(),
360                     isUsingIndex,
361                     dataInjection
362                 )
363                 .split("\\\\r\\\\n")
364             )
365             .forEach(header -> {
366                 if (header.split(":").length == 2) {
367                     try {  // TODO Should not catch, rethrow or use runtime exception
368                         HeaderUtil.sanitizeHeaders(
369                             httpRequest,
370                             new SimpleEntry<>(
371                                 header.split(":")[0],
372                                 header.split(":")[1]
373                             )
374                         );
375                     } catch (JSqlException e) {
376                         LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Headers sanitizing issue caught already during connection, ignoring", e);
377                     }
378                 }
379             });
380         }
381     }
382 
383     private String initRequest(boolean isUsingIndex, String dataInjection, Builder httpRequest) {
384         if (
385             this.mediatorUtils.parameterUtil().getListRequest().isEmpty()
386             && this.mediatorUtils.csrfUtil().getTokenCsrf() == null
387         ) {
388             return dataInjection;
389         }
390             
391         // Set connection method
392         // Active for query string injection too, in that case inject query string still with altered method
393         
394         if (this.mediatorUtils.parameterUtil().isRequestSoap()) {
395             httpRequest.setHeader(HeaderUtil.CONTENT_TYPE_REQUEST, "text/xml");
396         } else {
397             httpRequest.setHeader(HeaderUtil.CONTENT_TYPE_REQUEST, "application/x-www-form-urlencoded");
398         }
399 
400         var body = new StringBuilder();
401         this.mediatorUtils.csrfUtil().addRequestToken(body);
402             
403         if (this.mediatorUtils.connectionUtil().getTypeRequest().matches("PUT|POST")) {
404             if (this.mediatorUtils.parameterUtil().isRequestSoap()) {
405                 body.append(
406                     this.buildQuery(
407                         this.mediatorMethod.getRequest(),
408                         this.mediatorUtils.parameterUtil().getRawRequest(),
409                         isUsingIndex,
410                         dataInjection
411                     )
412                     // Invalid XML characters in recent Spring version
413                     // Server needs to urldecode, or stop using out of range chars
414                     .replace("\u0001", "&#01;")
415                     .replace("\u0003", "&#03;")
416                     .replace("\u0004", "&#04;")
417                     .replace("\u0005", "&#05;")
418                     .replace("\u0006", "&#06;")
419                     .replace("\u0007", "&#07;")
420                     .replace("+", "%2B")  // Prevent replace '+' into 'space' on server side urldecode
421                 );
422             } else {
423                 body.append(
424                     this.buildQuery(
425                         this.mediatorMethod.getRequest(),
426                         this.mediatorUtils.parameterUtil().getRequestFromEntries(),
427                         isUsingIndex,
428                         dataInjection
429                     )
430                 );
431             }
432         }
433         
434         var bodyPublisher = BodyPublishers.ofString(body.toString());
435         httpRequest.method(
436             this.mediatorUtils.connectionUtil().getTypeRequest(),
437             bodyPublisher
438         );
439         return body.toString();
440     }
441     
442     private String buildQuery(AbstractMethodInjection methodInjection, String paramLead, boolean isUsingIndex, String sqlTrail) {
443         String query;
444         String paramLeadFixed = paramLead.replace(
445             InjectionModel.STAR,
446             TamperingUtil.TAG_OPENED + InjectionModel.STAR + TamperingUtil.TAG_CLOSED
447         );
448         if (
449             // No parameter transformation if method is not selected by user
450             this.mediatorUtils.connectionUtil().getMethodInjection() != methodInjection
451             // No parameter transformation if injection point in URL
452             || this.mediatorUtils.connectionUtil().getUrlBase().contains(InjectionModel.STAR)
453         ) {
454             query = paramLeadFixed;  // Just pass parameters without any transformation
455         } else if (
456             // If method is selected by user and URL does not contain injection point
457             // but parameters contain an injection point
458             // then replace injection point by SQL expression in this parameter
459             paramLeadFixed.contains(InjectionModel.STAR)
460         ) {
461             query = this.initStarInjection(paramLeadFixed, isUsingIndex, sqlTrail);
462         } else {
463             query = this.initRawInjection(paramLeadFixed, isUsingIndex, sqlTrail);
464         }
465         query = this.cleanQuery(methodInjection, query);  // Remove comments except empty /**/
466         // Add empty comments with space=>/**/
467         if (this.mediatorUtils.connectionUtil().getMethodInjection() == methodInjection) {
468             query = this.mediatorUtils.tamperingUtil().tamper(query);
469         }
470         return this.applyEncoding(methodInjection, query);
471     }
472 
473     private String initRawInjection(String paramLead, boolean isUsingIndex, String sqlTrail) {
474         String query;
475         // Method is selected by user and there's no injection point
476         if (!isUsingIndex) {
477             // Several SQL expressions does not use indexes in SELECT,
478             // like Boolean, Error, Shell and search for character insertion,
479             // in that case concat SQL expression to the end of param.
480             query = paramLead + sqlTrail;
481         } else {
482             // Concat indexes found for Union strategy to params
483             // and use visible Index for injection
484             query = paramLead + this.getMediatorStrategy().getSpecificUnion().getIndexesInUrl().replaceAll(
485                 String.format(EngineYaml.FORMAT_INDEX, this.mediatorStrategy.getSpecificUnion().getVisibleIndex()),
486                 // Oracle column often contains $, which is reserved for regex.
487                 // => need to be escape with quoteReplacement()
488                 Matcher.quoteReplacement(sqlTrail)
489             );
490         }
491         // Add ending line comment by engine
492         return query + this.mediatorEngine.getEngine().instance().endingComment();
493     }
494 
495     private String initStarInjection(String paramLead, boolean isUsingIndex, String sqlTrail) {
496         String query;
497         // Several SQL expressions does not use indexes in SELECT,
498         // like Boolean, Error, Shell and search for character insertion,
499         // in that case replace injection point by SQL expression.
500         // Injection point is always at the end?
501         if (!isUsingIndex) {
502             query = paramLead.replace(
503                 InjectionModel.STAR,
504                 sqlTrail + this.mediatorEngine.getEngine().instance().endingComment()
505             );
506         } else {
507             // Replace injection point by indexes found for Union strategy
508             // and use visible Index for injection
509             query = paramLead.replace(
510                 InjectionModel.STAR,
511                 this.mediatorStrategy.getSpecificUnion().getIndexesInUrl().replace(
512                     String.format(EngineYaml.FORMAT_INDEX, this.mediatorStrategy.getSpecificUnion().getVisibleIndex()),
513                     sqlTrail
514                 )
515                 + this.mediatorEngine.getEngine().instance().endingComment()
516             );
517         }
518         return query;
519     }
520 
521     /**
522      * Dependency:
523      * - Tamper space=>comment
524      */
525     private String cleanQuery(AbstractMethodInjection methodInjection, String query) {
526         String queryFixed = query;
527         if (
528             methodInjection == this.mediatorMethod.getRequest()
529             && (
530                 this.mediatorUtils.parameterUtil().isRequestSoap()
531                 || this.mediatorUtils.parameterUtil().isMultipartRequest()
532             )
533         ) {
534             queryFixed = StringUtil.removeSqlComment(queryFixed)
535                 .replace("+", " ")
536                 .replace("%2b", "+")  // Failsafe
537                 .replace("%23", "#");  // End comment
538             if (this.mediatorUtils.parameterUtil().isMultipartRequest()) {
539                 // restore linefeed from textfield
540                 queryFixed = queryFixed.replaceAll("(?s)\\\\n", "\r\n");
541             }
542         } else {
543             queryFixed = StringUtil.cleanSql(queryFixed);
544         }
545         return queryFixed;
546     }
547 
548     private String applyEncoding(AbstractMethodInjection methodInjection, String query) {
549         String queryFixed = query;
550         if (!this.mediatorUtils.parameterUtil().isRequestSoap()) {
551             if (methodInjection == this.mediatorMethod.getQuery()) {
552                 // URL encode each character because no query parameter context
553                 if (!this.mediatorUtils.preferencesUtil().isUrlEncodingDisabled()) {
554                     queryFixed = queryFixed.replace("'", "%27");
555                     queryFixed = queryFixed.replace("(", "%28");
556                     queryFixed = queryFixed.replace(")", "%29");
557                     queryFixed = queryFixed.replace("{", "%7b");
558                     queryFixed = queryFixed.replace("[", "%5b");
559                     queryFixed = queryFixed.replace("]", "%5d");
560                     queryFixed = queryFixed.replace("}", "%7d");
561                     queryFixed = queryFixed.replace(">", "%3e");
562                     queryFixed = queryFixed.replace("<", "%3c");
563                     queryFixed = queryFixed.replace("?", "%3f");
564                     queryFixed = queryFixed.replace("_", "%5f");
565                     queryFixed = queryFixed.replace(",", "%2c");
566                 }
567                 // HTTP forbidden characters
568                 queryFixed = queryFixed.replace(StringUtils.SPACE, "+");
569                 queryFixed = queryFixed.replace("`", "%60");  // from `${database}`.`${table}`
570                 queryFixed = queryFixed.replace("\"", "%22");
571                 queryFixed = queryFixed.replace("|", "%7c");
572                 queryFixed = queryFixed.replace("\\", "%5c");
573             } else if (methodInjection != this.mediatorMethod.getRequest()) {
574                 // For cookies in Spring (confirmed, covered by integration tests)
575                 queryFixed = queryFixed.replace("+", "%20");
576                 queryFixed = queryFixed.replace(",", "%2c");
577                 try {  // fix #95709: IllegalArgumentException on decode()
578                     queryFixed = URLDecoder.decode(queryFixed, StandardCharsets.UTF_8);
579                 } catch (IllegalArgumentException e) {
580                     LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Incorrect values in [{}], please check the parameters", methodInjection.name());
581                     throw new JSqlRuntimeException(e);
582                 }
583             }
584         }
585         return queryFixed;
586     }
587     
588     /**
589      * Display source code in console.
590      * @param message Error message
591      * @param source Text to display in console
592      */
593     public void sendResponseFromSite(String message, String source) {
594         LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "{}, response from site:", message);
595         LOGGER.log(LogLevelUtil.CONSOLE_ERROR, ">>>{}", source);
596     }
597     
598     
599     // Getters and setters
600 
601     public boolean shouldErasePreviousInjection() {
602         return this.shouldErasePreviousInjection;
603     }
604 
605     public void setIsScanning(boolean isScanning) {
606         this.isScanning = isScanning;
607     }
608 
609     public PropertiesUtil getPropertiesUtil() {
610         return this.propertiesUtil;
611     }
612 
613     public MediatorUtils getMediatorUtils() {
614         return this.mediatorUtils;
615     }
616 
617     public MediatorEngine getMediatorEngine() {
618         return this.mediatorEngine;
619     }
620 
621     public MediatorMethod getMediatorMethod() {
622         return this.mediatorMethod;
623     }
624 
625     public DataAccess getDataAccess() {
626         return this.dataAccess;
627     }
628 
629     public ResourceAccess getResourceAccess() {
630         return this.resourceAccess;
631     }
632 
633     public MediatorStrategy getMediatorStrategy() {
634         return this.mediatorStrategy;
635     }
636 
637     public void appendAnalysisReport(String analysisReport) {
638         this.appendAnalysisReport(analysisReport, false);
639     }
640 
641     public void appendAnalysisReport(String analysisReport, boolean isInit) {
642         this.analysisReport += (isInit ? StringUtils.EMPTY : "<br>&#10;<br>&#10;") + analysisReport;
643     }
644 }