| 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 |