From 07f9a9df1f7f3a9fd52a7b3b40d6b56fe6439ae8 Mon Sep 17 00:00:00 2001 From: Maarten de Kruijf Date: Fri, 3 May 2024 17:00:33 +0200 Subject: [PATCH] Added decomposer tests for condition step --- internal/decomposer/decomposer.go | 10 +- internal/executors/condition/condition.go | 4 +- test/unittest/decomposer/decomposer_test.go | 200 +++++++++++++++++- .../condition/condition_executor.go | 19 ++ 4 files changed, 223 insertions(+), 10 deletions(-) create mode 100644 test/unittest/mocks/mock_executor/condition/condition_executor.go diff --git a/internal/decomposer/decomposer.go b/internal/decomposer/decomposer.go index 7016ca2a..3856e13f 100644 --- a/internal/decomposer/decomposer.go +++ b/internal/decomposer/decomposer.go @@ -40,12 +40,16 @@ 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 { @@ -158,7 +162,7 @@ func (decomposer *Decomposer) ExecuteStep(step cacao.Step, scopeVariables cacao. case cacao.StepTypePlaybookAction: return decomposer.playbookActionExecutor.Execute(metadata, step, variables) case cacao.StepTypeIfCondition: - stepId, branch, err := decomposer.conditionExecutor.Execute(metadata, step) + stepId, branch, err := decomposer.conditionExecutor.Execute(metadata, step, variables) if err != nil { return cacao.NewVariables(), err } diff --git a/internal/executors/condition/condition.go b/internal/executors/condition/condition.go index da9f332f..d25e8982 100644 --- a/internal/executors/condition/condition.go +++ b/internal/executors/condition/condition.go @@ -25,14 +25,14 @@ func New(capabilities map[string]capability.ICapability) *Executor { type IExecuter interface { Execute(metadata execution.Metadata, - step cacao.Step) (string, bool, error) + step cacao.Step, variables cacao.Variables) (string, bool, error) } type Executor struct { capabilities map[string]capability.ICapability } -func (executor *Executor) Execute(meta execution.Metadata, step cacao.Step) (string, bool, error) { +func (executor *Executor) Execute(meta execution.Metadata, step cacao.Step, variables cacao.Variables) (string, bool, error) { if step.Type != cacao.StepTypeIfCondition { err := errors.New("the provided step type is not compatible with this executor") diff --git a/test/unittest/decomposer/decomposer_test.go b/test/unittest/decomposer/decomposer_test.go index 2136cbab..b21ab99a 100644 --- a/test/unittest/decomposer/decomposer_test.go +++ b/test/unittest/decomposer/decomposer_test.go @@ -10,6 +10,7 @@ import ( "soarca/models/cacao" "soarca/models/execution" "soarca/test/unittest/mocks/mock_executor" + mock_condition_executor "soarca/test/unittest/mocks/mock_executor/condition" mock_playbook_action_executor "soarca/test/unittest/mocks/mock_executor/playbook_action" "soarca/test/unittest/mocks/mock_guid" "soarca/test/unittest/mocks/mock_reporter" @@ -21,6 +22,7 @@ import ( func TestExecutePlaybook(t *testing.T) { mock_action_executor := new(mock_executor.Mock_Action_Executor) mock_playbook_action_executor := new(mock_playbook_action_executor.Mock_PlaybookActionExecutor) + mock_condition_executor := new(mock_condition_executor.Mock_Condition) uuid_mock := new(mock_guid.Mock_Guid) mock_reporter := new(mock_reporter.Mock_Reporter) @@ -37,7 +39,9 @@ func TestExecutePlaybook(t *testing.T) { decomposer := decomposer.New(mock_action_executor, mock_playbook_action_executor, - uuid_mock, mock_reporter) + mock_condition_executor, + uuid_mock, + mock_reporter) step1 := cacao.Step{ Type: "action", @@ -116,6 +120,7 @@ func TestExecutePlaybook(t *testing.T) { func TestExecutePlaybookMultiStep(t *testing.T) { mock_action_executor := new(mock_executor.Mock_Action_Executor) mock_playbook_action_executor := new(mock_playbook_action_executor.Mock_PlaybookActionExecutor) + mock_condition_executor := new(mock_condition_executor.Mock_Condition) uuid_mock := new(mock_guid.Mock_Guid) mock_reporter := new(mock_reporter.Mock_Reporter) @@ -143,7 +148,9 @@ func TestExecutePlaybookMultiStep(t *testing.T) { decomposer := decomposer.New(mock_action_executor, mock_playbook_action_executor, - uuid_mock, mock_reporter) + mock_condition_executor, + uuid_mock, + mock_reporter) step1 := cacao.Step{ Type: "action", @@ -260,6 +267,7 @@ Test with an Empty OnCompletion will result in not executing the step. func TestExecuteEmptyMultiStep(t *testing.T) { mock_action_executor2 := new(mock_executor.Mock_Action_Executor) mock_playbook_action_executor2 := new(mock_playbook_action_executor.Mock_PlaybookActionExecutor) + mock_condition_executor := new(mock_condition_executor.Mock_Condition) uuid_mock2 := new(mock_guid.Mock_Guid) mock_reporter := new(mock_reporter.Mock_Reporter) @@ -286,7 +294,9 @@ func TestExecuteEmptyMultiStep(t *testing.T) { decomposer2 := decomposer.New(mock_action_executor2, mock_playbook_action_executor2, - uuid_mock2, mock_reporter) + mock_condition_executor, + uuid_mock2, + mock_reporter) step1 := cacao.Step{ Type: "ssh", @@ -329,6 +339,7 @@ Test with an not occuring on completion id will result in not executing the step func TestExecuteIllegalMultiStep(t *testing.T) { mock_action_executor2 := new(mock_executor.Mock_Action_Executor) mock_playbook_action_executor2 := new(mock_playbook_action_executor.Mock_PlaybookActionExecutor) + mock_condition_executor := new(mock_condition_executor.Mock_Condition) uuid_mock2 := new(mock_guid.Mock_Guid) mock_reporter := new(mock_reporter.Mock_Reporter) @@ -345,7 +356,9 @@ func TestExecuteIllegalMultiStep(t *testing.T) { decomposer2 := decomposer.New(mock_action_executor2, mock_playbook_action_executor2, - uuid_mock2, mock_reporter) + mock_condition_executor, + uuid_mock2, + mock_reporter) step1 := cacao.Step{ Type: "action", @@ -382,6 +395,7 @@ func TestExecuteIllegalMultiStep(t *testing.T) { func TestExecutePlaybookAction(t *testing.T) { mock_action_executor := new(mock_executor.Mock_Action_Executor) mock_playbook_action_executor := new(mock_playbook_action_executor.Mock_PlaybookActionExecutor) + mock_condition_executor := new(mock_condition_executor.Mock_Condition) uuid_mock := new(mock_guid.Mock_Guid) mock_reporter := new(mock_reporter.Mock_Reporter) expectedVariables := cacao.Variable{ @@ -392,7 +406,9 @@ func TestExecutePlaybookAction(t *testing.T) { decomposer := decomposer.New(mock_action_executor, mock_playbook_action_executor, - uuid_mock, mock_reporter) + mock_condition_executor, + uuid_mock, + mock_reporter) step1 := cacao.Step{ Type: "playbook-action", @@ -439,3 +455,177 @@ func TestExecutePlaybookAction(t *testing.T) { assert.Equal(t, found, true) assert.Equal(t, value.Value, "value") } + +func TestExecuteIfCondition(t *testing.T) { + + mock_action_executor := new(mock_executor.Mock_Action_Executor) + mock_playbook_action_executor := new(mock_playbook_action_executor.Mock_PlaybookActionExecutor) + mock_condition_executor := new(mock_condition_executor.Mock_Condition) + uuid_mock := new(mock_guid.Mock_Guid) + mock_reporter := new(mock_reporter.Mock_Reporter) + expectedVariables := cacao.Variable{ + Type: "string", + Name: "__var1__", + Value: "testing", + } + + // returned from step + expectedVariables2 := cacao.Variable{ + Type: "string", + Name: "__var2__", + Value: "testing2", + } + + expectedCommand := cacao.Command{ + Type: "ssh", + Command: "ssh ls -la", + } + + expectedTarget := cacao.AgentTarget{ + Name: "sometarget", + AuthInfoIdentifier: "id", + } + + expectedAgent := cacao.AgentTarget{ + Type: "soarca", + Name: "soarca-ssh", + } + + decomposer := decomposer.New(mock_action_executor, + mock_playbook_action_executor, + mock_condition_executor, + uuid_mock, + mock_reporter) + + end := cacao.Step{ + Type: cacao.StepTypeEnd, + ID: "end--test", + Name: "end step", + } + + endTrue := cacao.Step{ + Type: cacao.StepTypeEnd, + ID: "end--true", + Name: "end branch true step", + } + + stepTrue := cacao.Step{ + Type: cacao.StepTypeAction, + ID: "action--step-true", + Name: "ssh-tests", + Commands: []cacao.Command{expectedCommand}, + Targets: []string{expectedTarget.ID}, + StepVariables: cacao.NewVariables(expectedVariables), + OnCompletion: endTrue.ID, + } + + endFalse := cacao.Step{ + Type: cacao.StepTypeEnd, + ID: "end--false", + Name: "end branch false step", + } + + stepFalse := cacao.Step{ + Type: cacao.StepTypeAction, + ID: "action--step-false", + Name: "ssh-tests", + Commands: []cacao.Command{expectedCommand}, + Targets: []string{expectedTarget.ID}, + StepVariables: cacao.NewVariables(expectedVariables), + OnCompletion: endFalse.ID, + } + + stepCompletion := cacao.Step{ + Type: cacao.StepTypeAction, + ID: "action--step-completion", + Name: "ssh-tests", + Commands: []cacao.Command{expectedCommand}, + Targets: []string{expectedTarget.ID}, + StepVariables: cacao.NewVariables(expectedVariables), + OnCompletion: end.ID, + } + + stepIf := cacao.Step{ + Type: cacao.StepTypeIfCondition, + ID: "if-condition--test", + Name: "if condition", + StepVariables: cacao.NewVariables(expectedVariables), + Condition: "__var1__:value = testing", + OnTrue: stepTrue.ID, + OnFalse: stepFalse.ID, + OnCompletion: stepCompletion.ID, + } + + start := cacao.Step{ + Type: cacao.StepTypeStart, + ID: "start--test", + Name: "start step", + OnCompletion: stepIf.ID, + } + + playbook := cacao.Playbook{ + ID: "test", + Type: "test", + Name: "playbook-test", + WorkflowStart: start.ID, + Workflow: map[string]cacao.Step{start.ID: start, + stepIf.ID: stepIf, + stepTrue.ID: stepTrue, + stepFalse.ID: stepFalse, + stepCompletion.ID: stepCompletion, + end.ID: end, + endTrue.ID: endTrue, + endFalse.ID: endFalse}, + AgentDefinitions: map[string]cacao.AgentTarget{expectedAgent.ID: expectedAgent}, + TargetDefinitions: map[string]cacao.AgentTarget{expectedTarget.ID: expectedTarget}, + } + + executionId, _ := uuid.Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + metaStepIf := execution.Metadata{ExecutionId: executionId, PlaybookId: "test", StepId: stepIf.ID} + + uuid_mock.On("New").Return(executionId) + mock_reporter.On("ReportWorkflow", executionId, playbook).Return() + + mock_condition_executor.On("Execute", + metaStepIf, + stepIf, + cacao.NewVariables(expectedVariables)).Return(stepTrue.ID, true, nil) + + stepTrueDetails := action.PlaybookStepMetadata{ + Step: stepTrue, + Targets: playbook.TargetDefinitions, + Auth: playbook.AuthenticationInfoDefinitions, + Agent: expectedAgent, + Variables: cacao.NewVariables(expectedVariables), + } + + metaStepTrue := execution.Metadata{ExecutionId: executionId, PlaybookId: "test", StepId: stepTrue.ID} + + mock_action_executor.On("Execute", + metaStepTrue, + stepTrueDetails).Return(cacao.NewVariables(expectedVariables2), nil) + + stepCompletionDetails := action.PlaybookStepMetadata{ + Step: stepCompletion, + Targets: playbook.TargetDefinitions, + Auth: playbook.AuthenticationInfoDefinitions, + Agent: expectedAgent, + Variables: cacao.NewVariables(expectedVariables, expectedVariables2), + } + + metaStepCompletion := execution.Metadata{ExecutionId: executionId, PlaybookId: "test", StepId: stepCompletion.ID} + + mock_action_executor.On("Execute", + metaStepCompletion, + stepCompletionDetails).Return(cacao.NewVariables(), nil) + + details, err := decomposer.Execute(playbook) + uuid_mock.AssertExpectations(t) + fmt.Println(err) + assert.Equal(t, err, nil) + assert.Equal(t, details.ExecutionId, executionId) + mock_reporter.AssertExpectations(t) + mock_condition_executor.AssertExpectations(t) + mock_action_executor.AssertExpectations(t) + +} diff --git a/test/unittest/mocks/mock_executor/condition/condition_executor.go b/test/unittest/mocks/mock_executor/condition/condition_executor.go new file mode 100644 index 00000000..3774c2c6 --- /dev/null +++ b/test/unittest/mocks/mock_executor/condition/condition_executor.go @@ -0,0 +1,19 @@ +package mock_condition_executor + +import ( + "soarca/models/cacao" + "soarca/models/execution" + + "github.com/stretchr/testify/mock" +) + +type Mock_Condition struct { + mock.Mock +} + +func (executer *Mock_Condition) Execute(metadata execution.Metadata, + step cacao.Step, + variables cacao.Variables) (string, bool, error) { + args := executer.Called(metadata, step, variables) + return args.String(0), args.Bool(1), args.Error(2) +}