package core
import (
"context"
"sync"
"sync/atomic"
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider"
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
"github.com/projectdiscovery/nuclei/v3/pkg/types/scanstrategy"
stringsutil "github.com/projectdiscovery/utils/strings"
syncutil "github.com/projectdiscovery/utils/sync"
)
func (e *Engine) Execute(ctx context.Context, templates []*templates.Template, target provider.InputProvider) *atomic.Bool {
return e.ExecuteScanWithOpts(ctx, templates, target, false)
}
func (e *Engine) ExecuteWithResults(ctx context.Context, templatesList []*templates.Template, target provider.InputProvider, callback func(*output.ResultEvent)) *atomic.Bool {
e.Callback = callback
return e.ExecuteScanWithOpts(ctx, templatesList, target, false)
}
func (e *Engine) ExecuteScanWithOpts(ctx context.Context, templatesList []*templates.Template, target provider.InputProvider, noCluster bool) *atomic.Bool {
results := &atomic.Bool{}
selfcontainedWg := &sync.WaitGroup{}
totalReqBeforeCluster := getRequestCount(templatesList) * int(target.Count())
var finalTemplates []*templates.Template
clusterCount := 0
if !noCluster {
finalTemplates, clusterCount = templates.ClusterTemplates(templatesList, e.executerOpts)
} else {
finalTemplates = templatesList
}
totalReqAfterClustering := getRequestCount(finalTemplates) * int(target.Count())
if !noCluster && totalReqAfterClustering < totalReqBeforeCluster {
e.Logger.Info().Msgf("Templates clustered: %d (Reduced %d Requests)", clusterCount, totalReqBeforeCluster-totalReqAfterClustering)
}
if len(finalTemplates) == 0 {
return &atomic.Bool{}
}
if e.executerOpts.Progress != nil {
e.executerOpts.Progress.Init(target.Count(), len(templatesList), int64(totalReqAfterClustering))
}
if stringsutil.EqualFoldAny(e.options.ScanStrategy, scanstrategy.Auto.String(), "") {
e.options.ScanStrategy = scanstrategy.TemplateSpray.String()
}
filtered := []*templates.Template{}
selfContained := []*templates.Template{}
for _, v := range finalTemplates {
if v.SelfContained {
selfContained = append(selfContained, v)
} else {
filtered = append(filtered, v)
}
}
e.executeAllSelfContained(ctx, selfContained, results, selfcontainedWg)
strategyResult := &atomic.Bool{}
switch e.options.ScanStrategy {
case scanstrategy.TemplateSpray.String():
strategyResult = e.executeTemplateSpray(ctx, filtered, target)
case scanstrategy.HostSpray.String():
strategyResult = e.executeHostSpray(ctx, filtered, target)
}
results.CompareAndSwap(false, strategyResult.Load())
selfcontainedWg.Wait()
return results
}
func (e *Engine) executeTemplateSpray(ctx context.Context, templatesList []*templates.Template, target provider.InputProvider) *atomic.Bool {
results := &atomic.Bool{}
wp := e.GetWorkPool()
defer wp.Wait()
for _, template := range templatesList {
template := template
select {
case <-ctx.Done():
return results
default:
}
wp.RefreshWithConfig(e.GetWorkPoolConfig())
templateType := template.Type()
var wg *syncutil.AdaptiveWaitGroup
if templateType == types.HeadlessProtocol {
wg = wp.Headless
} else {
wg = wp.Default
}
wg.Add()
go func(tpl *templates.Template) {
defer wg.Done()
e.executeTemplateWithTargets(ctx, tpl, target, results)
}(template)
}
return results
}
func (e *Engine) executeHostSpray(ctx context.Context, templatesList []*templates.Template, target provider.InputProvider) *atomic.Bool {
results := &atomic.Bool{}
wp, _ := syncutil.New(syncutil.WithSize(e.options.BulkSize + e.options.HeadlessBulkSize))
defer wp.Wait()
target.Iterate(func(value *contextargs.MetaInput) bool {
select {
case <-ctx.Done():
return false
default:
}
wp.Add()
go func(targetval *contextargs.MetaInput) {
defer wp.Done()
e.executeTemplatesOnTarget(ctx, templatesList, targetval, results)
}(value)
return true
})
return results
}
func getRequestCount(templates []*templates.Template) int {
count := 0
for _, template := range templates {
if len(template.Workflows) > 0 {
continue
}
count += template.TotalRequests
}
return count
}