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