2023-08-31 18:03:01 +05:30
|
|
|
package flow
|
|
|
|
|
|
|
|
|
|
import (
|
2024-01-08 05:12:11 +05:30
|
|
|
"fmt"
|
2023-08-31 18:03:01 +05:30
|
|
|
"sync/atomic"
|
|
|
|
|
|
|
|
|
|
"github.com/dop251/goja"
|
2023-10-17 17:44:13 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
|
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
2023-08-31 18:03:01 +05:30
|
|
|
mapsutil "github.com/projectdiscovery/utils/maps"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// contains all internal/unexported methods of flow
|
|
|
|
|
|
|
|
|
|
// requestExecutor executes a protocol/request and returns true if any matcher was found
|
2024-01-31 01:59:49 +05:30
|
|
|
func (f *FlowExecutor) requestExecutor(runtime *goja.Runtime, reqMap mapsutil.Map[string, protocols.Request], opts *ProtoOptions) bool {
|
2023-08-31 18:03:01 +05:30
|
|
|
defer func() {
|
|
|
|
|
// evaluate all variables after execution of each protocol
|
2024-01-08 05:12:11 +05:30
|
|
|
variableMap := f.options.Variables.Evaluate(f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll())
|
|
|
|
|
f.options.GetTemplateCtx(f.ctx.Input.MetaInput).Merge(variableMap) // merge all variables into template context
|
2023-08-31 18:03:01 +05:30
|
|
|
|
|
|
|
|
// to avoid polling update template variables everytime we execute a protocol
|
2024-01-08 05:12:11 +05:30
|
|
|
var m map[string]interface{} = f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll()
|
2024-01-31 01:59:49 +05:30
|
|
|
_ = runtime.Set("template", m)
|
2023-08-31 18:03:01 +05:30
|
|
|
}()
|
|
|
|
|
matcherStatus := &atomic.Bool{} // due to interactsh matcher polling logic this needs to be atomic bool
|
|
|
|
|
// if no id is passed execute all requests in sequence
|
|
|
|
|
if len(opts.reqIDS) == 0 {
|
|
|
|
|
// execution logic for http()/dns() etc
|
|
|
|
|
for index := range f.allProtocols[opts.protoName] {
|
|
|
|
|
req := f.allProtocols[opts.protoName][index]
|
2024-08-01 18:13:47 +03:00
|
|
|
// transform input if required
|
|
|
|
|
inputItem := f.ctx.Input.Clone()
|
|
|
|
|
if f.options.InputHelper != nil && f.ctx.Input.MetaInput.Input != "" {
|
|
|
|
|
if inputItem.MetaInput.Input = f.options.InputHelper.Transform(inputItem.MetaInput.Input, req.Type()); inputItem.MetaInput.Input == "" {
|
|
|
|
|
f.ctx.LogError(fmt.Errorf("failed to transform input for protocol %s", req.Type()))
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
err := req.ExecuteWithResults(inputItem, output.InternalEvent(f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll()), nil, f.protocolResultCallback(req, matcherStatus, opts))
|
2023-08-31 18:03:01 +05:30
|
|
|
if err != nil {
|
|
|
|
|
// save all errors in a map with id as key
|
|
|
|
|
// its less likely that there will be race condition but just in case
|
|
|
|
|
id := req.GetID()
|
|
|
|
|
if id == "" {
|
|
|
|
|
id, _ = reqMap.GetKeyWithValue(req)
|
|
|
|
|
}
|
|
|
|
|
err = f.allErrs.Set(opts.protoName+":"+id, err)
|
|
|
|
|
if err != nil {
|
2024-01-08 05:12:11 +05:30
|
|
|
f.ctx.LogError(fmt.Errorf("failed to store flow runtime errors got %v", err))
|
2023-08-31 18:03:01 +05:30
|
|
|
}
|
|
|
|
|
return matcherStatus.Load()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return matcherStatus.Load()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// execution logic for http("0") or http("get-aws-vpcs")
|
|
|
|
|
for _, id := range opts.reqIDS {
|
|
|
|
|
req, ok := reqMap[id]
|
|
|
|
|
if !ok {
|
2024-01-08 05:12:11 +05:30
|
|
|
f.ctx.LogError(fmt.Errorf("[%v] invalid request id '%s' provided", f.options.TemplateID, id))
|
2023-08-31 18:03:01 +05:30
|
|
|
// compile error
|
2023-10-13 13:17:27 +05:30
|
|
|
if err := f.allErrs.Set(opts.protoName+":"+id, ErrInvalidRequestID.Msgf(f.options.TemplateID, id)); err != nil {
|
2024-01-08 05:12:11 +05:30
|
|
|
f.ctx.LogError(fmt.Errorf("failed to store flow runtime errors got %v", err))
|
2023-08-31 18:03:01 +05:30
|
|
|
}
|
|
|
|
|
return matcherStatus.Load()
|
|
|
|
|
}
|
2024-08-01 18:13:47 +03:00
|
|
|
// transform input if required
|
|
|
|
|
inputItem := f.ctx.Input.Clone()
|
|
|
|
|
if f.options.InputHelper != nil && f.ctx.Input.MetaInput.Input != "" {
|
|
|
|
|
if inputItem.MetaInput.Input = f.options.InputHelper.Transform(inputItem.MetaInput.Input, req.Type()); inputItem.MetaInput.Input == "" {
|
|
|
|
|
f.ctx.LogError(fmt.Errorf("failed to transform input for protocol %s", req.Type()))
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
err := req.ExecuteWithResults(inputItem, output.InternalEvent(f.options.GetTemplateCtx(f.ctx.Input.MetaInput).GetAll()), nil, f.protocolResultCallback(req, matcherStatus, opts))
|
2023-08-31 18:03:01 +05:30
|
|
|
if err != nil {
|
|
|
|
|
index := id
|
|
|
|
|
err = f.allErrs.Set(opts.protoName+":"+index, err)
|
|
|
|
|
if err != nil {
|
2024-01-08 05:12:11 +05:30
|
|
|
f.ctx.LogError(fmt.Errorf("failed to store flow runtime errors got %v", err))
|
2023-08-31 18:03:01 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return matcherStatus.Load()
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-08 05:12:11 +05:30
|
|
|
// protocolResultCallback returns a callback that is executed
|
2023-11-02 13:33:40 +05:30
|
|
|
// after execution of each protocol request
|
2024-08-01 18:13:47 +03:00
|
|
|
func (f *FlowExecutor) protocolResultCallback(req protocols.Request, matcherStatus *atomic.Bool, _ *ProtoOptions) func(result *output.InternalWrappedEvent) {
|
2023-11-02 13:33:40 +05:30
|
|
|
return func(result *output.InternalWrappedEvent) {
|
|
|
|
|
if result != nil {
|
2024-01-08 05:12:11 +05:30
|
|
|
// Note: flow specific implicit behaviours should be handled here
|
|
|
|
|
// before logging the event
|
|
|
|
|
f.ctx.LogEvent(result)
|
2023-11-02 13:33:40 +05:30
|
|
|
// export dynamic values from operators (i.e internal:true)
|
|
|
|
|
// add add it to template context
|
|
|
|
|
// this is a conflicting behaviour with iterate-all
|
|
|
|
|
if result.HasOperatorResult() {
|
2024-01-08 05:12:11 +05:30
|
|
|
f.results.CompareAndSwap(false, true)
|
2023-11-02 13:33:40 +05:30
|
|
|
// this is to handle case where there is any operator result (matcher or extractor)
|
|
|
|
|
matcherStatus.CompareAndSwap(false, result.OperatorsResult.Matched)
|
|
|
|
|
if !result.OperatorsResult.Matched && !hasMatchers(req.GetCompiledOperators()) {
|
|
|
|
|
// if matcher status is false . check if template/request contains any matcher at all
|
|
|
|
|
// if it does then we need to set matcher status to true
|
|
|
|
|
matcherStatus.CompareAndSwap(false, true)
|
|
|
|
|
}
|
|
|
|
|
if len(result.OperatorsResult.DynamicValues) > 0 {
|
|
|
|
|
for k, v := range result.OperatorsResult.DynamicValues {
|
2024-01-13 00:44:25 +05:30
|
|
|
// if length of v is 1 then remove slice and convert it to single value
|
|
|
|
|
if len(v) == 1 {
|
2024-01-29 05:20:01 +05:30
|
|
|
// add it to flatten keys list so it will be flattened to a string later
|
|
|
|
|
f.flattenKeys = append(f.flattenKeys, k)
|
2024-04-01 19:18:21 +05:30
|
|
|
// flatten and convert it to string
|
|
|
|
|
f.options.GetTemplateCtx(f.ctx.Input.MetaInput).Set(k, v[0])
|
|
|
|
|
} else {
|
|
|
|
|
// keep it as slice
|
|
|
|
|
f.options.GetTemplateCtx(f.ctx.Input.MetaInput).Set(k, v)
|
2024-01-13 00:44:25 +05:30
|
|
|
}
|
2023-11-02 13:33:40 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if !result.HasOperatorResult() && !hasOperators(req.GetCompiledOperators()) {
|
|
|
|
|
// this is to handle case where there are no operator result and there was no matcher in operators
|
|
|
|
|
// if matcher status is false . check if template/request contains any matcher at all
|
|
|
|
|
// if it does then we need to set matcher status to true
|
|
|
|
|
matcherStatus.CompareAndSwap(false, true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|