StrategyStack.java

package com.jsql.model.injection.strategy;

import com.jsql.model.InjectionModel;
import com.jsql.model.accessible.DataAccess;
import com.jsql.model.bean.util.Interaction;
import com.jsql.model.bean.util.Request;
import com.jsql.model.injection.vendor.model.VendorYaml;
import com.jsql.model.injection.vendor.model.yaml.Configuration;
import com.jsql.model.suspendable.AbstractSuspendable;
import com.jsql.util.I18nUtil;
import com.jsql.util.LogLevelUtil;
import com.jsql.util.StringUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StrategyStack extends AbstractStrategy {

    /**
     * Log4j logger sent to view.
     */
    private static final Logger LOGGER = LogManager.getRootLogger();

    private String performanceLength = "0";

    public StrategyStack(InjectionModel injectionModel) {
        super(injectionModel);
    }

    @Override
    public void checkApplicability() {
        if (this.injectionModel.getMediatorUtils().getPreferencesUtil().isStrategyStackDisabled()) {
            LOGGER.log(LogLevelUtil.CONSOLE_INFORM, AbstractStrategy.FORMAT_SKIP_STRATEGY_DISABLED, this.getName());
            return;
        }

        // Reset applicability of new Vendor
        this.isApplicable = false;
        var strategyYaml = this.injectionModel.getMediatorVendor().getVendor().instance().getModelYaml().getStrategy();
        var configurationYaml = strategyYaml.getConfiguration();

        this.logChecking();

        boolean methodIsApplicable = this.isApplicable(configurationYaml, strategyYaml.getStack());
        if (methodIsApplicable) {
            Matcher regexSearch = this.getPerformance(configurationYaml, strategyYaml.getStack());
            if (!regexSearch.find()) {
                LOGGER.log(
                    LogLevelUtil.CONSOLE_ERROR,
                    "{} {} but injectable size is incorrect",
                    () -> I18nUtil.valueByKey("LOG_VULNERABLE"),
                    () -> "Stack"
                );
                methodIsApplicable = false;
            } else {
                this.performanceLength = String.valueOf(regexSearch.group(1).length());
            }
        }

        if (methodIsApplicable) {
            LOGGER.log(
                LogLevelUtil.CONSOLE_SUCCESS,
                "{} Stack injection showing [{}] characters",
                () -> I18nUtil.valueByKey("LOG_VULNERABLE"),
                () -> this.performanceLength
            );
            this.allow();
        } else {
            this.unallow();
        }
    }

    private boolean isApplicable(Configuration configurationYaml, String stack) {
        var methodIsApplicable = false;
        var indexZeroToFind = "0";
        String performanceSourcePage = this.injectionModel.injectWithoutIndex(
            VendorYaml.replaceTags(
                stack
                .replace(VendorYaml.WINDOW, configurationYaml.getSlidingWindow())
                .replace(VendorYaml.INJECTION, configurationYaml.getFailsafe().replace(VendorYaml.INDICE,indexZeroToFind))
                .replace(VendorYaml.WINDOW_CHAR, "1")
                .replace(VendorYaml.CAPACITY, VendorYaml.DEFAULT_CAPACITY)
            ),
            "stack#confirm"
        );
        String regexIndexZero = String.format(VendorYaml.FORMAT_INDEX, indexZeroToFind);
        if (performanceSourcePage.matches("(?s).*"+ regexIndexZero +".*")) {
            methodIsApplicable = true;
            this.isApplicable = true;
        }
        return methodIsApplicable;
    }

    private Matcher getPerformance(Configuration configurationYaml, String stack) {
        String performanceSourcePage = this.injectionModel.injectWithoutIndex(
            VendorYaml.replaceTags(
                stack
                .replace(VendorYaml.WINDOW, configurationYaml.getSlidingWindow())
                .replace(VendorYaml.INJECTION, configurationYaml.getCalibrator())
                .replace(VendorYaml.WINDOW_CHAR, "1")
                .replace(VendorYaml.CAPACITY, VendorYaml.DEFAULT_CAPACITY)
            ),
            "stack#size"
        );
        return Pattern.compile("(?s)"+ DataAccess.LEAD +"("+ VendorYaml.CALIBRATOR_SQL +"+)").matcher(performanceSourcePage);
    }

    @Override
    public void allow(int... i) {
        this.injectionModel.appendAnalysisReport(
            StringUtil.formatReport(LogLevelUtil.COLOR_BLU, "### Strategy: " + this.getName())
            + this.injectionModel.getReportWithoutIndex(
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlStack(StringUtil.formatReport(LogLevelUtil.COLOR_GREEN, "<query>"), "0", true),
                "metadataInjectionProcess"
            )
        );
        this.markVulnerability(Interaction.MARK_STACK_VULNERABLE);
    }

    @Override
    public void unallow(int... i) {
        this.markVulnerability(Interaction.MARK_STACK_INVULNERABLE);
    }

    @Override
    public String inject(String sqlQuery, String startPosition, AbstractSuspendable stoppable, String metadataInjectionProcess) {
        return this.injectionModel.injectWithoutIndex(
            this.injectionModel.getMediatorVendor().getVendor().instance().sqlStack(sqlQuery, startPosition, false),
            metadataInjectionProcess
        );
    }

    @Override
    public void activateWhenApplicable() {
        if (this.injectionModel.getMediatorStrategy().getStrategy() == null && this.isApplicable()) {
            LOGGER.log(
                LogLevelUtil.CONSOLE_INFORM,
                "{} [{}]",
                () -> I18nUtil.valueByKey("LOG_USING_STRATEGY"),
                this::getName
            );
            this.injectionModel.getMediatorStrategy().setStrategy(this);

            var request = new Request();
            request.setMessage(Interaction.MARK_STACK_STRATEGY);
            this.injectionModel.sendToViews(request);
        }
    }

    @Override
    public String getPerformanceLength() {
        return this.performanceLength;
    }

    @Override
    public String getName() {
        return "Stack";
    }
}