AbstractMethodInjection.java
package com.jsql.model.injection.method;
import com.jsql.model.InjectionModel;
import com.jsql.model.exception.JSqlException;
import com.jsql.model.exception.StoppedByUserSlidingException;
import com.jsql.util.JsonUtil;
import com.jsql.util.LogLevelUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONException;
import java.io.Serializable;
import java.util.AbstractMap.SimpleEntry;
import java.util.List;
import java.util.regex.Pattern;
public abstract class AbstractMethodInjection implements Serializable {
/**
* Log4j logger sent to view.
*/
private static final Logger LOGGER = LogManager.getRootLogger();
protected final InjectionModel injectionModel;
protected AbstractMethodInjection(InjectionModel injectionModel) {
this.injectionModel = injectionModel;
}
public abstract boolean isCheckingAllParam();
public abstract String getParamsAsString();
public abstract List<SimpleEntry<String, String>> getParams();
public abstract String name();
public boolean testParameters(boolean hasFoundInjection) throws JSqlException {
if (!hasFoundInjection) {
LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Checking {} params...", () -> name().toLowerCase());
return this.testParameters();
}
return true;
}
/**
* Verify if injection works for specific Method using 3 modes: standard (last param), injection point
* and full params injection. Special injections like JSON and SOAP are checked.
* @return true if injection didn't fail
* @throws JSqlException when no params' integrity, process stopped by user, or injection failure
*/
public boolean testParameters() throws JSqlException {
var hasFoundInjection = false;
// Injects URL, Request or Header params only if user tests every params
// or method is selected by user.
if (
!this.injectionModel.getMediatorUtils().getPreferencesUtil().isCheckingAllParam()
&& this.injectionModel.getMediatorUtils().getConnectionUtil().getMethodInjection() != this
) {
return false;
}
// Force injection method of model to current running method
this.injectionModel.getMediatorUtils().getConnectionUtil().setMethodInjection(this);
// Injection by injection point in params or in path
if (
this.getParamsAsString().contains(InjectionModel.STAR)
|| this.injectionModel.getMediatorUtils().getConnectionUtil().getUrlBase().contains(InjectionModel.STAR)
) {
hasFoundInjection = this.checkParamWithStar();
} else if (!this.isCheckingAllParam()) {
hasFoundInjection = this.checkLastParam();
} else {
hasFoundInjection = this.checkAllParams();
}
return hasFoundInjection;
}
private boolean checkParamWithStar() throws JSqlException {
SimpleEntry<String, String> parameterToInject = this.getParams().stream()
.filter(entry -> entry.getValue().contains("*"))
.findFirst()
.orElse(null);
return this.injectionModel.getMediatorStrategy().testStrategies(parameterToInject);
}
/**
* Default injection: last param tested only
*/
private boolean checkLastParam() throws JSqlException {
// Will check param value by user.
// Notice options 'Inject each URL params' and 'inject JSON' must be checked both
// for JSON injection of last param
SimpleEntry<String, String> parameterToInject = this.getParams().stream()
.reduce((a, b) -> b)
.orElseThrow(NullPointerException::new);
return this.injectionModel.getMediatorStrategy().testStrategies(parameterToInject);
}
/**
* Injection of every params: isCheckingAllParam() == true.
* Params are tested one by one in two loops:
* - inner loop erases * from previous param
* - outer loop adds * to current param
* @throws StoppedByUserSlidingException
*/
private boolean checkAllParams() throws StoppedByUserSlidingException {
// This param will be marked by * if injection is found,
// inner loop will erase mark * otherwise
for (SimpleEntry<String, String> paramBase: this.getParams()) {
// This param is the current tested one.
// For JSON value attributes are traversed one by one to test every values.
// For standard value mark * is simply added to the end of its value.
for (SimpleEntry<String, String> paramStar: this.getParams()) {
if (paramStar == paramBase) {
try {
if (this.isParamInjectable(paramStar)) {
return true;
}
} catch (JSONException e) {
LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
}
}
}
}
return false;
}
private boolean isParamInjectable(SimpleEntry<String, String> paramStar) throws StoppedByUserSlidingException {
boolean hasFoundInjection;
// Will test if current value is a JSON entity
Object jsonEntity = JsonUtil.getJson(paramStar.getValue());
// Define a tree of JSON attributes with path as the key: root.a => value of a
List<SimpleEntry<String, String>> attributesJson = JsonUtil.createEntries(jsonEntity, "root", null);
// When option 'Inject JSON' is selected and there's a JSON entity to inject
// then loop through each paths to add * at the end of value and test each strategies.
// Marks * are erased between each tests.
if (!attributesJson.isEmpty() && this.injectionModel.getMediatorUtils().getPreferencesUtil().isCheckingAllJsonParam()) {
hasFoundInjection = this.injectionModel.getMediatorUtils().getJsonUtil().testJsonParam(this, paramStar);
} else {
hasFoundInjection = this.testJsonlessParam(paramStar); // Standard non JSON injection
}
return hasFoundInjection;
}
public boolean testJsonlessParam(SimpleEntry<String, String> paramStar) throws StoppedByUserSlidingException {
var hasFoundInjection = false;
paramStar.setValue(paramStar.getValue() + InjectionModel.STAR);
try {
LOGGER.log(
LogLevelUtil.CONSOLE_INFORM,
"Checking {} parameter {}={}",
this::name,
paramStar::getKey,
() -> paramStar.getValue().replace(InjectionModel.STAR, StringUtils.EMPTY)
);
// Test current standard value marked with * for injection
// Keep original param
hasFoundInjection = this.injectionModel.getMediatorStrategy().testStrategies(paramStar);
} catch (StoppedByUserSlidingException e) { // Break all params processing in upper methods
throw e;
} catch (JSqlException e) { // Injection failure
LOGGER.log(
LogLevelUtil.CONSOLE_ERROR,
"No {} injection found for parameter {}={} ({})",
this.name(),
paramStar.getKey(),
paramStar.getValue().replaceAll("\\+.?$|\\" + InjectionModel.STAR, StringUtils.EMPTY),
e.getMessage()
);
} finally {
if (!hasFoundInjection) { // Erase * from JSON if failure
// Erase * at the end of each params
this.getParams().forEach(e ->
e.setValue(
e.getValue().replaceAll(Pattern.quote(InjectionModel.STAR) +"$", StringUtils.EMPTY)
)
);
// TODO It erases STAR from value => * can't be used in parameter
paramStar.setValue(paramStar.getValue().replace(InjectionModel.STAR, StringUtils.EMPTY));
}
}
return hasFoundInjection;
}
}