Skip to content

Commit d97b65b

Browse files
committed
fuzz: Fix breadth/depth handling
Signed-off-by: kingthorin <kingthorin@users.noreply.github.com>
1 parent 28e494a commit d97b65b

5 files changed

Lines changed: 355 additions & 13 deletions

File tree

addOns/fuzz/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ 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+
### Fixed
8+
- Correct breadth-first payload replacement order when fuzzing multiple locations.
9+
- Breadth-first fuzz scans no longer report zero total requests when the number of payloads is known.
10+
711
### Changed
812
- Update minimum ZAP version to 2.17.0.
913
- Update dependency.

addOns/fuzz/src/main/java/org/zaproxy/zap/extension/fuzz/messagelocations/MultipleMessageLocationsBreadthFirstReplacer.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
import org.zaproxy.zap.extension.httppanel.Message;
3030
import org.zaproxy.zap.model.InvalidMessageException;
3131

32+
/**
33+
* Iterates the Cartesian product of payload lists with the first fuzz position (by {@code
34+
* MessageLocation} sort order) changing fastest.
35+
*/
3236
public class MultipleMessageLocationsBreadthFirstReplacer<T extends Message>
3337
implements MultipleMessageLocationsReplacer<T> {
3438

@@ -44,8 +48,8 @@ public class MultipleMessageLocationsBreadthFirstReplacer<T extends Message>
4448
private boolean initialised;
4549
private boolean setup;
4650

47-
private int tailIndex;
48-
private MessageLocationReplacementGenerator<?, ?> tail;
51+
private int headIndex;
52+
private MessageLocationReplacementGenerator<?, ?> head;
4953

5054
private long numberOfReplacements;
5155

@@ -65,6 +69,7 @@ public void init(
6569
listCurrentReplacements =
6670
new MessageLocationReplacement<?>[messageLocationReplacementGenerator.size()];
6771

72+
numberOfReplacements = 1;
6873
replacementGenerators = new ArrayList<>(messageLocationReplacementGenerator.size());
6974
for (MessageLocationReplacementGenerator<?, ?> mlr : messageLocationReplacementGenerator) {
7075
if (mlr.hasNext()) {
@@ -76,10 +81,11 @@ public void init(
7681
replacementGenerators.add(mlr);
7782
}
7883
}
79-
numberOfReplacements = 0;
8084

81-
tailIndex = replacementGenerators.size() - 1;
82-
tail = replacementGenerators.get(tailIndex);
85+
headIndex = 0;
86+
if (!replacementGenerators.isEmpty()) {
87+
head = replacementGenerators.get(headIndex);
88+
}
8389
initialised = true;
8490
setup = true;
8591
}
@@ -91,7 +97,7 @@ public long getNumberOfReplacements() {
9197

9298
@Override
9399
public boolean hasNext() {
94-
for (int i = tailIndex; i >= 0; i--) {
100+
for (int i = headIndex; i < replacementGenerators.size(); i++) {
95101
if (replacementGenerators.get(i).hasNext()) {
96102
return true;
97103
}
@@ -100,16 +106,16 @@ public boolean hasNext() {
100106
}
101107

102108
@Override
103-
public T next() throws InvalidMessageException {
109+
public T next() throws ReplacementException, InvalidMessageException {
104110
if (setup) {
105111
setup();
106112
setup = false;
107113
}
108114

109-
if (!tail.hasNext()) {
110-
tail.reset();
115+
if (!head.hasNext()) {
116+
head.reset();
111117

112-
for (int i = tailIndex - 1; i >= 0; i--) {
118+
for (int i = headIndex + 1; i < replacementGenerators.size(); i++) {
113119
if (replacementGenerators.get(i).hasNext()) {
114120
listCurrentReplacements[i] = replacementGenerators.get(i).next();
115121
break;
@@ -120,7 +126,7 @@ public T next() throws InvalidMessageException {
120126
}
121127
}
122128

123-
listCurrentReplacements[tailIndex] = tail.next();
129+
listCurrentReplacements[headIndex] = head.next();
124130

125131
currentReplacements.clear();
126132
currentReplacements.addAll(Arrays.asList(listCurrentReplacements));
@@ -129,7 +135,7 @@ public T next() throws InvalidMessageException {
129135
}
130136

131137
private void setup() {
132-
for (int i = 0; i < tailIndex; i++) {
138+
for (int i = headIndex + 1; i < replacementGenerators.size(); i++) {
133139
if (replacementGenerators.get(i).hasNext()) {
134140
listCurrentReplacements[i] = replacementGenerators.get(i).next();
135141
}

addOns/fuzz/src/main/java/org/zaproxy/zap/extension/fuzz/messagelocations/MultipleMessageLocationsDepthFirstReplacer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
import org.zaproxy.zap.extension.httppanel.Message;
3030
import org.zaproxy.zap.model.InvalidMessageException;
3131

32+
/**
33+
* Iterates the Cartesian product of payload lists with the last fuzz position (by {@code
34+
* MessageLocation} sort order) changing fastest.
35+
*/
3236
public class MultipleMessageLocationsDepthFirstReplacer<T extends Message>
3337
implements MultipleMessageLocationsReplacer<T> {
3438

addOns/fuzz/src/main/javahelp/org/zaproxy/zap/extension/fuzz/resources/help/contents/options.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ <H3>Max. Errors Allowed</H3>
3030
If the number of errors exceed this limit, the fuzzer will stop its execution.
3131

3232
<H3>Payload Replacement Strategy</H3>
33-
Rules defined to control the order that multiple payload lists are iterated.
33+
Rules defined to control the order that multiple payload lists are iterated when fuzzing more than one location.<br>
34+
The order follows the message location sort order, not the order the locations were added in the Fuzz dialog.<br>
35+
<ul>
36+
<li><strong>Depth First</strong> - exhausts all payloads in the last fuzz position before changing payloads in earlier positions.</li>
37+
<li><strong>Breadth First</strong> - exhausts all payloads in the first fuzz position before changing payloads in later positions.</li>
38+
</ul>
39+
With a single fuzz location both strategies produce the same order.
3440

3541
<H3>Concurrent Scanning Threads per Scan</H3>
3642
The number of threads the fuzzer will use per scan.<br>

0 commit comments

Comments
 (0)