All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.parosproxy.paros.core.scanner.PluginFactory Maven / Gradle / Ivy

Go to download

The Zed Attack Proxy (ZAP) is an easy to use integrated penetration testing tool for finding vulnerabilities in web applications. It is designed to be used by people with a wide range of security experience and as such is ideal for developers and functional testers who are new to penetration testing. ZAP provides automated scanners as well as a set of tools that allow you to find security vulnerabilities manually.

There is a newer version: 2.15.0
Show newest version
/*
 *
 * 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/04/23 Changed the method loadAllPlugin to reflect the changes made
// in the method DynamicLoader.getFilteredObject(Class).
// ZAP: 2012/04/25 Removed unnecessary casts, changed to use the method
// Integer.valueOf and added logging of exception.
// ZAP: 2012/11/20 Issue 419: Restructure jar loading code
// ZAP: 2013/01/16 Issue 453: Dynamic loading and unloading of add-ons
// ZAP: 2013/01/19 Issue 460 Add support for a scan progress dialog
// ZAP: 2013/01/25 Catch any exceptions thrown when loading plugins to allow ZAP to still start
// ZAP: 2013/03/18 Issue 564: Active scanner can hang if dependencies used
// ZAP: 2013/05/02 Re-arranged all modifiers into Java coding standard order
// ZAP: 2014/01/16 Added skip support functions and changed obsolete collections
// ZAP: 2014/02/12 Issue 1030: Load and save scan policies
// ZAP: 2014/02/21 Issue 1043: Custom active scan dialog
// ZAP: 2014/05/20 Issue 377: Unfulfilled dependencies hang the active scan
// ZAP: 2014/11/19 Issue 1412: Manage scan policies
// ZAP: 2014/11/19 Issue 1412: Init scan rule status (quality) from add-on
// ZAP: 2015/01/04 Issue 1484: NullPointerException during uninstallation of an add-on with active
// scanners
// ZAP: 2015/01/04 Issue 1486: Add-on components leak
// ZAP: 2015/07/25 Do not log error if the duplicated scanner is (apparently) a newer/older version
// ZAP: 2015/08/19 Issue 1785: Plugin enabled even if dependencies are not, "hangs" active scan
// ZAP: 2015/11/02 Issue 1969: Issues with installation of scanners
// ZAP: 2015/12/21 Issue 2112: Wrong policy on active Scan
// ZAP: 2016/01/26 Fixed findbugs warning
// ZAP: 2016/05/04 Use existing Plugin instances when setting them as completed
// ZAP: 2016/06/27 Reduce log level when loading the plugins
// ZAP: 2016/06/29 Do not log when cloning PluginFactory
// ZAP: 2016/07/25 Fix to correct handling of lists in plugins
// ZAP: 2017/06/20 Allow to obtain a Plugin by ID.
// ZAP: 2017/07/05 Log an error if the Plugin does not have a defined ID.
// ZAP: 2017/07/12 Order dependencies before dependent plugins (Issue 3154) and tweak status
// comparison.
// ZAP: 2017/10/05 Replace usage of Class.newInstance (deprecated in Java 9).
// ZAP: 2018/02/14 Remove unnecessary boxing / unboxing
// ZAP: 2019/06/01 Normalise line endings.
// ZAP: 2019/06/05 Normalise format/style.
package org.parosproxy.paros.core.scanner;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.log4j.Logger;
import org.zaproxy.zap.control.CoreFunctionality;
import org.zaproxy.zap.control.ExtensionFactory;

public class PluginFactory {

    private static Logger log = Logger.getLogger(PluginFactory.class);
    private static List loadedPlugins = null;
    private static Map mapLoadedPlugins;

    private List listAllPlugin = new ArrayList();
    private LinkedHashMap mapAllPlugin =
            new LinkedHashMap<>(); // insertion-ordered
    private LinkedHashMap mapAllPluginOrderCodeName =
            new LinkedHashMap<>(); // insertion-ordered
    private List listPending = new ArrayList();
    private List listRunning = new ArrayList();
    private List listCompleted = new ArrayList();
    private int totalPluginToRun = 0;
    private boolean init = false;
    private Configuration config;

    public PluginFactory() {
        super();
        HierarchicalConfiguration configuration = new HierarchicalConfiguration();
        configuration.setDelimiterParsingDisabled(true);
        config = configuration;
    }

    private static synchronized void initPlugins() {
        if (loadedPlugins == null) {
            init(true);
        }
    }

    // Helper method to ease tests.
    static void init(boolean includeAddOns) {
        loadedPlugins = new ArrayList<>(CoreFunctionality.getBuiltInActiveScanRules());
        if (includeAddOns) {
            loadedPlugins.addAll(ExtensionFactory.getAddOnLoader().getActiveScanRules());
        }
        // sort by the criteria below.
        Collections.sort(loadedPlugins, riskComparator);

        mapLoadedPlugins = new HashMap<>();
        for (Plugin plugin : loadedPlugins) {
            checkPluginId(plugin);
            mapLoadedPlugins.put(plugin.getId(), plugin);
        }
    }

    private static void checkPluginId(Plugin plugin) {
        if (plugin.getId() == -1) {
            log.error(
                    "The active scan rule ["
                            + plugin.getClass().getCanonicalName()
                            + "] does not have a defined ID.");
        }
    }

    /**
     * Gets the {@code Plugin} with the given ID.
     *
     * @param id the ID of the plugin.
     * @return the {@code Plugin}, or {@code null} if not found (e.g. not installed).
     * @since 2.7.0
     */
    public static Plugin getLoadedPlugin(int id) {
        initPlugins();
        return mapLoadedPlugins.get(id);
    }

    private static List getLoadedPlugins() {
        if (loadedPlugins == null) {
            initPlugins();
        }
        return loadedPlugins;
    }

    /**
     * Tells whether or not the given {@code plugin} was already loaded.
     *
     * @param plugin the plugin that will be checked
     * @return {@code true} if the plugin was already loaded, {@code false} otherwise
     * @since 2.4.3
     */
    public static boolean isPluginLoaded(AbstractPlugin plugin) {
        if (loadedPlugins == null) {
            return false;
        }
        return isPluginLoadedImpl(plugin);
    }

    private static boolean isPluginLoadedImpl(AbstractPlugin plugin) {
        for (AbstractPlugin otherPlugin : getLoadedPlugins()) {
            if (otherPlugin == plugin) {
                return true;
            }
        }
        return false;
    }

    /**
     * Adds the given loaded {@code plugin} to the {@code PluginFactory}. Loaded plugins, are used
     * by the active scanner, if enabled.
     *
     * 

Call to this method has not effect it the {@code plugin} was already added. * * @param plugin the plugin that should be loaded * @since 2.4.0 * @see #isPluginLoaded(AbstractPlugin) */ public static void loadedPlugin(AbstractPlugin plugin) { if (!isPluginLoadedImpl(plugin)) { checkPluginId(plugin); getLoadedPlugins().add(plugin); mapLoadedPlugins.put(plugin.getId(), plugin); Collections.sort(loadedPlugins, riskComparator); } } /** * @deprecated (2.4.3) Use {@link #loadedPlugin(AbstractPlugin)} instead, the status of the * scanner is not properly set. * @see AbstractPlugin#getStatus() */ @Deprecated @SuppressWarnings("javadoc") public static boolean loadedPlugin(String className) { try { Class c = ExtensionFactory.getAddOnLoader().loadClass(className); loadedPlugin((AbstractPlugin) c.getDeclaredConstructor().newInstance()); return true; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } public static void unloadedPlugin(AbstractPlugin plugin) { if (loadedPlugins == null) { return; } for (Iterator it = getLoadedPlugins().iterator(); it.hasNext(); ) { if (it.next() == plugin) { it.remove(); mapLoadedPlugins.remove(plugin.getId()); return; } } } /** * @deprecated (2.4.3) Use {@link #unloadedPlugin(AbstractPlugin)} instead, which ensures that * the exact scanner instance is unloaded. */ @Deprecated @SuppressWarnings("javadoc") public static boolean unloadedPlugin(String className) { if (loadedPlugins == null) { return true; } for (AbstractPlugin plugin : loadedPlugins) { if (plugin.getClass().getName().equals(className)) { loadedPlugins.remove(plugin); mapLoadedPlugins.remove(plugin.getId()); return true; } } return false; } // now order the list by the highest risk thrown, in descending order (to execute the more // critical checks first) private static final Comparator riskComparator = new Comparator() { @Override public int compare(AbstractPlugin e1, AbstractPlugin e2) { // Run stable plugins first int res = e1.getStatus().compareTo(e2.getStatus()); if (res != 0) { return -res; } if (e1.getRisk() > e2.getRisk()) { // High Risk alerts are checked before low risk alerts return -1; } else if (e1.getRisk() < e2.getRisk()) { return 1; } else { // need to look at a secondary factor (the Id of the plugin) to decide. Run // older plugins first, followed by newer plugins if (e1.getId() < e2.getId()) { // log numbered (older) plugins are run before newer plugins return -1; } else if (e1.getId() > e2.getId()) { return 1; } else { return 0; } } } }; public void reset() { Iterator iterator; Plugin plugin; synchronized (mapAllPlugin) { this.listPending.clear(); this.listRunning.clear(); this.listCompleted.clear(); // pass 1 - enable all plugin's dependency iterator = mapAllPlugin.values().iterator(); while (iterator.hasNext()) { // ZAP: Removed unnecessary cast. plugin = iterator.next(); if (plugin.isEnabled()) { enableDependency(plugin); } } // pass 2 - put enabled dependency in listPending iterator = mapAllPlugin.values().iterator(); while (iterator.hasNext()) { // ZAP: Removed unnecessary cast. plugin = iterator.next(); if (plugin.isEnabled()) { addAllDependencies(plugin, listPending); if (!listPending.contains(plugin)) { listPending.add(plugin); } } } totalPluginToRun = listPending.size(); } this.init = true; } private void enableDependency(Plugin plugin) { String[] dependency = plugin.getDependency(); if (dependency == null || dependency.length == 0) { return; } List dependencies = new ArrayList<>(dependency.length); if (addAllDependencies(plugin, dependencies)) { for (Plugin dep : dependencies) { if (!dep.isEnabled()) { dep.setEnabled(true); } } } else { plugin.setEnabled(false); plugin.setAlertThreshold(Plugin.AlertThreshold.OFF); log.warn( "Disabled scanner '" + plugin.getName() + "' because of unfulfilled dependencies."); } } public boolean hasAllDependenciesAvailable(Plugin plugin) { List deps = new ArrayList<>(); return addAllDependencies(plugin, deps); } public boolean addAllDependencies(Plugin plugin, List to) { String[] dependencies = plugin.getDependency(); if (dependencies == null || dependencies.length == 0) { return true; } boolean allDepsAvailable = true; List deps = new ArrayList<>(Arrays.asList(dependencies)); for (String dependency : deps) { Plugin pluginDep = mapAllPluginOrderCodeName.get(dependency); if (pluginDep == null) { allDepsAvailable = false; } else if (!to.contains(pluginDep)) { allDepsAvailable &= addAllDependencies(pluginDep, to); to.add(pluginDep); } } return allDepsAvailable; } public List getDependentPlugins(Plugin plugin) { List dependentPlugins = new ArrayList<>(); addDependentPlugins(plugin.getCodeName(), dependentPlugins); return dependentPlugins; } private void addDependentPlugins(String pluginName, List to) { for (Plugin plugin : listAllPlugin) { String[] dependencies = plugin.getDependency(); if (dependencies != null && dependencies.length != 0) { if (Arrays.asList(dependencies).contains(pluginName) && !to.contains(plugin)) { to.add(plugin); addDependentPlugins(plugin.getCodeName(), to); } } } } public List getDependencies(Plugin plugin) { String[] dependencies = plugin.getDependency(); if (dependencies == null || dependencies.length == 0) { return Collections.emptyList(); } List deps = new ArrayList<>(Arrays.asList(dependencies)); List depsPlugins = new ArrayList<>(deps.size()); for (String dependency : deps) { Plugin pluginDep = mapAllPluginOrderCodeName.get(dependency); if (pluginDep != null) { depsPlugins.add(pluginDep); } } return depsPlugins; } /** @param config */ public synchronized void loadAllPlugin(Configuration config) { log.debug("loadAllPlugin"); this.config = config; // mapAllPlugin is ordered by insertion order, so the ordering of plugins in listTest is // used // when mapAllPlugin is iterated synchronized (mapAllPlugin) { mapAllPlugin.clear(); listAllPlugin.clear(); mapAllPluginOrderCodeName.clear(); for (int i = 0; i < getLoadedPlugins().size(); i++) { // ZAP: Removed unnecessary cast. try { Plugin loadedPlugin = getLoadedPlugins().get(i); if (!loadedPlugin.isVisible()) { log.info("Plugin " + loadedPlugin.getName() + " not visible"); continue; } if (loadedPlugin.isDepreciated()) { // ZAP: ignore all depreciated plugins log.info("Plugin " + loadedPlugin.getName() + " deprecated"); continue; } if (!canAddPlugin(mapAllPlugin, loadedPlugin)) { continue; } Plugin plugin = createNewPlugin(loadedPlugin, config); if (log.isDebugEnabled()) { log.debug( "loaded plugin " + plugin.getName() + " with: Threshold=" + plugin.getAlertThreshold().name() + " Strength=" + plugin.getAttackStrength().toString()); } // ZAP: Changed to use the method Integer.valueOf. mapAllPlugin.put(plugin.getId(), plugin); mapAllPluginOrderCodeName.put(plugin.getCodeName(), plugin); } catch (Exception e) { log.error(e.getMessage(), e); } } Iterator iterator = mapAllPlugin.values().iterator(); while (iterator.hasNext()) { listAllPlugin.add(iterator.next()); } } } private static Plugin createNewPlugin(Plugin plugin, Configuration config) throws ReflectiveOperationException { Plugin newPlugin = plugin.getClass().getDeclaredConstructor().newInstance(); newPlugin.setConfig(new BaseConfiguration()); plugin.cloneInto(newPlugin); newPlugin.setConfig(config); newPlugin.createParamIfNotExist(); newPlugin.loadFrom(config); return newPlugin; } private static boolean canAddPlugin(Map plugins, Plugin plugin) { Plugin existingPlugin = plugins.get(plugin.getId()); if (existingPlugin == null) { return true; } // Check if it has also the same name, might be the same scanner but a newer/older version if (existingPlugin.getName().equals(plugin.getName())) { if (existingPlugin.getStatus().compareTo(plugin.getStatus()) > 0) { log.info( "Ignoring (apparently) less stable scanner version, id=" + plugin.getId() + ", ExistingPlugin[Status=" + existingPlugin.getStatus() + ", Class=" + existingPlugin.getClass().getCanonicalName() + "], LessStablePlugin[Status=" + plugin.getStatus() + ", Class=" + plugin.getClass().getCanonicalName() + "]"); return false; } if (existingPlugin.getStatus() != plugin.getStatus()) { log.info( "Replacing existing scanner with (apparently) stabler version, id=" + plugin.getId() + ", ExistingPlugin[Status=" + existingPlugin.getStatus() + ", Class=" + existingPlugin.getClass().getCanonicalName() + "], StablerPlugin[Status=" + plugin.getStatus() + ", Class=" + plugin.getClass().getCanonicalName() + "]"); return true; } } log.error( "Duplicate id " + plugin.getId() + " " + plugin.getClass().getCanonicalName() + " " + existingPlugin.getClass().getCanonicalName()); return true; } public synchronized void loadFrom(PluginFactory pf) { log.debug("loadFrom " + pf.listAllPlugin.size()); for (Plugin plugin : pf.listAllPlugin) { Plugin p = this.mapAllPlugin.get(plugin.getId()); if (p != null) { plugin.cloneInto(p); } } } public List getAllPlugin() { return listAllPlugin; } @Override public PluginFactory clone() { PluginFactory clone = new PluginFactory(); Plugin pluginCopy; for (Plugin plugin : listAllPlugin) { try { pluginCopy = plugin.getClass().getDeclaredConstructor().newInstance(); pluginCopy.setConfig(clone.config); plugin.cloneInto(pluginCopy); clone.addPlugin(pluginCopy); } catch (Exception e) { log.error(e.getMessage(), e); } } return clone; } public boolean addPlugin(String name) { try { Class c = ExtensionFactory.getAddOnLoader().loadClass(name); Plugin plugin = (AbstractPlugin) c.getDeclaredConstructor().newInstance(); boolean duplicatedId = mapAllPlugin.get(plugin.getId()) != null; if (this.addPlugin(plugin)) { log.info("loaded plugin " + plugin.getName()); if (duplicatedId) { log.error( "Duplicate id " + plugin.getName() + " " + mapAllPlugin.get(plugin.getId()).getName()); } return true; } if (!plugin.isVisible()) { log.info("Plugin " + plugin.getName() + " not visible"); return false; } if (plugin.isDepreciated()) { log.info("Plugin " + plugin.getName() + " deprecated"); return false; } return false; } catch (Exception e) { log.error(e.getMessage(), e); return false; } } private boolean addPlugin(Plugin plugin) { listAllPlugin.add(plugin); plugin.setConfig(this.config); plugin.createParamIfNotExist(); if (!plugin.isVisible()) { return false; } if (plugin.isDepreciated()) { return false; } mapAllPlugin.put(plugin.getId(), plugin); mapAllPluginOrderCodeName.put(plugin.getCodeName(), plugin); return true; } public boolean removePlugin(String className) { for (int i = 0; i < listAllPlugin.size(); i++) { Plugin plugin = listAllPlugin.get(i); if (plugin.getClass().getName().equals(className)) { listAllPlugin.remove(plugin); mapAllPlugin.remove(plugin.getId()); mapAllPluginOrderCodeName.remove(plugin.getCodeName()); return true; } } return false; } public Plugin getPlugin(int id) { // ZAP: Removed unnecessary cast and changed to use the method // Integer.valueOf. return mapAllPlugin.get(id); } public void setAllPluginEnabled(boolean enabled) { for (int i = 0; i < listAllPlugin.size(); i++) { // ZAP: Removed unnecessary cast. Plugin plugin = listAllPlugin.get(i); plugin.setEnabled(enabled); } } synchronized boolean existPluginToRun() { if (!init) { this.reset(); } if (probeNextPlugin() != null) { return true; } // no test ready to run. still exist test if due to dependency if (!listPending.isEmpty() || !listRunning.isEmpty()) { return true; } return false; } /** * Get next test ready to be run. Null = none. Test dependent on others will not be obtained. * * @return */ private Plugin probeNextPlugin() { Plugin plugin = null; int i = 0; while (i < listPending.size()) { // ZAP: Removed unnecessary cast. plugin = listPending.get(i); if (isAllDependencyCompleted(plugin)) { return plugin; } i++; } return null; } /** * Get next plugin ready to be run without any dependency outstanding. * * @return new instance of next plugin to be run. */ synchronized Plugin nextPlugin() { if (!init) { this.reset(); } Plugin plugin = probeNextPlugin(); if (plugin == null) { return null; } listPending.remove(plugin); plugin.setTimeStarted(); listRunning.add(plugin); return plugin; } private boolean isAllDependencyCompleted(Plugin plugin) { // note the plugin object checked may not be the exact plugin object stored in the completed // list. // but the comparison is basing on pluginId (see equals method) so it will work. String[] dependency = plugin.getDependency(); if (dependency == null || dependency.length == 0) { return true; } synchronized (listCompleted) { for (int i = 0; i < dependency.length; i++) { boolean isFound = false; for (int j = 0; j < listCompleted.size() && !isFound; j++) { // ZAP: Removed unnecessary cast. Plugin completed = listCompleted.get(j); if (completed.getCodeName().equalsIgnoreCase(dependency[i])) { isFound = true; } } if (!isFound) { return false; } } } return true; } public void saveTo(Configuration conf) throws ConfigurationException { for (Plugin plugin : listAllPlugin) { plugin.saveTo(conf); } } public void loadFrom(Configuration config) throws ConfigurationException { for (Plugin plugin : listAllPlugin) { plugin.loadFrom(config); } } synchronized void setRunningPluginCompleted(Plugin plugin) { if (listRunning.remove(plugin)) { Plugin completedPlugin = mapAllPlugin.get(plugin.getId()); listCompleted.add(completedPlugin); completedPlugin.setTimeFinished(); } } boolean isRunning(Plugin plugin) { return listRunning.contains(plugin); } int totalPluginToRun() { return totalPluginToRun; } int totalPluginCompleted() { return listCompleted.size(); } List getPending() { return this.listPending; } List getRunning() { return this.listRunning; } List getCompleted() { return this.listCompleted; } public int getEnabledPluginCount() { int count = 0; for (Plugin plugin : listAllPlugin) { if (plugin.isEnabled()) { count++; } } return count; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy