org.zaproxy.zap.extension.ascan.ExtensionActiveScan Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zap Show documentation
Show all versions of zap Show documentation
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.
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2010 The ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.zap.extension.ascan;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.KeyEvent;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.security.InvalidParameterException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.control.Control.Mode;
import org.parosproxy.paros.core.scanner.ScannerParam;
import org.parosproxy.paros.extension.CommandLineArgument;
import org.parosproxy.paros.extension.CommandLineListener;
import org.parosproxy.paros.extension.Extension;
import org.parosproxy.paros.extension.ExtensionAdaptor;
import org.parosproxy.paros.extension.ExtensionHook;
import org.parosproxy.paros.extension.SessionChangedListener;
import org.parosproxy.paros.extension.history.ProxyListenerLog;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.Session;
import org.parosproxy.paros.model.SiteNode;
import org.parosproxy.paros.view.AbstractParamPanel;
import org.zaproxy.zap.extension.alert.ExtensionAlert;
import org.zaproxy.zap.extension.help.ExtensionHelp;
import org.zaproxy.zap.extension.script.ExtensionScript;
import org.zaproxy.zap.extension.script.ScriptType;
import org.zaproxy.zap.model.ScanController;
import org.zaproxy.zap.model.StructuralNode;
import org.zaproxy.zap.model.StructuralSiteNode;
import org.zaproxy.zap.model.Target;
import org.zaproxy.zap.users.User;
import org.zaproxy.zap.utils.Stats;
import org.zaproxy.zap.view.ZapMenuItem;
public class ExtensionActiveScan extends ExtensionAdaptor
implements SessionChangedListener, CommandLineListener, ScanController {
private static final Logger LOGGER = LogManager.getLogger(ExtensionActiveScan.class);
private static final int ARG_SCAN_IDX = 0;
public static final String NAME = "ExtensionActiveScan";
public static final String SCRIPT_TYPE_ACTIVE = "active";
public static final String SCRIPT_TYPE_VARIANT = "variant";
// Could be after the last one that saves the HttpMessage, as this ProxyListener doesn't change
// the HttpMessage.
public static final int PROXY_LISTENER_ORDER = ProxyListenerLog.PROXY_LISTENER_ORDER + 1;
private static final List> DEPENDENCIES;
private AttackModeScanner attackModeScanner;
private ActiveScanController ascanController = null;
private boolean panelSwitch = true;
static {
List> dep = new ArrayList<>(1);
dep.add(ExtensionAlert.class);
DEPENDENCIES = Collections.unmodifiableList(dep);
}
private ZapMenuItem menuItemPolicy = null;
private ZapMenuItem menuItemCustomScan = null;
private PopupMenuActiveScanCustomWithContext popupMenuActiveScanCustomWithContext;
private OptionsScannerPanel optionsScannerPanel = null;
private OptionsVariantPanel optionsVariantPanel = null;
private ActiveScanPanel activeScanPanel = null;
private ScannerParam scannerParam = null;
private final CommandLineArgument[] arguments = new CommandLineArgument[1];
private final List policyPanels = new ArrayList<>();
private JButton policyButton = null;
private CustomScanDialog customScanDialog = null;
private PolicyManagerDialog policyManagerDialog = null;
private PolicyManager policyManager = null;
private List customScanPanels = new ArrayList<>();
private List excludeList = Collections.emptyList();
private ActiveScanAPI activeScanApi;
public ExtensionActiveScan() {
super(NAME);
this.setOrder(28);
policyManager = new PolicyManager(this);
ascanController = new ActiveScanController(this);
}
@Override
public String getUIName() {
return Constant.messages.getString("ascan.name");
}
@Override
public void postInit() {
policyManager.init();
if (Control.getSingleton().getMode().equals(Mode.attack)) {
if (hasView() && !this.getScannerParam().isAllowAttackOnStart()) {
// Disable attack mode for safeties sake (when running with the UI)
getView().getMainFrame().getMainToolbarPanel().setMode(Mode.standard);
} else {
// Needed to make sure the attackModeScanner starts up
this.attackModeScanner.sessionModeChanged(Control.getSingleton().getMode());
}
}
}
@Override
public void hook(ExtensionHook extensionHook) {
super.hook(extensionHook);
attackModeScanner = new AttackModeScanner(this);
if (getView() != null) {
extensionHook.getHookMenu().addAnalyseMenuItem(getMenuItemPolicy());
extensionHook.getHookMenu().addToolsMenuItem(getMenuItemCustomScan());
extensionHook.getHookMenu().addPopupMenuItem(getPopupMenuActiveScanCustomWithContext());
extensionHook.getHookView().addStatusPanel(getActiveScanPanel());
extensionHook.getHookView().addOptionPanel(getOptionsScannerPanel());
extensionHook.getHookView().addOptionPanel(getOptionsVariantPanel());
extensionHook.getHookView().addMainToolBarComponent(this.getPolicyButton());
getView()
.getMainFrame()
.getMainFooterPanel()
.addFooterToolbarRightLabel(attackModeScanner.getScanStatus().getCountLabel());
ExtensionHelp.enableHelpKey(getActiveScanPanel(), "ui.tabs.ascan");
}
extensionHook.addSessionListener(this);
extensionHook.addOptionsParamSet(getScannerParam());
// TODO this isn't currently implemented
// extensionHook.addCommandLine(getCommandLineArguments());
ExtensionScript extScript =
Control.getSingleton().getExtensionLoader().getExtension(ExtensionScript.class);
if (extScript != null) {
extScript.registerScriptType(
new ScriptType(
SCRIPT_TYPE_ACTIVE,
"ascan.scripts.type.active",
createIcon("script-ascan.png"),
true));
extScript.registerScriptType(
new ScriptType(
SCRIPT_TYPE_VARIANT,
"variant.scripts.type.variant",
createIcon("script-variant.png"),
true));
}
this.ascanController.setExtAlert(
Control.getSingleton().getExtensionLoader().getExtension(ExtensionAlert.class));
this.activeScanApi = new ActiveScanAPI(this);
this.activeScanApi.addApiOptions(getScannerParam());
extensionHook.addApiImplementor(activeScanApi);
String deprecationDesc = Constant.messages.getString("ascan.api.delay.deprecated");
activeScanApi.getApiView("optionDelayInMs").setDeprecatedDescription(deprecationDesc);
activeScanApi.getApiAction("setOptionDelayInMs").setDeprecatedDescription(deprecationDesc);
}
private ImageIcon createIcon(String iconName) {
if (getView() == null) {
return null;
}
return new ImageIcon(
ExtensionActiveScan.class.getResource("/resource/icon/16/" + iconName));
}
@Override
public List getActiveActions() {
List activeScans = ascanController.getActiveScans();
if (activeScans.isEmpty()) {
return null;
}
String activeActionPrefix = Constant.messages.getString("ascan.activeActionPrefix");
List activeActions = new ArrayList<>(activeScans.size());
for (ActiveScan activeScan : activeScans) {
if (activeScan instanceof AttackScan && ((AttackScan) activeScan).isDone()) {
continue;
}
activeActions.add(
MessageFormat.format(activeActionPrefix, activeScan.getDisplayName()));
}
return activeActions;
}
private ActiveScanPanel getActiveScanPanel() {
if (activeScanPanel == null) {
activeScanPanel = new ActiveScanPanel(this);
}
return activeScanPanel;
}
public void startScanAllInScope() {
SiteNode snroot = Model.getSingleton().getSession().getSiteTree().getRoot();
this.startScan(new Target(snroot, null, true, true));
}
/**
* Start the scanning process beginning to a specific node
*
* @param startNode the start node where the scanning should begin to work
* @return the ID of the scan
*/
public int startScan(SiteNode startNode) {
return this.startScan(new Target(startNode, true));
}
public int startScanNode(SiteNode startNode) {
return this.startScan(new Target(startNode, false));
}
public int startScan(Target target) {
return this.startScan(target, null, null);
}
public int startScan(Target target, User user, Object[] contextSpecificObjects) {
return this.startScan(target.getDisplayName(), target, user, contextSpecificObjects);
}
@Override
public int startScan(String name, Target target, User user, Object[] contextSpecificObjects) {
if (name == null) {
name = target.getDisplayName();
}
switch (Control.getSingleton().getMode()) {
case safe:
throw new InvalidParameterException("Scans are not allowed in Safe mode");
case protect:
List nodes = target.getStartNodes();
if (nodes != null) {
for (StructuralNode node : nodes) {
if (node instanceof StructuralSiteNode) {
SiteNode siteNode = ((StructuralSiteNode) node).getSiteNode();
if (!siteNode.isIncludedInScope()) {
throw new InvalidParameterException(
"Scans are not allowed on nodes not in scope Protected mode "
+ target.getStartNode().getHierarchicNodeName());
}
}
}
}
// No problem
break;
case standard:
// No problem
break;
case attack:
// No problem
break;
}
int id = this.ascanController.startScan(name, target, user, contextSpecificObjects);
if (hasView()) {
ActiveScan scanner = this.ascanController.getScan(id);
scanner.addScannerListener(getActiveScanPanel()); // So the UI get updated
this.getActiveScanPanel().scannerStarted(scanner);
this.getActiveScanPanel().switchView(scanner);
if (isPanelSwitch()) {
this.getActiveScanPanel().setTabFocus();
}
}
return id;
}
/**
* Returns true if the GUI will switch to the Active Scan panel when a scan is started.
*
* @since 2.11.0
*/
public boolean isPanelSwitch() {
return panelSwitch;
}
/**
* Sets if the GUI will switch to the Active Scan panel when a scan is started. Code should only
* set this to false just before starting a scan and reset it to true as soon as the scan has
* started.
*
* @since 2.11.0
*/
public void setPanelSwitch(boolean panelSwitch) {
this.panelSwitch = panelSwitch;
}
private JButton getPolicyButton() {
if (policyButton == null) {
policyButton = new JButton();
policyButton.setIcon(
new ImageIcon(
ActiveScanPanel.class.getResource(
"/resource/icon/fugue/equalizer.png")));
policyButton.setToolTipText(Constant.messages.getString("menu.analyse.scanPolicy"));
policyButton.addActionListener(
new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
showPolicyManagerDialog();
Stats.incCounter("stats.ui.maintoolbar.button.ascan.policy");
}
});
}
return policyButton;
}
/**
* This method initializes menuItemPolicy
*
* @return javax.swing.JMenuItem
*/
private ZapMenuItem getMenuItemPolicy() {
if (menuItemPolicy == null) {
menuItemPolicy =
new ZapMenuItem(
"menu.analyse.scanPolicy",
getView().getMenuShortcutKeyStroke(KeyEvent.VK_P, 0, false));
menuItemPolicy.addActionListener(
new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
showPolicyManagerDialog();
}
});
}
return menuItemPolicy;
}
protected void showPolicyDialog(PolicyManagerDialog parent) throws ConfigurationException {
this.showPolicyDialog(parent, null);
}
protected void showPolicyDialog(PolicyManagerDialog parent, String name)
throws ConfigurationException {
ScanPolicy policy;
if (name != null) {
policy = this.getPolicyManager().getPolicy(name);
} else {
policy = this.getPolicyManager().getTemplatePolicy();
}
PolicyDialog dialog = new PolicyDialog(this, parent, policy);
dialog.initParam(getModel().getOptionsParam());
for (AbstractParamPanel panel : policyPanels) {
dialog.addPolicyPanel(panel);
}
int result = dialog.showDialog(true);
if (result == JOptionPane.OK_OPTION) {
try {
getModel().getOptionsParam().getConfig().save();
} catch (ConfigurationException ce) {
LOGGER.error(ce.getMessage(), ce);
getView().showWarningDialog(Constant.messages.getString("scanner.save.warning"));
}
}
}
private ZapMenuItem getMenuItemCustomScan() {
if (menuItemCustomScan == null) {
menuItemCustomScan =
new ZapMenuItem(
"menu.tools.ascanadv",
getView()
.getMenuShortcutKeyStroke(
KeyEvent.VK_A, KeyEvent.ALT_DOWN_MASK, false));
menuItemCustomScan.setEnabled(Control.getSingleton().getMode() != Mode.safe);
menuItemCustomScan.addActionListener(e -> showCustomScanDialog((Target) null));
}
return menuItemCustomScan;
}
private PopupMenuActiveScanCustomWithContext getPopupMenuActiveScanCustomWithContext() {
if (popupMenuActiveScanCustomWithContext == null) {
popupMenuActiveScanCustomWithContext = new PopupMenuActiveScanCustomWithContext(this);
}
return popupMenuActiveScanCustomWithContext;
}
@Override
public void sessionChanged(final Session session) {
if (EventQueue.isDispatchThread()) {
sessionChangedEventHandler(session);
} else {
try {
EventQueue.invokeAndWait(
new Runnable() {
@Override
public void run() {
sessionChangedEventHandler(session);
}
});
} catch (InterruptedException | InvocationTargetException e) {
LOGGER.error(e.getMessage(), e);
}
}
}
private void sessionChangedEventHandler(Session session) {
// The scans are stopped in sessionAboutToChange(..)
if (hasView()) {
this.getActiveScanPanel().reset();
}
this.attackModeScanner.stop();
if (session == null) {
// Closedown
return;
}
if (Control.getSingleton().getMode().equals(Mode.attack)) {
// Start the attack mode scanner up again, have to rescan on change or it wont do
// anything
this.attackModeScanner.start();
this.attackModeScanner.setRescanOnChange(true);
}
}
/**
* This method initializes optionsScannerPanel
*
* @return org.parosproxy.paros.extension.scanner.OptionsScannerPanel
*/
private OptionsScannerPanel getOptionsScannerPanel() {
if (optionsScannerPanel == null) {
optionsScannerPanel = new OptionsScannerPanel(this);
}
return optionsScannerPanel;
}
/**
* This method initializes optionsVariantPanel
*
* @return org.zaproxy.zap.extension.ascan.OptionsVariantPanel
*/
private OptionsVariantPanel getOptionsVariantPanel() {
if (optionsVariantPanel == null) {
optionsVariantPanel = new OptionsVariantPanel();
}
return optionsVariantPanel;
}
/**
* This method initializes scannerParam
*
* @return org.parosproxy.paros.core.scanner.ScannerParam
*/
protected ScannerParam getScannerParam() {
if (scannerParam == null) {
scannerParam = new ScannerParam();
}
return scannerParam;
}
// TODO
@Override
public void execute(CommandLineArgument[] args) {
/*
if (arguments[ARG_SCAN_IDX].isEnabled()) {
System.out.println("Scanner started...");
startScan();
} else {
return;
}
while (!getScanner().isStop()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
System.out.println("Scanner completed.");
*/
}
@SuppressWarnings("unused")
private CommandLineArgument[] getCommandLineArguments() {
arguments[ARG_SCAN_IDX] =
new CommandLineArgument(
"-scan",
0,
null,
"",
"-scan : Run vulnerability scan depending on previously saved policy.");
return arguments;
}
/**
* Sets the exclude list.
*
* @param urls the new exclude list
*/
public void setExcludeList(List urls) {
if (urls == null || urls.isEmpty()) {
excludeList = Collections.emptyList();
return;
}
this.excludeList = urls;
}
/**
* Gets the exclude list.
*
* @return the exclude list
*/
public List getExcludeList() {
return excludeList;
}
public void addPolicyPanel(AbstractParamPanel panel) {
this.policyPanels.add(panel);
}
@Override
public List> getDependencies() {
return DEPENDENCIES;
}
@Override
public void sessionAboutToChange(final Session session) {
// Stop, remove all scans and reset the scan id counter
this.ascanController.reset();
this.attackModeScanner.stop();
if (hasView()) {
this.getActiveScanPanel().reset();
if (customScanDialog != null) {
customScanDialog.reset();
}
}
}
@Override
public String getAuthor() {
return Constant.ZAP_TEAM;
}
@Override
public String getDescription() {
return Constant.messages.getString("ascan.desc");
}
@Override
public void sessionScopeChanged(Session session) {
if (hasView()) {
this.getActiveScanPanel().sessionScopeChanged(session);
}
this.attackModeScanner.sessionScopeChanged(session);
}
@Override
public void sessionModeChanged(Mode mode) {
if (Mode.safe.equals(mode)) {
this.ascanController.stopAllScans();
}
if (hasView()) {
getMenuItemCustomScan().setEnabled(!Mode.safe.equals(mode));
this.getActiveScanPanel().sessionModeChanged(mode);
}
this.attackModeScanner.sessionModeChanged(mode);
}
@Override
public void destroy() {
this.ascanController.stopAllScans();
if (hasView()) {
this.getActiveScanPanel().reset();
}
}
public void showCustomScanDialog(SiteNode node) {
showCustomScanDialog(node != null ? new Target(node) : null);
}
/**
* Shows the active scan dialogue with the given target, if not already visible.
*
* @param target the target, might be {@code null}.
* @since 2.8.0.
*/
public void showCustomScanDialog(Target target) {
if (customScanDialog == null) {
// Work out the tabs
String[] tabs = CustomScanDialog.STD_TAB_LABELS;
if (this.customScanPanels.size() > 0) {
List tabList = new ArrayList<>();
for (String str : CustomScanDialog.STD_TAB_LABELS) {
tabList.add(str);
}
for (CustomScanPanel csp : customScanPanels) {
tabList.add(csp.getLabel());
}
tabs = tabList.toArray(new String[tabList.size()]);
}
customScanDialog =
new CustomScanDialog(
this,
tabs,
this.customScanPanels,
getView().getMainFrame(),
new Dimension(700, 500));
}
if (customScanDialog.isVisible()) {
customScanDialog.requestFocus();
// Its behind you! Actually not needed no the window is alwaysOnTop, but keeping in case
// we change that ;)
customScanDialog.toFront();
return;
}
if (target != null) {
customScanDialog.init(target);
} else {
// Keep the previously selected target
customScanDialog.init(null);
}
customScanDialog.setVisible(true);
}
public void addCustomScanPanel(CustomScanPanel panel) {
this.customScanPanels.add(panel);
customScanDialog = null; // Force it to be reinitialised
}
public void removeCustomScanPanel(CustomScanPanel panel) {
this.customScanPanels.remove(panel);
customScanDialog = null; // Force it to be reinitialised
}
public void showPolicyManagerDialog() {
if (policyManagerDialog == null) {
policyManagerDialog = new PolicyManagerDialog(getView().getMainFrame());
policyManagerDialog.init(this);
}
// The policy names _may_ have changed, e.g. via the api
policyManagerDialog.policyNamesChanged();
policyManagerDialog.setVisible(true);
}
@Override
public boolean handleFile(File file) {
// Cant handle any files
return false;
}
@Override
public List getHandledExtensions() {
// Cant handle any extensions
return null;
}
@Override
public List getAllScans() {
return ascanController.getAllScans();
}
@Override
public List getActiveScans() {
return ascanController.getActiveScans();
}
@Override
public ActiveScan getScan(int id) {
return ascanController.getScan(id);
}
@Override
public void stopScan(int id) {
ascanController.stopScan(id);
// Dont need to update the UI - this will happen automatically via the events
}
@Override
public void pauseScan(int id) {
ascanController.pauseScan(id);
if (hasView()) {
// Update the UI in case this was initiated from the API
this.getActiveScanPanel().updateScannerUI();
}
}
@Override
public void resumeScan(int id) {
ascanController.resumeScan(id);
if (hasView()) {
// Update the UI in case this was initiated from the API
this.getActiveScanPanel().updateScannerUI();
}
}
@Override
public void stopAllScans() {
ascanController.stopAllScans();
// Dont need to update the UI - this will happen automatically via the events
}
@Override
public void pauseAllScans() {
ascanController.pauseAllScans();
if (hasView()) {
// Update the UI in case this was initiated from the API
this.getActiveScanPanel().updateScannerUI();
}
}
@Override
public void resumeAllScans() {
ascanController.removeAllScans();
if (hasView()) {
// Update the UI in case this was initiated from the API
this.getActiveScanPanel().updateScannerUI();
}
}
@Override
public ActiveScan removeScan(int id) {
return ascanController.removeScan(id);
}
@Override
public int removeAllScans() {
return ascanController.removeAllScans();
}
@Override
public int removeFinishedScans() {
return ascanController.removeFinishedScans();
}
@Override
public ActiveScan getLastScan() {
return ascanController.getLastScan();
}
public int registerScan(ActiveScan scanner) {
int id = ascanController.registerScan(scanner);
if (hasView()) {
// Update the UI in case this was initiated from the API
scanner.addScannerListener(getActiveScanPanel()); // So the UI get updated
this.getActiveScanPanel().scannerStarted(scanner);
this.getActiveScanPanel().switchView(scanner);
if (isPanelSwitch()) {
this.getActiveScanPanel().setTabFocus();
}
}
return id;
}
public PolicyManager getPolicyManager() {
return policyManager;
}
public int getAttackModeStackSize() {
return this.attackModeScanner.getStackSize();
}
@Override
public boolean supportsLowMemory() {
return true;
}
/** Part of the core set of features that should be supported by all db types */
@Override
public boolean supportsDb(String type) {
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy