package core
import (
"fmt"
"net/http/cookiejar"
"sync/atomic"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
"github.com/projectdiscovery/nuclei/v3/pkg/workflows"
syncutil "github.com/projectdiscovery/utils/sync"
)
const workflowStepExecutionError = "[%s] Could not execute workflow step: %s\n"
func (e *Engine) executeWorkflow(ctx *scan.ScanContext, w *workflows.Workflow) bool {
results := &atomic.Bool{}
workflowCookieJar, _ := cookiejar.New(nil)
ctxArgs := contextargs.New(ctx.Context())
ctxArgs.MetaInput = ctx.Input.MetaInput
ctxArgs.CookieJar = workflowCookieJar
templateThreads := w.Options.Options.TemplateThreads
if templateThreads == 1 {
templateThreads++
}
swg, _ := syncutil.New(syncutil.WithSize(templateThreads))
for _, template := range w.Workflows {
swg.Add()
func(template *workflows.WorkflowTemplate) {
defer swg.Done()
if err := e.runWorkflowStep(template, ctx, results, swg, w); err != nil {
gologger.Warning().Msgf(workflowStepExecutionError, template.Template, err)
}
}(template)
}
swg.Wait()
return results.Load()
}
func (e *Engine) runWorkflowStep(template *workflows.WorkflowTemplate, ctx *scan.ScanContext, results *atomic.Bool, swg *syncutil.AdaptiveWaitGroup, w *workflows.Workflow) error {
var firstMatched bool
var err error
var mainErr error
if len(template.Matchers) == 0 {
for _, executer := range template.Executers {
executer.Options.Progress.AddToTotal(int64(executer.Executer.Requests()))
if len(template.Subtemplates) > 0 {
ctx.OnResult = func(result *output.InternalWrappedEvent) {
if result.OperatorsResult == nil {
return
}
if len(result.Results) > 0 {
firstMatched = true
}
if result.OperatorsResult != nil && result.OperatorsResult.Extracts != nil {
for k, v := range result.OperatorsResult.Extracts {
switch len(v) {
case 0, 1:
ctx.Input.Set(k, v[0])
default:
for vIdx, vVal := range v {
normalizedKIdx := fmt.Sprintf("%s%d", k, vIdx)
ctx.Input.Set(normalizedKIdx, vVal)
}
ctx.Input.Set(k, v)
}
}
}
}
_, err = executer.Executer.ExecuteWithResults(ctx)
} else {
var matched bool
matched, err = executer.Executer.Execute(ctx)
if matched {
firstMatched = true
}
}
if w.Options.HostErrorsCache != nil {
w.Options.HostErrorsCache.MarkFailedOrRemove(w.Options.ProtocolType.String(), ctx.Input, err)
}
if err != nil {
if len(template.Executers) == 1 {
mainErr = err
} else {
gologger.Warning().Msgf(workflowStepExecutionError, template.Template, err)
}
continue
}
}
}
if len(template.Subtemplates) == 0 {
results.CompareAndSwap(false, firstMatched)
}
if len(template.Matchers) > 0 {
for _, executer := range template.Executers {
executer.Options.Progress.AddToTotal(int64(executer.Executer.Requests()))
ctx.OnResult = func(event *output.InternalWrappedEvent) {
if event.OperatorsResult == nil {
return
}
if event.OperatorsResult.Extracts != nil {
for k, v := range event.OperatorsResult.Extracts {
ctx.Input.Set(k, v)
}
}
for _, matcher := range template.Matchers {
if !matcher.Match(event.OperatorsResult) {
continue
}
for _, subtemplate := range matcher.Subtemplates {
swg.Add()
go func(subtemplate *workflows.WorkflowTemplate) {
defer swg.Done()
subCtx := scan.NewScanContext(ctx.Context(), ctx.Input.Clone())
if err := e.runWorkflowStep(subtemplate, subCtx, results, swg, w); err != nil {
gologger.Warning().Msgf(workflowStepExecutionError, subtemplate.Template, err)
}
}(subtemplate)
}
}
}
_, err := executer.Executer.ExecuteWithResults(ctx)
if err != nil {
if len(template.Executers) == 1 {
mainErr = err
} else {
gologger.Warning().Msgf(workflowStepExecutionError, template.Template, err)
}
continue
}
}
return mainErr
}
if len(template.Subtemplates) > 0 && firstMatched {
for _, subtemplate := range template.Subtemplates {
swg.Add()
go func(template *workflows.WorkflowTemplate) {
subCtx := scan.NewScanContext(ctx.Context(), ctx.Input)
if err := e.runWorkflowStep(template, subCtx, results, swg, w); err != nil {
gologger.Warning().Msgf(workflowStepExecutionError, template.Template, err)
}
swg.Done()
}(subtemplate)
}
}
return mainErr
}