org.parosproxy.paros.core.scanner.AbstractPlugin.java Source code

Java tutorial

Introduction

Here is the source code for org.parosproxy.paros.core.scanner.AbstractPlugin.java

Source

/*
 *
 * Paros and its related class files.
 * 
 * Paros is an HTTP/HTTPS proxy for assessing web application security.
 * Copyright (C) 2003-2004 Chinotec Technologies Company
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the Clarified Artistic License
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Clarified Artistic License for more details.
 * 
 * You should have received a copy of the Clarified Artistic License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
// ZAP: 2011/08/30 Support for scanner levels
// ZAP: 2012/01/02 Separate param and attack
// ZAP: 2012/03/03 Added getLevel(boolean incDefault)
// ZAP: 2102/03/15 Changed the type of the parameter "sb" of the method matchBodyPattern to 
// StringBuilder.
// ZAP: 2012/04/25 Added @Override annotation to all appropriate methods.
// ZAP: 2012/08/07 Renamed Level to AlertThreshold and added support for AttackStrength
// ZAP: 2012/08/31 Enabled control of AttackStrength
// ZAP: 2012/10/03 Issue 388 Added enabling support for technologies
// ZAP: 2013/01/19 Issue 460 Add support for a scan progress dialog
// ZAP: 2013/01/25 Removed the "(non-Javadoc)" comments.
// ZAP: 2013/02/19 Issue 528 Scan progress dialog can show negative progress times
// ZAP: 2013/04/14 Issue 611: Log the exceptions thrown by active scanners as error
// ZAP: 2013/05/02 Re-arranged all modifiers into Java coding standard order
// ZAP: 2013/07/12 Issue 713: Add CWE and WASC numbers to issues
// ZAP: 2013/09/08 Issue 691: Handle old plugins
// ZAP: 2013/11/16 Issue 842: NullPointerException while active scanning with ExtensionAntiCSRF disabled
// ZAP: 2014/01/16 Add support to plugin skipping
// ZAP: 2014/02/12 Issue 1030: Load and save scan policies
// ZAP: 2014/02/21 Issue 1043: Custom active scan dialog
// ZAP: 2014/05/15 Issue 1196: AbstractPlugin.bingo incorrectly sets evidence to attack
// ZAP: 2014/05/23 Issue 1209: Reliability becomes Confidence and add levels
// ZAP: 2014/07/07 Issue 389: Enable technology scope for scanners
// ZAP: 2014/10/25 Issue 1062: Made plugins that calls sendandrecieve also invoke scanner 
// hook before and after message update
// ZAP: 2014/11/19 Issue 1412: Init scan rule status (quality) from add-on
// ZAP: 2015/03/26 Issue 1573: Add option to inject plugin ID in header for all ascan requests
// ZAP: 2015/07/26 Issue 1618: Target Technology Not Honored
// ZAP: 2015/08/19 Issue 1785: Plugin enabled even if dependencies are not, "hangs" active scan

package org.parosproxy.paros.core.scanner;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidParameterException;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.httpclient.HttpException;
import org.apache.log4j.Logger;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.extension.encoder.Encoder;
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.zap.control.AddOn;
import org.zaproxy.zap.extension.anticsrf.AntiCsrfToken;
import org.zaproxy.zap.extension.anticsrf.ExtensionAntiCSRF;
import org.zaproxy.zap.model.Tech;
import org.zaproxy.zap.model.TechSet;

public abstract class AbstractPlugin implements Plugin, Comparable<Object> {

    /**
     * Default pattern used in pattern check for most plugins.
     */
    protected static final int PATTERN_PARAM = Pattern.CASE_INSENSITIVE | Pattern.MULTILINE;
    /**
     * CRLF string.
     */
    protected static final String CRLF = "\r\n";
    private HostProcess parent = null;
    private HttpMessage msg = null;
    private boolean enabled = true;
    private Logger log = Logger.getLogger(this.getClass());
    private Configuration config = null;
    // ZAP Added delayInMs
    private int delayInMs;
    private ExtensionAntiCSRF extAntiCSRF = null;
    private Encoder encoder = new Encoder();
    private AlertThreshold defaultAttackThreshold = AlertThreshold.MEDIUM;
    private static final AlertThreshold[] alertThresholdsSupported = new AlertThreshold[] { AlertThreshold.MEDIUM };
    private AttackStrength defaultAttackStrength = AttackStrength.MEDIUM;
    private static final AttackStrength[] attackStrengthsSupported = new AttackStrength[] { AttackStrength.MEDIUM };
    private TechSet techSet = null;
    private Date started = null;
    private Date finished = null;
    private AddOn.Status status = AddOn.Status.unknown;

    /**
     * Default Constructor
     */
    public AbstractPlugin() {
    }

    @Override
    public abstract int getId();

    @Override
    public abstract String getName();

    @Override
    public String getCodeName() {
        String result = getClass().getName();
        int pos = getClass().getName().lastIndexOf(".");
        if (pos > -1) {
            result = result.substring(pos + 1);
        }
        return result;
    }

    @Override
    public abstract String[] getDependency();

    @Override
    public abstract String getDescription();

    @Override
    public abstract int getCategory();

    @Override
    public abstract String getSolution();

    @Override
    public abstract String getReference();

    @Override
    public void init(HttpMessage msg, HostProcess parent) {
        this.msg = msg.cloneAll();
        this.parent = parent;
        if (this.parent.getScannerParam().isInjectPluginIdInHeader()) {
            this.msg.getRequestHeader().setHeader(HttpHeader.X_ZAP_SCAN_ID, Integer.toString(getId()));
        }
        init();
    }

    public abstract void init();

    /**
     * Obtain a new HttpMessage with the same request as the base. The response
     * is empty. This is used by plugin to build/craft a new message to
     * send/receive. It does not affect the base message.
     *
     * @return A new HttpMessage with cloned request. Response is empty.
     */
    protected HttpMessage getNewMsg() {
        return msg.cloneRequest();
    }

    /**
     * Get the base reference HttpMessage for this check. Both request and
     * response is present. It should not be modified during when the plugin
     * runs.
     *
     * @return The base HttpMessage with request/response.
     */
    protected HttpMessage getBaseMsg() {
        return msg;
    }

    /**
     * Send and receive a HttpMessage. msg should have the request header/body
     * set. Fresh copy will always be retrieved via this method. The request
     * header content length will be modified by this method.
     *
     * @param msg
     * @throws HttpException
     * @throws IOException
     */
    protected void sendAndReceive(HttpMessage msg) throws HttpException, IOException {
        sendAndReceive(msg, true);
    }

    protected void sendAndReceive(HttpMessage msg, boolean isFollowRedirect) throws HttpException, IOException {
        sendAndReceive(msg, isFollowRedirect, true);
    }

    /**
     * Send and receive a HttpMessage. msg should have the request header/body
     * set. Fresh copy will always be retrieved via this method. The request
     * header content length will be modified by this method.
     *
     * @param msg
     * @param isFollowRedirect follow redirect response
     * @throws HttpException
     * @throws IOException
     */
    protected void sendAndReceive(HttpMessage msg, boolean isFollowRedirect, boolean handleAntiCSRF)
            throws HttpException, IOException {

        if (parent.handleAntiCsrfTokens() && handleAntiCSRF) {
            if (extAntiCSRF == null) {
                extAntiCSRF = (ExtensionAntiCSRF) Control.getSingleton().getExtensionLoader()
                        .getExtension(ExtensionAntiCSRF.NAME);
            }
            if (extAntiCSRF != null) {
                List<AntiCsrfToken> tokens = extAntiCSRF.getTokens(msg);
                AntiCsrfToken antiCsrfToken = null;
                if (tokens.size() > 0) {
                    antiCsrfToken = tokens.get(0);
                }

                if (antiCsrfToken != null) {
                    regenerateAntiCsrfToken(msg, antiCsrfToken);
                }
            }
        }

        // always get the fresh copy
        msg.getRequestHeader().setHeader(HttpHeader.IF_MODIFIED_SINCE, null);
        msg.getRequestHeader().setHeader(HttpHeader.IF_NONE_MATCH, null);
        msg.getRequestHeader().setContentLength(msg.getRequestBody().length());

        if (this.getDelayInMs() > 0) {
            try {
                Thread.sleep(this.getDelayInMs());
            } catch (InterruptedException e) {
                // Ignore
            }
        }

        //ZAP: Runs the "beforeScan" methods of any ScannerHooks
        parent.performScannerHookBeforeScan(msg, this);

        parent.getHttpSender().sendAndReceive(msg, isFollowRedirect);

        // ZAP: Notify parent
        parent.notifyNewMessage(msg);

        //ZAP: Set the history reference back and run the "afterScan" methods of any ScannerHooks
        parent.performScannerHookAfterScan(msg, this);
    }

    private void regenerateAntiCsrfToken(HttpMessage msg, AntiCsrfToken antiCsrfToken) {
        if (antiCsrfToken == null) {
            return;
        }

        String tokenValue = null;
        try {
            HttpMessage tokenMsg = antiCsrfToken.getMsg().cloneAll();

            // Ensure we dont loop
            sendAndReceive(tokenMsg, true, false);

            tokenValue = extAntiCSRF.getTokenValue(tokenMsg, antiCsrfToken.getName());

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        if (tokenValue != null) {
            // Replace token value - only supported in the body right now
            log.debug("regenerateAntiCsrfToken replacing " + antiCsrfToken.getValue() + " with "
                    + encoder.getURLEncode(tokenValue));
            String replaced = msg.getRequestBody().toString();
            replaced = replaced.replace(encoder.getURLEncode(antiCsrfToken.getValue()),
                    encoder.getURLEncode(tokenValue));
            msg.setRequestBody(replaced);
            extAntiCSRF.registerAntiCsrfToken(
                    new AntiCsrfToken(msg, antiCsrfToken.getName(), tokenValue, antiCsrfToken.getFormIndex()));
        }
    }

    @Override
    public void run() {
        // ZAP : set skipped to false otherwise the plugin shoud stop continously
        //this.skipped = false;

        try {
            if (!isStop()) {
                this.started = new Date();
                scan();
            }

        } catch (Exception e) {
            getLog().error(e.getMessage(), e);
        }

        notifyPluginCompleted(getParent());
        this.finished = new Date();
    }

    /**
     * The core scan method to be implmented by subclass.
     */
    @Override
    public abstract void scan();

    /**
     * Generate an alert when a security issue (risk/info) is found. Default
     * name, description, solution of this Plugin will be used.
     *
     * @param risk
     * @param confidence
     * @param uri
     * @param param
     * @param attack
     * @param otherInfo
     * @param msg
     */
    protected void bingo(int risk, int confidence, String uri, String param, String attack, String otherInfo,
            HttpMessage msg) {

        bingo(risk, confidence, this.getName(), this.getDescription(), uri, param, attack, otherInfo,
                this.getSolution(), msg);
    }

    /**
     * Generate an alert when a security issue (risk/info) is found. Custome
     * alert name, description and solution will be used.
     *
     * @param risk
     * @param confidence
     * @param name
     * @param description
     * @param uri
     * @param param
     * @param attack
     * @param otherInfo
     * @param solution
     * @param msg
     */
    protected void bingo(int risk, int confidence, String name, String description, String uri, String param,
            String attack, String otherInfo, String solution, HttpMessage msg) {

        log.debug("New alert pluginid=" + +this.getId() + " " + name + " uri=" + uri);

        Alert alert = new Alert(this.getId(), risk, confidence, name);
        if (uri == null || uri.equals("")) {
            uri = msg.getRequestHeader().getURI().toString();
        }

        if (param == null) {
            param = "";
        }

        alert.setDetail(description, uri, param, attack, otherInfo, solution, this.getReference(), "",
                this.getCweId(), this.getWascId(), msg);

        parent.alertFound(alert);
    }

    /**
     * Generate an alert when a security issue (risk/info) is found. Default
     * name, description, solution of this Plugin will be used.
     *
     * @param risk
     * @param confidence
     * @param uri
     * @param param
     * @param attack
     * @param otherInfo
     * @param evidence
     * @param msg
     */
    protected void bingo(int risk, int confidence, String uri, String param, String attack, String otherInfo,
            String evidence, HttpMessage msg) {

        bingo(risk, confidence, this.getName(), this.getDescription(), uri, param, attack, otherInfo,
                this.getSolution(), evidence, msg);
    }

    /**
     * Generate an alert when a security issue (risk/info) is found. Custome
     * alert name, description and solution will be used.
     *
     * @param risk
     * @param confidence
     * @param name
     * @param description
     * @param uri
     * @param param
     * @param attack
     * @param otherInfo
     * @param solution
     * @param evidence
     * @param msg
     */
    protected void bingo(int risk, int confidence, String name, String description, String uri, String param,
            String attack, String otherInfo, String solution, String evidence, HttpMessage msg) {

        log.debug("New alert pluginid=" + +this.getId() + " " + name + " uri=" + uri);
        Alert alert = new Alert(this.getId(), risk, confidence, name);
        if (uri == null || uri.equals("")) {
            uri = msg.getRequestHeader().getURI().toString();
        }

        if (param == null) {
            param = "";
        }

        alert.setDetail(description, uri, param, attack, otherInfo, solution, this.getReference(), evidence,
                this.getCweId(), this.getWascId(), msg);

        parent.alertFound(alert);
    }

    protected void bingo(int risk, int confidence, String name, String description, String uri, String param,
            String attack, String otherInfo, String solution, String evidence, int cweId, int wascId,
            HttpMessage msg) {

        log.debug("New alert pluginid=" + +this.getId() + " " + name + " uri=" + uri);
        Alert alert = new Alert(this.getId(), risk, confidence, name);

        if (uri == null || uri.equals("")) {
            uri = msg.getRequestHeader().getURI().toString();
        }

        if (param == null) {
            param = "";
        }

        alert.setDetail(description, uri, param, attack, otherInfo, solution, this.getReference(), evidence, cweId,
                wascId, msg);

        parent.alertFound(alert);
    }

    /**
     * Check i
     *
     * @param msg
     * @return
     */
    protected boolean isFileExist(HttpMessage msg) {
        return parent.getAnalyser().isFileExist(msg);
    }

    /**
     * Check if this test should be stopped. It should be checked periodically
     * in Plugin (eg when in loops) so the HostProcess can stop this Plugin
     * cleanly.
     *
     * @return
     */
    protected boolean isStop() {
        // ZAP: added skipping controls
        return parent.isStop() || parent.isSkipped(this);
    }

    /**
     * @return Returns if this test is enabled.
     */
    @Override
    public boolean isEnabled() {
        return enabled;

    }

    @Override
    public boolean isVisible() {
        return true;
    }

    /**
     * Enable this test
     */
    @Override
    public void setEnabled(boolean enabled) {
        if (this.enabled != enabled) {
            this.enabled = enabled;
            setProperty("enabled", Boolean.toString(enabled));
            if (enabled && getAlertThreshold() == AlertThreshold.OFF) {
                setAlertThreshold(AlertThreshold.DEFAULT);
            }
        }
    }

    @Override
    public AlertThreshold getAlertThreshold() {
        return this.getAlertThreshold(false);
    }

    @Override
    public AlertThreshold getAlertThreshold(boolean incDefault) {
        AlertThreshold level = null;
        try {
            level = AlertThreshold.valueOf(getProperty("level"));
            //log.debug("getAlertThreshold from configs: " + level.name());

        } catch (Exception e) {
            // Ignore
        }

        if (level == null) {
            if (this.isEnabled()) {
                if (incDefault) {
                    level = AlertThreshold.DEFAULT;

                } else {
                    level = defaultAttackThreshold;
                }

                //log.debug("getAlertThreshold default: " + level.name());

            } else {
                level = AlertThreshold.OFF;
                //log.debug("getAlertThreshold not enabled: " + level.name());
            }

        } else if (level.equals(AlertThreshold.DEFAULT)) {
            if (incDefault) {
                level = AlertThreshold.DEFAULT;

            } else {
                level = defaultAttackThreshold;
            }

            //log.debug("getAlertThreshold default: " + level.name());
        }

        return level;
    }

    @Override
    public void setAlertThreshold(AlertThreshold level) {
        setProperty("level", level.name());
        setEnabled(level != AlertThreshold.OFF);
    }

    @Override
    public void setDefaultAlertThreshold(AlertThreshold level) {
        this.defaultAttackThreshold = level;
    }

    /**
     * Override this if you plugin supports other levels.
     */
    @Override
    public AlertThreshold[] getAlertThresholdsSupported() {
        return alertThresholdsSupported;
    }

    @Override
    public AttackStrength getAttackStrength(boolean incDefault) {
        AttackStrength level = null;
        try {
            level = AttackStrength.valueOf(getProperty("strength"));
            //log.debug("getAttackStrength from configs: " + level.name());

        } catch (Exception e) {
            // Ignore
        }

        if (level == null) {
            if (incDefault) {
                level = AttackStrength.DEFAULT;

            } else {
                level = this.defaultAttackStrength;
            }

            //log.debug("getAttackStrength default: " + level.name());

        } else if (level.equals(AttackStrength.DEFAULT)) {
            if (incDefault) {
                level = AttackStrength.DEFAULT;

            } else {
                level = this.defaultAttackStrength;
            }

            //log.debug("getAttackStrength default: " + level.name());
        }

        return level;
    }

    @Override
    public AttackStrength getAttackStrength() {
        return this.getAttackStrength(false);
    }

    @Override
    public void setAttackStrength(AttackStrength level) {
        setProperty("strength", level.name());
    }

    @Override
    public void setDefaultAttackStrength(AttackStrength strength) {
        this.defaultAttackStrength = strength;
    }

    /**
     * Override this if you plugin supports other levels.
     */
    @Override
    public AttackStrength[] getAttackStrengthsSupported() {
        return attackStrengthsSupported;
    }

    /**
     * Compare if 2 plugin is the same.
     */
    @Override
    public int compareTo(Object obj) {
        int result = -1;
        if (obj instanceof AbstractPlugin) {
            AbstractPlugin test = (AbstractPlugin) obj;
            if (getId() < test.getId()) {
                result = -1;

            } else if (getId() > test.getId()) {
                result = 1;

            } else {
                result = 0;
            }
        }

        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (compareTo(obj) == 0) {
            return true;
        }

        return false;
    }

    /**
     * Check if the given pattern can be found in the header.
     *
     * @param msg
     * @param header name.
     * @param pattern
     * @return true if the pattern can be found.
     */
    protected boolean matchHeaderPattern(HttpMessage msg, String header, Pattern pattern) {
        if (msg.getResponseHeader().isEmpty()) {
            return false;
        }

        String val = msg.getResponseHeader().getHeader(header);
        if (val == null) {
            return false;
        }

        Matcher matcher = pattern.matcher(val);
        return matcher.find();
    }

    /**
     * Check if the given pattern can be found in the msg body. If the supplied
     * StringBuilder is not null, append the result to the StringBuilder.
     *
     * @param msg
     * @param pattern
     * @param sb
     * @return true if the pattern can be found.
     */
    protected boolean matchBodyPattern(HttpMessage msg, Pattern pattern, StringBuilder sb) { // ZAP: Changed the type of the parameter "sb" to StringBuilder.
        Matcher matcher = pattern.matcher(msg.getResponseBody().toString());
        boolean result = matcher.find();
        if (result) {
            if (sb != null) {
                sb.append(matcher.group());
            }
        }
        return result;
    }

    /**
     * Write a progress update message. Currently this just display in
     * System.out
     *
     * @param msg
     */
    protected void writeProgress(String msg) {
        //System.out.println(msg);
    }

    /**
     * Get the parent HostProcess.
     *
     * @return
     */
    //ZAP: Changed from protected to public access modifier.
    public HostProcess getParent() {
        return parent;
    }

    @Override
    public abstract void notifyPluginCompleted(HostProcess parent);

    /**
     * Replace body by stripping of pattern string. The URLencoded and
     * URLdecoded pattern will also be stripped off. This is mainly used for
     * stripping off a testing string in HTTP response for comparison against
     * the original response. Reference: TestInjectionSQL
     *
     * @param body
     * @param pattern
     * @return
     */
    protected String stripOff(String body, String pattern) {
        String urlEncodePattern = getURLEncode(pattern);
        String urlDecodePattern = getURLDecode(pattern);
        String htmlEncodePattern1 = getHTMLEncode(pattern);
        String htmlEncodePattern2 = getHTMLEncode(urlEncodePattern);
        String htmlEncodePattern3 = getHTMLEncode(urlDecodePattern);
        String result = body.replaceAll("\\Q" + pattern + "\\E", "")
                .replaceAll("\\Q" + urlEncodePattern + "\\E", "").replaceAll("\\Q" + urlDecodePattern + "\\E", "");
        result = result.replaceAll("\\Q" + htmlEncodePattern1 + "\\E", "")
                .replaceAll("\\Q" + htmlEncodePattern2 + "\\E", "")
                .replaceAll("\\Q" + htmlEncodePattern3 + "\\E", "");
        return result;
    }

    public static String getURLEncode(String msg) {
        String result = "";
        try {
            result = URLEncoder.encode(msg, "UTF8");

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return result;
    }

    public static String getURLDecode(String msg) {
        String result = "";
        try {
            result = URLDecoder.decode(msg, "UTF8");

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return result;
    }

    public static String getHTMLEncode(String msg) {
        String result = msg.replaceAll("<", "&#60;");
        result = result.replaceAll(">", "&#62;");
        return result;
    }

    protected Kb getKb() {
        return getParent().getKb();
    }

    protected Logger getLog() {
        return log;
    }

    public String getProperty(String key) {
        return this.getProperty(config, key);
    }

    private String getProperty(Configuration conf, String key) {
        return conf.getString("plugins." + "p" + getId() + "." + key);
    }

    public void setProperty(String key, String value) {
        this.setProperty(config, key, value);
    }

    private void setProperty(Configuration conf, String key, String value) {
        conf.setProperty("plugins." + "p" + getId() + "." + key, value);
    }

    @Override
    public void setConfig(Configuration config) {
        this.config = config;
    }

    @Override
    public Configuration getConfig() {
        return config;
    }

    @Override
    public void saveTo(Configuration conf) {
        setProperty(conf, "enabled", Boolean.toString(enabled));
        setProperty(conf, "level", getProperty("level"));
        setProperty(conf, "strength", getProperty("strength"));
    }

    @Override
    public void loadFrom(Configuration conf) {
        setProperty("level", getProperty(conf, "level"));
        setProperty("strength", getProperty(conf, "strength"));
        String enabledProperty = getProperty(conf, "enabled");
        if (enabledProperty != null) {
            enabled = Boolean.parseBoolean(enabledProperty);
        } else {
            enabled = getAlertThreshold() != AlertThreshold.OFF;
            enabledProperty = Boolean.toString(enabled);
        }
        setProperty("enabled", enabledProperty);
    }

    @Override
    public void cloneInto(Plugin plugin) {
        if (plugin instanceof AbstractPlugin) {
            AbstractPlugin ap = (AbstractPlugin) plugin;
            ap.setAlertThreshold(this.getAlertThreshold(true));
            ap.setEnabled(this.isEnabled());
            ap.setAttackStrength(this.getAttackStrength(true));
            ap.setDefaultAlertThreshold(this.defaultAttackThreshold);
            ap.setDefaultAttackStrength(this.defaultAttackStrength);
            ap.setTechSet(this.getTechSet());
            ap.setStatus(this.getStatus());
            ap.saveTo(plugin.getConfig());
        } else {
            throw new InvalidParameterException("Not an AbstractPlugin");
        }
    }

    /**
     * Check and create necessary parameter in config file if not already
     * present.
     *
     */
    @Override
    public void createParamIfNotExist() {
        if (getProperty("enabled") == null) {
            setEnabled(getAlertThreshold() != AlertThreshold.OFF);
        }
    }

    // ZAP Added isDepreciated
    @Override
    public boolean isDepreciated() {
        return false;
    }

    /**
     * @since 2.2.0
     */
    @Override
    public int getRisk() {
        return Alert.RISK_MEDIUM;
    }

    @Override
    public int getDelayInMs() {
        return delayInMs;
    }

    @Override
    public void setDelayInMs(int delayInMs) {
        this.delayInMs = delayInMs;
    }

    @Override
    public boolean inScope(Tech tech) {
        return this.techSet == null || this.techSet.includes(tech);
    }

    @Override
    public void setTechSet(TechSet ts) {
        this.techSet = ts;
    }

    /**
     * Returns the technologies enabled for the scan.
     *
     * @return a {@code TechSet} with the technologies enabled for the scan.
     * @see #inScope(Tech)
     * @see #targets(TechSet)
     */
    public TechSet getTechSet() {
        return this.techSet;
    }

    /**
     * Returns {@code true} by default.
     * 
     * @see #getTechSet()
     */
    @Override
    public boolean targets(TechSet technologies) {
        return true;
    }

    @Override
    public Date getTimeStarted() {
        return this.started;
    }

    @Override
    public Date getTimeFinished() {
        return this.finished;
    }

    @Override
    public void setTimeStarted() {
        this.started = new Date();
        this.finished = null;
    }

    @Override
    public void setTimeFinished() {
        this.finished = new Date();
    }

    @Override
    public int getCweId() {
        // Default 'unknown' value
        return 0;
    }

    @Override
    public int getWascId() {
        // Default 'unknown' value
        return 0;
    }

    @Override
    public AddOn.Status getStatus() {
        return status;
    }

    public void setStatus(AddOn.Status status) {
        this.status = status;
    }

}