package generic
import (
"sync/atomic"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/utils"
mapsutil "github.com/projectdiscovery/utils/maps"
)
type Generic struct {
requests []protocols.Request
options *protocols.ExecutorOptions
results *atomic.Bool
}
func NewGenericEngine(requests []protocols.Request, options *protocols.ExecutorOptions, results *atomic.Bool) *Generic {
if results == nil {
results = &atomic.Bool{}
}
return &Generic{requests: requests, options: options, results: results}
}
func (g *Generic) Compile() error {
return nil
}
func (g *Generic) ExecuteWithResults(ctx *scan.ScanContext) error {
dynamicValues := make(map[string]interface{})
if ctx.Input.HasArgs() {
ctx.Input.ForEach(func(key string, value interface{}) {
dynamicValues[key] = value
})
}
previous := mapsutil.NewSyncLockMap[string, any]()
for _, req := range g.requests {
select {
case <-ctx.Context().Done():
return ctx.Context().Err()
default:
}
inputItem := ctx.Input.Clone()
if g.options.InputHelper != nil && ctx.Input.MetaInput.Input != "" {
if inputItem.MetaInput.Input = g.options.InputHelper.Transform(inputItem.MetaInput.Input, req.Type()); inputItem.MetaInput.Input == "" {
return nil
}
}
err := req.ExecuteWithResults(inputItem, dynamicValues, output.InternalEvent(previous.GetAll()), func(event *output.InternalWrappedEvent) {
if event == nil {
return
}
utils.FillPreviousEvent(req.GetID(), event, previous)
if event.HasOperatorResult() {
g.results.CompareAndSwap(false, true)
}
ctx.LogEvent(event)
})
if err != nil {
ctx.LogError(err)
gologger.Warning().Msgf("[%s] Could not execute request for %s: %s\n", g.options.TemplateID, ctx.Input.MetaInput.PrettyPrint(), err)
}
if g.options.HostErrorsCache != nil {
g.options.HostErrorsCache.MarkFailedOrRemove(g.options.ProtocolType.String(), ctx.Input, err)
}
if g.results.Load() && (g.options.StopAtFirstMatch || g.options.Options.StopAtFirstMatch) {
break
}
}
return nil
}
func (g *Generic) Name() string {
return "generic"
}