Path: blob/dev/pkg/authprovider/authx/dynamic.go
2070 views
package authx12import (3"fmt"4"strings"5"sync"67"github.com/projectdiscovery/gologger"8"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer"9"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"10"github.com/projectdiscovery/utils/errkit"11sliceutil "github.com/projectdiscovery/utils/slice"12)1314type LazyFetchSecret func(d *Dynamic) error1516var (17_ json.Unmarshaler = &Dynamic{}18)1920// Dynamic is a struct for dynamic secret or credential21// these are high level secrets that take action to generate the actual secret22// ex: username and password are dynamic secrets, the actual secret is the token obtained23// after authenticating with the username and password24type Dynamic struct {25*Secret `yaml:",inline"` // this is a static secret that will be generated after the dynamic secret is resolved26Secrets []*Secret `yaml:"secrets"`27TemplatePath string `json:"template" yaml:"template"`28Variables []KV `json:"variables" yaml:"variables"`29Input string `json:"input" yaml:"input"` // (optional) target for the dynamic secret30Extracted map[string]interface{} `json:"-" yaml:"-"` // extracted values from the dynamic secret31fetchCallback LazyFetchSecret `json:"-" yaml:"-"`32m *sync.Mutex `json:"-" yaml:"-"` // mutex for lazy fetch33fetched bool `json:"-" yaml:"-"` // flag to check if the secret has been fetched34error error `json:"-" yaml:"-"` // error if any35}3637func (d *Dynamic) GetDomainAndDomainRegex() ([]string, []string) {38var domains []string39var domainRegex []string40for _, secret := range d.Secrets {41domains = append(domains, secret.Domains...)42domainRegex = append(domainRegex, secret.DomainsRegex...)43}44if d.Secret != nil {45domains = append(domains, d.Domains...)46domainRegex = append(domainRegex, d.DomainsRegex...)47}48uniqueDomains := sliceutil.Dedupe(domains)49uniqueDomainRegex := sliceutil.Dedupe(domainRegex)50return uniqueDomains, uniqueDomainRegex51}5253func (d *Dynamic) UnmarshalJSON(data []byte) error {54if d == nil {55return errkit.New("cannot unmarshal into nil Dynamic struct")56}5758// Use an alias type (auxiliary) to avoid a recursive call in this method.59type Alias Dynamic6061// If d.Secret was nil, json.Unmarshal will allocate a new Secret object62// and populate it from the top level JSON fields.63if err := json.Unmarshal(data, (*Alias)(d)); err != nil {64return err65}6667return nil68}6970// Validate validates the dynamic secret71func (d *Dynamic) Validate() error {72d.m = &sync.Mutex{}73if d.TemplatePath == "" {74return errkit.New(" template-path is required for dynamic secret")75}76if len(d.Variables) == 0 {77return errkit.New("variables are required for dynamic secret")78}7980if d.Secret != nil {81d.skipCookieParse = true // skip cookie parsing in dynamic secrets during validation82if err := d.Secret.Validate(); err != nil {83return err84}85}86for _, secret := range d.Secrets {87secret.skipCookieParse = true88if err := secret.Validate(); err != nil {89return err90}91}92return nil93}9495// SetLazyFetchCallback sets the lazy fetch callback for the dynamic secret96func (d *Dynamic) SetLazyFetchCallback(callback LazyFetchSecret) {97d.fetchCallback = func(d *Dynamic) error {98err := callback(d)99d.fetched = true100if err != nil {101d.error = err102return err103}104if len(d.Extracted) == 0 {105return fmt.Errorf("no extracted values found for dynamic secret")106}107108if d.Secret != nil {109if err := d.applyValuesToSecret(d.Secret); err != nil {110return err111}112}113114for _, secret := range d.Secrets {115if err := d.applyValuesToSecret(secret); err != nil {116return err117}118}119return nil120}121}122123func (d *Dynamic) applyValuesToSecret(secret *Secret) error {124// evaluate headers125for i, header := range secret.Headers {126if strings.Contains(header.Value, "{{") {127header.Value = replacer.Replace(header.Value, d.Extracted)128}129if strings.Contains(header.Key, "{{") {130header.Key = replacer.Replace(header.Key, d.Extracted)131}132secret.Headers[i] = header133}134135// evaluate cookies136for i, cookie := range secret.Cookies {137if strings.Contains(cookie.Value, "{{") {138cookie.Value = replacer.Replace(cookie.Value, d.Extracted)139}140if strings.Contains(cookie.Key, "{{") {141cookie.Key = replacer.Replace(cookie.Key, d.Extracted)142}143if strings.Contains(cookie.Raw, "{{") {144cookie.Raw = replacer.Replace(cookie.Raw, d.Extracted)145}146secret.Cookies[i] = cookie147}148149// evaluate query params150for i, query := range secret.Params {151if strings.Contains(query.Value, "{{") {152query.Value = replacer.Replace(query.Value, d.Extracted)153}154if strings.Contains(query.Key, "{{") {155query.Key = replacer.Replace(query.Key, d.Extracted)156}157secret.Params[i] = query158}159160// check username, password and token161if strings.Contains(secret.Username, "{{") {162secret.Username = replacer.Replace(secret.Username, d.Extracted)163}164if strings.Contains(secret.Password, "{{") {165secret.Password = replacer.Replace(secret.Password, d.Extracted)166}167if strings.Contains(secret.Token, "{{") {168secret.Token = replacer.Replace(secret.Token, d.Extracted)169}170171// now attempt to parse the cookies172secret.skipCookieParse = false173for i, cookie := range secret.Cookies {174if cookie.Raw != "" {175if err := cookie.Parse(); err != nil {176return fmt.Errorf("[%s] invalid raw cookie in cookiesAuth: %s", d.TemplatePath, err)177}178secret.Cookies[i] = cookie179}180}181return nil182}183184// GetStrategy returns the auth strategies for the dynamic secret185func (d *Dynamic) GetStrategies() []AuthStrategy {186if !d.fetched {187_ = d.Fetch(true)188}189if d.error != nil {190return nil191}192var strategies []AuthStrategy193if d.Secret != nil {194strategies = append(strategies, d.GetStrategy())195}196for _, secret := range d.Secrets {197strategies = append(strategies, secret.GetStrategy())198}199return strategies200}201202// Fetch fetches the dynamic secret203// if isFatal is true, it will stop the execution if the secret could not be fetched204func (d *Dynamic) Fetch(isFatal bool) error {205d.m.Lock()206defer d.m.Unlock()207if d.fetched {208return nil209}210d.error = d.fetchCallback(d)211if d.error != nil && isFatal {212gologger.Fatal().Msgf("Could not fetch dynamic secret: %s\n", d.error)213}214return d.error215}216217// Error returns the error if any218func (d *Dynamic) Error() error {219return d.error220}221222223