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  }