Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/authprovider/authx/file.go
2070 views
1
package authx
2
3
import (
4
"fmt"
5
"os"
6
"path/filepath"
7
"regexp"
8
"strings"
9
10
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
11
"github.com/projectdiscovery/utils/errkit"
12
"github.com/projectdiscovery/utils/generic"
13
stringsutil "github.com/projectdiscovery/utils/strings"
14
"gopkg.in/yaml.v3"
15
)
16
17
type AuthType string
18
19
const (
20
BasicAuth AuthType = "BasicAuth"
21
BearerTokenAuth AuthType = "BearerToken"
22
HeadersAuth AuthType = "Header"
23
CookiesAuth AuthType = "Cookie"
24
QueryAuth AuthType = "Query"
25
)
26
27
// SupportedAuthTypes returns the supported auth types
28
func SupportedAuthTypes() []string {
29
return []string{
30
string(BasicAuth),
31
string(BearerTokenAuth),
32
string(HeadersAuth),
33
string(CookiesAuth),
34
string(QueryAuth),
35
}
36
}
37
38
// Authx is a struct for secrets or credentials file
39
type Authx struct {
40
ID string `json:"id" yaml:"id"`
41
Info AuthFileInfo `json:"info" yaml:"info"`
42
Secrets []Secret `json:"static" yaml:"static"`
43
Dynamic []Dynamic `json:"dynamic" yaml:"dynamic"`
44
}
45
46
type AuthFileInfo struct {
47
Name string `json:"name" yaml:"name"`
48
Author string `json:"author" yaml:"author"`
49
Severity string `json:"severity" yaml:"severity"`
50
Description string `json:"description" yaml:"description"`
51
}
52
53
// Secret is a struct for secret or credential
54
type Secret struct {
55
Type string `json:"type" yaml:"type"`
56
Domains []string `json:"domains" yaml:"domains"`
57
DomainsRegex []string `json:"domains-regex" yaml:"domains-regex"`
58
Headers []KV `json:"headers" yaml:"headers"`
59
Cookies []Cookie `json:"cookies" yaml:"cookies"`
60
Params []KV `json:"params" yaml:"params"`
61
Username string `json:"username" yaml:"username"` // can be either email or username
62
Password string `json:"password" yaml:"password"`
63
Token string `json:"token" yaml:"token"` // Bearer Auth token
64
skipCookieParse bool `json:"-" yaml:"-"` // temporary flag to skip cookie parsing (used in dynamic secrets)
65
}
66
67
// GetStrategy returns the auth strategy for the secret
68
func (s *Secret) GetStrategy() AuthStrategy {
69
switch {
70
case strings.EqualFold(s.Type, string(BasicAuth)):
71
return NewBasicAuthStrategy(s)
72
case strings.EqualFold(s.Type, string(BearerTokenAuth)):
73
return NewBearerTokenAuthStrategy(s)
74
case strings.EqualFold(s.Type, string(HeadersAuth)):
75
return NewHeadersAuthStrategy(s)
76
case strings.EqualFold(s.Type, string(CookiesAuth)):
77
return NewCookiesAuthStrategy(s)
78
case strings.EqualFold(s.Type, string(QueryAuth)):
79
return NewQueryAuthStrategy(s)
80
}
81
return nil
82
}
83
84
func (s *Secret) Validate() error {
85
if !stringsutil.EqualFoldAny(s.Type, SupportedAuthTypes()...) {
86
return fmt.Errorf("invalid type: %s", s.Type)
87
}
88
if len(s.Domains) == 0 && len(s.DomainsRegex) == 0 {
89
return fmt.Errorf("domains or domains-regex cannot be empty")
90
}
91
if len(s.DomainsRegex) > 0 {
92
for _, domain := range s.DomainsRegex {
93
_, err := regexp.Compile(domain)
94
if err != nil {
95
return fmt.Errorf("invalid domain regex: %s", domain)
96
}
97
}
98
}
99
100
switch {
101
case strings.EqualFold(s.Type, string(BasicAuth)):
102
if s.Username == "" {
103
return fmt.Errorf("username cannot be empty in basic auth")
104
}
105
if s.Password == "" {
106
return fmt.Errorf("password cannot be empty in basic auth")
107
}
108
case strings.EqualFold(s.Type, string(BearerTokenAuth)):
109
if s.Token == "" {
110
return fmt.Errorf("token cannot be empty in bearer token auth")
111
}
112
case strings.EqualFold(s.Type, string(HeadersAuth)):
113
if len(s.Headers) == 0 {
114
return fmt.Errorf("headers cannot be empty in headers auth")
115
}
116
for _, header := range s.Headers {
117
if err := header.Validate(); err != nil {
118
return fmt.Errorf("invalid header in headersAuth: %s", err)
119
}
120
}
121
case strings.EqualFold(s.Type, string(CookiesAuth)):
122
if len(s.Cookies) == 0 {
123
return fmt.Errorf("cookies cannot be empty in cookies auth")
124
}
125
for _, cookie := range s.Cookies {
126
if cookie.Raw != "" && !s.skipCookieParse {
127
if err := cookie.Parse(); err != nil {
128
return fmt.Errorf("invalid raw cookie in cookiesAuth: %s", err)
129
}
130
}
131
if err := cookie.Validate(); err != nil {
132
return fmt.Errorf("invalid cookie in cookiesAuth: %s", err)
133
}
134
}
135
case strings.EqualFold(s.Type, string(QueryAuth)):
136
if len(s.Params) == 0 {
137
return fmt.Errorf("query cannot be empty in query auth")
138
}
139
for _, query := range s.Params {
140
if err := query.Validate(); err != nil {
141
return fmt.Errorf("invalid query in queryAuth: %s", err)
142
}
143
}
144
default:
145
return fmt.Errorf("invalid type: %s", s.Type)
146
}
147
return nil
148
}
149
150
type KV struct {
151
Key string `json:"key" yaml:"key"`
152
Value string `json:"value" yaml:"value"`
153
}
154
155
func (k *KV) Validate() error {
156
if k.Key == "" {
157
return fmt.Errorf("key cannot be empty")
158
}
159
if k.Value == "" {
160
return fmt.Errorf("value cannot be empty")
161
}
162
return nil
163
}
164
165
type Cookie struct {
166
Key string `json:"key" yaml:"key"`
167
Value string `json:"value" yaml:"value"`
168
Raw string `json:"raw" yaml:"raw"`
169
}
170
171
func (c *Cookie) Validate() error {
172
if c.Raw != "" {
173
return nil
174
}
175
if c.Key == "" {
176
return fmt.Errorf("key cannot be empty")
177
}
178
if c.Value == "" {
179
return fmt.Errorf("value cannot be empty")
180
}
181
return nil
182
}
183
184
// Parse parses the cookie
185
// in raw the cookie is in format of
186
// Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>; Path=<path>; Domain=<domain_name>; Secure; HttpOnly
187
func (c *Cookie) Parse() error {
188
if c.Raw == "" {
189
return fmt.Errorf("raw cookie cannot be empty")
190
}
191
tmp := strings.TrimPrefix(c.Raw, "Set-Cookie: ")
192
slice := strings.Split(tmp, ";")
193
if len(slice) == 0 {
194
return fmt.Errorf("invalid raw cookie no ; found")
195
}
196
// first element is the cookie name and value
197
cookie := strings.Split(slice[0], "=")
198
if len(cookie) == 2 {
199
c.Key = cookie[0]
200
c.Value = cookie[1]
201
return nil
202
}
203
return fmt.Errorf("invalid raw cookie: %s", c.Raw)
204
}
205
206
// GetAuthDataFromFile reads the auth data from file
207
func GetAuthDataFromFile(file string) (*Authx, error) {
208
ext := filepath.Ext(file)
209
if !generic.EqualsAny(ext, ".yml", ".yaml", ".json") {
210
return nil, fmt.Errorf("invalid file extension: supported extensions are .yml,.yaml and .json got %s", ext)
211
}
212
bin, err := os.ReadFile(file)
213
if err != nil {
214
return nil, err
215
}
216
if ext == ".yml" || ext == ".yaml" {
217
return GetAuthDataFromYAML(bin)
218
}
219
return GetAuthDataFromJSON(bin)
220
}
221
222
// GetTemplatePathsFromSecretFile reads the template IDs from the secret file
223
func GetTemplatePathsFromSecretFile(file string) ([]string, error) {
224
auth, err := GetAuthDataFromFile(file)
225
if err != nil {
226
return nil, err
227
}
228
var paths []string
229
for _, dynamic := range auth.Dynamic {
230
paths = append(paths, dynamic.TemplatePath)
231
}
232
return paths, nil
233
}
234
235
// GetAuthDataFromYAML reads the auth data from yaml
236
func GetAuthDataFromYAML(data []byte) (*Authx, error) {
237
var auth Authx
238
err := yaml.Unmarshal(data, &auth)
239
if err != nil {
240
errorErr := errkit.FromError(err)
241
errorErr.Msgf("could not unmarshal yaml")
242
return nil, errorErr
243
}
244
return &auth, nil
245
}
246
247
// GetAuthDataFromJSON reads the auth data from json
248
func GetAuthDataFromJSON(data []byte) (*Authx, error) {
249
var auth Authx
250
err := json.Unmarshal(data, &auth)
251
if err != nil {
252
errorErr := errkit.FromError(err)
253
errorErr.Msgf("could not unmarshal json")
254
return nil, errorErr
255
}
256
return &auth, nil
257
}
258
259