Skip to content

Commit 3235add

Browse files
MaartendeKruijfjp
andauthored
Feature/93 refactor executor interface into multiple interfaces (#94)
Co-authored-by: jp <[email protected]>
1 parent 6ff9246 commit 3235add

File tree

7 files changed

+315
-232
lines changed

7 files changed

+315
-232
lines changed

internal/controller/controller.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"soarca/internal/capability/openc2"
1414
"soarca/internal/capability/ssh"
1515
"soarca/internal/decomposer"
16-
"soarca/internal/executer"
16+
"soarca/internal/executors/action"
1717
"soarca/internal/fin/protocol"
1818
"soarca/internal/guid"
1919
"soarca/logger"
@@ -66,9 +66,9 @@ func (controller *Controller) NewDecomposer() decomposer.IDecomposer {
6666
}
6767
}
6868

69-
executer := executer.New(capabilities)
69+
actionExecutor := action.New(capabilities)
7070
guid := new(guid.Guid)
71-
decompose := decomposer.New(executer, guid)
71+
decompose := decomposer.New(actionExecutor, guid)
7272
return decompose
7373
}
7474

internal/decomposer/decomposer.go

Lines changed: 28 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import (
44
"errors"
55
"fmt"
66
"reflect"
7-
"soarca/internal/executer"
7+
8+
"soarca/internal/executors/action"
89
"soarca/internal/guid"
910
"soarca/logger"
1011
"soarca/models/cacao"
@@ -15,8 +16,10 @@ import (
1516

1617
type Empty struct{}
1718

18-
var component = reflect.TypeOf(Empty{}).PkgPath()
19-
var log *logger.Log
19+
var (
20+
component = reflect.TypeOf(Empty{}).PkgPath()
21+
log *logger.Log
22+
)
2023

2124
type ExecutionDetails struct {
2225
ExecutionId uuid.UUID
@@ -32,10 +35,10 @@ func init() {
3235
log = logger.Logger(component, logger.Info, "", logger.Json)
3336
}
3437

35-
func New(executor executer.IExecuter, guid guid.IGuid) *Decomposer {
36-
var instance = Decomposer{}
37-
if instance.executor == nil {
38-
instance.executor = executor
38+
func New(actionExecutor action.IExecuter, guid guid.IGuid) *Decomposer {
39+
instance := Decomposer{}
40+
if instance.actionExecutor == nil {
41+
instance.actionExecutor = actionExecutor
3942
}
4043
if instance.guid == nil {
4144
instance.guid = guid
@@ -44,10 +47,10 @@ func New(executor executer.IExecuter, guid guid.IGuid) *Decomposer {
4447
}
4548

4649
type Decomposer struct {
47-
playbook cacao.Playbook
48-
details ExecutionDetails
49-
executor executer.IExecuter
50-
guid guid.IGuid
50+
playbook cacao.Playbook
51+
details ExecutionDetails
52+
actionExecutor action.IExecuter
53+
guid guid.IGuid
5154
}
5255

5356
// Execute a Playbook
@@ -129,59 +132,22 @@ func (decomposer *Decomposer) ExecuteStep(step cacao.Step, scopeVariables cacao.
129132
variables.Merge(step.StepVariables)
130133

131134
switch step.Type {
132-
case "action":
133-
return decomposer.ExecuteActionStep(step, variables)
135+
case cacao.StepTypeAction:
136+
actionMetadata := action.PlaybookStepMetadata{
137+
Step: step,
138+
Targets: decomposer.playbook.TargetDefinitions,
139+
Auth: decomposer.playbook.AuthenticationInfoDefinitions,
140+
Agent: decomposer.playbook.AgentDefinitions[step.Agent],
141+
Variables: variables,
142+
}
143+
metadata := execution.Metadata{
144+
ExecutionId: decomposer.details.ExecutionId,
145+
PlaybookId: decomposer.details.PlaybookId,
146+
StepId: step.ID,
147+
}
148+
return decomposer.actionExecutor.Execute(metadata, actionMetadata)
134149
default:
135150
// NOTE: This currently silently handles unknown step types. Should we return an error instead?
136151
return cacao.NewVariables(), nil
137152
}
138153
}
139-
140-
// Execute a Step of type Action
141-
func (decomposer *Decomposer) ExecuteActionStep(step cacao.Step, scopeVariables cacao.Variables) (cacao.Variables, error) {
142-
log.Debug("Executing action step")
143-
144-
agent := decomposer.playbook.AgentDefinitions[step.Agent]
145-
returnVariables := cacao.NewVariables()
146-
147-
for _, command := range step.Commands {
148-
// NOTE: This assumes we want to run Command for every Target individually.
149-
// Is that something we want to enforce or leave up to the capability?
150-
for _, element := range step.Targets {
151-
target := decomposer.playbook.TargetDefinitions[element]
152-
// NOTE: What about Agent authentication?
153-
auth := decomposer.playbook.AuthenticationInfoDefinitions[target.AuthInfoIdentifier]
154-
155-
meta := execution.Metadata{
156-
ExecutionId: decomposer.details.ExecutionId,
157-
PlaybookId: decomposer.playbook.ID,
158-
StepId: step.ID,
159-
}
160-
161-
_, outputVariables, err := decomposer.executor.Execute(
162-
meta,
163-
command,
164-
auth,
165-
target,
166-
scopeVariables,
167-
agent)
168-
169-
if len(step.OutArgs) > 0 {
170-
// If OutArgs is set, only update execution args that are explicitly referenced
171-
outputVariables = outputVariables.Select(step.OutArgs)
172-
}
173-
174-
returnVariables.Merge(outputVariables)
175-
scopeVariables.Merge(outputVariables)
176-
177-
if err != nil {
178-
log.Error("Error executing Command")
179-
return cacao.NewVariables(), err
180-
} else {
181-
log.Debug("Command executed")
182-
}
183-
}
184-
}
185-
186-
return returnVariables, nil
187-
}

internal/executer/executer.go

Lines changed: 0 additions & 77 deletions
This file was deleted.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package action
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"reflect"
7+
"soarca/internal/capability"
8+
"soarca/logger"
9+
"soarca/models/cacao"
10+
"soarca/models/execution"
11+
)
12+
13+
var component = reflect.TypeOf(Executor{}).PkgPath()
14+
var log *logger.Log
15+
16+
func init() {
17+
log = logger.Logger(component, logger.Info, "", logger.Json)
18+
}
19+
20+
func New(capabilities map[string]capability.ICapability) *Executor {
21+
var instance = Executor{}
22+
instance.capabilities = capabilities
23+
return &instance
24+
}
25+
26+
type PlaybookStepMetadata struct {
27+
Step cacao.Step
28+
Targets map[string]cacao.AgentTarget
29+
Auth map[string]cacao.AuthenticationInformation
30+
Agent cacao.AgentTarget
31+
Variables cacao.Variables
32+
}
33+
34+
type IExecuter interface {
35+
Execute(metadata execution.Metadata,
36+
step PlaybookStepMetadata) (cacao.Variables, error)
37+
}
38+
39+
type Executor struct {
40+
capabilities map[string]capability.ICapability
41+
}
42+
43+
func (executor *Executor) Execute(meta execution.Metadata, metadata PlaybookStepMetadata) (cacao.Variables, error) {
44+
45+
if metadata.Step.Type != cacao.StepTypeAction {
46+
err := errors.New("the provided step type is not compatible with this executor")
47+
log.Error(err)
48+
return cacao.NewVariables(), err
49+
}
50+
returnVariables := cacao.NewVariables()
51+
for _, command := range metadata.Step.Commands {
52+
// NOTE: This assumes we want to run Command for every Target individually.
53+
// Is that something we want to enforce or leave up to the capability?
54+
for _, element := range metadata.Step.Targets {
55+
// NOTE: What about Agent authentication?
56+
target := metadata.Targets[element]
57+
auth := metadata.Auth[target.AuthInfoIdentifier]
58+
59+
outputVariables, err := executor.ExecuteActionStep(
60+
meta,
61+
command,
62+
auth,
63+
target,
64+
metadata.Variables,
65+
metadata.Agent)
66+
67+
if len(metadata.Step.OutArgs) > 0 {
68+
// If OutArgs is set, only update execution args that are explicitly referenced
69+
outputVariables = outputVariables.Select(metadata.Step.OutArgs)
70+
}
71+
72+
returnVariables.Merge(outputVariables)
73+
74+
if err != nil {
75+
log.Error("Error executing Command ", err)
76+
return cacao.NewVariables(), err
77+
} else {
78+
log.Debug("Command executed")
79+
}
80+
}
81+
}
82+
return returnVariables, nil
83+
}
84+
85+
func (executor *Executor) ExecuteActionStep(metadata execution.Metadata,
86+
command cacao.Command,
87+
authentication cacao.AuthenticationInformation,
88+
target cacao.AgentTarget,
89+
variables cacao.Variables,
90+
agent cacao.AgentTarget) (cacao.Variables, error) {
91+
92+
if capability, ok := executor.capabilities[agent.Name]; ok {
93+
command.Command = variables.Interpolate(command.Command)
94+
95+
for key, addresses := range target.Address {
96+
var slice []string
97+
for _, address := range addresses {
98+
slice = append(slice, variables.Interpolate(address))
99+
}
100+
target.Address[key] = slice
101+
}
102+
103+
authentication.Password = variables.Interpolate(authentication.Password)
104+
authentication.Username = variables.Interpolate(authentication.Username)
105+
authentication.UserId = variables.Interpolate(authentication.UserId)
106+
authentication.Token = variables.Interpolate(authentication.Token)
107+
authentication.OauthHeader = variables.Interpolate(authentication.OauthHeader)
108+
authentication.PrivateKey = variables.Interpolate(authentication.PrivateKey)
109+
110+
returnVariables, err := capability.Execute(metadata, command, authentication, target, variables)
111+
return returnVariables, err
112+
} else {
113+
empty := cacao.NewVariables()
114+
err := errors.New(fmt.Sprint("capability: ", agent.Name, " is not available in soarca"))
115+
log.Error(err)
116+
return empty, err
117+
}
118+
119+
}

0 commit comments

Comments
 (0)