Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/common/contextargs/contextargs.go
2072 views
1
package contextargs
2
3
import (
4
"context"
5
"net/http/cookiejar"
6
"strings"
7
"sync/atomic"
8
9
"github.com/projectdiscovery/gologger"
10
mapsutil "github.com/projectdiscovery/utils/maps"
11
sliceutil "github.com/projectdiscovery/utils/slice"
12
stringsutil "github.com/projectdiscovery/utils/strings"
13
urlutil "github.com/projectdiscovery/utils/url"
14
)
15
16
var (
17
// reservedPorts contains list of reserved ports for non-network requests in nuclei
18
reservedPorts = []string{"80", "443", "8080", "8443", "8081", "53"}
19
)
20
21
// Context implements a shared context struct to share information across multiple templates within a workflow
22
type Context struct {
23
ctx context.Context
24
25
// Meta is the target for the executor
26
MetaInput *MetaInput
27
28
// CookieJar shared within workflow's http templates
29
CookieJar *cookiejar.Jar
30
31
// Args is a workflow shared key-value store
32
args *mapsutil.SyncLockMap[string, interface{}]
33
}
34
35
// Create a new contextargs instance
36
func New(ctx context.Context) *Context {
37
return NewWithInput(ctx, "")
38
}
39
40
// NewWithMetaInput creates a new contextargs instance with meta input
41
func NewWithMetaInput(ctx context.Context, input *MetaInput) *Context {
42
n := New(ctx)
43
n.MetaInput = input
44
return n
45
}
46
47
// Create a new contextargs instance with input string
48
func NewWithInput(ctx context.Context, input string) *Context {
49
jar, err := cookiejar.New(nil)
50
if err != nil {
51
gologger.Error().Msgf("contextargs: could not create cookie jar: %s\n", err)
52
}
53
metaInput := NewMetaInput()
54
metaInput.Input = input
55
return &Context{
56
ctx: ctx,
57
MetaInput: metaInput,
58
CookieJar: jar,
59
args: &mapsutil.SyncLockMap[string, interface{}]{
60
Map: make(map[string]interface{}),
61
ReadOnly: atomic.Bool{},
62
},
63
}
64
}
65
66
// Context returns the context of the current contextargs
67
func (ctx *Context) Context() context.Context {
68
return ctx.ctx
69
}
70
71
// Set the specific key-value pair
72
func (ctx *Context) Set(key string, value interface{}) {
73
_ = ctx.args.Set(key, value)
74
}
75
76
func (ctx *Context) hasArgs() bool {
77
return !ctx.args.IsEmpty()
78
}
79
80
// Merge the key-value pairs
81
func (ctx *Context) Merge(args map[string]interface{}) {
82
_ = ctx.args.Merge(args)
83
}
84
85
// Add the specific key-value pair
86
func (ctx *Context) Add(key string, v interface{}) {
87
values, ok := ctx.args.Get(key)
88
if !ok {
89
ctx.Set(key, v)
90
}
91
92
// If the key exists, append the value to the existing value
93
switch v := v.(type) {
94
case []string:
95
if values, ok := values.([]string); ok {
96
values = append(values, v...)
97
ctx.Set(key, values)
98
}
99
case string:
100
if values, ok := values.(string); ok {
101
tmp := []string{values, v}
102
ctx.Set(key, tmp)
103
}
104
default:
105
values, _ := ctx.Get(key)
106
ctx.Set(key, []interface{}{values, v})
107
}
108
}
109
110
// UseNetworkPort updates input with required/default network port for that template
111
// but is ignored if input/target contains non-http ports like 80,8080,8081 etc
112
func (ctx *Context) UseNetworkPort(port string, excludePorts string) error {
113
ignorePorts := reservedPorts
114
if excludePorts != "" {
115
// TODO: add support for service names like http,https,ssh etc once https://github.com/projectdiscovery/netdb is ready
116
ignorePorts = sliceutil.Dedupe(strings.Split(excludePorts, ","))
117
}
118
if port == "" {
119
// if template does not contain port, do nothing
120
return nil
121
}
122
target, err := urlutil.Parse(ctx.MetaInput.Input)
123
if err != nil {
124
return err
125
}
126
inputPort := target.Port()
127
if inputPort == "" || stringsutil.EqualFoldAny(inputPort, ignorePorts...) {
128
// replace port with networkPort
129
target.UpdatePort(port)
130
ctx.MetaInput.Input = target.Host
131
}
132
return nil
133
}
134
135
// Port returns the port of the target
136
func (ctx *Context) Port() string {
137
target, err := urlutil.Parse(ctx.MetaInput.Input)
138
if err != nil {
139
return ""
140
}
141
return target.Port()
142
}
143
144
// Get the value with specific key if exists
145
func (ctx *Context) Get(key string) (interface{}, bool) {
146
if !ctx.hasArgs() {
147
return nil, false
148
}
149
150
return ctx.args.Get(key)
151
}
152
153
func (ctx *Context) GetAll() map[string]interface{} {
154
if !ctx.hasArgs() {
155
return nil
156
}
157
158
return ctx.args.Clone().Map
159
}
160
161
func (ctx *Context) ForEach(f func(string, interface{})) {
162
_ = ctx.args.Iterate(func(k string, v interface{}) error {
163
f(k, v)
164
return nil
165
})
166
}
167
168
// Has check if the key exists
169
func (ctx *Context) Has(key string) bool {
170
return ctx.hasArgs() && ctx.args.Has(key)
171
}
172
173
func (ctx *Context) HasArgs() bool {
174
return !ctx.args.IsEmpty()
175
}
176
177
func (ctx *Context) Clone() *Context {
178
newCtx := &Context{
179
ctx: ctx.ctx,
180
MetaInput: ctx.MetaInput.Clone(),
181
args: ctx.args.Clone(),
182
CookieJar: ctx.CookieJar,
183
}
184
return newCtx
185
}
186
187
// GetCopyIfHostOutdated returns a new contextargs if the host is outdated
188
func GetCopyIfHostOutdated(ctx *Context, url string) *Context {
189
if ctx.MetaInput.Input == "" {
190
newctx := ctx.Clone()
191
newctx.MetaInput.Input = url
192
return newctx
193
}
194
orig, _ := urlutil.Parse(ctx.MetaInput.Input)
195
newURL, _ := urlutil.Parse(url)
196
if orig != nil && newURL != nil && orig.Host != newURL.Host {
197
newCtx := ctx.Clone()
198
newCtx.MetaInput.Input = newURL.Host
199
return newCtx
200
}
201
return ctx
202
}
203
204