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