Skip to content

Commit ad254e0

Browse files
authored
Merge pull request #6541 from psiinon/automation/ascanexc
automation: Support exclude regexs in ascan config
2 parents 577a72f + b9e57a6 commit ad254e0

File tree

11 files changed

+184
-47
lines changed

11 files changed

+184
-47
lines changed

addOns/automation/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ All notable changes to this add-on will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
55

66
## Unreleased
7-
7+
### Added
8+
- Support for exclude regexes to active scan config job.
89

910
## [0.50.0] - 2025-06-20
1011
### Added

addOns/automation/src/main/java/org/zaproxy/addon/automation/ContextWrapper.java

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,11 @@
2424
import com.fasterxml.jackson.annotation.JsonIgnore;
2525
import com.fasterxml.jackson.databind.json.JsonMapper;
2626
import java.util.ArrayList;
27-
import java.util.Collections;
2827
import java.util.HashMap;
2928
import java.util.LinkedHashMap;
3029
import java.util.List;
3130
import java.util.Map;
3231
import java.util.Map.Entry;
33-
import java.util.regex.Pattern;
34-
import java.util.regex.PatternSyntaxException;
3532
import java.util.stream.Collectors;
3633
import lombok.Getter;
3734
import lombok.Setter;
@@ -190,10 +187,12 @@ public ContextWrapper(
190187
Constant.messages.getString("automation.error.context.url.deprecated"));
191188
break;
192189
case "includePaths":
193-
data.setIncludePaths(verifyRegexes(value, "badincludelist", progress));
190+
data.setIncludePaths(
191+
JobUtils.verifyRegexes(value, cdata.getKey().toString(), progress));
194192
break;
195193
case "excludePaths":
196-
data.setExcludePaths(verifyRegexes(value, "badexcludelist", progress));
194+
data.setExcludePaths(
195+
JobUtils.verifyRegexes(value, cdata.getKey().toString(), progress));
197196
break;
198197
case "authentication":
199198
data.setAuthentication(new AuthenticationData(value, progress));
@@ -307,31 +306,6 @@ private void validateUrl(String url, AutomationProgress progress) {
307306
}
308307
}
309308

310-
private List<String> verifyRegexes(Object value, String key, AutomationProgress progress) {
311-
if (!(value instanceof ArrayList<?>)) {
312-
progress.error(Constant.messages.getString("automation.error.context." + key, value));
313-
return Collections.emptyList();
314-
}
315-
ArrayList<String> regexes = new ArrayList<>();
316-
for (Object regex : (ArrayList<?>) value) {
317-
String regexStr = regex.toString();
318-
regexes.add(regexStr);
319-
try {
320-
if (!JobUtils.containsVars(regexStr)) {
321-
// Only validate the regex if it doesnt contain vars
322-
Pattern.compile(regexStr);
323-
}
324-
} catch (PatternSyntaxException e) {
325-
progress.error(
326-
Constant.messages.getString(
327-
"automation.error.context.badregex",
328-
regex.toString(),
329-
e.getMessage()));
330-
}
331-
}
332-
return regexes;
333-
}
334-
335309
public Context getContext() {
336310
return this.context;
337311
}

addOns/automation/src/main/java/org/zaproxy/addon/automation/gui/ActiveScanConfigJobDialog.java

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
*/
2020
package org.zaproxy.addon.automation.gui;
2121

