Skip to content

Commit

Permalink
Merge pull request #5845 from dossantospat/feature/af-enable-flag
Browse files Browse the repository at this point in the history
automation: add a flag to enable/disable jobs
  • Loading branch information
kingthorin authored Nov 11, 2024
2 parents 676d9e8 + de74a90 commit a296372
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 7 deletions.
1 change: 1 addition & 0 deletions addOns/automation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
- Active scan policy job.
- Add job to configure the active scanner, `activeScan-config`.
- Allow to enable/disable jobs (Issue 5845).

### Changed
- Updated automation framework documentation and templates for `activeScan` job to reflect changes to the default value of threadPerHost parameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public abstract class AutomationJob implements Comparable<AutomationJob> {
private static final int ZERO_TESTS = 0;

public enum Status {
NOT_ENABLED,
NOT_STARTED,
RUNNING,
COMPLETED
Expand All @@ -62,6 +63,7 @@ public enum Status {
private AutomationPlan plan;
private long timeStarted;
private long timeFinished;
private boolean enabled = true;

public enum Order {
RUN_FIRST,
Expand Down Expand Up @@ -123,6 +125,14 @@ public String getSummary() {
return EMPTY_SUMMARY;
}

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public int addDefaultTests(AutomationProgress progress) {
return ZERO_TESTS;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ public AutomationPlan(ExtensionAutomation ext, File file) throws IOException {
"automation.error.job.data", paramsObj));
continue;
}

Object jobEnabled = jobData.get("enabled");
if (jobEnabled != null) {
if (jobEnabled instanceof Boolean) {
job.setEnabled((Boolean) jobEnabled);
} else {
progress.warn(
Constant.messages.getString(
"automation.error.job.enabled", jobEnabled));
}
}

job.setEnv(env);
job.setJobData(jobData);
job.verifyParameters(progress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,14 @@ public AutomationProgress runPlan(AutomationPlan plan, boolean resetProgress) {
}

for (AutomationJob job : jobsToRun) {

if (!job.isEnabled()) {
progress.info(
Constant.messages.getString("automation.info.jobdisabled", job.getType()));
job.setStatus(AutomationJob.Status.NOT_ENABLED);
continue;
}

job.applyParameters(progress);
progress.info(Constant.messages.getString("automation.info.jobstart", job.getType()));
job.setStatus(AutomationJob.Status.RUNNING);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ public class AutomationPanel extends AbstractPanel implements EventConsumer {
protected static final ImageIcon WHITE_BALL_ICON =
DisplayUtils.getScaledIcon(
new ImageIcon(AutomationPanel.class.getResource("/resource/icon/16/160.png")));
protected static final ImageIcon GREY_BALL_ICON =
DisplayUtils.getScaledIcon(
new ImageIcon(AutomationPanel.class.getResource("/resource/icon/16/158.png")));

private static final Logger LOGGER = LogManager.getLogger(AutomationPanel.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ public Component getTreeCellRendererComponent(
case RUNNING:
this.setIcon(AutomationPanel.YELLOW_BALL_ICON);
break;
case NOT_ENABLED:
this.setIcon(AutomationPanel.GREY_BALL_ICON);
break;
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,18 @@ public PlanTreeTableModel(DefaultMutableTreeNode root) {
}

protected static final int HIERARCHY_INDEX = 0;
protected static final int STATUS_INDEX = 1;
protected static final int TIME_INDEX = 2;
protected static final int TYPE_INDEX = 3;
protected static final int NAME_INDEX = 4;
protected static final int INFO_INDEX = 5;
protected static final int ENABLED_INDEX = 1;
protected static final int STATUS_INDEX = 2;
protected static final int TIME_INDEX = 3;
protected static final int TYPE_INDEX = 4;
protected static final int NAME_INDEX = 5;
protected static final int INFO_INDEX = 6;

private static final String INDENT = " ";

private static final String[] COLUMN_NAMES = {
"", // The tree control
Constant.messages.getString("automation.panel.table.header.enabled"),
Constant.messages.getString("automation.panel.table.header.status"),
Constant.messages.getString("automation.panel.table.header.time"),
Constant.messages.getString("automation.panel.table.header.type"),
Expand Down Expand Up @@ -113,6 +115,8 @@ public Object getValueAt(Object node, int columnIndex) {
switch (columnIndex) {
case HIERARCHY_INDEX:
return "";
case ENABLED_INDEX:
return true;
case STATUS_INDEX:
if (!env.isCreated()) {
return Constant.messages.getString(
Expand Down Expand Up @@ -143,12 +147,15 @@ public Object getValueAt(Object node, int columnIndex) {
} else {
return env.getSummary();
}

default:
}
} else if (obj instanceof AutomationJob) {
AutomationJob job = (AutomationJob) obj;
JobResults jobResults = plan.getProgress().getJobResults(job);
switch (columnIndex) {
case ENABLED_INDEX:
return job.isEnabled();
case STATUS_INDEX:
switch (job.getStatus()) {
case COMPLETED:
Expand All @@ -171,6 +178,9 @@ public Object getValueAt(Object node, int columnIndex) {
case RUNNING:
return Constant.messages.getString(
"automation.panel.table.status.running");
case NOT_ENABLED:
return Constant.messages.getString(
"automation.panel.table.status.notenabled");
default:
break;
}
Expand Down Expand Up @@ -203,6 +213,8 @@ public Object getValueAt(Object node, int columnIndex) {
} else if (obj instanceof AbstractAutomationTest) {
AbstractAutomationTest test = (AbstractAutomationTest) obj;
switch (columnIndex) {
case ENABLED_INDEX:
return true;
case STATUS_INDEX:
if (!test.hasRun()) {
return "";
Expand Down Expand Up @@ -248,7 +260,12 @@ public Object getValueAt(Object node, int columnIndex) {

@Override
public Class<?> getColumnClass(int columnIndex) {
return null;
switch (columnIndex) {
case ENABLED_INDEX:
return Boolean.class;
default:
return String.class;
}
}

@Override
Expand All @@ -258,12 +275,31 @@ public int getHierarchicalColumn() {

@Override
public boolean isCellEditable(Object node, int column) {
if (column == ENABLED_INDEX) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
if (!treeNode.isRoot()) {
Object obj = treeNode.getUserObject();
return (obj instanceof AutomationJob);
}
}

return false;
}

@Override
public void setValueAt(Object value, Object node, int column) {
// Not supported
if (column == ENABLED_INDEX) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
if (!treeNode.isRoot()) {
Object obj = treeNode.getUserObject();
if (obj instanceof AutomationJob) {
AutomationJob job = (AutomationJob) obj;
job.setEnabled(((Boolean) value).booleanValue());
job.setChanged();
jobChanged(job);
}
}
}
}

public void envChanged() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
package org.zaproxy.addon.automation.jobs;

import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
import java.util.stream.Collectors;
import org.zaproxy.addon.automation.AutomationData;
Expand All @@ -39,6 +40,9 @@ public boolean isDefaultValue(String name) {
if ("name".equals(name)) {
return getName().equals(getType());
}
if ("enabled".equals(name)) {
return isEnabled();
}
return super.isDefaultValue(name);
}

Expand All @@ -54,6 +58,15 @@ public void setName(String name) {
this.job.setName(name);
}

@JsonInclude(value = JsonInclude.Include.ALWAYS)
public boolean isEnabled() {
return this.job.isEnabled();
}

public void setEnabled(boolean enabled) {
this.job.setEnabled(enabled);
}

public List<AutomationData> getTests() {
List<AbstractAutomationTest> tests = this.job.getTests();
if (tests.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ <H2>File Paths</H2>
Relative paths are recommended for portability.

<H2>Jobs</H2>
The jobs can be enabled/disabled through the GUI and the automation plan, with the <code>enabled</code> flag. Jobs are enabled by default.
<p>
The following automation jobs are supported by this add-on:
<ul>
<li><a href="job-ascanconfig.html">activeScan-config</a> - configures the active scanner</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ automation.dialog.alerttest.ruleid = Scan Rule Id:
automation.dialog.alerttest.title = Alert Test
automation.dialog.alerttest.url = URL (regex):

automation.dialog.all.enabled = Enabled:
automation.dialog.all.name = Job Name:
automation.dialog.all.user = Authenticated User:

Expand Down Expand Up @@ -329,6 +330,7 @@ automation.error.env.verification.pollunits.bad = Invalid verification pollUnits
automation.error.env.verification.type.bad = Invalid verification method: {0}
automation.error.job.baduser = Job {0} unrecognised user: {1}
automation.error.job.data = Unsupported job data format: {0}
automation.error.job.enabled = Unsupported job enabled format: {0}
automation.error.job.internal = Job {0} internal error: {1}
automation.error.job.name = Unsupported job name format: {0}
automation.error.job.notype = Missing job type: {0}
Expand Down Expand Up @@ -370,6 +372,7 @@ automation.info.delay.endjob = Job {0} ended by programmatic or API call
automation.info.delay.filecreated = Job {0} ended by creation of file {1}
automation.info.delay.interrupted = Job {0} interrupted
automation.info.delay.timeout = Job {0} ended after specified time {1}
automation.info.jobdisabled = Job {0} is disabled
automation.info.jobend = Job {0} finished, time taken: {1}
automation.info.jobstart = Job {0} started
automation.info.jobstopped = Job {0} terminated
Expand All @@ -393,6 +396,7 @@ automation.panel.load.failed = YAML file failed to load: {0}
automation.panel.load.warning = YAML file loaded with warnings: {0}
automation.panel.load.yaml = YAML Configuration Files
automation.panel.table.env.name = Environment
automation.panel.table.header.enabled = Enabled
automation.panel.table.header.info = Info
automation.panel.table.header.name = Name
automation.panel.table.header.status = Status
Expand All @@ -405,6 +409,7 @@ automation.panel.table.info.warning = WARNING: {0}
automation.panel.table.status.error = ERROR
automation.panel.table.status.failed = FAILED
automation.panel.table.status.notcreated = Not created
automation.panel.table.status.notenabled = Not enabled
automation.panel.table.status.notstarted = Not started
automation.panel.table.status.ok = OK
automation.panel.table.status.passed = Passed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,19 @@ public Order getOrder() {
assertThat(list.get(3), is(equalTo(lastJob)));
}

@Test
void shouldBeEnabledByDefault() {
// Given
TestParamContainer tpc = new TestParamContainer();
AutomationJob job = new AutomationJobImpl(tpc);

// When
boolean isEnabled = job.isEnabled();

// Then
assertThat(isEnabled, is(equalTo(true)));
}

// Methods are accessed via reflection
private static class TestParamContainer {
private TestParam testParam = new TestParam();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,63 @@ public AutomationJob newJob() {
assertThat(progress.getErrors(), contains("!automation.error.unexpected.internal!"));
}

@Test
void shouldIgnoreDisabledJob() throws Exception {
// Given
ExtensionAutomation extAuto = new ExtensionAutomation();
String job1Name = "job1";

AutomationJobImpl job1 =
new AutomationJobImpl() {
@Override
public String getType() {
return job1Name;
}
};

String job2Name = "job2";
AutomationJobImpl job2 =
new AutomationJobImpl() {
@Override
public String getType() {
return job2Name;
}
};
String job3Name = "job3";
AutomationJobImpl job3 =
new AutomationJobImpl() {
@Override
public String getType() {
return job3Name;
}
};
Path filePath = getResourcePath("resources/testplan-withDisabledJob.yaml");
InMemoryStats stats = new InMemoryStats();
Stats.addListener(stats);
extAuto.registerAutomationJob(job1);
extAuto.registerAutomationJob(job2);
extAuto.registerAutomationJob(job3);
File f = new File(filePath.toAbsolutePath().toString());

AutomationPlan plan = new AutomationPlan(extAuto, f);

// When
extAuto.runPlan(plan, false);
AutomationProgress progress = plan.getProgress();

List<AutomationJob> runJobs = progress.getRunJobs();

// Then
assertThat(plan.getJob(0).getStatus(), is(equalTo(AutomationJob.Status.NOT_ENABLED)));
assertThat(runJobs.size(), is(equalTo(2)));
assertThat(runJobs.get(0).getName(), is(equalTo("Job 2")));
assertThat(runJobs.get(0).getStatus(), is(equalTo(AutomationJob.Status.COMPLETED)));
assertThat(runJobs.get(1).getName(), is(equalTo("Job 3")));
assertThat(runJobs.get(1).getStatus(), is(equalTo(AutomationJob.Status.COMPLETED)));
assertThat(progress.hasWarnings(), is(equalTo(false)));
assertThat(progress.hasErrors(), is(equalTo(false)));
}

// Methods are accessed via reflection
@SuppressWarnings("unused")
private static class TestParamContainer {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
env:
contexts:
- name: example
urls:
- https://www.example.com/
parameters:
failOnError: false
failOnWarning: false
progressToStdout: false

jobs:
- type: job1
name: "Job 1"
enabled: false
parameters:

- type: job2
name: "Job 2"
enabled: true
parameters:

- type: job3 # jobs are automatically enabled if the "enabled" key is missing
name: "Job 3"
parameters:

0 comments on commit a296372

Please sign in to comment.