org.parosproxy.paros.control.MenuFileControl 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.
The 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/05/15 Improved error logging
// ZAP: 2012/02/18 Rationalised session handling
// ZAP: 2012/04/23 Added @Override annotation to all appropriate methods.
// ZAP: 2012/06/11 Changed to call the method Control.shutdown(boolean) with the
// parameter set as true.
// ZAP: 2012/06/19 Changed the method sessionOpened(File,Exception) to not call
// the method ExtensionLoader.sessionChangedAllPlugin, now it's done in the
// class Control.
// ZAP: 2012/07/02 Changed to use the new database compact option in the method
// exit().
// ZAP: 2012/07/23 Removed parameter from View.getSessionDialog call.
// ZAP: 2012/12/06 Issue 428: Moved exit code to control to support the marketplace
// ZAP: 2013/01/25 Removed the "(non-Javadoc)" comments.
// ZAP: 2013/03/03 Issue 546: Remove all template Javadoc comments
// ZAP: 2013/03/03 Issue 547: Deprecate unused classes and methods
// ZAP: 2013/04/16 Issue 638: Persist and snapshot sessions instead of saving them
// ZAP: 2013/08/05 Proper call for starting Session Properties dialog
// ZAP: 2013/08/28 Issue 695: Sites tree doesn't clear on new session created by API
// ZAP: 2014/05/20 Issue 1191: Cmdline session params have no effect
// ZAP: 2014/12/22 Issue 1476: Display contexts in the Sites tree
// ZAP: 2015/01/29 Issue 1489: Version number in window title
// ZAP: 2015/02/05 Issue 1524: New Persist Session dialog
// ZAP: 2015/04/02 Issue 321: Support multiple databases
// ZAP: 2015/12/14 Log exception and internationalise error message
// ZAP: 2016/10/26 Issue 1952: Do not allow Contexts with same name
// ZAP: 2017/02/25 Issue 2618: Let the user select the name for snapshots
// ZAP: 2017/06/01 Issue 3555: setTitle() functionality moved in order to ensure consistent
// application
// ZAP: 2017/06/20 Inform of active actions before changing the session.
// ZAP: 2017/08/31 Use helper method I18N.getString(String, Object...).
// ZAP: 2017/11/22 Do not allow to snapshot the session with active actions (Issue 3711).
// ZAP: 2017/12/15 Confirm when overwriting session file (Issue 4153).
// ZAP: 2018/01/01 Prevent the selection of the current session on save/snapshot.
// ZAP: 2019/06/01 Normalise line endings.
// ZAP: 2019/06/05 Normalise format/style.
// ZAP: 2020/11/26 Use Log4j 2 classes for logging.
// ZAP: 2021/05/14 Remove redundant type arguments.
// ZAP: 2022/08/05 Address warns with Java 18.
// ZAP: 2022/09/21 Use format specifiers instead of concatenation when logging.
// ZAP: 2023/01/10 Tidy up logger.
// ZAP: 2023/05/21 Handle duplicate context names when importing in a more userfriendly way (Issue
// 7421).
package org.parosproxy.paros.control;
import java.awt.EventQueue;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.db.Database;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.db.RecordSession;
import org.parosproxy.paros.extension.option.DatabaseParam;
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.model.Context;
import org.zaproxy.zap.model.IllegalContextNameException;
import org.zaproxy.zap.utils.ZapHtmlLabel;
import org.zaproxy.zap.utils.ZapXmlConfiguration;
import org.zaproxy.zap.view.ContextExportDialog;
import org.zaproxy.zap.view.PersistSessionDialog;
import org.zaproxy.zap.view.SessionTableSelectDialog;
import org.zaproxy.zap.view.widgets.WritableFileChooser;
public class MenuFileControl implements SessionListener {
private static final Logger LOGGER = LogManager.getLogger(MenuFileControl.class);
private View view = null;
private Model model = null;
private Control control = null;
private WaitMessageDialog waitMessageDialog = null;
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss");
public MenuFileControl(Model model, View view, Control control) {
this.view = view;
this.model = model;
this.control = control;
}
public void exit() {
control.exit(false, null);
}
public void newSession(boolean isPromptNewSession) throws ClassNotFoundException, Exception {
if (isPromptNewSession) {
if (!informStopActiveActions()) {
return;
}
// ZAP: i18n
if (model.getSession().isNewState()) {
if (view.showConfirmDialog(Constant.messages.getString("menu.file.discardSession"))
!= JOptionPane.OK_OPTION) {
return;
}
control.discardSession();
} else if (view.showConfirmDialog(Constant.messages.getString("menu.file.closeSession"))
!= JOptionPane.OK_OPTION) {
return;
}
}
int newSessionOption = model.getOptionsParam().getDatabaseParam().getNewSessionOption();
if (model.getOptionsParam().getDatabaseParam().isNewSessionPrompt()) {
PersistSessionDialog psd = new PersistSessionDialog(View.getSingleton().getMainFrame());
// Set up the default option - i.e. the same one the user chose last time
switch (newSessionOption) {
case DatabaseParam.NEW_SESSION_TIMESTAMPED:
psd.setTimestampChosen();
break;
case DatabaseParam.NEW_SESSION_USER_SPECIFIED:
psd.setPersistChosen();
break;
case DatabaseParam.NEW_SESSION_TEMPORARY:
psd.setTemporaryChosen();
break;
default:
break;
}
psd.setVisible(true);
if (psd.isTimestampChosen()) {
newSessionOption = DatabaseParam.NEW_SESSION_TIMESTAMPED;
} else if (psd.isPersistChosen()) {
newSessionOption = DatabaseParam.NEW_SESSION_USER_SPECIFIED;
} else {
newSessionOption = DatabaseParam.NEW_SESSION_TEMPORARY;
}
// Save for next time
model.getOptionsParam().getDatabaseParam().setNewSessionOption(newSessionOption);
model.getOptionsParam().getDatabaseParam().setNewSessionPrompt(!psd.isDontAskAgain());
}
switch (newSessionOption) {
case DatabaseParam.NEW_SESSION_TIMESTAMPED:
String filename = getTimestampFilename();
if (filename != null) {
this.newSession(filename);
} else {
control.newSession();
}
break;
case DatabaseParam.NEW_SESSION_USER_SPECIFIED:
control.newSession();
this.saveAsSession();
break;
default:
control.newSession();
break;
}
}
private boolean informStopActiveActions() {
String activeActions = wrapEntriesInLiTags(control.getExtensionLoader().getActiveActions());
if (!activeActions.isEmpty()) {
String message =
Constant.messages.getString("menu.file.session.activeactions", activeActions);
if (view.showConfirmDialog(new ZapHtmlLabel(message)) != JOptionPane.OK_OPTION) {
return false;
}
}
return true;
}
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();
}
private String getTimestampFilename() {
File dir = new File(Constant.getZapHome(), "sessions");
if (!dir.exists()) {
if (!dir.mkdirs()) {
return null;
}
}
String timestamp = dateFormat.format(new Date());
File tmpFile = new File(dir, timestamp + ".session");
return tmpFile.getAbsolutePath();
}
public boolean newSession(String fileName) {
final Object[] created = {Boolean.TRUE};
waitMessageDialog =
view.getWaitMessageDialog(
Constant.messages.getString("menu.file.newSession.wait.dialogue"));
control.newSession(
fileName,
new SessionListener() {
@Override
public void sessionSnapshot(Exception e) {}
@Override
public void sessionSaved(final Exception e) {
if (EventQueue.isDispatchThread()) {
if (e == null) {
view.getSiteTreePanel()
.getTreeSite()
.setModel(model.getSession().getSiteTree());
} else {
view.showWarningDialog(
Constant.messages.getString("menu.file.newSession.error"));
LOGGER.error(
"Error creating session file {}",
model.getSession().getFileName(),
e);
created[0] = Boolean.FALSE;
}
if (waitMessageDialog != null) {
waitMessageDialog.setVisible(false);
waitMessageDialog = null;
}
} else {
EventQueue.invokeLater(
new Runnable() {
@Override
public void run() {
sessionSaved(e);
}
});
}
}
@Override
public void sessionOpened(File file, Exception e) {}
});
waitMessageDialog.setVisible(true);
return created[0] == Boolean.TRUE;
}
public boolean openSession(String session) {
final Object[] opened = {Boolean.TRUE};
File sessionFile = new File(session);
waitMessageDialog =
view.getWaitMessageDialog(Constant.messages.getString("menu.file.loadSession"));
LOGGER.info("opening session file {}", sessionFile.getAbsolutePath());
control.openSession(
sessionFile,
new SessionListener() {
@Override
public void sessionSnapshot(Exception e) {}
@Override
public void sessionSaved(Exception e) {}
@Override
public void sessionOpened(final File file, final Exception e) {
if (EventQueue.isDispatchThread()) {
if (e != null) {
view.showWarningDialog(
Constant.messages.getString("menu.file.openSession.error"));
LOGGER.error(
"error opening session file {}",
model.getSession().getFileName(),
e);
opened[0] = Boolean.FALSE;
}
view.getSiteTreePanel()
.getTreeSite()
.setModel(model.getSession().getSiteTree());
if (waitMessageDialog != null) {
waitMessageDialog.setVisible(false);
waitMessageDialog = null;
}
} else {
EventQueue.invokeLater(
new Runnable() {
@Override
public void run() {
sessionOpened(file, e);
}
});
}
}
});
waitMessageDialog.setVisible(true);
return opened[0] == Boolean.TRUE;
}
public void openSession() {
if (!informStopActiveActions()) {
return;
}
// TODO extract into db specific classes??
if (Database.DB_TYPE_HSQLDB.equals(model.getDb().getType())) {
this.openFileBasedSession();
} else {
this.openDbBasedSession();
}
}
private void openFileBasedSession() {
JFileChooser chooser = new JFileChooser(model.getOptionsParam().getUserDirectory());
chooser.setFileHidingEnabled(
false); // By default ZAP on linux puts timestamped sessions under a 'dot' directory
File file = null;
chooser.setFileFilter(SessionFileChooser.SESSION_FILE_FILTER);
int rc = chooser.showOpenDialog(view.getMainFrame());
if (rc == JFileChooser.APPROVE_OPTION) {
try {
file = chooser.getSelectedFile();
if (file == null) {
return;
}
model.getOptionsParam().setUserDirectory(chooser.getCurrentDirectory());
LOGGER.info("opening session file {}", file.getAbsolutePath());
waitMessageDialog =
view.getWaitMessageDialog(
Constant.messages.getString("menu.file.loadSession"));
control.openSession(file, this);
waitMessageDialog.setVisible(true);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
}
private void openDbBasedSession() {
try {
List sessionList = new ArrayList<>();
for (RecordSession rs : model.getDb().getTableSession().listSessions()) {
sessionList.add("" + rs.getSessionId());
}
SessionTableSelectDialog ssd =
new SessionTableSelectDialog(View.getSingleton().getMainFrame(), sessionList);
ssd.setVisible(true);
if (ssd.getSelectedSession() != null) {
waitMessageDialog =
view.getWaitMessageDialog(
Constant.messages.getString("menu.file.loadSession"));
control.openSession(ssd.getSelectedSession(), this);
waitMessageDialog.setVisible(true);
}
} catch (DatabaseException e) {
LOGGER.error(e.getMessage(), e);
}
}
public void saveSession() {
Session session = model.getSession();
if (session.isNewState()) {
view.showWarningDialog("Please use Save As...");
return;
}
try {
waitMessageDialog =
view.getWaitMessageDialog(
Constant.messages.getString("menu.file.savingSession")); // ZAP: i18n
control.saveSession(session.getFileName(), this);
LOGGER.info("saving session file {}", session.getFileName());
// ZAP: If the save is quick the dialog can already be null here
if (waitMessageDialog != null) {
waitMessageDialog.setVisible(true);
}
} catch (Exception e) {
view.showWarningDialog(
Constant.messages.getString("menu.file.savingSession.error")); // ZAP: i18n
LOGGER.error("error saving session file {}", session.getFileName());
LOGGER.error(e.getMessage(), e);
}
}
public void saveAsSession() {
if (!informStopActiveActions()) {
return;
}
Session session = model.getSession();
JFileChooser chooser =
new SessionFileChooser(model.getOptionsParam().getUserDirectory(), session);
// ZAP: set session name as file name proposal
File fileproposal = new File(session.getSessionName());
if (session.getFileName() != null && session.getFileName().trim().length() > 0) {
// if there is already a file name, use it
fileproposal = new File(session.getFileName());
}
chooser.setSelectedFile(fileproposal);
File file = null;
int rc = chooser.showSaveDialog(view.getMainFrame());
if (rc == JFileChooser.APPROVE_OPTION) {
file = chooser.getSelectedFile();
if (file == null) {
return;
}
String fileName = createSessionFileName(file);
try {
waitMessageDialog =
view.getWaitMessageDialog(
Constant.messages.getString(
"menu.file.savingSession")); // ZAP: i18n
control.saveSession(fileName, this);
LOGGER.info("save as session file {}", session.getFileName());
waitMessageDialog.setVisible(true);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
}
private static String createSessionFileName(File file) {
String fileName = file.getAbsolutePath();
if (!fileName.endsWith(".session")) {
fileName += ".session";
}
return fileName;
}
public void saveSnapshot() {
String activeActions = wrapEntriesInLiTags(control.getExtensionLoader().getActiveActions());
if (!activeActions.isEmpty()) {
view.showMessageDialog(
Constant.messages.getString("menu.file.snapshot.activeactions", activeActions));
return;
}
Session session = model.getSession();
JFileChooser chooser =
new SessionFileChooser(model.getOptionsParam().getUserDirectory(), session);
// ZAP: set session name as file name proposal
File fileproposal = new File(session.getSessionName());
if (session.getFileName() != null && session.getFileName().trim().length() > 0) {
String proposedFileName;
// if there is already a file name, use it and add a timestamp
proposedFileName = StringUtils.removeEnd(session.getFileName(), ".session");
proposedFileName += "-" + dateFormat.format(new Date()) + ".session";
fileproposal = new File(proposedFileName);
}
chooser.setSelectedFile(fileproposal);
File file = null;
int rc = chooser.showSaveDialog(view.getMainFrame());
if (rc == JFileChooser.APPROVE_OPTION) {
file = chooser.getSelectedFile();
if (file == null) {
return;
}
String fileName = createSessionFileName(file);
try {
waitMessageDialog =
view.getWaitMessageDialog(
Constant.messages.getString(
"menu.file.savingSnapshot")); // ZAP: i18n
control.snapshotSession(fileName, this);
LOGGER.info("Snapshotting: {} as {}", session.getFileName(), fileName);
waitMessageDialog.setVisible(true);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
}
public void properties() {
// ZAP: proper call of existing method
View.getSingleton().showSessionDialog(model.getSession(), null);
}
@Override
public void sessionOpened(File file, Exception e) {
if (e == null) {
// ZAP: Removed the statement that called the method
// ExtensionLoader.sessionChangedAllPlugin, now it's done in the
// class Control.
} else {
view.showWarningDialog(Constant.messages.getString("menu.file.openSession.errorFile"));
if (file != null) {
LOGGER.error("Error opening session file {}", file.getAbsolutePath(), e);
} else {
// File is null for table based sessions (i.e. non HSQLDB)
LOGGER.error(e.getMessage(), e);
}
}
if (waitMessageDialog != null) {
waitMessageDialog.setVisible(false);
waitMessageDialog = null;
}
}
@Override
public void sessionSaved(Exception e) {
if (e != null) {
view.showWarningDialog(
Constant.messages.getString("menu.file.savingSession.error")); // ZAP: i18n
LOGGER.error("error saving session file {}", model.getSession().getFileName(), e);
LOGGER.error(e.getMessage(), e);
}
if (waitMessageDialog != null) {
waitMessageDialog.setVisible(false);
waitMessageDialog = null;
}
}
@Override
public void sessionSnapshot(Exception e) {
if (e != null) {
view.showWarningDialog(
Constant.messages.getString("menu.file.snapshotSession.error")); // ZAP: i18n
LOGGER.error("error saving snapshot file {}", model.getSession().getFileName(), e);
LOGGER.error(e.getMessage(), e);
}
if (waitMessageDialog != null) {
waitMessageDialog.setVisible(false);
waitMessageDialog = null;
}
}
/** Prompt the user to export a context */
public void importContext() {
JFileChooser chooser = new JFileChooser(Constant.getContextsDir());
File file = null;
chooser.setFileFilter(
new FileFilter() {
@Override
public boolean accept(File file) {
if (file.isDirectory()) {
return true;
} else if (file.isFile() && file.getName().endsWith(".context")) {
return true;
}
return false;
}
@Override
public String getDescription() {
return Constant.messages.getString("file.format.zap.context");
}
});
int rc = chooser.showOpenDialog(View.getSingleton().getMainFrame());
if (rc == JFileChooser.APPROVE_OPTION) {
try {
file = chooser.getSelectedFile();
if (file == null || !file.exists()) {
return;
}
ZapXmlConfiguration config = new ZapXmlConfiguration(file);
String ctxName = config.getString(Context.CONTEXT_CONFIG_NAME);
Session session = Model.getSingleton().getSession();
boolean forcedImport = true;
while (session.getContext(ctxName) != null) {
forcedImport = false;
ctxName =
(String)
JOptionPane.showInputDialog(
View.getSingleton().getMainFrame(),
Constant.messages.getString(
"context.import.duplicate.dialog.message",
ctxName),
Constant.messages.getString(
"context.import.duplicate.dialog.title"),
JOptionPane.QUESTION_MESSAGE,
null,
null,
ctxName);
}
if (forcedImport || (ctxName != null && !ctxName.isBlank())) {
config.setProperty(Context.CONTEXT_CONFIG_NAME, ctxName);
session.importContext(config);
// Show the dialog
View.getSingleton()
.showSessionDialog(
Model.getSingleton().getSession(),
Constant.messages.getString("context.list"),
true);
}
} catch (IllegalContextNameException e) {
String detailError;
if (e.getReason() == IllegalContextNameException.Reason.EMPTY_NAME) {
detailError = Constant.messages.getString("context.error.name.empty");
} else if (e.getReason() == IllegalContextNameException.Reason.DUPLICATED_NAME) {
detailError = Constant.messages.getString("context.error.name.duplicated");
} else {
detailError = Constant.messages.getString("context.error.name.unknown");
}
View.getSingleton()
.showWarningDialog(
Constant.messages.getString("context.import.error", detailError));
} catch (Exception e1) {
LOGGER.error(e1.getMessage(), e1);
View.getSingleton()
.showWarningDialog(
Constant.messages.getString(
"context.import.error", e1.getMessage()));
}
}
}
/** Prompt the user to export a context */
public void exportContext() {
ContextExportDialog exportDialog =
new ContextExportDialog(View.getSingleton().getMainFrame());
exportDialog.setVisible(true);
}
@SuppressWarnings("serial")
private static class SessionFileChooser extends WritableFileChooser {
public static final FileFilter SESSION_FILE_FILTER =
new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory()
|| file.isFile() && file.getName().endsWith(".session");
}
@Override
public String getDescription() {
return Constant.messages.getString("file.format.zap.session");
}
};
private static final long serialVersionUID = 1L;
private final Session currentSession;
public SessionFileChooser(File currentDirectory, Session currentSession) {
super(currentDirectory);
setFileFilter(SESSION_FILE_FILTER);
this.currentSession = currentSession;
}
@Override
public void approveSelection() {
File file = getSelectedFile();
if (file != null) {
File sessionFile = new File(createSessionFileName(file));
setSelectedFile(sessionFile);
if (!currentSession.isNewState()) {
File currentFile = new File(currentSession.getFileName());
if (currentFile.getAbsolutePath().equals(sessionFile.getAbsolutePath())) {
showErrorDialog(
Constant.messages.getString(
"menu.file.error.selectedCurrentSession.msg"),
Constant.messages.getString(
"menu.file.error.selectedCurrentSession.title"));
return;
}
}
}
super.approveSelection();
}
}
}