Path: blob/dev/pkg/tmplexec/flow/flow_internal.go
2070 views
package flow12import (3"fmt"4"sync/atomic"56"github.com/Mzack9999/goja"7"github.com/projectdiscovery/nuclei/v3/pkg/output"8"github.com/projectdiscovery/nuclei/v3/pkg/protocols"9"github.com/projectdiscovery/utils/errkit"10mapsutil "github.com/projectdiscovery/utils/maps"11)1213// contains all internal/unexported methods of flow1415// requestExecutor executes a protocol/request and returns true if any matcher was found16func (f *FlowExecutor) requestExecutor(runtime *goja.Runtime, reqMap mapsutil.Map[string, protocols.Request], opts *ProtoOptions) bool {17defer func() {18// evaluate all variables after execution of each protocol19variableMap := f.options.Variables.Evaluate(f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll())20f.options.GetTemplateCtx(f.ctx.Input.MetaInput).Merge(variableMap) // merge all variables into template context2122// to avoid polling update template variables everytime we execute a protocol23m := f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll()24_ = runtime.Set("template", m)25}()26matcherStatus := &atomic.Bool{} // due to interactsh matcher polling logic this needs to be atomic bool27// if no id is passed execute all requests in sequence28if len(opts.reqIDS) == 0 {29// execution logic for http()/dns() etc30for index := range f.allProtocols[opts.protoName] {31req := f.allProtocols[opts.protoName][index]32// transform input if required33inputItem := f.ctx.Input.Clone()34if f.options.InputHelper != nil && f.ctx.Input.MetaInput.Input != "" {35if inputItem.MetaInput.Input = f.options.InputHelper.Transform(inputItem.MetaInput.Input, req.Type()); inputItem.MetaInput.Input == "" {36f.ctx.LogError(fmt.Errorf("failed to transform input for protocol %s", req.Type()))37return false38}39}40err := req.ExecuteWithResults(inputItem, output.InternalEvent(f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll()), output.InternalEvent{}, f.protocolResultCallback(req, matcherStatus, opts))41if err != nil {42// save all errors in a map with id as key43// its less likely that there will be race condition but just in case44id := req.GetID()45if id == "" {46id, _ = reqMap.GetKeyWithValue(req)47}48err = f.allErrs.Set(opts.protoName+":"+id, err)49if err != nil {50f.ctx.LogError(fmt.Errorf("failed to store flow runtime errors got %v", err))51}52return matcherStatus.Load()53}54}55return matcherStatus.Load()56}5758// execution logic for http("0") or http("get-aws-vpcs")59for _, id := range opts.reqIDS {60req, ok := reqMap[id]61if !ok {62f.ctx.LogError(fmt.Errorf("[%v] invalid request id '%s' provided", f.options.TemplateID, id))63// compile error64if err := f.allErrs.Set(opts.protoName+":"+id, errkit.Newf("[%s] invalid request id '%s' provided", f.options.TemplateID, id)); err != nil {65f.ctx.LogError(fmt.Errorf("failed to store flow runtime errors got %v", err))66}67return matcherStatus.Load()68}69// transform input if required70inputItem := f.ctx.Input.Clone()71if f.options.InputHelper != nil && f.ctx.Input.MetaInput.Input != "" {72if inputItem.MetaInput.Input = f.options.InputHelper.Transform(inputItem.MetaInput.Input, req.Type()); inputItem.MetaInput.Input == "" {73f.ctx.LogError(fmt.Errorf("failed to transform input for protocol %s", req.Type()))74return false75}76}77err := req.ExecuteWithResults(inputItem, output.InternalEvent(f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll()), output.InternalEvent{}, f.protocolResultCallback(req, matcherStatus, opts))78// Mark the request as seen79_ = f.executed.Set(requestKey(opts.protoName, req, id), struct{}{})80if err != nil {81index := id82err = f.allErrs.Set(opts.protoName+":"+index, err)83if err != nil {84f.ctx.LogError(fmt.Errorf("failed to store flow runtime errors got %v", err))85}86}87}88return matcherStatus.Load()89}9091func requestKey(proto string, req protocols.Request, id string) string {92if id == "" {93id = req.GetID()94}95return proto + ":" + id96}9798// protocolResultCallback returns a callback that is executed99// after execution of each protocol request100func (f *FlowExecutor) protocolResultCallback(req protocols.Request, matcherStatus *atomic.Bool, _ *ProtoOptions) func(result *output.InternalWrappedEvent) {101return func(result *output.InternalWrappedEvent) {102if result != nil {103// Note: flow specific implicit behaviours should be handled here104// before logging the event105f.ctx.LogEvent(result)106// export dynamic values from operators (i.e internal:true)107// add add it to template context108// this is a conflicting behaviour with iterate-all109if result.HasOperatorResult() {110f.results.CompareAndSwap(false, true)111// this is to handle case where there is any operator result (matcher or extractor)112matcherStatus.CompareAndSwap(false, result.OperatorsResult.Matched)113if !result.OperatorsResult.Matched && !hasMatchers(req.GetCompiledOperators()) {114// if matcher status is false . check if template/request contains any matcher at all115// if it does then we need to set matcher status to true116matcherStatus.CompareAndSwap(false, true)117}118if len(result.OperatorsResult.DynamicValues) > 0 {119for k, v := range result.OperatorsResult.DynamicValues {120// if length of v is 1 then remove slice and convert it to single value121if len(v) == 1 {122// add it to flatten keys list so it will be flattened to a string later123f.flattenKeys = append(f.flattenKeys, k)124// flatten and convert it to string125f.options.GetTemplateCtx(f.ctx.Input.MetaInput).Set(k, v[0])126} else {127// keep it as slice128f.options.GetTemplateCtx(f.ctx.Input.MetaInput).Set(k, v)129}130}131}132} else if !result.HasOperatorResult() && !hasOperators(req.GetCompiledOperators()) {133// this is to handle case where there are no operator result and there was no matcher in operators134// if matcher status is false . check if template/request contains any matcher at all135// if it does then we need to set matcher status to true136matcherStatus.CompareAndSwap(false, true)137}138}139}140}141142143