org.parosproxy.paros.control.Control 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.
/*
*
* 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/04/16 Support for running ZAP as a daemon
// ZAP: 2011/05/09 Support for API
// ZAP: 2011/05/15 Support for exclusions
// ZAP: 2011/10/29 Support for parameters
// ZAP: 2011/11/20 Changed to use ExtensionFactory
// ZAP: 2012/02/18 Rationalised session handling
// ZAP: 2012/04/23 Changed the method shutdown(boolean) to save the configurations
// of the main http panels and save the configuration file.
// ZAP: 2012/04/23 Added @Override annotation to all appropriate methods.
// ZAP: 2012/05/16 Added new initialization method plus ctor for testing purposes.
// ZAP: 2012/06/25 Moved call of init() from ctor to singleton methods to
// initialize singleton variable first. Allows to retrieve the singleton while
// not fully initialized (e.g.: to get another extension in the hook() method of
// and extension).
// ZAP: 2012/07/29 Issue 43: added sessionScopeChanged event
// ZAP: 2012/08/01 Issue 332: added support for Modes
// ZAP: 2012/11/01 Issue 411: Allow proxy port to be specified on the command line
// ZAP: 2012/12/06 Issue 428: Added exit method to support the marketplace
// ZAP: 2012/12/27 Hook new persistent connection listener.
// ZAP: 2013/03/03 Issue 546: Remove all template Javadoc comments
// ZAP: 2013/03/20 Issue 568: Allow extensions to run from the command line
// ZAP: 2013/04/16 Issue 638: Persist and snapshot sessions instead of saving them
// ZAP: 2013/08/28 Issue 695: Sites tree doesnt clear on new session created by API
// ZAP: 2013/08/29 Issue 776: Allow add-ons to warn user if they're closing ZAP with unsaved resources open
// ZAP: 2013/09/16 Issue 791: Saved sessions are discarded on ZAP's exit
// ZAP: 2014/01/16 Issue 979: Sites and Alerts trees can get corrupted
// ZAP: 2014/02/21 Issue 1043: Custom active scan dialog
// ZAP: 2014/02/27 Issue 1055: Load extensions before plugins
// ZAP: 2014/05/20 Issue 1114: core.newSession doesn't clear Sites
// ZAP: 2014/05/20 Issue 1191: Cmdline session params have no effect
// ZAP: 2014/09/22 Issue 1345: Support Attack mode
// ZAP: 2014/11/19 Issue 1412: Manage scan policies
// ZAP: 2015/01/29 Issue 1489: Version number in window title
// ZAP: 2015/02/05 Issue 1524: New Persist Session dialog
// ZAP: 2015/02/09 Issue 1525: Introduce a database interface layer to allow for alternative implementations
// ZAP: 2015/02/10 Issue 1208: Search classes/resources in add-ons declared as dependencies
// ZAP: 2015/04/02 Issue 321: Support multiple databases and Issue 1582: Low memory option
// ZAP: 2015/09/17 Issue 1914: Support multiple add-on directories
// ZAP: 2015/11/04 Issue 1920: Report the host:port ZAP is listening on in daemon mode, or exit if it cant
// ZAP: 2016/03/23 Issue 2331: Custom Context Panels not show in existing contexts after installation of add-on
// ZAP: 2016/04/22 Issue 2428: Memory leak on session creation/loading
// ZAP: 2016/05/30 Issue 2494: ZAP Proxy is not showing the HTTP CONNECT Request in history tab
// ZAP: 2016/09/06 Hook OverrideMessageProxyListener into the Proxy
// ZAP: 2016/10/06 Issue 2855: Added method to allow for testing when a model is required
// ZAP: 2017/03/10 Reset proxy excluded URLs on new session
// ZAP: 2017/03/13 Set global excluded URLs to the proxy when creating a new session or initialising.
// ZAP: 2017/03/16 Allow to initialise Control without starting the Local Proxy.
package org.parosproxy.paros.control;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.Session;
import org.parosproxy.paros.model.SessionListener;
import org.parosproxy.paros.view.View;
import org.parosproxy.paros.view.WaitMessageDialog;
import org.zaproxy.zap.control.ControlOverrides;
import org.zaproxy.zap.control.ExtensionFactory;
/**
*
* Overall control with interaction on model and view.
*/
public class Control extends AbstractControl implements SessionListener {
public enum Mode {safe, protect, standard, attack};
private static Logger log = Logger.getLogger(Control.class);
private static Control control = null;
private Proxy proxy = null;
private MenuFileControl menuFileControl = null;
private MenuToolsControl menuToolsControl = null;
private SessionListener lastCallback = null;
private Mode mode = null;
private Control(Model model, View view) {
super(model, view);
// ZAP: moved call of init() to singleton methods
}
// ZAP: Added constructor that will be used by initSingletonForTesting()
private Control() {
super(null, null);
}
private boolean init(ControlOverrides overrides, boolean startProxy) {
// Load extensions first as message bundles are loaded as a side effect
loadExtension();
// ZAP: Start proxy even if no view
Proxy proxy = getProxy(overrides);
proxy.setIgnoreList(model.getOptionsParam().getGlobalExcludeURLParam().getTokensNames());
getExtensionLoader().hookProxyListener(proxy);
getExtensionLoader().hookOverrideMessageProxyListener(proxy);
getExtensionLoader().hookPersistentConnectionListener(proxy);
getExtensionLoader().hookConnectRequestProxyListeners(proxy);
if (view != null) {
// ZAP: Add site map listeners
getExtensionLoader().hookSiteMapListener(view.getSiteTreePanel());
}
model.postInit();
if (startProxy) {
return proxy.startServer();
}
return false;
}
public Proxy getProxy() {
return this.getProxy(null);
}
public Proxy getProxy(ControlOverrides overrides) {
if (proxy == null) {
proxy = new Proxy(model, overrides);
}
return proxy;
}
@Override
protected void addExtension() {
ExtensionFactory.loadAllExtension(getExtensionLoader(), model.getOptionsParam());
}
public MenuFileControl getMenuFileControl() {
if (menuFileControl == null) {
menuFileControl = new MenuFileControl(model, view, this);
}
return menuFileControl;
}
public MenuToolsControl getMenuToolsControl() {
if (menuToolsControl == null) {
menuToolsControl = new MenuToolsControl(model, view, this);
}
return menuToolsControl;
}
/**
* Override inherited shutdown to add stopping proxy servers.
*/
@Override
public void shutdown(boolean compact) {
// ZAP: Save the configurations of the main panels.
if (view != null) {
view.getRequestPanel().saveConfig(model.getOptionsParam().getConfig());
view.getResponsePanel().saveConfig(model.getOptionsParam().getConfig());
}
// ZAP: Save the configuration file.
try {
model.getOptionsParam().getConfig().save();
} catch (ConfigurationException e) {
log.error("Error saving config", e);
}
getProxy(null).stopServer();
super.shutdown(compact);
}
public void exit (boolean noPrompt, final File openOnExit) {
boolean isNewState = model.getSession().isNewState();
int rootCount = 0;
if (! Constant.isLowMemoryOptionSet()) {
rootCount = model.getSession().getSiteTree().getChildCount(model.getSession().getSiteTree().getRoot());
}
boolean askOnExit = view != null && Model.getSingleton().getOptionsParam().getViewParam().getAskOnExitOption() > 0;
boolean sessionUnsaved = isNewState && rootCount > 0;
if (! noPrompt) {
List list = getExtensionLoader().getUnsavedResources();
if (sessionUnsaved && askOnExit) {
list.add(0, Constant.messages.getString("menu.file.exit.message.sessionResNotSaved"));
}
String message = null;
String activeActions = wrapEntriesInLiTags(getExtensionLoader().getActiveActions());
if (list.size() > 0) {
String unsavedResources = wrapEntriesInLiTags(list);
if (activeActions.isEmpty()) {
message = MessageFormat.format(
Constant.messages.getString("menu.file.exit.message.resourcesNotSaved"),
unsavedResources);
} else {
message = MessageFormat.format(
Constant.messages.getString("menu.file.exit.message.resourcesNotSavedAndActiveActions"),
unsavedResources,
activeActions);
}
} else if (!activeActions.isEmpty()) {
message = MessageFormat.format(
Constant.messages.getString("menu.file.exit.message.activeActions"),
activeActions);
}
if (message != null && view.showConfirmDialog(message) != JOptionPane.OK_OPTION) {
return;
}
}
if (sessionUnsaved) {
control.discardSession();
}
Thread t = new Thread(new Runnable() {
@Override
public void run() {
// ZAP: Changed to use the option compact database.
control.shutdown(Model.getSingleton().getOptionsParam().getDatabaseParam().isCompactDatabase());
log.info(Constant.PROGRAM_TITLE + " terminated.");
if (openOnExit != null && Desktop.isDesktopSupported()) {
try {
log.info("Openning file " + openOnExit.getAbsolutePath());
Desktop.getDesktop().open(openOnExit);
} catch (IOException e) {
log.error("Failed to open file " + openOnExit.getAbsolutePath(), e);
}
}
System.exit(0);
}
});
if (view != null) {
WaitMessageDialog dialog = view.getWaitMessageDialog(Constant.messages.getString("menu.file.shuttingDown")); // ZAP: i18n
t.start();
dialog.setVisible(true);
} else {
t.start();
}
}
private static String wrapEntriesInLiTags(List entries) {
if (entries.isEmpty()) {
return "";
}
StringBuilder strBuilder = new StringBuilder(entries.size() * 15);
for (String entry : entries) {
strBuilder.append("");
strBuilder.append(entry);
strBuilder.append(" ");
}
return strBuilder.toString();
}
public void exitAndDeleteSession (String sessionName) {
shutdown(false);
Model.getSingleton().getDb().deleteSession(sessionName);
log.info(Constant.PROGRAM_TITLE + " terminated.");
System.exit(0);
}
public static Control getSingleton() {
return control;
}
public static boolean initSingletonWithView(ControlOverrides overrides) {
control = new Control(Model.getSingleton(), View.getSingleton());
return control.init(overrides, true);
}
public static boolean initSingletonWithoutView(ControlOverrides overrides) {
control = new Control(Model.getSingleton(), null);
return control.init(overrides, true);
}
public static void initSingletonWithoutViewAndProxy(ControlOverrides overrides) {
control = new Control(Model.getSingleton(), null);
control.init(overrides, false);
}
// ZAP: Added method to allow for testing
public static void initSingletonForTesting() {
control = new Control();
}
// ZAP: Added method to allow for testing when a model is required
public static void initSingletonForTesting(Model model) {
control = new Control(model, null);
}
public void runCommandLine() throws Exception {
log.debug("runCommand");
getExtensionLoader().runCommandLine();
}
public void runCommandLineNewSession(String fileName) throws Exception {
log.debug("runCommandLineNewSession " + fileName);
getExtensionLoader().sessionAboutToChangeAllPlugin(null);
model.createAndOpenUntitledDb();
final Session session = createNewSession();
model.saveSession(fileName);
if (View.isInitialised()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
view.getSiteTreePanel().getTreeSite().setModel(session.getSiteTree());
// refresh display
view.getMainFrame().setTitle(session.getSessionName());
view.getOutputPanel().clear();
}
});
}
log.info("New session file created");
control.getExtensionLoader().databaseOpen(model.getDb());
control.getExtensionLoader().sessionChangedAllPlugin(session);
}
/**
* Creates a new session and resets session related data in other components (e.g. proxy excluded URLs).
*
* @return the newly created session.
*/
private Session createNewSession() {
Session session = model.newSession();
getProxy().setIgnoreList(model.getOptionsParam().getGlobalExcludeURLParam().getTokensNames());
return session;
}
public void runCommandLineOpenSession(String fileName) throws Exception {
log.debug("runCommandLineOpenSession " + fileName);
getExtensionLoader().sessionAboutToChangeAllPlugin(null);
Session session = Model.getSingleton().getSession();
Model.getSingleton().openSession(fileName);
log.info("Session file opened");
control.getExtensionLoader().databaseOpen(model.getDb());
control.getExtensionLoader().sessionChangedAllPlugin(session);
}
public void setExcludeFromProxyUrls(List urls) {
this.getProxy(null).setIgnoreList(urls);
}
public void openSession(final File file, final SessionListener callback) {
log.info("Open Session");
getExtensionLoader().sessionAboutToChangeAllPlugin(null);
lastCallback = callback;
model.openSession(file, this);
// The session is opened in a thread, so notify the listeners via the callback
}
public void openSession(final String fileName, final SessionListener callback) {
log.info("Open Session");
getExtensionLoader().sessionAboutToChangeAllPlugin(null);
lastCallback = callback;
model.openSession(fileName, this);
// The session is opened in a thread, so notify the listeners via the callback
}
public Session newSession() throws Exception {
log.info("New Session");
closeSessionAndCreateAndOpenUntitledDb();
final Session session = createNewSession();
getExtensionLoader().databaseOpen(model.getDb());
getExtensionLoader().sessionChangedAllPlugin(session);
if (View.isInitialised()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
view.getSiteTreePanel().getTreeSite().setModel(session.getSiteTree());
view.getSiteTreePanel().reloadContextTree();
}
});
// refresh display
view.getMainFrame().setTitle(session.getSessionName());
view.getOutputPanel().clear();
}
try {
model.getDb().getTableSession().insert(session.getSessionId(), session.getSessionName());
} catch (DatabaseException e) {
log.error(e.getMessage(), e);
}
return session;
}
/**
* Closes the old session and creates and opens an untitled database.
*
* @throws Exception if an error occurred while creating or opening the database.
*/
private void closeSessionAndCreateAndOpenUntitledDb() throws Exception {
getExtensionLoader().sessionAboutToChangeAllPlugin(null);
model.closeSession();
log.info("Create and Open Untitled Db");
model.createAndOpenUntitledDb();
}
public void newSession(String fileName, final SessionListener callback) {
log.info("New Session");
try {
closeSessionAndCreateAndOpenUntitledDb();
lastCallback = callback;
createNewSession();
model.saveSession(fileName, this);
} catch (Exception e) {
if (lastCallback != null) {
lastCallback.sessionSaved(e);
lastCallback = null;
}
}
}
public void saveSession(final String fileName) {
this.saveSession(fileName, null);
}
public void saveSession(final String fileName, final SessionListener callback) {
log.info("Save Session");
getExtensionLoader().sessionAboutToChangeAllPlugin(null);
lastCallback = callback;
model.saveSession(fileName, this);
// The session is saved in a thread, so notify the listeners via the callback
}
public void snapshotSession(final String fileName, final SessionListener callback) {
log.info("Snapshot Session");
lastCallback = callback;
model.snapshotSession(fileName, this);
// The session is saved in a thread, so notify the listeners via the callback
}
public void discardSession() {
log.info("Discard Session");
getExtensionLoader().sessionAboutToChangeAllPlugin(null);
model.discardSession();
getExtensionLoader().sessionChangedAllPlugin(null);
}
/**
* @deprecated (2.5.0) Use just {@link #newSession()} (or {@link #newSession(String, SessionListener)}) instead,
* which already takes care to create and open an untitled database.
*/
@Deprecated
@SuppressWarnings("javadoc")
public void createAndOpenUntitledDb() throws ClassNotFoundException, Exception {
log.info("Create and Open Untitled Db");
getExtensionLoader().sessionAboutToChangeAllPlugin(null);
model.closeSession();
model.createAndOpenUntitledDb();
getExtensionLoader().databaseOpen(model.getDb());
getExtensionLoader().sessionChangedAllPlugin(model.getSession());
}
@Override
public void sessionOpened(File file, Exception e) {
getExtensionLoader().databaseOpen(model.getDb());
getExtensionLoader().sessionChangedAllPlugin(model.getSession());
if (lastCallback != null) {
lastCallback.sessionOpened(file, e);
lastCallback = null;
}
}
@Override
public void sessionSaved(Exception e) {
getExtensionLoader().databaseOpen(model.getDb());
getExtensionLoader().sessionChangedAllPlugin(model.getSession());
if (lastCallback != null) {
lastCallback.sessionSaved(e);
lastCallback = null;
}
}
@Override
public void sessionSnapshot(Exception e) {
if (lastCallback != null) {
lastCallback.sessionSnapshot(e);
lastCallback = null;
}
}
public void sessionScopeChanged() {
getExtensionLoader().sessionScopeChangedAllPlugin(model.getSession());
}
public Mode getMode() {
if (mode == null) {
mode = Mode.valueOf(model.getOptionsParam().getViewParam().getMode());
}
return mode;
}
public void setMode(Mode mode) {
this.mode = mode;
getExtensionLoader().sessionModeChangedAllPlugin(mode);
model.getOptionsParam().getViewParam().setMode(mode.name());
}
}