error handling + support offlinehttp in flow templates (#4653)

This commit is contained in:
Tarun Koyalwar 2024-01-17 23:16:57 +05:30 committed by GitHub
parent 44745cb0c9
commit 68b9dd52ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 32 additions and 17 deletions

View File

@ -183,8 +183,9 @@ func (template *Template) compileProtocolRequests(options *protocols.ExecutorOpt
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsJavascript)...)
}
}
template.Executer = tmplexec.NewTemplateExecuter(requests, options)
return nil
var err error
template.Executer, err = tmplexec.NewTemplateExecuter(requests, options)
return err
}
// convertRequestToProtocolsRequest is a convenience wrapper to convert
@ -228,8 +229,13 @@ mainLoop:
}
if len(operatorsList) > 0 {
options.Operators = operatorsList
template.Executer = tmplexec.NewTemplateExecuter([]protocols.Request{&offlinehttp.Request{}}, options)
return nil
var err error
template.Executer, err = tmplexec.NewTemplateExecuter([]protocols.Request{&offlinehttp.Request{}}, options)
if err != nil {
// it seems like flow executor cannot be used for offline http matching (ex:http(1) && http(2))
return ErrIncompatibleWithOfflineMatching
}
return err
}
return ErrIncompatibleWithOfflineMatching

View File

@ -31,7 +31,7 @@ type TemplateExecuter struct {
var _ protocols.Executer = &TemplateExecuter{}
// NewTemplateExecuter creates a new request TemplateExecuter for list of requests
func NewTemplateExecuter(requests []protocols.Request, options *protocols.ExecutorOptions) *TemplateExecuter {
func NewTemplateExecuter(requests []protocols.Request, options *protocols.ExecutorOptions) (*TemplateExecuter, error) {
isMultiProto := false
lastProto := ""
for _, request := range requests {
@ -47,7 +47,11 @@ func NewTemplateExecuter(requests []protocols.Request, options *protocols.Execut
// we use a dummy input here because goal of flow executor at this point is to just check
// syntax and other things are correct before proceeding to actual execution
// during execution new instance of flow will be created as it is tightly coupled with lot of executor options
e.engine = flow.NewFlowExecutor(requests, scan.NewScanContext(contextargs.NewWithInput("dummy")), options, e.results)
var err error
e.engine, err = flow.NewFlowExecutor(requests, scan.NewScanContext(contextargs.NewWithInput("dummy")), options, e.results)
if err != nil {
return nil, fmt.Errorf("could not create flow executor: %s", err)
}
} else {
// Review:
// multiproto engine is only used if there is more than one protocol in template
@ -58,8 +62,7 @@ func NewTemplateExecuter(requests []protocols.Request, options *protocols.Execut
e.engine = generic.NewGenericEngine(requests, options, e.results)
}
}
return e
return e, nil
}
// Compile compiles the execution generators preparing any requests possible.
@ -146,7 +149,7 @@ func (e *TemplateExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
}
}
}
var err error
var errx error
// Note: this is required for flow executor
// flow executer is tightly coupled with lot of executor options
@ -155,20 +158,24 @@ func (e *TemplateExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
// so in compile step earlier we compile it to validate javascript syntax and other things
// and while executing we create new instance of flow executor everytime
if e.options.Flow != "" {
flowexec := flow.NewFlowExecutor(e.requests, ctx, e.options, results)
flowexec, err := flow.NewFlowExecutor(e.requests, ctx, e.options, results)
if err != nil {
ctx.LogError(err)
return false, fmt.Errorf("could not create flow executor: %s", err)
}
if err := flowexec.Compile(); err != nil {
ctx.LogError(err)
return false, err
}
err = flowexec.ExecuteWithResults(ctx)
errx = flowexec.ExecuteWithResults(ctx)
} else {
err = e.engine.ExecuteWithResults(ctx)
errx = e.engine.ExecuteWithResults(ctx)
}
if lastMatcherEvent != nil {
writeFailureCallback(lastMatcherEvent, e.options.Options.MatcherStatus)
}
return results.Load(), err
return results.Load(), errx
}
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.

View File

@ -55,7 +55,7 @@ type FlowExecutor struct {
// NewFlowExecutor creates a new flow executor from a list of requests
// Note: Unlike other engine for every target x template flow needs to be compiled and executed everytime
// unlike other engines where we compile once and execute multiple times
func NewFlowExecutor(requests []protocols.Request, ctx *scan.ScanContext, options *protocols.ExecutorOptions, results *atomic.Bool) *FlowExecutor {
func NewFlowExecutor(requests []protocols.Request, ctx *scan.ScanContext, options *protocols.ExecutorOptions, results *atomic.Bool) (*FlowExecutor, error) {
allprotos := make(map[string][]protocols.Request)
for _, req := range requests {
switch req.Type() {
@ -79,9 +79,11 @@ func NewFlowExecutor(requests []protocols.Request, ctx *scan.ScanContext, option
allprotos[templateTypes.CodeProtocol.String()] = append(allprotos[templateTypes.CodeProtocol.String()], req)
case templateTypes.JavascriptProtocol:
allprotos[templateTypes.JavascriptProtocol.String()] = append(allprotos[templateTypes.JavascriptProtocol.String()], req)
case templateTypes.OfflineHTTPProtocol:
// offlinehttp is run in passive mode but templates are same so instead of using offlinehttp() we use http() in flow
allprotos[templateTypes.HTTPProtocol.String()] = append(allprotos[templateTypes.OfflineHTTPProtocol.String()], req)
default:
ctx.LogError(fmt.Errorf("invalid request type %s", req.Type().String()))
return nil
return nil, fmt.Errorf("invalid request type %s", req.Type().String())
}
}
f := &FlowExecutor{
@ -96,7 +98,7 @@ func NewFlowExecutor(requests []protocols.Request, ctx *scan.ScanContext, option
jsVM: protocolstate.NewJSRuntime(),
ctx: ctx,
}
return f
return f, nil
}
// Compile compiles js program and registers all functions