
org.zaproxy.zap.extension.ascan.ScanProgressDialog 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!
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2013 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.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridBagLayout;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdesktop.swingx.plaf.basic.core.BasicTransferable;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.ui.Layer;
import org.jfree.chart.ui.TextAnchor;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.core.scanner.HostProcess;
import org.parosproxy.paros.core.scanner.Plugin;
import org.parosproxy.paros.extension.AbstractDialog;
import org.parosproxy.paros.view.View;
import org.zaproxy.zap.utils.FontUtils;
import org.zaproxy.zap.view.LayoutHelper;
/**
* Dialog reviewed for a new lifestyle...
*
* @author yhawke (2014)
*/
@SuppressWarnings("serial")
public class ScanProgressDialog extends AbstractDialog {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LogManager.getLogger(ScanProgressDialog.class);
private ExtensionActiveScan extension;
private JScrollPane jScrollPane;
private JTable table;
private ScanProgressTableModel model;
private JButton closeButton;
private JButton copyToClipboardButton;
private JComboBox hostSelect;
private ActiveScan scan;
private boolean stopThread;
private JFreeChart chart;
private List labelsAdded = new ArrayList<>();
private TimeSeries seriesTotal;
private TimeSeries series100;
private TimeSeries series200;
private TimeSeries series300;
private TimeSeries series400;
private TimeSeries series500;
private double lastCentre = -1;
/**
* Constructs a modal {@code ScanProgressDialog} with the given owner, target and active scan
* extension.
*
* @param owner the {@code Frame} from which the dialog is displayed
* @param target the scan target, shown as title if not {@code null}
* @param extension the active scan extension, to obtain chart options
* @throws HeadlessException when {@code GraphicsEnvironment.isHeadless()} returns {@code true}
*/
public ScanProgressDialog(Frame owner, String target, ExtensionActiveScan extension) {
super(owner, false);
if (target != null) {
this.setTitle(Constant.messages.getString("ascan.progress.title", target));
}
this.extension = extension;
this.initialize();
}
private void initialize() {
JTabbedPane tabbedPane = new JTabbedPane();
JPanel tab1 = new JPanel();
tab1.setLayout(new GridBagLayout());
JPanel hostPanel = new JPanel();
hostPanel.setLayout(new GridBagLayout());
hostPanel.add(
new JLabel(Constant.messages.getString("ascan.progress.label.host")),
LayoutHelper.getGBC(0, 0, 1, 0.4D));
hostPanel.add(getHostSelect(), LayoutHelper.getGBC(1, 0, 1, 0.6D));
tab1.add(hostPanel, LayoutHelper.getGBC(0, 0, 3, 1.0D, 0.0D));
tab1.add(getJScrollPane(), LayoutHelper.getGBC(0, 1, 3, 1.0D, 1.0D));
JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
buttonsPanel.add(getCopyToClipboardButton());
buttonsPanel.add(getCloseButton());
tab1.add(buttonsPanel, LayoutHelper.getGBC(0, 2, 3, 1.0D));
tabbedPane.insertTab(
Constant.messages.getString("ascan.progress.tab.progress"), null, tab1, null, 0);
this.add(tabbedPane);
int mins = extension.getScannerParam().getMaxChartTimeInMins();
if (mins > 0) {
// Treat zero mins as disabled
JPanel tab2 = new JPanel();
tab2.setLayout(new GridBagLayout());
this.seriesTotal =
new TimeSeries("TotalResponses"); // Name not shown, so no need to i18n
final TimeSeriesCollection dataset = new TimeSeriesCollection(this.seriesTotal);
this.series100 =
new TimeSeries(Constant.messages.getString("ascan.progress.chart.1xx"));
this.series200 =
new TimeSeries(Constant.messages.getString("ascan.progress.chart.2xx"));
this.series300 =
new TimeSeries(Constant.messages.getString("ascan.progress.chart.3xx"));
this.series400 =
new TimeSeries(Constant.messages.getString("ascan.progress.chart.4xx"));
this.series500 =
new TimeSeries(Constant.messages.getString("ascan.progress.chart.5xx"));
long maxAge = mins * 60L;
this.seriesTotal.setMaximumItemAge(maxAge);
this.series100.setMaximumItemAge(maxAge);
this.series200.setMaximumItemAge(maxAge);
this.series300.setMaximumItemAge(maxAge);
this.series400.setMaximumItemAge(maxAge);
this.series500.setMaximumItemAge(maxAge);
dataset.addSeries(series100);
dataset.addSeries(series200);
dataset.addSeries(series300);
dataset.addSeries(series400);
dataset.addSeries(series500);
chart = createChart(dataset);
// Set up some vaguely sensible colours
chart.getXYPlot().getRenderer(0).setSeriesPaint(0, Color.BLACK); // Totals
chart.getXYPlot().getRenderer(0).setSeriesPaint(1, Color.GRAY); // 100: Info
chart.getXYPlot().getRenderer(0).setSeriesPaint(2, Color.GREEN); // 200: OK
chart.getXYPlot().getRenderer(0).setSeriesPaint(3, Color.BLUE); // 300: Info
chart.getXYPlot().getRenderer(0).setSeriesPaint(4, Color.MAGENTA); // 400: Bad req
chart.getXYPlot().getRenderer(0).setSeriesPaint(5, Color.RED); // 500: Internal error
final ChartPanel chartPanel = new ChartPanel(chart);
tab2.add(chartPanel, LayoutHelper.getGBC(0, 0, 1, 1.0D, 1.0D));
tabbedPane.insertTab(
Constant.messages.getString("ascan.progress.tab.chart"), null, tab2, null, 1);
}
// Stop the updating thread when the window is closed
this.addWindowListener(
new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
stopThread = true;
}
});
pack();
}
private JFreeChart createChart(final XYDataset dataset) {
JFreeChart result =
ChartFactory.createTimeSeriesChart(
null, // No title - it just takes up space
Constant.messages.getString("ascan.progress.chart.time"),
Constant.messages.getString("ascan.progress.chart.responses"),
dataset,
true,
true,
false);
XYPlot plot = result.getXYPlot();
ValueAxis daxis = plot.getDomainAxis();
daxis.setAutoRange(true);
daxis.setAutoRangeMinimumSize(60000.0);
plot.getRangeAxis().setAutoRangeMinimumSize(20);
return result;
}
/**
* Get the dialog scroll panel
*
* @return the panel
*/
private JScrollPane getJScrollPane() {
if (jScrollPane == null) {
jScrollPane = new JScrollPane();
jScrollPane.setViewportView(getMainPanel());
jScrollPane.setName("ScanProgressScrollPane");
jScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
jScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
}
return jScrollPane;
}
private JButton getCloseButton() {
// Note that on Linux dialogs dont get close buttons on the frame decoration
if (closeButton == null) {
closeButton = new JButton(Constant.messages.getString("all.button.close"));
closeButton.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispatchEvent(
new WindowEvent(
ScanProgressDialog.this, WindowEvent.WINDOW_CLOSING));
}
});
}
return closeButton;
}
private JButton getCopyToClipboardButton() {
if (copyToClipboardButton == null) {
copyToClipboardButton =
new JButton(
Constant.messages.getString(
"ascan.progress.copyclipboard.button.label"));
copyToClipboardButton.setToolTipText(
Constant.messages.getString("ascan.progress.copyclipboard.button.tooltip"));
copyToClipboardButton.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
// Mimics the implementation of
// BasicTableUI.TableTransferHandler.createTransferable(JComponent) but
// copies
// all rows (including column names), not just selected rows/columns
// (which are none in this case).
StringBuilder plainContent = new StringBuilder();
StringBuilder htmlContent = new StringBuilder();
htmlContent.append("\n\n\n");
TableModel tableModel = getMainPanel().getModel();
htmlContent.append("\n");
for (int col = 0; col < tableModel.getColumnCount(); col++) {
String val = tableModel.getColumnName(col);
plainContent.append(val).append('\t');
htmlContent.append(" ").append(val).append(" \n");
}
plainContent.deleteCharAt(plainContent.length() - 1).append("\n");
htmlContent.append(" \n");
for (int row = 0; row < tableModel.getRowCount(); row++) {
htmlContent.append("\n");
for (int col = 0; col < tableModel.getColumnCount(); col++) {
Object obj = tableModel.getValueAt(row, col);
String val = (obj == null) ? "" : obj.toString();
plainContent.append(val).append('\t');
htmlContent.append(" ").append(val).append(" \n");
}
plainContent.deleteCharAt(plainContent.length() - 1).append("\n");
htmlContent.append(" \n");
}
plainContent.deleteCharAt(plainContent.length() - 1);
htmlContent.append("
\n\n");
Transferable transferable =
new BasicTransferable(
plainContent.toString(), htmlContent.toString());
try {
Toolkit.getDefaultToolkit()
.getSystemClipboard()
.setContents(transferable, null);
} catch (IllegalStateException e) {
View.getSingleton()
.showWarningDialog(
ScanProgressDialog.this,
Constant.messages.getString(
"ascan.progress.copyclipboard.error"));
LOGGER.warn("Failed to copy the contents to clipboard:", e);
}
}
});
}
return copyToClipboardButton;
}
/**
* Get the main content panel of the dialog
*
* @return the main panel
*/
private JTable getMainPanel() {
if (table == null) {
model = new ScanProgressTableModel();
table = new JTable();
table.setModel(model);
table.setRowSelectionAllowed(false);
table.setColumnSelectionAllowed(false);
table.setDoubleBuffered(true);
// First column is for plugin's name
table.getColumnModel().getColumn(0).setPreferredWidth(256);
table.getColumnModel().getColumn(1).setPreferredWidth(80);
// Second column is for plugin's status
table.getColumnModel().getColumn(2).setPreferredWidth(80);
table.getColumnModel().getColumn(2).setCellRenderer(new ScanProgressBarRenderer());
// Third column is for plugin's elapsed time
DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
centerRenderer.setHorizontalAlignment(JLabel.CENTER);
table.getColumnModel().getColumn(3).setPreferredWidth(85);
table.getColumnModel().getColumn(3).setCellRenderer(centerRenderer);
// Forth column is for plugin's request count
DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer();
rightRenderer.setHorizontalAlignment(JLabel.RIGHT);
table.getColumnModel().getColumn(4).setPreferredWidth(60);
table.getColumnModel().getColumn(4).setCellRenderer(rightRenderer);
table.getColumnModel().getColumn(5).setPreferredWidth(60);
table.getColumnModel().getColumn(5).setCellRenderer(rightRenderer);
// Fifth column is for plugin's completion and actions
table.getColumnModel().getColumn(6).setPreferredWidth(40);
table.getColumnModel().getColumn(6).setCellRenderer(new ScanProgressActionRenderer());
ScanProgressActionListener listener = new ScanProgressActionListener(table, model);
table.addMouseListener(listener);
table.addMouseMotionListener(listener);
}
return table;
}
/** Updates the scan progress shown by the dialogue (scanners' progress/state and chart). */
private void updateProgress() {
// Start panel data settings
HostProcess hp = getSelectedHostProcess();
if (scan.getHostProcesses() != null && hp != null) {
// Update the main table entries
model.updateValues(scan, hp);
if (scan.isStopped()) {
this.stopThread = true;
}
if (chart != null) {
ResponseCountSnapshot snapshot = scan.getRequestHistory();
while (snapshot != null) {
try {
Second second = new Second(snapshot.getDate());
this.seriesTotal.add(second, snapshot.getTotal());
this.series100.add(second, snapshot.getResp100());
this.series200.add(second, snapshot.getResp200());
this.series300.add(second, snapshot.getResp300());
this.series400.add(second, snapshot.getResp400());
this.series500.add(second, snapshot.getResp500());
snapshot = scan.getRequestHistory();
for (Plugin plugin : scan.getHostProcesses().get(0).getRunning()) {
if (!labelsAdded.contains(plugin.getName())) {
// Add a vertical line with the plugin name
ValueMarker vm = new ValueMarker(plugin.getTimeStarted().getTime());
double center =
chart.getXYPlot()
.getRangeAxis()
.getRange()
.getCentralValue();
if (lastCentre != center) {
if (lastCentre != -1) {
// Move the existing labels so they stay in the centre
@SuppressWarnings("rawtypes")
List annotations = chart.getXYPlot().getAnnotations();
for (Object o : annotations) {
if (o instanceof XYTextAnnotation) {
XYTextAnnotation annotation = (XYTextAnnotation) o;
annotation.setY(center);
}
}
}
lastCentre = center;
}
XYTextAnnotation updateLabel =
new XYTextAnnotation(
plugin.getName(),
plugin.getTimeStarted().getTime(),
center);
updateLabel.setFont(FontUtils.getFont("Sans Serif"));
updateLabel.setRotationAnchor(TextAnchor.BASELINE_CENTER);
updateLabel.setTextAnchor(TextAnchor.BASELINE_CENTER);
updateLabel.setRotationAngle(-3.14 / 2);
updateLabel.setPaint(Color.black);
chart.getXYPlot().addDomainMarker(vm, Layer.BACKGROUND);
chart.getXYPlot().addAnnotation(updateLabel);
labelsAdded.add(plugin.getName());
}
}
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
snapshot = null;
}
}
}
}
}
private HostProcess getSelectedHostProcess() {
String str = (String) this.getHostSelect().getSelectedItem();
if (str == null) {
return null;
}
for (HostProcess hp : scan.getHostProcesses()) {
if (str.equals(hp.getHostAndPort())) {
return hp;
}
}
return null;
}
/**
* Set the scan that will be shown in this dialog.
*
* @param scan the active scan, might be {@code null}.
*/
public void setActiveScan(ActiveScan scan) {
this.scan = scan;
if (scan == null) {
return;
}
getHostSelect().removeAll();
for (HostProcess hp : scan.getHostProcesses()) {
getHostSelect().addItem(hp.getHostAndPort());
}
Thread thread =
new Thread() {
@Override
public void run() {
while (!stopThread) {
SwingUtilities.invokeLater(
new Runnable() {
@Override
public void run() {
updateProgress();
}
});
try {
sleep(200);
} catch (InterruptedException e) {
// Ignore
}
}
}
};
thread.start();
}
private JComboBox getHostSelect() {
if (hostSelect == null) {
hostSelect = new JComboBox<>();
hostSelect.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// Switch results, esp necessary when the scan has finished
updateProgress();
}
});
}
return hostSelect;
}
/** Custom Renderer for the progress bar plugin column */
private static class ScanProgressBarRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
@Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
if (value != null) {
ScanProgressItem item = (ScanProgressItem) value;
JProgressBar bar = new JProgressBar();
bar.setMaximum(100);
bar.setValue(item.getProgressPercentage());
return bar;
}
return super.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
}
}
/** Custom Renderer for the actions column (skipping) */
private class ScanProgressActionRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
@Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (value != null) {
ScanProgressActionIcon action = (ScanProgressActionIcon) value;
if (action == model.getFocusedAction()) {
action.setOver();
} else {
action.setNormal();
}
action.setOpaque(true);
action.setBackground(getBackground());
return action;
}
return this;
}
}
/** Listener for all Action's management (skipping for now) */
private static class ScanProgressActionListener extends MouseAdapter {
/** Constant that indicates that a column or row was not found. */
private static final int NOT_FOUND = -1;
private final JTable table;
private final ScanProgressTableModel model;
public ScanProgressActionListener(JTable table, ScanProgressTableModel model) {
this.table = table;
this.model = model;
}
@Override
public void mouseClicked(MouseEvent e) {
ScanProgressActionIcon action = getScanProgressAction(e.getPoint());
if (action != null) {
action.invokeAction();
}
}
@Override
public void mousePressed(MouseEvent e) {
ScanProgressActionIcon action = getScanProgressAction(e.getPoint());
if (action != null) {
action.setPressed();
action.repaint();
}
}
@Override
public void mouseReleased(MouseEvent e) {
ScanProgressActionIcon action = getScanProgressAction(e.getPoint());
if (action != null) {
action.setReleased();
action.repaint();
}
}
@Override
public void mouseMoved(MouseEvent me) {
ScanProgressActionIcon action = getScanProgressAction(me.getPoint());
if (action != null) {
model.setFocusedAction(action);
action.repaint();
} else if (model.getFocusedAction() != null) {
model.setFocusedAction(action);
table.repaint();
}
}
/**
* Gets the {@code ScanProgressActionIcon} at the given point, if any.
*
* @param point the point to get the scan progress action icon
* @return the {@code ScanProgressActionIcon} at the given point, or {@code null} if none
*/
private ScanProgressActionIcon getScanProgressAction(Point point) {
int column = table.columnAtPoint(point);
if (column == NOT_FOUND) {
return null;
}
int row = table.rowAtPoint(point);
if (row == NOT_FOUND) {
return null;
}
Object value = table.getValueAt(row, column);
if (value instanceof ScanProgressActionIcon) {
return (ScanProgressActionIcon) value;
}
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy