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

Feature/116 add if condition executor #138

Merged
merged 17 commits into from
May 13, 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
6 changes: 5 additions & 1 deletion docs/content/en/docs/core-components/executer.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,11 @@ The result of the step comparison will be returned to the decomposer. A result c
### While condition executor
The if-condition executor will process a cacao while-condition step and determine it's output.

The result of the step comparison will be returned to the decomposer. A result can be either a next step id and/or error status.
The result of the step comparison will be returned to the decomposer. A result can be either a next step id and/or error status. Only STIX comparison expressions are implemented at this time.

{{% alert title="Warning" color="warning" %}}
Note only [Comparison Expression](http://docs.oasis-open.org/cti/stix/v2.0/cs01/part5-stix-patterning/stix-v2.0-cs01-part5-stix-patterning.html#_Toc496717749) are implemented for all CACAO variable types.
{{% /alert %}}

### Parallel step executor
The parallel executor will execute the parallel step. This wil be done in sequence to simplify implementation. As parallel steps must not be depended on each other sequential execution is possible. Later this will be changed.
10 changes: 9 additions & 1 deletion internal/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import (
"soarca/internal/capability/ssh"
"soarca/internal/decomposer"
"soarca/internal/executors/action"
"soarca/internal/executors/condition"
"soarca/internal/executors/playbook_action"
"soarca/internal/fin/protocol"
"soarca/internal/guid"
"soarca/internal/reporter"
"soarca/logger"
"soarca/utils"
httpUtil "soarca/utils/http"
"soarca/utils/stix/expression/comparison"

downstreamReporter "soarca/internal/reporter/downstream_reporter"

Expand Down Expand Up @@ -78,8 +80,14 @@ func (controller *Controller) NewDecomposer() decomposer.IDecomposer {

actionExecutor := action.New(capabilities, reporter)
playbookActionExecutor := playbook_action.New(controller, controller, reporter)
stixComparison := comparison.New()
conditionExecutor := condition.New(stixComparison, reporter)
guid := new(guid.Guid)
decompose := decomposer.New(actionExecutor, playbookActionExecutor, guid, reporter)
decompose := decomposer.New(actionExecutor,
playbookActionExecutor,
conditionExecutor,
guid,
reporter)
return decompose
}

Expand Down
19 changes: 17 additions & 2 deletions internal/decomposer/decomposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"soarca/internal/executors"
"soarca/internal/executors/action"
"soarca/internal/executors/condition"
"soarca/internal/guid"
"soarca/internal/reporter"
"soarca/logger"
Expand Down Expand Up @@ -39,19 +40,24 @@ func init() {

func New(actionExecutor action.IExecuter,
playbookActionExecutor executors.IPlaybookExecuter,
guid guid.IGuid, reporter reporter.IWorkflowReporter) *Decomposer {
condition condition.IExecuter,
guid guid.IGuid,
reporter reporter.IWorkflowReporter) *Decomposer {

return &Decomposer{actionExecutor: actionExecutor,
playbookActionExecutor: playbookActionExecutor,
conditionExecutor: condition,
guid: guid,
reporter: reporter}
reporter: reporter,
}
}

type Decomposer struct {
playbook cacao.Playbook
details ExecutionDetails
actionExecutor action.IExecuter
playbookActionExecutor executors.IPlaybookExecuter
conditionExecutor condition.IExecuter
guid guid.IGuid
reporter reporter.IWorkflowReporter
}
Expand Down Expand Up @@ -157,6 +163,15 @@ func (decomposer *Decomposer) ExecuteStep(step cacao.Step, scopeVariables cacao.
return decomposer.actionExecutor.Execute(metadata, actionMetadata)
case cacao.StepTypePlaybookAction:
return decomposer.playbookActionExecutor.Execute(metadata, step, variables)
case cacao.StepTypeIfCondition:
stepId, branch, err := decomposer.conditionExecutor.Execute(metadata, step, variables)
if err != nil {
return cacao.NewVariables(), err
}
if branch {
return decomposer.ExecuteBranch(stepId, variables)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If on_completion is handled at decomposer level, then I think its invocation should be performed here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not branching if the on_true or on_false are not taken but for consistency sake we return the onCompletionId

return variables, nil
default:
// NOTE: This currently silently handles unknown step types. Should we return an error instead?
return cacao.NewVariables(), nil
Expand Down
76 changes: 76 additions & 0 deletions internal/executors/condition/condition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package condition

import (
"errors"
"fmt"
"reflect"
"soarca/internal/reporter"
"soarca/logger"
"soarca/models/cacao"
"soarca/models/execution"
"soarca/utils/stix/expression/comparison"
)

var component = reflect.TypeOf(Executor{}).PkgPath()
var log *logger.Log

func init() {
log = logger.Logger(component, logger.Info, "", logger.Json)
}

func New(comparison comparison.IComparison,
reporter reporter.IStepReporter) *Executor {
return &Executor{comparison: comparison,
reporter: reporter}
}

type IExecuter interface {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for clarity - perhaps it would be clearer to rename this interface to something like IIfConditionExecuter?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to keep it short because it's already in the package condition

Execute(metadata execution.Metadata,
step cacao.Step, variables cacao.Variables) (string, bool, error)
}

type Executor struct {
comparison comparison.IComparison
reporter reporter.IStepReporter
}

func (executor *Executor) Execute(meta execution.Metadata, step cacao.Step, variables cacao.Variables) (string, bool, error) {

MaartendeKruijf marked this conversation as resolved.
Show resolved Hide resolved
if step.Type != cacao.StepTypeIfCondition {
err := errors.New("the provided step type is not compatible with this executor")
log.Error(err)
return step.OnFailure, false, err
}

executor.reporter.ReportStepStart(meta.ExecutionId, step, variables)

result, err := executor.comparison.Evaluate(step.Condition, variables)

// We are reporting early to not have double reporting
executor.reporter.ReportStepEnd(meta.ExecutionId,
step,
variables,
err)

if err != nil {
log.Error(err)
return "", false, err
}

log.Debug("the result was: ", fmt.Sprint(result))

if result {
if step.OnTrue != "" {
log.Trace("will return on true step ", step.OnTrue)
return step.OnTrue, true, nil
}
} else {
if step.OnFalse != "" {
MaartendeKruijf marked this conversation as resolved.
Show resolved Hide resolved
log.Trace("will return on false step ", step.OnFalse)
return step.OnFalse, true, nil
}
}
log.Trace("will return on completion step ", step.OnCompletion)

return step.OnCompletion, false, nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably never invoked, right? I would believe that the "on completion" step handling should be performed at decomposer level

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is invoked when on_true or on_false are not set but the condition is valid en evaluates to true or false respectively

MaartendeKruijf marked this conversation as resolved.
Show resolved Hide resolved
}
Loading