Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/core/executors.go
2842 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
if e.workPool == nil {
52
e.workPool = e.GetWorkPool()
53
}
54
// Bounded worker pool using input concurrency
55
pool := e.workPool.InputPool(template.Type())
56
workerCount := 1
57
if pool != nil && pool.Size > 0 {
58
workerCount = pool.Size
59
}
60
61
var (
62
index uint32
63
)
64
65
e.executerOpts.ResumeCfg.Lock()
66
currentInfo, ok := e.executerOpts.ResumeCfg.Current[template.ID]
67
if !ok {
68
currentInfo = &generalTypes.ResumeInfo{}
69
e.executerOpts.ResumeCfg.Current[template.ID] = currentInfo
70
}
71
if currentInfo.InFlight == nil {
72
currentInfo.InFlight = make(map[uint32]struct{})
73
}
74
resumeFromInfo, ok := e.executerOpts.ResumeCfg.ResumeFrom[template.ID]
75
if !ok {
76
resumeFromInfo = &generalTypes.ResumeInfo{}
77
e.executerOpts.ResumeCfg.ResumeFrom[template.ID] = resumeFromInfo
78
}
79
e.executerOpts.ResumeCfg.Unlock()
80
81
// track progression
82
cleanupInFlight := func(index uint32) {
83
currentInfo.Lock()
84
delete(currentInfo.InFlight, index)
85
currentInfo.Unlock()
86
}
87
88
// task represents a single target execution unit
89
type task struct {
90
index uint32
91
skip bool
92
value *contextargs.MetaInput
93
}
94
95
tasks := make(chan task)
96
var workersWg sync.WaitGroup
97
workersWg.Add(workerCount)
98
for i := 0; i < workerCount; i++ {
99
go func() {
100
defer workersWg.Done()
101
for t := range tasks {
102
func() {
103
defer cleanupInFlight(t.index)
104
select {
105
case <-ctx.Done():
106
return
107
default:
108
}
109
if t.skip {
110
return
111
}
112
113
match, err := e.executeTemplateOnInput(ctx, template, t.value)
114
if err != nil {
115
e.options.Logger.Warning().Msgf("[%s] Could not execute step on %s: %s\n", template.ID, t.value.Input, err)
116
}
117
results.CompareAndSwap(false, match)
118
}()
119
}
120
}()
121
}
122
123
target.Iterate(func(scannedValue *contextargs.MetaInput) bool {
124
select {
125
case <-ctx.Done():
126
return false // exit
127
default:
128
}
129
130
// Best effort to track the host progression
131
// skips indexes lower than the minimum in-flight at interruption time
132
var skip bool
133
if resumeFromInfo.Completed { // the template was completed
134
e.options.Logger.Debug().Msgf("[%s] Skipping \"%s\": Resume - Template already completed", template.ID, scannedValue.Input)
135
skip = true
136
} else if index < resumeFromInfo.SkipUnder { // index lower than the sliding window (bulk-size)
137
e.options.Logger.Debug().Msgf("[%s] Skipping \"%s\": Resume - Target already processed", template.ID, scannedValue.Input)
138
skip = true
139
} else if _, isInFlight := resumeFromInfo.InFlight[index]; isInFlight { // the target wasn't completed successfully
140
e.options.Logger.Debug().Msgf("[%s] Repeating \"%s\": Resume - Target wasn't completed", template.ID, scannedValue.Input)
141
// skip is already false, but leaving it here for clarity
142
skip = false
143
} else if index > resumeFromInfo.DoAbove { // index above the sliding window (bulk-size)
144
// skip is already false - but leaving it here for clarity
145
skip = false
146
}
147
148
currentInfo.Lock()
149
currentInfo.InFlight[index] = struct{}{}
150
currentInfo.Unlock()
151
152
// Skip if the host has had errors
153
if e.executerOpts.HostErrorsCache != nil && e.executerOpts.HostErrorsCache.Check(e.executerOpts.ProtocolType.String(), contextargs.NewWithMetaInput(ctx, scannedValue)) {
154
skipEvent := &output.ResultEvent{
155
TemplateID: template.ID,
156
TemplatePath: template.Path,
157
Info: template.Info,
158
Type: e.executerOpts.ProtocolType.String(),
159
Host: scannedValue.Input,
160
MatcherStatus: false,
161
Error: "host was skipped as it was found unresponsive",
162
Timestamp: time.Now(),
163
}
164
165
if e.Callback != nil {
166
e.Callback(skipEvent)
167
} else if e.executerOpts.Output != nil {
168
_ = e.executerOpts.Output.Write(skipEvent)
169
}
170
return true
171
}
172
173
tasks <- task{index: index, skip: skip, value: scannedValue}
174
index++
175
return true
176
})
177
178
close(tasks)
179
workersWg.Wait()
180
181
// on completion marks the template as completed
182
currentInfo.Lock()
183
currentInfo.Completed = true
184
currentInfo.Unlock()
185
}
186
187
// executeTemplatesOnTarget execute given templates on given single target
188
func (e *Engine) executeTemplatesOnTarget(ctx context.Context, alltemplates []*templates.Template, target *contextargs.MetaInput, results *atomic.Bool) {
189
// all templates are executed on single target
190
191
// wp is workpool that contains different waitgroups for
192
// headless and non-headless templates
193
// global waitgroup should not be used here
194
wp := e.GetWorkPool()
195
defer wp.Wait()
196
197
for _, tpl := range alltemplates {
198
select {
199
case <-ctx.Done():
200
return
201
default:
202
}
203
204
// resize check point - nop if there are no changes
205
wp.RefreshWithConfig(e.GetWorkPoolConfig())
206
207
var sg *syncutil.AdaptiveWaitGroup
208
if tpl.Type() == types.HeadlessProtocol {
209
sg = wp.Headless
210
} else {
211
sg = wp.Default
212
}
213
sg.Add()
214
go func(template *templates.Template, value *contextargs.MetaInput, wg *syncutil.AdaptiveWaitGroup) {
215
defer wg.Done()
216
217
match, err := e.executeTemplateOnInput(ctx, template, value)
218
if err != nil {
219
e.options.Logger.Warning().Msgf("[%s] Could not execute step on %s: %s\n", template.ID, value.Input, err)
220
}
221
results.CompareAndSwap(false, match)
222
}(tpl, target, sg)
223
}
224
}
225
226
// executeTemplateOnInput performs template execution for a single input and returns match status and error
227
func (e *Engine) executeTemplateOnInput(ctx context.Context, template *templates.Template, value *contextargs.MetaInput) (bool, error) {
228
ctxArgs := contextargs.New(ctx)
229
ctxArgs.MetaInput = value
230
scanCtx := scan.NewScanContext(ctx, ctxArgs)
231
232
switch template.Type() {
233
case types.WorkflowProtocol:
234
return e.executeWorkflow(scanCtx, template.CompiledWorkflow), nil
235
default:
236
if e.Callback != nil {
237
results, err := template.Executer.ExecuteWithResults(scanCtx)
238
if err != nil {
239
return false, err
240
}
241
for _, result := range results {
242
e.Callback(result)
243
}
244
return len(results) > 0, nil
245
}
246
return template.Executer.Execute(scanCtx)
247
}
248
}
249
250