forked from projectdiscovery/nuclei
-
Notifications
You must be signed in to change notification settings - Fork 0
/
request_generator.go
125 lines (108 loc) · 3.56 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
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.ExecuterOptions
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 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, r.currentPayloads, r.okCurrentPayload
}
return request, r.currentPayloads, true
} else {
return "", nil, false
}
}
func (r *requestGenerator) findNextIteration(sequence []string, index int) (string, int, bool) {
for i, request := range sequence[index:] {
if !r.wasMarked(request, Once) {
return request, index + i, true
}
}
if r.payloadIterator != nil {
r.currentPayloads, r.okCurrentPayload = r.payloadIterator.Value()
}
return "", 0, false
}
func (r *requestGenerator) applyMark(request string, mark flowMark) {
switch mark {
case Once:
r.onceFlow[request] = struct{}{}
}
}
func (r *requestGenerator) wasMarked(request string, mark flowMark) bool {
switch mark {
case Once:
_, ok := r.onceFlow[request]
return ok
}
return false
}
func (r *requestGenerator) hasMarker(request string, mark flowMark) bool {
fo, hasOverrides := parseFlowAnnotations(request)
return hasOverrides && fo == mark
}