Skip to content

Commit

Permalink
Updated decomposer
Browse files Browse the repository at this point in the history
  • Loading branch information
MaartendeKruijf committed Mar 21, 2024
1 parent ff2db45 commit 170a612
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 100 deletions.
6 changes: 3 additions & 3 deletions internal/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"soarca/internal/capability/openc2"
"soarca/internal/capability/ssh"
"soarca/internal/decomposer"
"soarca/internal/executer"
"soarca/internal/executors/action"
"soarca/internal/fin/protocol"
"soarca/internal/guid"
"soarca/logger"
Expand Down Expand Up @@ -66,9 +66,9 @@ func (controller *Controller) NewDecomposer() decomposer.IDecomposer {
}
}

executer := executer.New(capabilities)
actionExecutor := action.New(capabilities)
guid := new(guid.Guid)
decompose := decomposer.New(executer, guid)
decompose := decomposer.New(actionExecutor, guid)
return decompose
}

Expand Down
72 changes: 13 additions & 59 deletions internal/decomposer/decomposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"reflect"
"soarca/internal/executer"
"soarca/internal/executors/action"
"soarca/internal/guid"
"soarca/logger"
"soarca/models/cacao"
Expand Down Expand Up @@ -32,10 +32,10 @@ func init() {
log = logger.Logger(component, logger.Info, "", logger.Json)
}

