forked from projectdiscovery/nuclei
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrequest_generator.go
137 lines (121 loc) · 4.35 KB
/
request_generator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package http
import (
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
)
// requestGenerator generates requests sequentially based on various
// configurations for a http request template.
//
// If payload values are present, an iterator is created for the payload
// values. Paths and Raw requests are supported as base input, so
// it will automatically select between them based on the template.
type requestGenerator struct {
currentIndex int
currentPayloads map[string]interface{}
okCurrentPayload bool
request *Request
options *protocols.ExecutorOptions
payloadIterator *generators.Iterator
interactshURLs []string
onceFlow map[string]struct{}
}
// LeaveDefaultPorts skips normalization of default standard ports
var LeaveDefaultPorts = false
// newGenerator creates a new request generator instance
func (request *Request) newGenerator(disablePayloads bool) *requestGenerator {
generator := &requestGenerator{
request: request,
options: request.options,
onceFlow: make(map[string]struct{}),
}
if len(request.Payloads) > 0 && !disablePayloads {
generator.payloadIterator = request.generator.NewIterator()
}
return generator
}
// nextValue returns the next path or the next raw request depending on user input
// It returns false if all the inputs have been exhausted by the generator instance.
func (r *requestGenerator) nextValue() (value string, payloads map[string]interface{}, result bool) {
// Iterate each payload sequentially for each request path/raw
//
// If the sequence has finished for the current payload values
// then restart the sequence from the beginning and move on to the next payloads values
// otherwise use the last request.
var sequence []string
switch {
case len(r.request.Path) > 0:
sequence = r.request.Path
case len(r.request.Raw) > 0:
sequence = r.request.Raw
default:
return "", nil, false
}
hasPayloadIterator := r.payloadIterator != nil
if hasPayloadIterator && r.currentPayloads == nil {
r.currentPayloads, r.okCurrentPayload = r.payloadIterator.Value()
}
var request string
var shouldContinue bool
if nextRequest, nextIndex, found := r.findNextIteration(sequence, r.currentIndex); found {
r.currentIndex = nextIndex + 1
request = nextRequest
shouldContinue = true
} else {
// if found is false which happens at end of iteration of reqData(path or raw request)
// try again from start with index 0
if nextRequest, nextIndex, found := r.findNextIteration(sequence, 0); found && hasPayloadIterator {
r.currentIndex = nextIndex + 1
request = nextRequest
shouldContinue = true
}
}
if shouldContinue {
if r.hasMarker(request, Once) {
r.applyMark(request, Once)
}
if hasPayloadIterator {
return request, generators.MergeMaps(r.currentPayloads), r.okCurrentPayload
}
// next should return a copy of payloads and not pointer to payload to avoid data race
return request, generators.MergeMaps(r.currentPayloads), true
} else {
return "", nil, false
}
}
// findNextIteration iterates and returns next Request(path or raw request)
// at end of each iteration payload is incremented
func (r *requestGenerator) findNextIteration(sequence []string, index int) (string, int, bool) {
for i, request := range sequence[index:] {
if r.wasMarked(request, Once) {
// if request contains flowmark i.e `@once` and is marked skip it
continue
}
return request, index + i, true
}
// move on to next payload if current payload is applied/returned for all Requests(path or raw request)
if r.payloadIterator != nil {
r.currentPayloads, r.okCurrentPayload = r.payloadIterator.Value()
}
return "", 0, false
}
// applyMark marks given request i.e blacklist request
func (r *requestGenerator) applyMark(request string, mark flowMark) {
switch mark {
case Once:
r.onceFlow[request] = struct{}{}
}
}
// wasMarked checks if request is marked using request blacklist
func (r *requestGenerator) wasMarked(request string, mark flowMark) bool {
switch mark {
case Once:
_, ok := r.onceFlow[request]
return ok
}
return false
}
// hasMarker returns true if request has a marker (ex: @once which means request should only be executed once)
func (r *requestGenerator) hasMarker(request string, mark flowMark) bool {
fo, hasOverrides := parseFlowAnnotations(request)
return hasOverrides && fo == mark
}