22+
import java.util.List;
23+
import java.util.regex.Pattern;
24+
import java.util.stream.Stream;
25+
import org.parosproxy.paros.Constant;
2226
import org.parosproxy.paros.view.View;
2327
import org.zaproxy.addon.automation.jobs.ActiveScanConfigJob;
2428
import org.zaproxy.addon.automation.jobs.ActiveScanConfigJob.InputVectors;
@@ -32,7 +36,9 @@ public class ActiveScanConfigJobDialog extends StandardFieldsDialog {
3236
private static final long serialVersionUID = 1L;
3337

3438
private static final String[] TAB_LABELS = {
35-
"automation.dialog.tab.params", "automation.dialog.ascanconfig.tab.iv"
39+
"automation.dialog.tab.params",
40+
"automation.dialog.ascanconfig.tab.iv",
41+
"automation.dialog.ascanconfig.tab.exclude"
3642
};
3743

3844
private static final String TITLE = "automation.dialog.ascanconfig.title";
@@ -78,6 +84,8 @@ public class ActiveScanConfigJobDialog extends StandardFieldsDialog {
7884

7985
private static final String SCRIPTS_PARAM = "automation.dialog.ascanconfig.iv.scripts";
8086

87+
private static final String EXCLUDE_PARAM = "automation.dialog.ascanconfig.exclude";
88+
8189
private ActiveScanConfigJob job;
8290

8391
public ActiveScanConfigJobDialog(ActiveScanConfigJob job) {
@@ -182,6 +190,15 @@ public ActiveScanConfigJobDialog(ActiveScanConfigJob job) {
182190
addCheckBoxField(1, SCRIPTS_PARAM, iv.isScripts());
183191

184192
addPadding(1);
193+
194+
this.addMultilineField(2, EXCLUDE_PARAM, listToString(job.getData().getExcludePaths()));
195+
}
196+
197+
private String listToString(List<String> list) {
198+
if (list != null) {
199+
return String.join("\n", list);
200+
}
201+
return "";
185202
}
186203

187204
@Override
@@ -227,12 +244,32 @@ public void save() {
227244

228245
iv.setScripts(getBoolValue(SCRIPTS_PARAM));
229246

247+
job.getData().setExcludePaths(stringParamToList(EXCLUDE_PARAM));
248+
230249
job.resetAndSetChanged();
231250
}
232251

252+
private List<String> stringParamToList(String param) {
253+
// Return a list of the trimmed and non empty strings
254+
return Stream.of(this.getStringValue(param).split("\n"))
255+
.map(String::trim)
256+
.filter(item -> !item.isEmpty())
257+
.toList();
258+
}
259+
233260
@Override
234261
public String validateFields() {
235-
// Nothing to do
262+
for (String str : stringParamToList(EXCLUDE_PARAM)) {
263+
if (!JobUtils.containsVars(str)) {
264+
// Can only validate strings that dont contain env vars
265+
try {
266+
Pattern.compile(str);
267+
} catch (Exception e) {
268+
return Constant.messages.getString(
269+
"automation.dialog.ascanconfig.error.excregex", str);
270+
}
271+
}
272+
}
236273
return null;
237274
}
238275
}

addOns/automation/src/main/java/org/zaproxy/addon/automation/jobs/ActiveScanConfigJob.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,18 @@
2323
import com.fasterxml.jackson.databind.JsonMappingException;
2424
import com.fasterxml.jackson.databind.ObjectMapper;
2525
import com.fasterxml.jackson.databind.json.JsonMapper;
26+
import java.util.ArrayList;
2627
import java.util.BitSet;
28+
import java.util.List;
2729
import java.util.Map;
2830
import lombok.Getter;
2931
import lombok.Setter;
32+
import org.apache.logging.log4j.LogManager;
33+
import org.apache.logging.log4j.Logger;
3034
import org.parosproxy.paros.Constant;
3135
import org.parosproxy.paros.core.scanner.ScannerParam;
36+
import org.parosproxy.paros.db.DatabaseException;
37+
import org.parosproxy.paros.model.Model;
3238
import org.zaproxy.addon.automation.AutomationData;
3339
import org.zaproxy.addon.automation.AutomationEnvironment;
3440
import org.zaproxy.addon.automation.AutomationJob;
@@ -48,6 +54,8 @@ public class ActiveScanConfigJob extends AutomationJob {
4854
public static final String JOB_NAME = "activeScan-config";
4955
private static final String OPTIONS_METHOD_NAME = "getScannerParam";
5056

57+
private static final Logger LOGGER = LogManager.getLogger(ActiveScanConfigJob.class);
58+
5159
private ExtensionActiveScan ascan;
5260

5361
private Parameters parameters = new Parameters();
@@ -82,6 +90,11 @@ public void verifyParameters(AutomationProgress progress) {
8290
updateValue(data.getInputVectors(), jobData, key, progress);
8391
break;
8492

93+
case "excludePaths":
94+
data.setExcludePaths(
95+
JobUtils.verifyRegexes(jobData.get(key), key.toString(), progress));
96+
break;
97+
8598
case "name":
8699
case "type":
87100
break;
@@ -177,7 +190,13 @@ private static int toInt(BitSet bitSet) {
177190

178191
@Override
179192
public void runJob(AutomationEnvironment env, AutomationProgress progress) {
180-
// Nothing to do, work done earlier in applyParameters.
193+
try {
194+
Model.getSingleton().getSession().setExcludeFromScanRegexs(data.getExcludePaths());
195+
} catch (DatabaseException e) {
196+
progress.error(
197+
Constant.messages.getString("automation.dialog.error.misc", e.getMessage()));
198+
LOGGER.error(e.getMessage(), e);
199+
}
181200
}
182201

183202
@Override
@@ -245,6 +264,7 @@ public void showDialog() {
245264
public static class Data extends JobData {
246265
private final Parameters parameters;
247266
private final InputVectors inputVectors;
267+
@Setter private List<String> excludePaths = new ArrayList<>();
248268

249269
public Data(AutomationJob job, Parameters parameters, InputVectors inputVectors) {
250270
super(job);

addOns/automation/src/main/java/org/zaproxy/addon/automation/jobs/JobUtils.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import java.util.Locale;
3838
import java.util.Map;
3939
import java.util.Map.Entry;
40+
import java.util.regex.Pattern;
41+
import java.util.regex.PatternSyntaxException;
4042
import java.util.stream.Collectors;
4143
import java.util.stream.Stream;
4244
import org.apache.commons.lang3.ClassUtils;
@@ -758,4 +760,32 @@ public static boolean isAbsoluteLiteralPath(String path) {
758760
public static boolean containsVars(String str) {
759761
return str.contains("${");
760762
}
763+
764+
public static List<String> verifyRegexes(
765+
Object value, String key, AutomationProgress progress) {
766+
if (!(value instanceof ArrayList<?>)) {
767+
progress.error(
768+
Constant.messages.getString("automation.error.regex.badlist", key, value));
769+
return Collections.emptyList();
770+
}
771+
ArrayList<String> regexes = new ArrayList<>();
772+
for (Object regex : (ArrayList<?>) value) {
773+
String regexStr = regex.toString();
774+
regexes.add(regexStr);
775+
try {
776+
if (!JobUtils.containsVars(regexStr)) {
777+
// Only validate the regex if it doesnt contain vars
778+
Pattern.compile(regexStr);
779+
}
780+
} catch (PatternSyntaxException e) {
781+
progress.error(
782+
Constant.messages.getString(
783+
"automation.error.regex.badregex",
784+
regex.toString(),
785+
key,
786+
e.getMessage()));
787+
}
788+
}
789+
return regexes;
790+
}
761791
}

addOns/automation/src/main/javahelp/org/zaproxy/addon/automation/resources/help/contents/job-ascanconfig.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ <H2>YAML</H2>
4444
enabled: # Bool: If enabled. Default: false
4545
encodeCookieValues: # Bool: If cookie values should be encoded. Default: false
4646
scripts: # Bool: If Input Vector scripts should be used. Default: true
47+
excludePaths: # An optional list of regexes to exclude
4748
</pre>
4849

50+
Note that the 'excludePaths' will overwrite any existing session "Exclude from Scanner" paths.
51+
4952
</BODY>
5053
</HTML>
5154

addOns/automation/src/main/resources/org/zaproxy/addon/automation/resources/Messages.properties

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ automation.dialog.ascan.threads = Threads Per Host:
8787
automation.dialog.ascan.title = Active Scan Job
8888

8989
automation.dialog.ascanconfig.defaultpolicy = Default Policy:
90+
automation.dialog.ascanconfig.error.excregex = Invalid ''Exclude'' RegEx: {0}
9091
automation.dialog.ascanconfig.error.field = Job {0}: Error reading {1}, cause: {2}
92+
automation.dialog.ascanconfig.exclude = Exclude RegExs:
9193
automation.dialog.ascanconfig.handleanticsrf = Handle Anti CSRF Tokens
9294
automation.dialog.ascanconfig.injectid = Inject Scan Rule ID:
9395
automation.dialog.ascanconfig.iv.cookie = Cookie Data
@@ -110,6 +112,7 @@ automation.dialog.ascanconfig.maxalertsperrule = Max Alerts Per Rule:
110112
automation.dialog.ascanconfig.maxruleduration = Max Rule Duration (in mins):
111113
automation.dialog.ascanconfig.maxscanduration = Max Scan Duration (in mins):
112114
automation.dialog.ascanconfig.summary = Active Scan Config
115+
automation.dialog.ascanconfig.tab.exclude = Exclude
113116
automation.dialog.ascanconfig.tab.iv = Input Vectors
114117
automation.dialog.ascanconfig.threads = Threads Per Host:
115118
automation.dialog.ascanconfig.title = Active Scan Config Job
@@ -125,8 +128,8 @@ automation.dialog.button.remove = Remove
125128

126129
automation.dialog.context.error.badname = You must supply a name
127130
automation.dialog.context.error.badurl = Invalid URL: {0}
128-
automation.dialog.context.error.excregex = Invalid 'Exclude' RegEx: {0}
129-
automation.dialog.context.error.incregex = Invalid 'Include' RegEx: {0}
131+
automation.dialog.context.error.excregex = Invalid ''Exclude'' RegEx: {0}
132+
automation.dialog.context.error.incregex = Invalid ''Include'' RegEx: {0}
130133
automation.dialog.context.error.nourls = You must specify at least one URL
131134
automation.dialog.context.exclude = Exclude RegExs:
132135
automation.dialog.context.include = Include RegExs:
@@ -294,9 +297,6 @@ automation.error.badconfidence = Invalid confidence for job {0} : {1}
294297
automation.error.badrisk = Invalid risk for job {0} : {1}
295298
automation.error.badurl = Invalid URL for job {0} : {1}
296299

297-
automation.error.context.badexcludelist = Exclude regexes should be a list: {0}
298-
automation.error.context.badincludelist = Incude regexes should be a list: {0}
299-
automation.error.context.badregex = Invalid value for regex: {0} : {1}
300300
automation.error.context.badstructuralparametername = You must supply a Structural Parameter name that contains only alpha numeric characters: {0}
301301
automation.error.context.badstructuralparameterslist = Context Structural Parameters should be a list: {0}
302302
automation.error.context.badstructure = Context structure should be a map: {0}
@@ -356,6 +356,8 @@ automation.error.options.method = Failed to access {0} methods for {1} : {2}
356356
automation.error.options.methods = Failed to access {0} methods for {1}
357357
automation.error.options.unknown = Unrecognised parameter for job {0} : {1}
358358
automation.error.read = Cannot read file: {0}
359+
automation.error.regex.badlist = Regexes for key {0} should be a list: {1}
360+
automation.error.regex.badregex = Invalid regex: {0} for key {1} : {2}
359361
automation.error.requestor.badcode = Job {0} has invalid response code {1} for request: {2}
360362
automation.error.requestor.badheader = Headers should be a list: {0}
361363
automation.error.requestor.badlist = Requests should be a list: {0}

addOns/automation/src/main/resources/org/zaproxy/addon/automation/resources/activeScan-config-max.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@
2929
enabled: # Bool: If enabled. Default: false
3030
encodeCookieValues: # Bool: If cookie values should be encoded. Default: false
3131
scripts: # Bool: If Input Vector scripts should be used. Default: true
32+
excludePaths: # An optional list of regexes to exclude

addOns/automation/src/test/java/org/zaproxy/addon/automation/AutomationEnvironmentUnitTest.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,7 @@ void shouldFailIfBadIncludeRegexList() {
380380
// Then
381381
assertThat(progress.hasErrors(), is(equalTo(true)));
382382
assertThat(progress.getErrors().size(), is(equalTo(1)));
383-
assertThat(
384-
progress.getErrors().get(0),
385-
is(equalTo("!automation.error.context.badincludelist!")));
383+
assertThat(progress.getErrors().get(0), is(equalTo("!automation.error.regex.badlist!")));
386384
}
387385

388386
@Test
@@ -407,9 +405,7 @@ void shouldFailIfBadExcludeRegexList() {
407405
// Then
408406
assertThat(progress.hasErrors(), is(equalTo(true)));
409407
assertThat(progress.getErrors().size(), is(equalTo(1)));
410-
assertThat(
411-
progress.getErrors().get(0),
412-
is(equalTo("!automation.error.context.badexcludelist!")));
408+
assertThat(progress.getErrors().get(0), is(equalTo("!automation.error.regex.badlist!")));
413409
}
414410

415411
@Test
@@ -435,7 +431,7 @@ void shouldFailIfBadIncludeRegexValue() {
435431
// Then
436432
assertThat(progress.hasErrors(), is(equalTo(true)));
437433
assertThat(progress.getErrors().size(), is(equalTo(1)));
438-
assertThat(progress.getErrors().get(0), is(equalTo("!automation.error.context.badregex!")));
434+
assertThat(progress.getErrors().get(0), is(equalTo("!automation.error.regex.badregex!")));
439435
}
440436

441437
@Test
@@ -461,7 +457,7 @@ void shouldFailIfBadExcludeRegexValue() {
461457
// Then
462458
assertThat(progress.hasErrors(), is(equalTo(true)));
463459
assertThat(progress.getErrors().size(), is(equalTo(1)));
464-
assertThat(progress.getErrors().get(0), is(equalTo("!automation.error.context.badregex!")));
460+
assertThat(progress.getErrors().get(0), is(equalTo("!automation.error.regex.badregex!")));
465461
}
466462

467463
@Test

0 commit comments

Comments
 (0)