Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/authprovider/authx/dynamic.go
2070 views
1
package authx
2
3
import (
4
"fmt"
5
"strings"
6
"sync"
7
8
"github.com/projectdiscovery/gologger"
9
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer"
10
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
11
"github.com/projectdiscovery/utils/errkit"
12
sliceutil "github.com/projectdiscovery/utils/slice"
13
)
14
15
type LazyFetchSecret func(d *Dynamic) error
16
17
var (
18
_ json.Unmarshaler = &Dynamic{}
19
)
20
21
// Dynamic is a struct for dynamic secret or credential
22
// these are high level secrets that take action to generate the actual secret
23
// ex: username and password are dynamic secrets, the actual secret is the token obtained
24
// after authenticating with the username and password
25
type Dynamic struct {
26
*Secret `yaml:",inline"` // this is a static secret that will be generated after the dynamic secret is resolved
27
Secrets []*Secret `yaml:"secrets"`
28
TemplatePath string `json:"template" yaml:"template"`
29
Variables []KV `json:"variables" yaml:"variables"`
30
Input string `json:"input" yaml:"input"` // (optional) target for the dynamic secret
31
Extracted map[string]interface{} `json:"-" yaml:"-"` // extracted values from the dynamic secret
32
fetchCallback LazyFetchSecret `json:"-" yaml:"-"`
33
m *sync.Mutex `json:"-" yaml:"-"` // mutex for lazy fetch
34
fetched bool `json:"-" yaml:"-"` // flag to check if the secret has been fetched
35
error error `json:"-" yaml:"-"` // error if any
36
}
37
38
func (d *Dynamic) GetDomainAndDomainRegex() ([]string, []string) {
39
var domains []string
40
var domainRegex []string
41
for _, secret := range d.Secrets {
42
domains = append(domains, secret.Domains...)
43
domainRegex = append(domainRegex, secret.DomainsRegex...)
44
}
45
if d.Secret != nil {
46
domains = append(domains, d.Domains...)
47
domainRegex = append(domainRegex, d.DomainsRegex...)
48
}
49
uniqueDomains := sliceutil.Dedupe(domains)
50
uniqueDomainRegex := sliceutil.Dedupe(domainRegex)
51
return uniqueDomains, uniqueDomainRegex
52
}
53
54
func (d *Dynamic) UnmarshalJSON(data []byte) error {
55
if d == nil {
56
return errkit.New("cannot unmarshal into nil Dynamic struct")
57
}
58
59
// Use an alias type (auxiliary) to avoid a recursive call in this method.
60
type Alias Dynamic
61
62
// If d.Secret was nil, json.Unmarshal will allocate a new Secret object
63
// and populate it from the top level JSON fields.
64
if err := json.Unmarshal(data, (*Alias)(d)); err != nil {
65
return err
66
}
67
68
return nil
69
}
70
71
// Validate validates the dynamic secret
72
func (d *Dynamic) Validate() error {
73
d.m = &sync.Mutex{}
74
if d.TemplatePath == "" {
75
return errkit.New(" template-path is required for dynamic secret")
76
}
77
if len(d.Variables) == 0 {
78
return errkit.New("variables are required for dynamic secret")
79
}
80
81
if d.Secret != nil {
82
d.skipCookieParse = true // skip cookie parsing in dynamic secrets during validation
83
if err := d.Secret.Validate(); err != nil {
84
return err
85
}
86
}
87
for _, secret := range d.Secrets {
88
secret.skipCookieParse = true
89
if err := secret.Validate(); err != nil {
90
return err
91
}
92
}
93
return nil
94
}
95
96
// SetLazyFetchCallback sets the lazy fetch callback for the dynamic secret
97
func (d *Dynamic) SetLazyFetchCallback(callback LazyFetchSecret) {
98
d.fetchCallback = func(d *Dynamic) error {
99
err := callback(d)
100
d.fetched = true
101
if err != nil {
102
d.error = err
103
return err
104
}
105
if len(d.Extracted) == 0 {
106
return fmt.Errorf("no extracted values found for dynamic secret")
107
}
108
109
if d.Secret != nil {
110
if err := d.applyValuesToSecret(d.Secret); err != nil {
111
return err
112
}
113
}
114
115
for _, secret := range d.Secrets {
116
if err := d.applyValuesToSecret(secret); err != nil {
117
return err
118
}
119
}
120
return nil
121
}
122
}
123
124
func (d *Dynamic) applyValuesToSecret(secret *Secret) error {
125
// evaluate headers
126
for i, header := range secret.Headers {
127
if strings.Contains(header.Value, "{{") {
128
header.Value = replacer.Replace(header.Value, d.Extracted)
129
}
130
if strings.Contains(header.Key, "{{") {
131
header.Key = replacer.Replace(header.Key, d.Extracted)
132
}
133
secret.Headers[i] = header
134
}
135
136
// evaluate cookies
137
for i, cookie := range secret.Cookies {
138
if strings.Contains(cookie.Value, "{{") {
139
cookie.Value = replacer.Replace(cookie.Value, d.Extracted)
140
}
141
if strings.Contains(cookie.Key, "{{") {
142
cookie.Key = replacer.Replace(cookie.Key, d.Extracted)
143
}
144
if strings.Contains(cookie.Raw, "{{") {
145
cookie.Raw = replacer.Replace(cookie.Raw, d.Extracted)
146
}
147
secret.Cookies[i] = cookie
148
}
149
150
// evaluate query params
151
for i, query := range secret.Params {
152
if strings.Contains(query.Value, "{{") {
153
query.Value = replacer.Replace(query.Value, d.Extracted)
154
}
155
if strings.Contains(query.Key, "{{") {
156
query.Key = replacer.Replace(query.Key, d.Extracted)
157
}
158
secret.Params[i] = query
159
}
160
161
// check username, password and token
162
if strings.Contains(secret.Username, "{{") {
163
secret.Username = replacer.Replace(secret.Username, d.Extracted)
164
}
165
if strings.Contains(secret.Password, "{{") {
166
secret.Password = replacer.Replace(secret.Password, d.Extracted)
167
}
168
if strings.Contains(secret.Token, "{{") {
169
secret.Token = replacer.Replace(secret.Token, d.Extracted)
170
}
171
172
// now attempt to parse the cookies
173
secret.skipCookieParse = false
174
for i, cookie := range secret.Cookies {
175
if cookie.Raw != "" {
176
if err := cookie.Parse(); err != nil {
177
return fmt.Errorf("[%s] invalid raw cookie in cookiesAuth: %s", d.TemplatePath, err)
178
}
179
secret.Cookies[i] = cookie
180
}
181
}
182
return nil
183
}
184
185
// GetStrategy returns the auth strategies for the dynamic secret
186
func (d *Dynamic) GetStrategies() []AuthStrategy {
187
if !d.fetched {
188
_ = d.Fetch(true)
189
}
190
if d.error != nil {
191
return nil
192
}
193
var strategies []AuthStrategy
194
if d.Secret != nil {
195
strategies = append(strategies, d.GetStrategy())
196
}
197
for _, secret := range d.Secrets {
198
strategies = append(strategies, secret.GetStrategy())
199
}
200
return strategies
201
}
202
203
// Fetch fetches the dynamic secret
204
// if isFatal is true, it will stop the execution if the secret could not be fetched
205
func (d *Dynamic) Fetch(isFatal bool) error {
206
d.m.Lock()
207
defer d.m.Unlock()
208
if d.fetched {
209
return nil
210
}
211
d.error = d.fetchCallback(d)
212
if d.error != nil && isFatal {
213
gologger.Fatal().Msgf("Could not fetch dynamic secret: %s\n", d.error)
214
}
215
return d.error
216
}
217
218
// Error returns the error if any
219
func (d *Dynamic) Error() error {
220
return d.error
221
}
222
223