diff --git a/pkg/core/capability/manual/manual.go b/pkg/core/capability/manual/manual.go new file mode 100644 index 00000000..5e697146 --- /dev/null +++ b/pkg/core/capability/manual/manual.go @@ -0,0 +1,85 @@ +package manual + +import ( + "errors" + "reflect" + "soarca/internal/logger" + "soarca/pkg/core/capability" + "soarca/pkg/interaction" + "soarca/pkg/models/cacao" + "soarca/pkg/models/execution" + "time" +) + +var ( + component = reflect.TypeOf(ManualCapability{}).PkgPath() + log *logger.Log +) + +const ( + manualResultVariableName = "__soarca_manual_result__" + manualCapabilityName = "soarca-manual-http" + fallbackTimeout = time.Minute * 1 +) + +func New(controller interaction.ICapabilityInteraction, + channel chan interaction.InteractionResponse) ManualCapability { + // channel := make(chan interaction.InteractionResponse) + return ManualCapability{interaction: controller, channel: channel} +} + +func init() { + log = logger.Logger(component, logger.Info, "", logger.Json) +} + +type ManualCapability struct { + interaction interaction.ICapabilityInteraction + channel chan interaction.InteractionResponse +} + +func (manual *ManualCapability) GetType() string { + return manualCapabilityName +} + +func (manual *ManualCapability) Execute( + metadata execution.Metadata, + commandContext capability.Context) (cacao.Variables, error) { + + command := interaction.InteractionCommand{Metadata: metadata, Context: commandContext} + + err := manual.interaction.Queue(command, manual.channel) + if err != nil { + return cacao.NewVariables(), err + } + + result, err := manual.awaitUserInput(manual.getTimeoutValue(commandContext.Step.Timeout)) + if err != nil { + return cacao.NewVariables(), err + } + return result.Select(commandContext.Step.OutArgs), nil + +} + +func (manual *ManualCapability) awaitUserInput(timeout time.Duration) (cacao.Variables, error) { + timer := time.NewTimer(time.Duration(timeout)) + for { + select { + case <-timer.C: + err := errors.New("manual response timeout, user responded not in time") + log.Error(err) + return cacao.NewVariables(), err + case response := <-manual.channel: + log.Trace("received response from api") + return response.Variables, response.ResponseError + + } + } +} + +func (manual *ManualCapability) getTimeoutValue(userTimeout int) time.Duration { + if userTimeout == 0 { + log.Warning("timeout is not set or set to 0 fallback timeout of 1 minute is used to complete step") + return fallbackTimeout + } + return time.Duration(userTimeout) * time.Millisecond +} diff --git a/pkg/core/capability/manual/manual_test.go b/pkg/core/capability/manual/manual_test.go new file mode 100644 index 00000000..72608ffd --- /dev/null +++ b/pkg/core/capability/manual/manual_test.go @@ -0,0 +1,52 @@ +package manual + +import ( + "soarca/pkg/core/capability" + "soarca/pkg/interaction" + "soarca/pkg/models/execution" + "soarca/test/unittest/mocks/mock_interaction" + "testing" + "time" + + "github.com/go-playground/assert/v2" +) + +func returnQueueCall(channel chan interaction.InteractionResponse) { + + time.Sleep(time.Millisecond * 10) + response := interaction.InteractionResponse{} + channel <- response +} + +func TestManualExecution(t *testing.T) { + interactionMock := mock_interaction.MockInteraction{} + channel := make(chan interaction.InteractionResponse) + manual := New(&interactionMock, channel) + + meta := execution.Metadata{} + context := capability.Context{} + + command := interaction.InteractionCommand{} + go returnQueueCall(channel) + interactionMock.On("Queue", command, channel).Return(nil) + vars, err := manual.Execute(meta, context) + assert.Equal(t, err, nil) + assert.NotEqual(t, vars, nil) + +} + +func TestTimetoutCalculationNotSet(t *testing.T) { + interactionMock := mock_interaction.MockInteraction{} + channel := make(chan interaction.InteractionResponse) + manual := New(&interactionMock, channel) + timeout := manual.getTimeoutValue(0) + assert.Equal(t, timeout, time.Minute) +} + +func TestTimetoutCalculation(t *testing.T) { + interactionMock := mock_interaction.MockInteraction{} + channel := make(chan interaction.InteractionResponse) + manual := New(&interactionMock, channel) + timeout := manual.getTimeoutValue(1) + assert.Equal(t, timeout, time.Millisecond*1) +}