github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/offlinehttp/operators.go (about) 1 package offlinehttp 2 3 import ( 4 "net/http" 5 "strings" 6 "time" 7 8 "github.com/projectdiscovery/nuclei/v2/pkg/model" 9 "github.com/projectdiscovery/nuclei/v2/pkg/operators" 10 "github.com/projectdiscovery/nuclei/v2/pkg/operators/extractors" 11 "github.com/projectdiscovery/nuclei/v2/pkg/operators/matchers" 12 "github.com/projectdiscovery/nuclei/v2/pkg/output" 13 "github.com/projectdiscovery/nuclei/v2/pkg/protocols" 14 "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/responsehighlighter" 15 "github.com/projectdiscovery/nuclei/v2/pkg/protocols/utils" 16 "github.com/projectdiscovery/nuclei/v2/pkg/types" 17 ) 18 19 // Match matches a generic data response again a given matcher 20 func (request *Request) Match(data map[string]interface{}, matcher *matchers.Matcher) (bool, []string) { 21 item, ok := getMatchPart(matcher.Part, data) 22 if !ok && matcher.Type.MatcherType != matchers.DSLMatcher { 23 return false, []string{} 24 } 25 26 switch matcher.GetType() { 27 case matchers.StatusMatcher: 28 statusCode, ok := getStatusCode(data) 29 if !ok { 30 return false, []string{} 31 } 32 return matcher.Result(matcher.MatchStatusCode(statusCode)), []string{responsehighlighter.CreateStatusCodeSnippet(data["response"].(string), statusCode)} 33 case matchers.SizeMatcher: 34 return matcher.Result(matcher.MatchSize(len(item))), []string{} 35 case matchers.WordsMatcher: 36 return matcher.ResultWithMatchedSnippet(matcher.MatchWords(item, nil)) 37 case matchers.RegexMatcher: 38 return matcher.ResultWithMatchedSnippet(matcher.MatchRegex(item)) 39 case matchers.BinaryMatcher: 40 return matcher.ResultWithMatchedSnippet(matcher.MatchBinary(item)) 41 case matchers.DSLMatcher: 42 return matcher.Result(matcher.MatchDSL(data)), []string{} 43 case matchers.XPathMatcher: 44 return matcher.Result(matcher.MatchXPath(item)), []string{} 45 } 46 return false, []string{} 47 } 48 49 func getStatusCode(data map[string]interface{}) (int, bool) { 50 statusCodeValue, ok := data["status_code"] 51 if !ok { 52 return 0, false 53 } 54 statusCode, ok := statusCodeValue.(int) 55 if !ok { 56 return 0, false 57 } 58 return statusCode, true 59 } 60 61 // Extract performs extracting operation for an extractor on model and returns true or false. 62 func (request *Request) Extract(data map[string]interface{}, extractor *extractors.Extractor) map[string]struct{} { 63 item, ok := getMatchPart(extractor.Part, data) 64 if !ok && !extractors.SupportsMap(extractor) { 65 return nil 66 } 67 switch extractor.GetType() { 68 case extractors.RegexExtractor: 69 return extractor.ExtractRegex(item) 70 case extractors.KValExtractor: 71 return extractor.ExtractKval(data) 72 case extractors.DSLExtractor: 73 return extractor.ExtractDSL(data) 74 } 75 return nil 76 } 77 78 // getMatchPart returns the match part honoring "all" matchers + others. 79 func getMatchPart(part string, data output.InternalEvent) (string, bool) { 80 if part == "header" { 81 part = "all_headers" 82 } 83 var itemStr string 84 85 if part == "all" { 86 builder := &strings.Builder{} 87 builder.WriteString(types.ToString(data["body"])) 88 builder.WriteString(types.ToString(data["all_headers"])) 89 itemStr = builder.String() 90 } else { 91 item, ok := data[part] 92 if !ok { 93 return "", false 94 } 95 itemStr = types.ToString(item) 96 } 97 return itemStr, true 98 } 99 100 // responseToDSLMap converts an HTTP response to a map for use in DSL matching 101 func (request *Request) responseToDSLMap(resp *http.Response, host, matched, rawReq, rawResp, body, headers string, duration time.Duration, extra map[string]interface{}) output.InternalEvent { 102 data := make(output.InternalEvent, 12+len(extra)+len(resp.Header)+len(resp.Cookies())) 103 for k, v := range extra { 104 data[k] = v 105 } 106 for _, cookie := range resp.Cookies() { 107 data[strings.ToLower(cookie.Name)] = cookie.Value 108 } 109 for k, v := range resp.Header { 110 k = strings.ToLower(strings.TrimSpace(k)) 111 data[k] = strings.Join(v, " ") 112 } 113 114 data["path"] = host 115 data["matched"] = matched 116 data["request"] = rawReq 117 data["response"] = rawResp 118 data["status_code"] = resp.StatusCode 119 data["body"] = body 120 data["type"] = request.Type().String() 121 data["all_headers"] = headers 122 data["duration"] = duration.Seconds() 123 data["template-id"] = request.options.TemplateID 124 data["template-info"] = request.options.TemplateInfo 125 data["template-path"] = request.options.TemplatePath 126 data["content_length"] = utils.CalculateContentLength(resp.ContentLength, int64(len(body))) 127 128 return data 129 } 130 131 // MakeResultEvent creates a result event from internal wrapped event 132 func (request *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*output.ResultEvent { 133 return protocols.MakeDefaultResultEvent(request, wrapped) 134 } 135 136 func (request *Request) GetCompiledOperators() []*operators.Operators { 137 return request.compiledOperators 138 } 139 140 func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent { 141 data := &output.ResultEvent{ 142 TemplateID: types.ToString(wrapped.InternalEvent["template-id"]), 143 TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]), 144 Info: wrapped.InternalEvent["template-info"].(model.Info), 145 Type: types.ToString(wrapped.InternalEvent["type"]), 146 Path: types.ToString(wrapped.InternalEvent["path"]), 147 Matched: types.ToString(wrapped.InternalEvent["matched"]), 148 Metadata: wrapped.OperatorsResult.PayloadValues, 149 ExtractedResults: wrapped.OperatorsResult.OutputExtracts, 150 MatcherStatus: true, 151 IP: types.ToString(wrapped.InternalEvent["ip"]), 152 Request: types.ToString(wrapped.InternalEvent["request"]), 153 Response: types.ToString(wrapped.InternalEvent["raw"]), 154 } 155 return data 156 }