Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/core/execute_options.go
2848 views
1
package core
2
3
import (
4
"context"
5
"sync"
6
"sync/atomic"
7
8
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider"
9
"github.com/projectdiscovery/nuclei/v3/pkg/output"
10
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
11
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
12
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
13
"github.com/projectdiscovery/nuclei/v3/pkg/types/scanstrategy"
14
stringsutil "github.com/projectdiscovery/utils/strings"
15
syncutil "github.com/projectdiscovery/utils/sync"
16
)
17
18
// Execute takes a list of templates/workflows that have been compiled
19
// and executes them based on provided concurrency options.
20
//
21
// All the execution logic for the templates/workflows happens in this part
22
// of the engine.
23
func (e *Engine) Execute(ctx context.Context, templates []*templates.Template, target provider.InputProvider) *atomic.Bool {
24
return e.ExecuteScanWithOpts(ctx, templates, target, false)
25
}
26
27
// ExecuteWithResults a list of templates with results
28
func (e *Engine) ExecuteWithResults(ctx context.Context, templatesList []*templates.Template, target provider.InputProvider, callback func(*output.ResultEvent)) *atomic.Bool {
29
e.Callback = callback
30
return e.ExecuteScanWithOpts(ctx, templatesList, target, false)
31
}
32
33
// ExecuteScanWithOpts executes scan with given scanStrategy
34
func (e *Engine) ExecuteScanWithOpts(ctx context.Context, templatesList []*templates.Template, target provider.InputProvider, noCluster bool) *atomic.Bool {
35
results := &atomic.Bool{}
36
selfcontainedWg := &sync.WaitGroup{}
37
38
totalReqBeforeCluster := getRequestCount(templatesList) * int(target.Count())
39
40
// attempt to cluster templates if noCluster is false
41
var finalTemplates []*templates.Template
42
clusterCount := 0
43
if !noCluster {
44
finalTemplates, clusterCount = templates.ClusterTemplates(templatesList, e.executerOpts)
45
} else {
46
finalTemplates = templatesList
47
}
48
49
totalReqAfterClustering := getRequestCount(finalTemplates) * int(target.Count())
50
51
if !noCluster && totalReqAfterClustering < totalReqBeforeCluster {
52
e.Logger.Info().Msgf("Templates clustered: %d (Reduced %d Requests)", clusterCount, totalReqBeforeCluster-totalReqAfterClustering)
53
}
54
55
// 0 matches means no templates were found in the directory
56
if len(finalTemplates) == 0 {
57
return &atomic.Bool{}
58
}
59
60
if e.executerOpts.Progress != nil {
61
// Notes:
62
// workflow requests are not counted as they can be conditional
63
// templateList count is user requested templates count (before clustering)
64
// totalReqAfterClustering is total requests count after clustering
65
e.executerOpts.Progress.Init(target.Count(), len(templatesList), int64(totalReqAfterClustering))
66
}
67
68
if stringsutil.EqualFoldAny(e.options.ScanStrategy, scanstrategy.Auto.String(), "") {
69
// TODO: this is only a placeholder, auto scan strategy should choose scan strategy
70
// based on no of hosts , templates , stream and other optimization parameters
71
e.options.ScanStrategy = scanstrategy.TemplateSpray.String()
72
}
73
74
filtered := []*templates.Template{}
75
selfContained := []*templates.Template{}
76
// Filter Self Contained templates since they are not bound to target
77
for _, v := range finalTemplates {
78
if v.SelfContained {
79
selfContained = append(selfContained, v)
80
} else {
81
filtered = append(filtered, v)
82
}
83
}
84
85
// Execute All SelfContained in parallel
86
e.executeAllSelfContained(ctx, selfContained, results, selfcontainedWg)
87
88
strategyResult := &atomic.Bool{}
89
switch e.options.ScanStrategy {
90
case scanstrategy.TemplateSpray.String():
91
strategyResult = e.executeTemplateSpray(ctx, filtered, target)
92
case scanstrategy.HostSpray.String():
93
strategyResult = e.executeHostSpray(ctx, filtered, target)
94
}
95
96
results.CompareAndSwap(false, strategyResult.Load())
97
98
selfcontainedWg.Wait()
99
return results
100
}
101
102
// executeTemplateSpray executes scan using template spray strategy where targets are iterated over each template
103
func (e *Engine) executeTemplateSpray(ctx context.Context, templatesList []*templates.Template, target provider.InputProvider) *atomic.Bool {
104
results := &atomic.Bool{}
105
106
// wp is workpool that contains different waitgroups for
107
// headless and non-headless templates
108
wp := e.GetWorkPool()
109
defer wp.Wait()
110
111
for _, template := range templatesList {
112
113
select {
114
case <-ctx.Done():
115
return results
116
default:
117
}
118
119
// resize check point - nop if there are no changes
120
wp.RefreshWithConfig(e.GetWorkPoolConfig())
121
122
templateType := template.Type()
123
var wg *syncutil.AdaptiveWaitGroup
124
if templateType == types.HeadlessProtocol {
125
wg = wp.Headless
126
} else {
127
wg = wp.Default
128
}
129
130
wg.Add()
131
go func(tpl *templates.Template) {
132
defer wg.Done()
133
// All other request types are executed here
134
// Note: executeTemplateWithTargets creates goroutines and blocks
135
// given template is executed on all targets
136
e.executeTemplateWithTargets(ctx, tpl, target, results)
137
}(template)
138
}
139
return results
140
}
141
142
// executeHostSpray executes scan using host spray strategy where templates are iterated over each target
143
func (e *Engine) executeHostSpray(ctx context.Context, templatesList []*templates.Template, target provider.InputProvider) *atomic.Bool {
144
results := &atomic.Bool{}
145
wp, _ := syncutil.New(syncutil.WithSize(e.options.BulkSize + e.options.HeadlessBulkSize))
146
defer wp.Wait()
147
148
target.Iterate(func(value *contextargs.MetaInput) bool {
149
select {
150
case <-ctx.Done():
151
return false
152
default:
153
}
154
155
wp.Add()
156
go func(targetval *contextargs.MetaInput) {
157
defer wp.Done()
158
e.executeTemplatesOnTarget(ctx, templatesList, targetval, results)
159
}(value)
160
return true
161
})
162
return results
163
}
164
165
// returns total requests count
166
func getRequestCount(templates []*templates.Template) int {
167
count := 0
168
for _, template := range templates {
169
// ignore requests in workflows as total requests in workflow
170
// depends on what templates will be called in workflow
171
if len(template.Workflows) > 0 {
172
continue
173
}
174
count += template.TotalRequests
175
}
176
return count
177
}
178
179