Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/core/executors.go
2070 views
1
package core
2
3
import (
4
"context"
5
"sync"
6
"sync/atomic"
7
"time"
8
9
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider"
10
"github.com/projectdiscovery/nuclei/v3/pkg/output"
11
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
12
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
13
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
14
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
15
generalTypes "github.com/projectdiscovery/nuclei/v3/pkg/types"
16
syncutil "github.com/projectdiscovery/utils/sync"
17
)
18
19
// Executors are low level executors that deals with template execution on a target
20
21
// executeAllSelfContained executes all self contained templates that do not use `target`
22
func (e *Engine) executeAllSelfContained(ctx context.Context, alltemplates []*templates.Template, results *atomic.Bool, sg *sync.WaitGroup) {
23
for _, v := range alltemplates {
24
sg.Add(1)
25
go func(template *templates.Template) {
26
defer sg.Done()
27
var err error
28
var match bool
29
ctx := scan.NewScanContext(ctx, contextargs.New(ctx))
30
if e.Callback != nil {
31
if results, err := template.Executer.ExecuteWithResults(ctx); err == nil {
32
for _, result := range results {
33
e.Callback(result)
34
}
35
}
36
37
match = true
38
} else {
39
match, err = template.Executer.Execute(ctx)
40
}
41
if err != nil {
42
e.options.Logger.Warning().Msgf("[%s] Could not execute step (self-contained): %s\n", e.executerOpts.Colorizer.BrightBlue(template.ID), err)
43
}
44
results.CompareAndSwap(false, match)
45
}(v)
46
}
47
}
48
49
// executeTemplateWithTargets executes a given template on x targets (with a internal targetpool(i.e concurrency))
50
func (e *Engine) executeTemplateWithTargets(ctx context.Context, template *templates.Template, target provider.InputProvider, results *atomic.Bool) {
51
// this is target pool i.e max target to execute
52
wg := e.workPool.InputPool(template.Type())
53
54
var (
55
index uint32
56
)
57
58
e.executerOpts.ResumeCfg.Lock()
59
currentInfo, ok := e.executerOpts.ResumeCfg.Current[template.ID]
60
if !ok {
61
currentInfo = &generalTypes.ResumeInfo{}
62
e.executerOpts.ResumeCfg.Current[template.ID] = currentInfo
63
}
64
if currentInfo.InFlight == nil {
65
currentInfo.InFlight = make(map[uint32]struct{})
66
}
67
resumeFromInfo, ok := e.executerOpts.ResumeCfg.ResumeFrom[template.ID]
68
if !ok {
69
resumeFromInfo = &generalTypes.ResumeInfo{}
70
e.executerOpts.ResumeCfg.ResumeFrom[template.ID] = resumeFromInfo
71
}
72
e.executerOpts.ResumeCfg.Unlock()
73
74
// track progression
75
cleanupInFlight := func(index uint32) {
76
currentInfo.Lock()
77
delete(currentInfo.InFlight, index)
78
currentInfo.Unlock()
79
}
80
81
target.Iterate(func(scannedValue *contextargs.MetaInput) bool {
82
select {
83
case <-ctx.Done():
84
return false // exit
85
default:
86
}
87
88
// Best effort to track the host progression
89
// skips indexes lower than the minimum in-flight at interruption time
90
var skip bool
91
if resumeFromInfo.Completed { // the template was completed
92
e.options.Logger.Debug().Msgf("[%s] Skipping \"%s\": Resume - Template already completed", template.ID, scannedValue.Input)
93
skip = true
94
} else if index < resumeFromInfo.SkipUnder { // index lower than the sliding window (bulk-size)
95
e.options.Logger.Debug().Msgf("[%s] Skipping \"%s\": Resume - Target already processed", template.ID, scannedValue.Input)
96
skip = true
97
} else if _, isInFlight := resumeFromInfo.InFlight[index]; isInFlight { // the target wasn't completed successfully
98
e.options.Logger.Debug().Msgf("[%s] Repeating \"%s\": Resume - Target wasn't completed", template.ID, scannedValue.Input)
99
// skip is already false, but leaving it here for clarity
100
skip = false
101
} else if index > resumeFromInfo.DoAbove { // index above the sliding window (bulk-size)
102
// skip is already false - but leaving it here for clarity
103
skip = false
104
}
105
106
currentInfo.Lock()
107
currentInfo.InFlight[index] = struct{}{}
108
currentInfo.Unlock()
109
110
// Skip if the host has had errors
111
if e.executerOpts.HostErrorsCache != nil && e.executerOpts.HostErrorsCache.Check(e.executerOpts.ProtocolType.String(), contextargs.NewWithMetaInput(ctx, scannedValue)) {
112
skipEvent := &output.ResultEvent{
113
TemplateID: template.ID,
114
TemplatePath: template.Path,
115
Info: template.Info,
116
Type: e.executerOpts.ProtocolType.String(),
117
Host: scannedValue.Input,
118
MatcherStatus: false,
119
Error: "host was skipped as it was found unresponsive",
120
Timestamp: time.Now(),
121
}
122
123
if e.Callback != nil {
124
e.Callback(skipEvent)
125
} else if e.executerOpts.Output != nil {
126
_ = e.executerOpts.Output.Write(skipEvent)
127
}
128
return true
129
}
130
131
wg.Add()
132
go func(index uint32, skip bool, value *contextargs.MetaInput) {
133
defer wg.Done()
134
defer cleanupInFlight(index)
135
if skip {
136
return
137
}
138
139
var match bool
140
var err error
141
ctxArgs := contextargs.New(ctx)
142
ctxArgs.MetaInput = value
143
ctx := scan.NewScanContext(ctx, ctxArgs)
144
switch template.Type() {
145
case types.WorkflowProtocol:
146
match = e.executeWorkflow(ctx, template.CompiledWorkflow)
147
default:
148
if e.Callback != nil {
149
if results, err := template.Executer.ExecuteWithResults(ctx); err == nil {
150
for _, result := range results {
151
e.Callback(result)
152
}
153
}
154
match = true
155
} else {
156
match, err = template.Executer.Execute(ctx)
157
}
158
}
159
if err != nil {
160
e.options.Logger.Warning().Msgf("[%s] Could not execute step on %s: %s\n", e.executerOpts.Colorizer.BrightBlue(template.ID), value.Input, err)
161
}
162
results.CompareAndSwap(false, match)
163
}(index, skip, scannedValue)
164
index++
165
return true
166
})
167
wg.Wait()
168
169
// on completion marks the template as completed
170
currentInfo.Lock()
171
currentInfo.Completed = true
172
currentInfo.Unlock()
173
}
174
175
// executeTemplatesOnTarget execute given templates on given single target
176
func (e *Engine) executeTemplatesOnTarget(ctx context.Context, alltemplates []*templates.Template, target *contextargs.MetaInput, results *atomic.Bool) {
177
// all templates are executed on single target
178
179
// wp is workpool that contains different waitgroups for
180
// headless and non-headless templates
181
// global waitgroup should not be used here
182
wp := e.GetWorkPool()
183
defer wp.Wait()
184
185
for _, tpl := range alltemplates {
186
select {
187
case <-ctx.Done():
188
return
189
default:
190
}
191
192
// resize check point - nop if there are no changes
193
wp.RefreshWithConfig(e.GetWorkPoolConfig())
194
195
var sg *syncutil.AdaptiveWaitGroup
196
if tpl.Type() == types.HeadlessProtocol {
197
sg = wp.Headless
198
} else {
199
sg = wp.Default
200
}
201
sg.Add()
202
go func(template *templates.Template, value *contextargs.MetaInput, wg *syncutil.AdaptiveWaitGroup) {
203
defer wg.Done()
204
205
var match bool
206
var err error
207
ctxArgs := contextargs.New(ctx)
208
ctxArgs.MetaInput = value
209
ctx := scan.NewScanContext(ctx, ctxArgs)
210
switch template.Type() {
211
case types.WorkflowProtocol:
212
match = e.executeWorkflow(ctx, template.CompiledWorkflow)
213
default:
214
if e.Callback != nil {
215
if results, err := template.Executer.ExecuteWithResults(ctx); err == nil {
216
for _, result := range results {
217
e.Callback(result)
218
}
219
}
220
match = true
221
} else {
222
match, err = template.Executer.Execute(ctx)
223
}
224
}
225
if err != nil {
226
e.options.Logger.Warning().Msgf("[%s] Could not execute step on %s: %s\n", e.executerOpts.Colorizer.BrightBlue(template.ID), value.Input, err)
227
}
228
results.CompareAndSwap(false, match)
229
}(tpl, target, sg)
230
}
231
}
232
233