github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/http/request_generator.go (about) 1 package http 2 3 import ( 4 "github.com/projectdiscovery/nuclei/v2/pkg/protocols" 5 "github.com/projectdiscovery/nuclei/v2/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 }