func New(executor executer.IExecuter, guid guid.IGuid) *Decomposer {
func New(actionExecutor action.IExecuter, guid guid.IGuid) *Decomposer {
var instance = Decomposer{}
if instance.executor == nil {
instance.executor = executor
if instance.actionExecutor == nil {
instance.actionExecutor = actionExecutor
}
if instance.guid == nil {
instance.guid = guid
Expand All @@ -44,10 +44,10 @@ func New(executor executer.IExecuter, guid guid.IGuid) *Decomposer {
}

type Decomposer struct {
playbook cacao.Playbook
details ExecutionDetails
executor executer.IExecuter
guid guid.IGuid
playbook cacao.Playbook
details ExecutionDetails
actionExecutor action.IExecuter
guid guid.IGuid
}

// Execute a Playbook
Expand Down Expand Up @@ -129,59 +129,13 @@ func (decomposer *Decomposer) ExecuteStep(step cacao.Step, scopeVariables cacao.
variables.Merge(step.StepVariables)

switch step.Type {
case "action":
return decomposer.ExecuteActionStep(step, variables)
case cacao.StepTypeAction:
details := action.StepDetails{Step: step, Targets: decomposer.playbook.TargetDefinitions, Auth: decomposer.playbook.AuthenticationInfoDefinitions, Agent: decomposer.playbook.AgentDefinitions[step.Agent], Variables: variables}
metadata := execution.Metadata{ExecutionId: decomposer.details.ExecutionId, PlaybookId: decomposer.details.PlaybookId, StepId: step.ID}
result, err := decomposer.actionExecutor.Execute(metadata, details)
return result, err
default:
// NOTE: This currently silently handles unknown step types. Should we return an error instead?
return cacao.NewVariables(), nil
}
}

// Execute a Step of type Action
func (decomposer *Decomposer) ExecuteActionStep(step cacao.Step, scopeVariables cacao.Variables) (cacao.Variables, error) {
log.Debug("Executing action step")

agent := decomposer.playbook.AgentDefinitions[step.Agent]
returnVariables := cacao.NewVariables()

for _, command := range step.Commands {
// NOTE: This assumes we want to run Command for every Target individually.
// Is that something we want to enforce or leave up to the capability?
for _, element := range step.Targets {
target := decomposer.playbook.TargetDefinitions[element]
// NOTE: What about Agent authentication?
auth := decomposer.playbook.AuthenticationInfoDefinitions[target.AuthInfoIdentifier]

meta := execution.Metadata{
ExecutionId: decomposer.details.ExecutionId,
PlaybookId: decomposer.playbook.ID,
StepId: step.ID,
}

_, outputVariables, err := decomposer.executor.Execute(
meta,
command,
auth,
target,
scopeVariables,
agent)

if len(step.OutArgs) > 0 {
// If OutArgs is set, only update execution args that are explicitly referenced
outputVariables = outputVariables.Select(step.OutArgs)
}

returnVariables.Merge(outputVariables)
scopeVariables.Merge(outputVariables)

if err != nil {
log.Error("Error executing Command")
return cacao.NewVariables(), err
} else {
log.Debug("Command executed")
}
}
}

return returnVariables, nil
}
69 changes: 31 additions & 38 deletions test/unittest/decomposer/decomposer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

"soarca/internal/decomposer"
"soarca/internal/executors/action"
"soarca/models/cacao"
"soarca/models/execution"
"soarca/test/unittest/mocks/mock_executor"
Expand All @@ -16,7 +17,7 @@ import (
)

func TestExecutePlaybook(t *testing.T) {
mock_executer := new(mock_executor.Mock_Executor)
mock_action_executor := new(mock_executor.Mock_Action_Executor)
uuid_mock := new(mock_guid.Mock_Guid)

expectedCommand := cacao.Command{
Expand All @@ -30,7 +31,7 @@ func TestExecutePlaybook(t *testing.T) {
Value: "testing",
}

decomposer := decomposer.New(mock_executer, uuid_mock)
decomposer := decomposer.New(mock_action_executor, uuid_mock)

var step1 = cacao.Step{
Type: "action",
Expand All @@ -52,11 +53,13 @@ func TestExecutePlaybook(t *testing.T) {

expectedAuth := cacao.AuthenticationInformation{
Name: "user",
ID: "auth1",
}

expectedTarget := cacao.AgentTarget{
Name: "sometarget",
AuthInfoIdentifier: "id",
AuthInfoIdentifier: "auth1",
ID: "target1",
}

expectedAgent := cacao.AgentTarget{
Expand All @@ -81,26 +84,24 @@ func TestExecutePlaybook(t *testing.T) {

uuid_mock.On("New").Return(executionId)

mock_executer.On("Execute", metaStep1, expectedCommand,
expectedAuth,
expectedTarget,
cacao.NewVariables(expectedVariables),
expectedAgent).Return(executionId, cacao.NewVariables(cacao.Variable{Name: "return", Value: "value"}), nil)
stepDetails := action.StepDetails{Step: step1, Targets: playbook.TargetDefinitions, Auth: playbook.AuthenticationInfoDefinitions, Agent: expectedAgent, Variables: cacao.NewVariables(expectedVariables)}

mock_action_executor.On("Execute", metaStep1, stepDetails).Return(cacao.NewVariables(cacao.Variable{Name: "return", Value: "value"}), 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_executer.AssertExpectations(t)
mock_action_executor.AssertExpectations(t)
value, found := details.Variables.Find("return")
assert.Equal(t, found, true)
assert.Equal(t, value.Value, "value")
}

func TestExecutePlaybookMultiStep(t *testing.T) {

mock_executer := new(mock_executor.Mock_Executor)
mock_action_executor := new(mock_executor.Mock_Action_Executor)
uuid_mock := new(mock_guid.Mock_Guid)

expectedCommand := cacao.Command{
Expand All @@ -125,7 +126,7 @@ func TestExecutePlaybookMultiStep(t *testing.T) {
Value: "testing2",
}

decomposer := decomposer.New(mock_executer, uuid_mock)
decomposer := decomposer.New(mock_action_executor, uuid_mock)

var step1 = cacao.Step{
Type: "action",
Expand Down Expand Up @@ -168,11 +169,13 @@ func TestExecutePlaybookMultiStep(t *testing.T) {

expectedAuth := cacao.AuthenticationInformation{
Name: "user",
ID: "auth1",
}

expectedTarget := cacao.AgentTarget{
Name: "sometarget",
AuthInfoIdentifier: "id",
AuthInfoIdentifier: "auth1",
ID: "target1",
}

expectedAgent := cacao.AgentTarget{
Expand All @@ -198,32 +201,22 @@ func TestExecutePlaybookMultiStep(t *testing.T) {

uuid_mock.On("New").Return(executionId)

mock_executer.On("Execute", metaStep1,
expectedCommand,
expectedAuth,
expectedTarget,
cacao.NewVariables(expectedVariables),
expectedAgent).Return(
executionId,
cacao.NewVariables(cacao.Variable{Name: "result", Value: "value"}),
nil)

mock_executer.On("Execute", metaStep2,
expectedCommand2,
expectedAuth,
expectedTarget,
cacao.NewVariables(expectedVariables2, cacao.Variable{Name: "result", Value: "value"}),
expectedAgent).Return(
executionId,
cacao.NewVariables(cacao.Variable{Name: "result", Value: "updated"}),
nil)
firstResult := cacao.Variable{Name: "result", Value: "value"}

stepDetails1 := action.StepDetails{Step: step1, Targets: playbook.TargetDefinitions, Auth: playbook.AuthenticationInfoDefinitions, Agent: expectedAgent, Variables: cacao.NewVariables(expectedVariables)}

mock_action_executor.On("Execute", metaStep1, stepDetails1).Return(cacao.NewVariables(firstResult), nil)

stepDetails2 := action.StepDetails{Step: step2, Targets: playbook.TargetDefinitions, Auth: playbook.AuthenticationInfoDefinitions, Agent: expectedAgent, Variables: cacao.NewVariables(expectedVariables2, firstResult)}

mock_action_executor.On("Execute", metaStep2, stepDetails2).Return(cacao.NewVariables(cacao.Variable{Name: "result", Value: "updated"}), 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_executer.AssertExpectations(t)
mock_action_executor.AssertExpectations(t)

value, found := details.Variables.Find("result")
assert.Equal(t, found, true)
Expand All @@ -235,7 +228,7 @@ Test with an Empty OnCompletion will result in not executing the step.
*/
func TestExecuteEmptyMultiStep(t *testing.T) {

mock_executer2 := new(mock_executor.Mock_Executor)
mock_action_executor2 := new(mock_executor.Mock_Action_Executor)
uuid_mock2 := new(mock_guid.Mock_Guid)

expectedCommand := cacao.Command{
Expand All @@ -259,7 +252,7 @@ func TestExecuteEmptyMultiStep(t *testing.T) {
Name: "soarca-ssh",
}

decomposer2 := decomposer.New(mock_executer2, uuid_mock2)
decomposer2 := decomposer.New(mock_action_executor2, uuid_mock2)

var step1 = cacao.Step{
Type: "ssh",
Expand Down Expand Up @@ -290,15 +283,15 @@ func TestExecuteEmptyMultiStep(t *testing.T) {
fmt.Println(err)
assert.Equal(t, err, errors.New("empty success step"))
assert.Equal(t, returnedId.ExecutionId, id)
mock_executer2.AssertExpectations(t)
mock_action_executor2.AssertExpectations(t)
}

/*
Test with an not occuring on completion id will result in not executing the step.
*/
func TestExecuteIllegalMultiStep(t *testing.T) {

mock_executer2 := new(mock_executor.Mock_Executor)
mock_action_executor2 := new(mock_executor.Mock_Action_Executor)
uuid_mock2 := new(mock_guid.Mock_Guid)

expectedCommand := cacao.Command{
Expand All @@ -312,7 +305,7 @@ func TestExecuteIllegalMultiStep(t *testing.T) {
Value: "testing",
}

decomposer2 := decomposer.New(mock_executer2, uuid_mock2)
decomposer2 := decomposer.New(mock_action_executor2, uuid_mock2)

var step1 = cacao.Step{
Type: "action",
Expand Down Expand Up @@ -341,5 +334,5 @@ func TestExecuteIllegalMultiStep(t *testing.T) {
fmt.Println(err)
assert.Equal(t, err, errors.New("empty success step"))
assert.Equal(t, returnedId.ExecutionId, id)
mock_executer2.AssertExpectations(t)
mock_action_executor2.AssertExpectations(t)
}

0 comments on commit 170a612

Please sign in to comment.