Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/tmplexec/generic/exec.go
2070 views
1
package generic
2
3
import (
4
"sync/atomic"
5
6
"github.com/projectdiscovery/gologger"
7
"github.com/projectdiscovery/nuclei/v3/pkg/output"
8
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
9
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
10
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/utils"
11
mapsutil "github.com/projectdiscovery/utils/maps"
12
)
13
14
// generic engine as name suggests is a generic template
15
// execution engine and executes all requests one after another
16
// without any logic in between
17
type Generic struct {
18
requests []protocols.Request
19
options *protocols.ExecutorOptions
20
results *atomic.Bool
21
}
22
23
// NewGenericEngine creates a new generic engine from a list of requests
24
func NewGenericEngine(requests []protocols.Request, options *protocols.ExecutorOptions, results *atomic.Bool) *Generic {
25
if results == nil {
26
results = &atomic.Bool{}
27
}
28
return &Generic{requests: requests, options: options, results: results}
29
}
30
31
// Compile engine specific compilation
32
func (g *Generic) Compile() error {
33
// protocol/ request is already handled by template executer
34
return nil
35
}
36
37
// ExecuteWithResults executes the template and returns results
38
func (g *Generic) ExecuteWithResults(ctx *scan.ScanContext) error {
39
dynamicValues := make(map[string]interface{})
40
if ctx.Input.HasArgs() {
41
ctx.Input.ForEach(func(key string, value interface{}) {
42
dynamicValues[key] = value
43
})
44
}
45
previous := mapsutil.NewSyncLockMap[string, any]()
46
47
for _, req := range g.requests {
48
select {
49
case <-ctx.Context().Done():
50
return ctx.Context().Err()
51
default:
52
}
53
54
inputItem := ctx.Input.Clone()
55
if g.options.InputHelper != nil && ctx.Input.MetaInput.Input != "" {
56
if inputItem.MetaInput.Input = g.options.InputHelper.Transform(inputItem.MetaInput.Input, req.Type()); inputItem.MetaInput.Input == "" {
57
return nil
58
}
59
}
60
61
err := req.ExecuteWithResults(inputItem, dynamicValues, output.InternalEvent(previous.GetAll()), func(event *output.InternalWrappedEvent) {
62
// this callback is not concurrent safe so mutex should be used to synchronize
63
if event == nil {
64
// ideally this should never happen since protocol exits on error and callback is not called
65
return
66
}
67
68
utils.FillPreviousEvent(req.GetID(), event, previous)
69
70
if event.HasOperatorResult() {
71
g.results.CompareAndSwap(false, true)
72
}
73
// for ExecuteWithResults : this callback will execute user defined callback and some error handling
74
// for Execute : this callback will print the result to output
75
ctx.LogEvent(event)
76
})
77
if err != nil {
78
ctx.LogError(err)
79
gologger.Warning().Msgf("[%s] Could not execute request for %s: %s\n", g.options.TemplateID, ctx.Input.MetaInput.PrettyPrint(), err)
80
}
81
if g.options.HostErrorsCache != nil {
82
g.options.HostErrorsCache.MarkFailedOrRemove(g.options.ProtocolType.String(), ctx.Input, err)
83
}
84
// If a match was found and stop at first match is set, break out of the loop and return
85
if g.results.Load() && (g.options.StopAtFirstMatch || g.options.Options.StopAtFirstMatch) {
86
break
87
}
88
}
89
return nil
90
}
91
92
// Type returns the type of engine
93
func (g *Generic) Name() string {
94
return "generic"
95
}
96
97