Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

error handling + support offlinehttp in flow templates #4653

Merged
merged 1 commit into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions pkg/templates/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
25 changes: 16 additions & 9 deletions pkg/tmplexec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down
10 changes: 6 additions & 4 deletions pkg/tmplexec/flow/flow_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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{
Expand All @@ -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
Expand Down
Loading