Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/core/execute_options.go
2070 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
template := template
113
114
select {
115
case <-ctx.Done():
116
return results
117
default:
118
}
119
120
// resize check point - nop if there are no changes
121
wp.RefreshWithConfig(e.GetWorkPoolConfig())
122
123
templateType := template.Type()
124
var wg *syncutil.AdaptiveWaitGroup
125
if templateType == types.HeadlessProtocol {
126
wg = wp.Headless
127
} else {
128
wg = wp.Default
129
}
130
131
wg.Add()
132
go func(tpl *templates.Template) {
133
defer wg.Done()
134
// All other request types are executed here
135
// Note: executeTemplateWithTargets creates goroutines and blocks
136
// given template is executed on all targets
137
e.executeTemplateWithTargets(ctx, tpl, target, results)
138
}(template)
139
}
140
return results
141
}
142
143
// executeHostSpray executes scan using host spray strategy where templates are iterated over each target
144
func (e *Engine) executeHostSpray(ctx context.Context, templatesList []*templates.Template, target provider.InputProvider) *atomic.Bool {
145
results := &atomic.Bool{}
146
wp, _ := syncutil.New(syncutil.WithSize(e.options.BulkSize + e.options.HeadlessBulkSize))
147
defer wp.Wait()
148
149
target.Iterate(func(value *contextargs.MetaInput) bool {
150
select {
151
case <-ctx.Done():
152
return false
153
default:
154
}
155
156
wp.Add()
157
go func(targetval *contextargs.MetaInput) {
158
defer wp.Done()
159
e.executeTemplatesOnTarget(ctx, templatesList, targetval, results)
160
}(value)
161
return true
162
})
163
return results
164
}
165
166
// returns total requests count
167
func getRequestCount(templates []*templates.Template) int {
168
count := 0
169
for _, template := range templates {
170
// ignore requests in workflows as total requests in workflow
171
// depends on what templates will be called in workflow
172
if len(template.Workflows) > 0 {
173
continue
174
}
175
count += template.TotalRequests
176
}
177
return count
178
}
179
180