Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/http/request_generator.go
2070 views
1
package http
2
3
import (
4
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
5
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
6
)
7
8
// requestGenerator generates requests sequentially based on various
9
// configurations for a http request template.
10
//
11
// If payload values are present, an iterator is created for the payload
12
// values. Paths and Raw requests are supported as base input, so
13
// it will automatically select between them based on the template.
14
type requestGenerator struct {
15
currentIndex int
16
currentPayloads map[string]interface{}
17
okCurrentPayload bool
18
request *Request
19
options *protocols.ExecutorOptions
20
payloadIterator *generators.Iterator
21
interactshURLs []string
22
onceFlow map[string]struct{}
23
}
24
25
// LeaveDefaultPorts skips normalization of default standard ports
26
var LeaveDefaultPorts = false
27
28
// newGenerator creates a new request generator instance
29
func (request *Request) newGenerator(disablePayloads bool) *requestGenerator {
30
generator := &requestGenerator{
31
request: request,
32
options: request.options,
33
onceFlow: make(map[string]struct{}),
34
}
35
36
if len(request.Payloads) > 0 && !disablePayloads {
37
generator.payloadIterator = request.generator.NewIterator()
38
}
39
return generator
40
}
41
42
// nextValue returns the next path or the next raw request depending on user input
43
// It returns false if all the inputs have been exhausted by the generator instance.
44
func (r *requestGenerator) nextValue() (value string, payloads map[string]interface{}, result bool) {
45
// Iterate each payload sequentially for each request path/raw
46
//
47
// If the sequence has finished for the current payload values
48
// then restart the sequence from the beginning and move on to the next payloads values
49
// otherwise use the last request.
50
var sequence []string
51
switch {
52
case len(r.request.Path) > 0:
53
sequence = r.request.Path
54
case len(r.request.Raw) > 0:
55
sequence = r.request.Raw
56
default:
57
return "", nil, false
58
}
59
60
hasPayloadIterator := r.payloadIterator != nil
61
62
if hasPayloadIterator && r.currentPayloads == nil {
63
r.currentPayloads, r.okCurrentPayload = r.payloadIterator.Value()
64
}
65
66
var request string
67
var shouldContinue bool
68
if nextRequest, nextIndex, found := r.findNextIteration(sequence, r.currentIndex); found {
69
r.currentIndex = nextIndex + 1
70
request = nextRequest
71
shouldContinue = true
72
} else {
73
// if found is false which happens at end of iteration of reqData(path or raw request)
74
// try again from start with index 0
75
if nextRequest, nextIndex, found := r.findNextIteration(sequence, 0); found && hasPayloadIterator {
76
r.currentIndex = nextIndex + 1
77
request = nextRequest
78
shouldContinue = true
79
}
80
}
81
82
if shouldContinue {
83
if r.hasMarker(request, Once) {
84
r.applyMark(request, Once)
85
}
86
if hasPayloadIterator {
87
return request, generators.MergeMaps(r.currentPayloads), r.okCurrentPayload
88
}
89
// next should return a copy of payloads and not pointer to payload to avoid data race
90
return request, generators.MergeMaps(r.currentPayloads), true
91
} else {
92
return "", nil, false
93
}
94
}
95
96
// findNextIteration iterates and returns next Request(path or raw request)
97
// at end of each iteration payload is incremented
98
func (r *requestGenerator) findNextIteration(sequence []string, index int) (string, int, bool) {
99
for i, request := range sequence[index:] {
100
if r.wasMarked(request, Once) {
101
// if request contains flowmark i.e `@once` and is marked skip it
102
continue
103
}
104
return request, index + i, true
105
106
}
107
// move on to next payload if current payload is applied/returned for all Requests(path or raw request)
108
if r.payloadIterator != nil {
109
r.currentPayloads, r.okCurrentPayload = r.payloadIterator.Value()
110
}
111
return "", 0, false
112
}
113
114
// applyMark marks given request i.e blacklist request
115
func (r *requestGenerator) applyMark(request string, mark flowMark) {
116
switch mark {
117
case Once:
118
r.onceFlow[request] = struct{}{}
119
}
120
121
}
122
123
// wasMarked checks if request is marked using request blacklist
124
func (r *requestGenerator) wasMarked(request string, mark flowMark) bool {
125
switch mark {
126
case Once:
127
_, ok := r.onceFlow[request]
128
return ok
129
}
130
return false
131
}
132
133
// hasMarker returns true if request has a marker (ex: @once which means request should only be executed once)
134
func (r *requestGenerator) hasMarker(request string, mark flowMark) bool {
135
fo, hasOverrides := parseFlowAnnotations(request)
136
return hasOverrides && fo == mark
137
}
138
139
// Remaining returns the number of requests that are still left to be
140
// generated (and therefore to be sent) by this generator.
141
func (r *requestGenerator) Remaining() int {
142
var sequence []string
143
switch {
144
case len(r.request.Path) > 0:
145
sequence = r.request.Path
146
case len(r.request.Raw) > 0:
147
sequence = r.request.Raw
148
default:
149
return 0
150
}
151
152
remainingInCurrentPass := 0
153
for i := r.currentIndex; i < len(sequence); i++ {
154
if !r.hasMarker(sequence[i], Once) {
155
remainingInCurrentPass++
156
}
157
}
158
159
if r.payloadIterator == nil {
160
return remainingInCurrentPass
161
}
162
163
numRemainingPayloadSets := r.payloadIterator.Remaining()
164
totalValidInSequence := 0
165
for _, req := range sequence {
166
if !r.hasMarker(req, Once) {
167
totalValidInSequence++
168
}
169
}
170
171
// Total remaining = remaining in current pass + (remaining payload sets * requests per full pass)
172
return remainingInCurrentPass + numRemainingPayloadSets*totalValidInSequence
173
}
174
175
func (r *requestGenerator) Total() int {
176
var sequence []string
177
switch {
178
case len(r.request.Path) > 0:
179
sequence = r.request.Path
180
case len(r.request.Raw) > 0:
181
sequence = r.request.Raw
182
default:
183
return 0
184
}
185
186
applicableRequests := 0
187
additionalRequests := 0
188
for _, request := range sequence {
189
if !r.hasMarker(request, Once) {
190
applicableRequests++
191
} else {
192
additionalRequests++
193
}
194
}
195
196
if r.payloadIterator == nil {
197
return applicableRequests + additionalRequests
198
}
199
200
return (applicableRequests * r.payloadIterator.Total()) + additionalRequests
201
}
202
203