Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/common/contextargs/metainput.go
2072 views
1
package contextargs
2
3
import (
4
"bytes"
5
"crypto/md5"
6
"fmt"
7
"net"
8
"strings"
9
"sync"
10
11
jsoniter "github.com/json-iterator/go"
12
"github.com/projectdiscovery/nuclei/v3/pkg/input/types"
13
urlutil "github.com/projectdiscovery/utils/url"
14
"github.com/segmentio/ksuid"
15
)
16
17
// MetaInput represents a target with metadata (TODO: replace with https://github.com/projectdiscovery/metainput)
18
type MetaInput struct {
19
// Input represent the target
20
Input string `json:"input,omitempty"`
21
// CustomIP to use for connection
22
CustomIP string `json:"customIP,omitempty"`
23
// hash of the input
24
hash string `json:"-"`
25
26
// ReqResp is the raw request for the input
27
ReqResp *types.RequestResponse `json:"raw-request,omitempty"`
28
29
mu *sync.Mutex
30
}
31
32
func NewMetaInput() *MetaInput {
33
return &MetaInput{mu: &sync.Mutex{}}
34
}
35
36
func (metaInput *MetaInput) marshalToBuffer() (bytes.Buffer, error) {
37
var b bytes.Buffer
38
err := jsoniter.NewEncoder(&b).Encode(metaInput)
39
return b, err
40
}
41
42
// Target returns the target of the metainput
43
func (metaInput *MetaInput) Target() string {
44
if metaInput.ReqResp != nil && metaInput.ReqResp.URL.URL != nil {
45
return metaInput.ReqResp.URL.String()
46
}
47
return metaInput.Input
48
}
49
50
// URL returns request url
51
func (metaInput *MetaInput) URL() (*urlutil.URL, error) {
52
instance, err := urlutil.ParseAbsoluteURL(metaInput.Target(), false)
53
if err != nil {
54
return nil, err
55
}
56
return instance, nil
57
}
58
59
// Port returns the port of the target
60
// if port is not present then empty string is returned
61
func (metaInput *MetaInput) Port() string {
62
target, err := urlutil.ParseAbsoluteURL(metaInput.Input, false)
63
if err != nil {
64
return ""
65
}
66
return target.Port()
67
}
68
69
// Address return the remote address of target
70
// Note: it does not resolve the domain to ip
71
// it is meant to be used by hosterrorsCache if invalid metainput
72
// is provided then it uses a random ksuid as hostname to avoid skipping valid targets
73
func (metaInput *MetaInput) Address() string {
74
var hostname, port string
75
target, err := urlutil.ParseAbsoluteURL(metaInput.Target(), false)
76
if err != nil {
77
if metaInput.CustomIP == "" {
78
// since this is used in hosterrorscache we add a random id
79
// which will never be used to avoid skipping valid targets
80
hostname = fmt.Sprintf("invalid-%s", ksuid.New().String())
81
}
82
} else {
83
hostname = target.Hostname()
84
port = target.Port()
85
if port == "" {
86
switch target.Scheme {
87
case urlutil.HTTP:
88
port = "80"
89
case urlutil.HTTPS:
90
port = "443"
91
default:
92
port = "80"
93
}
94
}
95
}
96
if metaInput.CustomIP != "" {
97
hostname = metaInput.CustomIP
98
}
99
if port == "" {
100
if strings.HasPrefix(hostname, "http://") {
101
port = "80"
102
} else if strings.HasPrefix(hostname, "https://") {
103
port = "443"
104
}
105
}
106
return net.JoinHostPort(hostname, port)
107
}
108
109
// ID returns a unique id/hash for metainput
110
func (metaInput *MetaInput) ID() string {
111
if metaInput.CustomIP != "" {
112
return fmt.Sprintf("%s-%s", metaInput.Input, metaInput.CustomIP)
113
}
114
if metaInput.ReqResp != nil {
115
return metaInput.ReqResp.ID()
116
}
117
return metaInput.Input
118
}
119
120
func (metaInput *MetaInput) MarshalString() (string, error) {
121
b, err := metaInput.marshalToBuffer()
122
return b.String(), err
123
}
124
125
func (metaInput *MetaInput) MustMarshalString() string {
126
marshaled, _ := metaInput.MarshalString()
127
return marshaled
128
}
129
130
func (metaInput *MetaInput) MarshalBytes() ([]byte, error) {
131
b, err := metaInput.marshalToBuffer()
132
return b.Bytes(), err
133
}
134
135
func (metaInput *MetaInput) MustMarshalBytes() []byte {
136
marshaled, _ := metaInput.MarshalBytes()
137
return marshaled
138
}
139
140
func (metaInput *MetaInput) Unmarshal(data string) error {
141
return jsoniter.NewDecoder(strings.NewReader(data)).Decode(metaInput)
142
}
143
144
func (metaInput *MetaInput) Clone() *MetaInput {
145
metaInput.mu.Lock()
146
defer metaInput.mu.Unlock()
147
148
input := NewMetaInput()
149
input.Input = metaInput.Input
150
input.CustomIP = metaInput.CustomIP
151
input.hash = metaInput.hash
152
if metaInput.ReqResp != nil {
153
input.ReqResp = metaInput.ReqResp.Clone()
154
}
155
return input
156
}
157
158
func (metaInput *MetaInput) PrettyPrint() string {
159
if metaInput.CustomIP != "" {
160
return fmt.Sprintf("%s [%s]", metaInput.Input, metaInput.CustomIP)
161
}
162
if metaInput.ReqResp != nil {
163
return fmt.Sprintf("%s [%s]", metaInput.ReqResp.URL.String(), metaInput.ReqResp.Request.Method)
164
}
165
return metaInput.Input
166
}
167
168
// GetScanHash returns a unique hash that represents a scan by hashing (metainput + templateId)
169
func (metaInput *MetaInput) GetScanHash(templateId string) string {
170
// there may be some cases where metainput is changed ex: while executing self-contained template etc
171
// but that totally changes the scanID/hash so to avoid that we compute hash only once
172
// and reuse it for all subsequent calls
173
metaInput.mu.Lock()
174
defer metaInput.mu.Unlock()
175
176
if metaInput.hash == "" {
177
var rawRequest string
178
if metaInput.ReqResp != nil {
179
rawRequest = metaInput.ReqResp.ID()
180
}
181
metaInput.hash = getMd5Hash(templateId + ":" + metaInput.Input + ":" + metaInput.CustomIP + rawRequest)
182
}
183
return metaInput.hash
184
}
185
186
func getMd5Hash(data string) string {
187
bin := md5.Sum([]byte(data))
188
return string(bin[:])
189
}
190
191