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