mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 13:55:24 +00:00
fix(authx): prevent deadlock in dynamic auth fetching
resolve deadlock that occurs when dynamic auth templates trigger recursive auth requests during execution. RCA: 1. `GetStrategies()` calls `Fetch()` to retrieve auth creds. 2. `Fetch()` executes auth template via cb. 3. template exec triggers HTTP requests requiring auth. 4. recursive calls `GetStrategies()` → `Fetch()` cause deadlock on mutex. notable changes: * add `fetching` flag to `Dynamic` struct to track fetch-in-progress state. * modify `GetStrategies()` to return empty strategies if already fetching. * update `Fetch()` method with proper recursive call prevention. * use mutex-protected flag reads to ensure thread safety. * refactor `GetStrategies()` with local function for code reuse. this prevents infinite recursion during auth template execution while maintaining proper sync and err handling. fixes goroutine deadlocks in auth system when using dynamic secrets with templates that require auth. Signed-off-by: Dwi Siswanto <git@dw1.io>
This commit is contained in:
parent
5ef37da8f4
commit
e7968de431
@ -32,6 +32,7 @@ type Dynamic struct {
|
||||
fetchCallback LazyFetchSecret `json:"-" yaml:"-"`
|
||||
m *sync.Mutex `json:"-" yaml:"-"` // mutex for lazy fetch
|
||||
fetched bool `json:"-" yaml:"-"` // flag to check if the secret has been fetched
|
||||
fetching bool `json:"-" yaml:"-"` // flag to check if we're currently fetching (prevents recursion)
|
||||
error error `json:"-" yaml:"-"` // error if any
|
||||
}
|
||||
|
||||
@ -184,20 +185,44 @@ func (d *Dynamic) applyValuesToSecret(secret *Secret) error {
|
||||
|
||||
// GetStrategy returns the auth strategies for the dynamic secret
|
||||
func (d *Dynamic) GetStrategies() []AuthStrategy {
|
||||
if !d.fetched {
|
||||
_ = d.Fetch(true)
|
||||
getStrategies := func() []AuthStrategy {
|
||||
var strategies []AuthStrategy
|
||||
if d.Secret != nil {
|
||||
strategies = append(strategies, d.GetStrategy())
|
||||
}
|
||||
|
||||
for _, secret := range d.Secrets {
|
||||
strategies = append(strategies, secret.GetStrategy())
|
||||
}
|
||||
|
||||
return strategies
|
||||
}
|
||||
|
||||
d.m.Lock()
|
||||
isFetched := d.fetched
|
||||
isFetching := d.fetching
|
||||
d.m.Unlock()
|
||||
|
||||
if isFetched {
|
||||
if d.error != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return getStrategies()
|
||||
}
|
||||
|
||||
if isFetching {
|
||||
// NOTE(dwisiswant0): Bail out w/ empty here, or we will yeet into
|
||||
// recursion hell. See line 242-245.
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = d.Fetch(true)
|
||||
if d.error != nil {
|
||||
return nil
|
||||
}
|
||||
var strategies []AuthStrategy
|
||||
if d.Secret != nil {
|
||||
strategies = append(strategies, d.GetStrategy())
|
||||
}
|
||||
for _, secret := range d.Secrets {
|
||||
strategies = append(strategies, secret.GetStrategy())
|
||||
}
|
||||
return strategies
|
||||
|
||||
return getStrategies()
|
||||
}
|
||||
|
||||
// Fetch fetches the dynamic secret
|
||||
@ -205,13 +230,29 @@ func (d *Dynamic) GetStrategies() []AuthStrategy {
|
||||
func (d *Dynamic) Fetch(isFatal bool) error {
|
||||
d.m.Lock()
|
||||
defer d.m.Unlock()
|
||||
|
||||
if d.fetched {
|
||||
return d.error
|
||||
}
|
||||
|
||||
if d.fetching {
|
||||
return nil
|
||||
}
|
||||
|
||||
d.fetching = true
|
||||
defer func() {
|
||||
d.fetching = false
|
||||
}()
|
||||
|
||||
d.error = d.fetchCallback(d)
|
||||
if d.error != nil && isFatal {
|
||||
gologger.Fatal().Msgf("Could not fetch dynamic secret: %s\n", d.error)
|
||||
if d.error != nil {
|
||||
if isFatal {
|
||||
gologger.Fatal().Msgf("Could not fetch dynamic secret: %s\n", d.error)
|
||||
}
|
||||
} else {
|
||||
d.fetched = true
|
||||
}
|
||||
|
||||
return d.error
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user