1 | package com.jsql.model.suspendable; | |
2 | ||
3 | import com.jsql.model.InjectionModel; | |
4 | import com.jsql.model.bean.util.Header; | |
5 | import com.jsql.model.bean.util.Interaction; | |
6 | import com.jsql.model.bean.util.Request; | |
7 | import com.jsql.model.exception.JSqlException; | |
8 | import com.jsql.model.exception.StoppedByUserSlidingException; | |
9 | import com.jsql.model.injection.strategy.blind.InjectionCharInsertion; | |
10 | import com.jsql.model.injection.vendor.MediatorVendor; | |
11 | import com.jsql.model.injection.vendor.model.Vendor; | |
12 | import com.jsql.model.suspendable.callable.CallablePageSource; | |
13 | import com.jsql.util.I18nUtil; | |
14 | import com.jsql.util.LogLevelUtil; | |
15 | import org.apache.commons.lang3.RandomStringUtils; | |
16 | import org.apache.commons.lang3.StringUtils; | |
17 | import org.apache.logging.log4j.LogManager; | |
18 | import org.apache.logging.log4j.Logger; | |
19 | ||
20 | import java.util.*; | |
21 | import java.util.concurrent.CompletionService; | |
22 | import java.util.concurrent.ExecutionException; | |
23 | import java.util.concurrent.ExecutorCompletionService; | |
24 | import java.util.concurrent.ExecutorService; | |
25 | import java.util.regex.Pattern; | |
26 | import java.util.stream.Collectors; | |
27 | import java.util.stream.Stream; | |
28 | ||
29 | /** | |
30 | * Runnable class, define insertionCharacter to be used during injection, | |
31 | * i.e -1 in "...php?id=-1 union select..", sometimes it's -1, 0', 0, etc. | |
32 | * Find working insertion char when error message occurs in source. | |
33 | * Force to 1 if no insertion char works and empty value from user, | |
34 | * Force to user's value if no insertion char works, | |
35 | * Force to insertion char otherwise. | |
36 | */ | |
37 | public class SuspendableGetCharInsertion extends AbstractSuspendable { | |
38 | | |
39 | /** | |
40 | * Log4j logger sent to view. | |
41 | */ | |
42 | private static final Logger LOGGER = LogManager.getRootLogger(); | |
43 | ||
44 | private static final String LABEL_PREFIX = "prefix"; | |
45 | ||
46 | public SuspendableGetCharInsertion(InjectionModel injectionModel) { | |
47 | super(injectionModel); | |
48 | } | |
49 | ||
50 | @Override | |
51 | public String run(Object... args) throws JSqlException { | |
52 | String characterInsertionByUser = (String) args[0]; | |
53 | | |
54 | ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetInsertionCharacter"); | |
55 | CompletionService<CallablePageSource> taskCompletionService = new ExecutorCompletionService<>(taskExecutor); | |
56 | ||
57 | var charFromBooleanMatch = new String[1]; | |
58 | List<String> charactersInsertion = this.initCallables(taskCompletionService, charFromBooleanMatch); | |
59 | | |
60 | var mediatorVendor = this.injectionModel.getMediatorVendor(); | |
61 | LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Fingerprinting database and character insertion with Order by match..."); | |
62 | ||
63 | String charFromOrderBy = null; | |
64 | | |
65 | int total = charactersInsertion.size(); | |
66 |
2
1. run : negated conditional → NO_COVERAGE 2. run : changed conditional boundary → NO_COVERAGE |
while (0 < total) { |
67 |
1
1. run : negated conditional → NO_COVERAGE |
if (this.isSuspended()) { |
68 | throw new StoppedByUserSlidingException(); | |
69 | } | |
70 | try { | |
71 | CallablePageSource currentCallable = taskCompletionService.take().get(); | |
72 |
1
1. run : Changed increment from -1 to 1 → NO_COVERAGE |
total--; |
73 | String pageSource = currentCallable.getContent(); | |
74 | | |
75 | List<Vendor> vendorsOrderByMatch = this.getVendorsOrderByMatch(mediatorVendor, pageSource); | |
76 |
1
1. run : negated conditional → NO_COVERAGE |
if (!vendorsOrderByMatch.isEmpty()) { |
77 |
1
1. run : removed call to com/jsql/model/suspendable/SuspendableGetCharInsertion::setVendor → NO_COVERAGE |
this.setVendor(mediatorVendor, vendorsOrderByMatch); |
78 | | |
79 | LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Using [{}]", mediatorVendor.getVendor()); | |
80 | var requestSetVendor = new Request(); | |
81 |
1
1. run : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE |
requestSetVendor.setMessage(Interaction.SET_VENDOR); |
82 | Map<Header, Object> msgHeader = new EnumMap<>(Header.class); | |
83 | msgHeader.put(Header.URL, this.injectionModel.getMediatorUtils().getConnectionUtil().getUrlByUser()); | |
84 | msgHeader.put(Header.VENDOR, mediatorVendor.getVendor()); | |
85 |
1
1. run : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE |
requestSetVendor.setParameters(msgHeader); |
86 |
1
1. run : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE |
this.injectionModel.sendToViews(requestSetVendor); |
87 | | |
88 | charFromOrderBy = currentCallable.getCharacterInsertion(); | |
89 | LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Character insertion [{}] matching with Order by and compatible with Error strategy", charFromOrderBy); | |
90 | break; | |
91 | } | |
92 | } catch (InterruptedException e) { | |
93 | LOGGER.log(LogLevelUtil.IGNORE, e, e); | |
94 |
1
1. run : removed call to java/lang/Thread::interrupt → NO_COVERAGE |
Thread.currentThread().interrupt(); |
95 | } catch (ExecutionException e) { | |
96 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
97 | } | |
98 | } | |
99 |
1
1. run : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE |
this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor); |
100 |
2
1. run : negated conditional → NO_COVERAGE 2. run : negated conditional → NO_COVERAGE |
if (charFromOrderBy == null && charFromBooleanMatch[0] != null) { |
101 | charFromOrderBy = charFromBooleanMatch[0]; | |
102 | } | |
103 |
1
1. run : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetCharInsertion::run → NO_COVERAGE |
return this.getCharacterInsertion(characterInsertionByUser, charFromOrderBy); |
104 | } | |
105 | ||
106 | private void setVendor(MediatorVendor mediatorVendor, List<Vendor> vendorsOrderByMatch) { | |
107 | if ( | |
108 |
1
1. setVendor : negated conditional → NO_COVERAGE |
vendorsOrderByMatch.size() == 1 |
109 |
1
1. setVendor : negated conditional → NO_COVERAGE |
&& vendorsOrderByMatch.get(0) != mediatorVendor.getVendor() |
110 | ) { | |
111 |
1
1. setVendor : removed call to com/jsql/model/injection/vendor/MediatorVendor::setVendor → NO_COVERAGE |
mediatorVendor.setVendor(vendorsOrderByMatch.get(0)); |
112 |
2
1. setVendor : changed conditional boundary → NO_COVERAGE 2. setVendor : negated conditional → NO_COVERAGE |
} else if (vendorsOrderByMatch.size() > 1) { |
113 |
1
1. setVendor : negated conditional → NO_COVERAGE |
if (vendorsOrderByMatch.contains(mediatorVendor.getPostgres())) { |
114 |
1
1. setVendor : removed call to com/jsql/model/injection/vendor/MediatorVendor::setVendor → NO_COVERAGE |
mediatorVendor.setVendor(mediatorVendor.getPostgres()); |
115 |
1
1. setVendor : negated conditional → NO_COVERAGE |
} else if (vendorsOrderByMatch.contains(mediatorVendor.getMysql())) { |
116 |
1
1. setVendor : removed call to com/jsql/model/injection/vendor/MediatorVendor::setVendor → NO_COVERAGE |
mediatorVendor.setVendor(mediatorVendor.getMysql()); |
117 | } else { | |
118 |
1
1. setVendor : removed call to com/jsql/model/injection/vendor/MediatorVendor::setVendor → NO_COVERAGE |
mediatorVendor.setVendor(vendorsOrderByMatch.get(0)); |
119 | } | |
120 | } | |
121 | } | |
122 | ||
123 | private List<Vendor> getVendorsOrderByMatch(MediatorVendor mediatorVendor, String pageSource) { | |
124 |
1
1. getVendorsOrderByMatch : replaced return value with Collections.emptyList for com/jsql/model/suspendable/SuspendableGetCharInsertion::getVendorsOrderByMatch → NO_COVERAGE |
return mediatorVendor.getVendors() |
125 | .stream() | |
126 |
2
1. lambda$getVendorsOrderByMatch$0 : replaced boolean return with true for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$0 → NO_COVERAGE 2. lambda$getVendorsOrderByMatch$0 : negated conditional → NO_COVERAGE |
.filter(vendor -> vendor != mediatorVendor.getAuto()) |
127 |
2
1. lambda$getVendorsOrderByMatch$1 : replaced boolean return with false for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$1 → NO_COVERAGE 2. lambda$getVendorsOrderByMatch$1 : replaced boolean return with true for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$1 → NO_COVERAGE |
.filter(vendor -> StringUtils.isNotEmpty( |
128 | vendor.instance().getModelYaml().getStrategy().getConfiguration().getFingerprint().getOrderByErrorMessage() | |
129 | )) | |
130 | .filter(vendor -> { | |
131 | Optional<String> optionalOrderByErrorMatch = Stream.of( | |
132 | vendor.instance().getModelYaml().getStrategy().getConfiguration().getFingerprint().getOrderByErrorMessage() | |
133 | .split("[\\r\\n]+") | |
134 | ) | |
135 | .filter(errorMessage -> | |
136 |
2
1. lambda$getVendorsOrderByMatch$2 : replaced boolean return with false for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$2 → NO_COVERAGE 2. lambda$getVendorsOrderByMatch$2 : replaced boolean return with true for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$2 → NO_COVERAGE |
Pattern |
137 | .compile(".*" + errorMessage + ".*", Pattern.DOTALL) | |
138 | .matcher(pageSource) | |
139 | .matches() | |
140 | ) | |
141 | .findAny(); | |
142 |
1
1. lambda$getVendorsOrderByMatch$3 : negated conditional → NO_COVERAGE |
if (optionalOrderByErrorMatch.isPresent()) { |
143 | LOGGER.log( | |
144 | LogLevelUtil.CONSOLE_SUCCESS, | |
145 | String.format("Order by fingerprint matching vendor [%s]", vendor) | |
146 | ); | |
147 | } | |
148 |
2
1. lambda$getVendorsOrderByMatch$3 : replaced boolean return with false for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$3 → NO_COVERAGE 2. lambda$getVendorsOrderByMatch$3 : replaced boolean return with true for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getVendorsOrderByMatch$3 → NO_COVERAGE |
return optionalOrderByErrorMatch.isPresent(); |
149 | }) | |
150 | .collect(Collectors.toList()); | |
151 | } | |
152 | ||
153 | private List<String> initCallables(CompletionService<CallablePageSource> taskCompletionService, String[] charFromBooleanMatch) throws JSqlException { | |
154 | List<String> prefixValues = Arrays.asList( | |
155 | RandomStringUtils.secure().next(10, "012"), // to trigger probable failure | |
156 | "1" // to trigger eventual success | |
157 | ); | |
158 | List<String> prefixQuotes = Arrays.asList( | |
159 | SuspendableGetCharInsertion.LABEL_PREFIX +"'", | |
160 | SuspendableGetCharInsertion.LABEL_PREFIX, | |
161 | SuspendableGetCharInsertion.LABEL_PREFIX +"`", // TODO add ITs | |
162 | SuspendableGetCharInsertion.LABEL_PREFIX +"\"", | |
163 | SuspendableGetCharInsertion.LABEL_PREFIX +"%bf'" // GBK slash encoding use case | |
164 | ); | |
165 | List<String> prefixParentheses = Arrays.asList(StringUtils.EMPTY, ")", "))"); | |
166 | List<String> charactersInsertion = new ArrayList<>(); | |
167 | LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Fingerprinting character insertion with Boolean match..."); | |
168 | for (String prefixValue: prefixValues) { | |
169 | for (String prefixQuote: prefixQuotes) { | |
170 | for (String prefixParenthesis: prefixParentheses) { | |
171 |
1
1. initCallables : removed call to com/jsql/model/suspendable/SuspendableGetCharInsertion::checkInsertionChar → NO_COVERAGE |
this.checkInsertionChar(charFromBooleanMatch, charactersInsertion, prefixValue, prefixQuote, prefixParenthesis); |
172 | } | |
173 | } | |
174 | } | |
175 | for (String characterInsertion: charactersInsertion) { | |
176 | taskCompletionService.submit( | |
177 | new CallablePageSource( | |
178 | characterInsertion | |
179 | + StringUtils.SPACE // covered by cleaning | |
180 | + this.injectionModel.getMediatorVendor().getVendor().instance().sqlOrderBy(), | |
181 | characterInsertion, | |
182 | this.injectionModel, | |
183 | "prefix#orderby" | |
184 | ) | |
185 | ); | |
186 | } | |
187 |
1
1. initCallables : replaced return value with Collections.emptyList for com/jsql/model/suspendable/SuspendableGetCharInsertion::initCallables → NO_COVERAGE |
return charactersInsertion; |
188 | } | |
189 | ||
190 | private void checkInsertionChar( | |
191 | String[] charFromBooleanMatch, | |
192 | List<String> charactersInsertion, | |
193 | String prefixValue, | |
194 | String prefixQuote, | |
195 | String prefixParenthesis | |
196 | ) throws StoppedByUserSlidingException { | |
197 | String characterInsertion = prefixQuote.replace(SuspendableGetCharInsertion.LABEL_PREFIX, prefixValue) + prefixParenthesis; | |
198 | charactersInsertion.add(characterInsertion); | |
199 | // Skipping Boolean match when already found | |
200 |
1
1. checkInsertionChar : negated conditional → NO_COVERAGE |
if (charFromBooleanMatch[0] == null) { |
201 | var injectionCharInsertion = new InjectionCharInsertion( | |
202 | this.injectionModel, | |
203 | characterInsertion, | |
204 | prefixQuote + prefixParenthesis | |
205 | ); | |
206 |
1
1. checkInsertionChar : negated conditional → NO_COVERAGE |
if (injectionCharInsertion.isInjectable()) { |
207 |
1
1. checkInsertionChar : negated conditional → NO_COVERAGE |
if (this.isSuspended()) { |
208 | throw new StoppedByUserSlidingException(); | |
209 | } | |
210 | charFromBooleanMatch[0] = characterInsertion; | |
211 | LOGGER.log( | |
212 | LogLevelUtil.CONSOLE_SUCCESS, | |
213 | "Found character insertion [{}] using Boolean match", | |
214 |
1
1. lambda$checkInsertionChar$4 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$checkInsertionChar$4 → NO_COVERAGE |
() -> charFromBooleanMatch[0] |
215 | ); | |
216 | } | |
217 | } | |
218 | } | |
219 | | |
220 | private String getCharacterInsertion(String characterInsertionByUser, String characterInsertionDetected) { | |
221 | String characterInsertionDetectedFixed = characterInsertionDetected; | |
222 |
1
1. getCharacterInsertion : negated conditional → NO_COVERAGE |
if (characterInsertionDetectedFixed == null) { |
223 | characterInsertionDetectedFixed = characterInsertionByUser; | |
224 | String logCharacterInsertion = characterInsertionDetectedFixed; | |
225 | LOGGER.log( | |
226 | LogLevelUtil.CONSOLE_ERROR, | |
227 | "No character insertion found, forcing to [{}]", | |
228 |
1
1. lambda$getCharacterInsertion$5 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$5 → NO_COVERAGE |
() -> logCharacterInsertion.replace(InjectionModel.STAR, StringUtils.EMPTY) |
229 | ); | |
230 |
1
1. getCharacterInsertion : negated conditional → NO_COVERAGE |
} else if (!characterInsertionByUser.replace(InjectionModel.STAR, StringUtils.EMPTY).equals(characterInsertionDetectedFixed)) { |
231 | String characterInsertionByUserFormat = characterInsertionByUser.replace(InjectionModel.STAR, StringUtils.EMPTY); | |
232 | LOGGER.log( | |
233 | LogLevelUtil.CONSOLE_INFORM, | |
234 | "Using [{}] and matching [{}]", | |
235 |
1
1. lambda$getCharacterInsertion$6 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$6 → NO_COVERAGE |
() -> this.injectionModel.getMediatorVendor().getVendor(), |
236 |
1
1. lambda$getCharacterInsertion$7 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$7 → NO_COVERAGE |
() -> characterInsertionDetected |
237 | ); | |
238 | LOGGER.log( | |
239 | LogLevelUtil.CONSOLE_DEFAULT, | |
240 | "Disable search for char insertion in Preferences to force the value [{}]", | |
241 |
1
1. lambda$getCharacterInsertion$8 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$8 → NO_COVERAGE |
() -> characterInsertionByUserFormat |
242 | ); | |
243 | } else { | |
244 | LOGGER.log( | |
245 | LogLevelUtil.CONSOLE_INFORM, | |
246 | "{} [{}]", | |
247 |
1
1. lambda$getCharacterInsertion$9 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$9 → NO_COVERAGE |
() -> I18nUtil.valueByKey("LOG_USING_INSERTION_CHARACTER"), |
248 |
1
1. lambda$getCharacterInsertion$10 : replaced return value with null for com/jsql/model/suspendable/SuspendableGetCharInsertion::lambda$getCharacterInsertion$10 → NO_COVERAGE |
() -> characterInsertionDetected.replace(InjectionModel.STAR, StringUtils.EMPTY) |
249 | ); | |
250 | } | |
251 |
1
1. getCharacterInsertion : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetCharInsertion::getCharacterInsertion → NO_COVERAGE |
return characterInsertionDetectedFixed; |
252 | } | |
253 | } | |
Mutations | ||
66 |
1.1 2.2 |
|
67 |
1.1 |
|
72 |
1.1 |
|
76 |
1.1 |
|
77 |
1.1 |
|
81 |
1.1 |
|
85 |
1.1 |
|
86 |
1.1 |
|
94 |
1.1 |
|
99 |
1.1 |
|
100 |
1.1 2.2 |
|
103 |
1.1 |
|
108 |
1.1 |
|
109 |
1.1 |
|
111 |
1.1 |
|
112 |
1.1 2.2 |
|
113 |
1.1 |
|
114 |
1.1 |
|
115 |
1.1 |
|
116 |
1.1 |
|
118 |
1.1 |
|
124 |
1.1 |
|
126 |
1.1 2.2 |
|
127 |
1.1 2.2 |
|
136 |
1.1 2.2 |
|
142 |
1.1 |
|
148 |
1.1 2.2 |
|
171 |
1.1 |
|
187 |
1.1 |
|
200 |
1.1 |
|
206 |
1.1 |
|
207 |
1.1 |
|
214 |
1.1 |
|
222 |
1.1 |
|
228 |
1.1 |
|
230 |
1.1 |
|
235 |
1.1 |
|
236 |
1.1 |
|
241 |
1.1 |
|
247 |
1.1 |
|
248 |
1.1 |
|
251 |
1.1 |