Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion addOns/automation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ All notable changes to this add-on will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased

### Added
- Show the path of the loaded plan in the Automation panel.
- Allow to reload a plan from the loaded file to pick changes done externally.

## [0.60.0] - 2026-05-08
### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
Expand All @@ -40,6 +41,7 @@
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
Expand All @@ -65,6 +67,7 @@
import org.zaproxy.zap.eventBus.Event;
import org.zaproxy.zap.eventBus.EventConsumer;
import org.zaproxy.zap.utils.DisplayUtils;
import org.zaproxy.zap.utils.ZapLabel;
import org.zaproxy.zap.view.LayoutHelper;

@SuppressWarnings("serial")
Expand All @@ -78,6 +81,9 @@ public class AutomationPanel extends AbstractPanel implements EventConsumer {
private static final ImageIcon LOAD_ICON =
DisplayUtils.getScaledIcon(
AutomationPanel.class.getResource("/resource/icon/16/047.png"));
private static final ImageIcon RELOAD_ICON =
DisplayUtils.getScaledIcon(
AutomationPanel.class.getResource("/resource/icon/16/126.png"));
private static final ImageIcon SAVE_ICON =
DisplayUtils.getScaledIcon(
AutomationPanel.class.getResource("/resource/icon/16/096.png"));
Expand Down Expand Up @@ -151,6 +157,7 @@ public class AutomationPanel extends AbstractPanel implements EventConsumer {
private JButton runPlanButton;
private JButton savePlanButton;
private JButton saveAsPlanButton;
private JButton reloadPlanButton;
private JButton stopPlanButton;
private JButton jobUpButton;
private JButton jobDownButton;
Expand All @@ -159,6 +166,8 @@ public class AutomationPanel extends AbstractPanel implements EventConsumer {
private JButton addTestButton;
private JButton removeTestButton;
private JButton optionsButton;
private JPanel planFilePanel;
private ZapLabel planFileLabel;
private JXTreeTable tree;
private PlanTreeTableModel treeModel;
private AutomationPlan currentPlan;
Expand All @@ -173,18 +182,36 @@ public AutomationPanel(ExtensionAutomation ext) {
this.setLayout(new GridBagLayout());

this.add(this.getToolbar(), LayoutHelper.getGBC(0, 0, 1, 1.0));
this.add(this.getPlanScrollpane(), LayoutHelper.getGBC(0, 1, 1, 1.0, 1.0));
this.add(this.getPlanFilePanel(), LayoutHelper.getGBC(0, 1, 1, 1.0));
this.add(this.getPlanScrollpane(), LayoutHelper.getGBC(0, 2, 1, 1.0, 1.0));

ZAP.getEventBus()
.registerConsumer(this, AutomationEventPublisher.getPublisher().getPublisherName());
}

private JPanel getPlanFilePanel() {
if (planFilePanel == null) {
planFilePanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
planFileLabel = new ZapLabel("");
planFilePanel.add(planFileLabel);
}
return planFilePanel;
}

private void updatePlanFileLabel() {
planFileLabel.setText(
currentPlan != null && currentPlan.getFile() != null
? currentPlan.getFile().getAbsolutePath()
: "");
}

private JToolBar getToolbar() {
if (toolbar == null) {
toolbar = new JToolBar();
toolbar.setFloatable(false);
toolbar.add(getAddPlanButton());
toolbar.add(getLoadPlanButton());
toolbar.add(getReloadPlanButton());
toolbar.add(getLoadFromClipboardButton());
toolbar.add(getSavePlanButton());
toolbar.add(getSaveAsPlanButton());
Expand Down Expand Up @@ -273,6 +300,7 @@ public String getDescription() {
}
}
currentPlan.save();
updatePlanFileLabel();
ext.getParam().setLastPlanPath(currentPlan.getFile().getAbsolutePath());
} catch (JsonProcessingException | FileNotFoundException e1) {
LOGGER.error(e1.getMessage(), e1);
Expand Down Expand Up @@ -376,6 +404,29 @@ public String getDescription() {
return loadPlanButton;
}

private JButton getReloadPlanButton() {
if (reloadPlanButton == null) {
reloadPlanButton = new JButton();
reloadPlanButton.setIcon(RELOAD_ICON);
reloadPlanButton.setToolTipText(
Constant.messages.getString("automation.dialog.plan.reload"));
reloadPlanButton.setEnabled(false);
reloadPlanButton.addActionListener(
e -> {
if (currentPlan == null) {
return;
}

File fileToReload = currentPlan.getFile();
if (fileToReload == null || refuseUnsavedChanges()) {
return;
}
loadPlanWithErrorHandling(() -> loadPlan(ext.loadPlan(fileToReload)));
});
}
return reloadPlanButton;
}

private boolean refuseUnsavedChanges() {
return currentPlan != null
&& currentPlan.isChanged()
Expand Down Expand Up @@ -674,7 +725,9 @@ public void setCurrentPlan(AutomationPlan plan) {
getAddJobButton().setEnabled(currentPlan != null);
getSavePlanButton().setEnabled(false);
getSaveAsPlanButton().setEnabled(currentPlan != null);
getReloadPlanButton().setEnabled(currentPlan != null && currentPlan.getFile() != null);
getStopPlanButton().setEnabled(currentPlan != null && !getRunPlanButton().isEnabled());
updatePlanFileLabel();
}

public List<String> getUnsavedPlans() {
Expand Down Expand Up @@ -858,6 +911,12 @@ private void handleEvent(Event event) {
break;
case AutomationEventPublisher.PLAN_SAVED:
updateSaveButton(event, false);
AutomationPlan savedPlan = this.getPlan(event);
if (savedPlan != null
&& savedPlan.equals(this.currentPlan)
&& currentPlan.getFile() != null) {
getReloadPlanButton().setEnabled(true);
}
break;
case AutomationEventPublisher.JOB_STARTED:
updateJob(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ <H2>Automation Tab</H2>
<ul>
<li>New Plan... - this launches the New Plan dialog
<li>Load Plan... - this allows you to load a plan from a yaml file
<li>Reload Plan - this allows you to reload a plan previously opened (e.g. pick changes done in the file)
<li>Load Plan from Clipboard - this allows you to load a plan from the contents of the clipboard
<li>Save Plan - this saves the current plan to a yaml file
<li>Run Plan - this runs the current plan
Expand All @@ -31,7 +32,7 @@ <H2>Automation Tab</H2>
<li>Remove Test... - this removes the selected test from the current job
</ul>

Below the toolbar, you can find a graphical representation of the plan
Below the toolbar it's show the path of the loaded plan (if any), after you can find a graphical representation of the plan
which also shows the state of the plan when it is run.<br>
You can edit any of the elements of the plan by double clicking on them.
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ automation.dialog.plan.fromclipboard.notext = Cannot load plan from clipboard, d
automation.dialog.plan.load = Load Plan...
automation.dialog.plan.loosechanges = The current plan has unsaved changes.\nProceed and lose these changes?
automation.dialog.plan.new = New Plan...
automation.dialog.plan.reload = Reload Plan
automation.dialog.plan.run = Run Plan
automation.dialog.plan.save = Save Plan
automation.dialog.plan.save-as = Save Plan As...
Expand Down
Loading