diff --git a/internal/capability/capability.go b/internal/capability/capability.go index a995101d..af883a3f 100644 --- a/internal/capability/capability.go +++ b/internal/capability/capability.go @@ -10,6 +10,6 @@ type ICapability interface { command cacao.Command, authentication cacao.AuthenticationInformation, target cacao.AgentTarget, - variables cacao.VariableMap) (cacao.VariableMap, error) + variables cacao.Variables) (cacao.Variables, error) GetType() string } diff --git a/internal/capability/http/http.go b/internal/capability/http/http.go index 7d10a781..70852f82 100644 --- a/internal/capability/http/http.go +++ b/internal/capability/http/http.go @@ -38,13 +38,13 @@ func (httpCapability *HttpCapability) Execute( command cacao.Command, authentication cacao.AuthenticationInformation, target cacao.AgentTarget, - variables cacao.VariableMap) (cacao.VariableMap, error) { + variables cacao.Variables) (cacao.Variables, error) { // Get request data and handle errors method, url, errmethod := ObtainHttpMethodAndUrlFromCommand(command) if errmethod != nil { log.Error(errmethod) - return cacao.VariableMap{}, errmethod + return cacao.Variables{}, errmethod } content_data, errcontent := ObtainHttpRequestContentDataFromCommand(command) if errcontent != nil { @@ -56,7 +56,7 @@ func (httpCapability *HttpCapability) Execute( request, err := http.NewRequest(method, url, bytes.NewBuffer(content_data)) if err != nil { log.Error(err) - return cacao.VariableMap{}, err + return cacao.Variables{}, err } for key, httpCapability := range command.Headers { @@ -65,12 +65,12 @@ func (httpCapability *HttpCapability) Execute( if target.ID != "" { if err := verifyAuthInfoMatchesAgentTarget(&target, &authentication); err != nil { log.Error(err) - return cacao.VariableMap{}, err + return cacao.Variables{}, err } if err := setupAuthHeaders(request, &authentication); err != nil { log.Error(err) - return cacao.VariableMap{}, err + return cacao.Variables{}, err } } @@ -79,22 +79,22 @@ func (httpCapability *HttpCapability) Execute( response, err := client.Do(request) if err != nil { log.Error(err) - return cacao.VariableMap{}, err + return cacao.Variables{}, err } defer response.Body.Close() responseBytes, err := io.ReadAll(response.Body) if err != nil { log.Error(err) - return cacao.VariableMap{}, err + return cacao.Variables{}, err } respString := string(responseBytes) sc := response.StatusCode if sc < 200 || sc > 299 { - return cacao.VariableMap{}, errors.New(respString) + return cacao.Variables{}, errors.New(respString) } - return cacao.VariableMap{ + return cacao.Variables{ "__soarca_http_result__": {Name: "result", Value: respString}}, nil } diff --git a/internal/capability/ssh/ssh.go b/internal/capability/ssh/ssh.go index a255ad72..4da13bf3 100644 --- a/internal/capability/ssh/ssh.go +++ b/internal/capability/ssh/ssh.go @@ -31,7 +31,7 @@ func (sshCapability *SshCapability) Execute(metadata execution.Metadata, command cacao.Command, authentication cacao.AuthenticationInformation, target cacao.AgentTarget, - variables cacao.VariableMap) (cacao.VariableMap, error) { + variables cacao.Variables) (cacao.Variables, error) { log.Trace(metadata.ExecutionId) host := CombinePortAndAddress(target.Address, target.Port) @@ -40,7 +40,7 @@ func (sshCapability *SshCapability) Execute(metadata execution.Metadata, if errAuth != nil { log.Error(errAuth) - return cacao.VariableMap{}, errAuth + return cacao.Variables{}, errAuth } else { log.Trace(host) } @@ -60,7 +60,7 @@ func (sshCapability *SshCapability) Execute(metadata execution.Metadata, signer, errKey := ssh.ParsePrivateKey([]byte(authentication.PrivateKey)) if errKey != nil || authentication.Password == "" { log.Error("no valid authentication information: ", errKey) - return cacao.VariableMap{}, errKey + return cacao.Variables{}, errKey } config = ssh.ClientConfig{ User: authentication.Username, @@ -78,13 +78,13 @@ func (sshCapability *SshCapability) Execute(metadata execution.Metadata, conn, err := ssh.Dial("tcp", host, &config) if err != nil { log.Error(err) - return cacao.VariableMap{}, err + return cacao.Variables{}, err } var session *ssh.Session session, err = conn.NewSession() if err != nil { log.Error(err) - return cacao.VariableMap{}, err + return cacao.Variables{}, err } response, err := session.Output(StripSshPrepend(command.Command)) @@ -92,9 +92,9 @@ func (sshCapability *SshCapability) Execute(metadata execution.Metadata, if err != nil { log.Error(err) - return cacao.VariableMap{"__soarca_ssh_result__": {Name: "result", Value: string(response)}}, err + return cacao.Variables{"__soarca_ssh_result__": {Name: "result", Value: string(response)}}, err } - results := cacao.VariableMap{"__soarca_ssh_result__": {Name: "result", Value: string(response)}} + results := cacao.Variables{"__soarca_ssh_result__": {Name: "result", Value: string(response)}} log.Trace("Finished ssh execution will return the variables: ", results) return results, err } diff --git a/internal/decomposer/decomposer.go b/internal/decomposer/decomposer.go index ed0b7e96..9db1c3a3 100644 --- a/internal/decomposer/decomposer.go +++ b/internal/decomposer/decomposer.go @@ -49,7 +49,7 @@ type Decomposer struct { guid guid.IGuid } -func Callback(executionId uuid.UUID, outputVariables cacao.VariableMap) { +func Callback(executionId uuid.UUID, outputVariables cacao.Variables) { } diff --git a/internal/executer/executer.go b/internal/executer/executer.go index be2e04c4..37e38536 100644 --- a/internal/executer/executer.go +++ b/internal/executer/executer.go @@ -21,8 +21,8 @@ type IExecuter interface { command cacao.Command, authentication cacao.AuthenticationInformation, target cacao.AgentTarget, - variable cacao.VariableMap, - module cacao.AgentTarget) (uuid.UUID, cacao.VariableMap, error) + variable cacao.Variables, + module cacao.AgentTarget) (uuid.UUID, cacao.Variables, error) } func init() { @@ -43,14 +43,14 @@ func (executer *Executer) Execute(metadata execution.Metadata, command cacao.Command, authentication cacao.AuthenticationInformation, target cacao.AgentTarget, - variable cacao.VariableMap, - agent cacao.AgentTarget) (uuid.UUID, cacao.VariableMap, error) { + variable cacao.Variables, + agent cacao.AgentTarget) (uuid.UUID, cacao.Variables, error) { if capability, ok := executer.capabilities[agent.Name]; ok { returnVariables, err := capability.Execute(metadata, command, authentication, target, variable) return metadata.ExecutionId, returnVariables, err } else { - empty := cacao.VariableMap{} + empty := cacao.Variables{} message := "executor is not available in soarca" err := errors.New(message) log.Error(message) diff --git a/models/cacao/cacao.go b/models/cacao/cacao.go index 36f928ce..20973f73 100644 --- a/models/cacao/cacao.go +++ b/models/cacao/cacao.go @@ -29,6 +29,17 @@ type ( Workflow map[string]Step ) +type Variable struct { + Type string `bson:"type" json:"type" validate:"required"` + Name string `bson:"name,omitempty" json:"name,omitempty"` + Description string `bson:"description,omitempty" json:"description,omitempty"` + Value string `bson:"value,omitempty" json:"value,omitempty"` + Constant bool `bson:"constant,omitempty" json:"constant,omitempty"` + External bool `bson:"external,omitempty" json:"external,omitempty"` +} + +type Variables map[string]Variable + type Playbook struct { ID string `bson:"_id" json:"id" validate:"required"` Type string `bson:"type" json:"type" validate:"required"` @@ -56,7 +67,7 @@ type Playbook struct { AgentDefinitions map[string]AgentTarget `bson:"agent_definitions,omitempty" json:"agent_definitions,omitempty"` TargetDefinitions map[string]AgentTarget `bson:"target_definitions,omitempty" json:"target_definitions,omitempty"` ExtensionDefinitions map[string]ExtensionDefinition `bson:"extension_definitions,omitempty" json:"extension_definitions,omitempty"` - PlaybookVariables VariableMap `bson:"playbook_variables,omitempty" json:"playbook_variables,omitempty"` + PlaybookVariables Variables `bson:"playbook_variables,omitempty" json:"playbook_variables,omitempty"` PlaybookExtensions Extensions `bson:"playbook_extensions,omitempty" json:"playbook_extensions,omitempty"` } @@ -151,7 +162,7 @@ type Step struct { ExternalReferences []ExternalReferences `bson:"external_references,omitempty" json:"external_references,omitempty"` Delay int `bson:"delay,omitempty" json:"delay,omitempty"` Timeout int `bson:"timeout,omitempty" json:"timeout,omitempty"` - StepVariables VariableMap `bson:"step_variables,omitempty" json:"step_variables,omitempty"` + StepVariables Variables `bson:"step_variables,omitempty" json:"step_variables,omitempty"` Owner string `bson:"owner,omitempty" json:"owner,omitempty"` OnCompletion string `bson:"on_completion,omitempty" json:"on_completion,omitempty"` OnSuccess string `bson:"on_success,omitempty" json:"on_success,omitempty"` diff --git a/models/cacao/variables.go b/models/cacao/variables.go index 4d8f1627..780d029f 100644 --- a/models/cacao/variables.go +++ b/models/cacao/variables.go @@ -5,39 +5,82 @@ import ( "strings" ) -type Variable struct { - Type string `bson:"type" json:"type" validate:"required"` - Name string `bson:"name,omitempty" json:"name,omitempty"` - Description string `bson:"description,omitempty" json:"description,omitempty"` - Value string `bson:"value,omitempty" json:"value,omitempty"` - Constant bool `bson:"constant,omitempty" json:"constant,omitempty"` - External bool `bson:"external,omitempty" json:"external,omitempty"` +// Initialize new Variables +// +// Allows passing in multiple variables at once +func NewVariables(new ...Variable) Variables { + variables := make(Variables) + for _, variable := range new { + variables.Insert(variable) + } + return variables +} + +// Insert a variable +// +// Returns true if the variable was inserted +func (variables *Variables) Insert(new Variable) bool { + if _, found := (*variables)[new.Name]; found { + return false + } + (*variables)[new.Name] = new + return true +} + +// Insert or replace a variable +// +// Returns true if the variable was replaced +func (variables *Variables) InsertOrReplace(new Variable) bool { + _, found := (*variables)[new.Name] + (*variables)[new.Name] = new + return found +} + +// Insert variables map at once into the base and keep base variables in favor of source duplicates +func (variables *Variables) InsertRange(source Variables) { + for _, variable := range source { + variables.Insert(variable) + } } -type VariableMap map[string]Variable +// Find a variable by name +// +// Returns a Variable struct and a boolean indicating if it was found +func (variables Variables) Find(key string) (Variable, bool) { + val, ok := variables[key] + return val, ok +} -func (variableMap VariableMap) Merge(otherMap VariableMap) VariableMap { - newMap := make(VariableMap) - for k, v := range variableMap { - newMap[k] = v +// Interpolate variable references into a target string +// +// Returns the Interpolated string with variables values available in the map +func (variables *Variables) Interpolate(input string) string { + replacements := make([]string, 0) + for key, value := range *variables { + replacementKey := fmt.Sprint(key, ":value") + replacements = append(replacements, replacementKey, value.Value) } + return strings.NewReplacer(replacements...).Replace(input) +} + +// Select a subset of variables from the map +// +// Unknown keys are ignored. +func (variables *Variables) Select(keys []string) Variables { + newVariables := NewVariables() - for k, v := range otherMap { - if _, ok := variableMap[k]; !ok || v.Constant { - newMap[k] = v + for _, key := range keys { + if value, ok := variables.Find(key); ok { + newVariables.InsertOrReplace(value) } } - return newMap + return newVariables } -// TODO: Find a way to store the Replacer 'inside' the VariableMap -func (variableMap VariableMap) Replace(s string) string { - replacements := make([]string, 0) - for k, v := range variableMap { - replacementKey := fmt.Sprintf("%s:value", k) - replacements = append(replacements, replacementKey, v.Value) +// Merge two maps of Cacao variables and replace the base with the source if exists +func (variables *Variables) Merge(source Variables) { + for _, variable := range source { + variables.InsertOrReplace(variable) } - r := strings.NewReplacer(replacements...) - return r.Replace(s) } diff --git a/models/decoder/cacao.go b/models/decoder/cacao.go index 73fc5555..8b847aa6 100644 --- a/models/decoder/cacao.go +++ b/models/decoder/cacao.go @@ -68,5 +68,17 @@ func decode(data []byte) *cacao.Playbook { playbook.AuthenticationInfoDefinitions[key] = auth } + for key, variable := range playbook.PlaybookVariables { + variable.Name = key + playbook.PlaybookVariables.InsertOrReplace(variable) + } + + for _, step := range playbook.Workflow { + for key, variable := range step.StepVariables { + variable.Name = key + step.StepVariables.InsertOrReplace(variable) + } + } + return &playbook } diff --git a/test/integration/capability/http/http_integration_test.go b/test/integration/capability/http/http_integration_test.go index 404da926..05497b14 100644 --- a/test/integration/capability/http/http_integration_test.go +++ b/test/integration/capability/http/http_integration_test.go @@ -35,7 +35,7 @@ func TestHttpConnection(t *testing.T) { metadata, expectedCommand, cacao.AuthenticationInformation{}, cacao.AgentTarget{}, - cacao.VariableMap{"test": variable1}) + cacao.Variables{"test": variable1}) if err != nil { fmt.Println(err) t.Fail() @@ -81,7 +81,7 @@ func TestHttpOAuth2(t *testing.T) { expectedCommand, oauth2_info, target, - cacao.VariableMap{"test": variable1}) + cacao.Variables{"test": variable1}) if err != nil { fmt.Println(err) t.Fail() @@ -125,7 +125,7 @@ func TestHttpBasicAuth(t *testing.T) { metadata, expectedCommand, basicauth_info, - target, cacao.VariableMap{"test": variable1}) + target, cacao.Variables{"test": variable1}) if err != nil { fmt.Println(err) t.Fail() diff --git a/test/integration/capability/ssh/ssh_integration_test.go b/test/integration/capability/ssh/ssh_integration_test.go index fae7d450..57188a73 100644 --- a/test/integration/capability/ssh/ssh_integration_test.go +++ b/test/integration/capability/ssh/ssh_integration_test.go @@ -45,7 +45,7 @@ func TestSshConnection(t *testing.T) { expectedCommand, expectedAuthenticationInformation, expectedTarget, - cacao.VariableMap{expectedVariables.Name: expectedVariables}) + cacao.Variables{expectedVariables.Name: expectedVariables}) if err != nil { fmt.Println(err) t.Fail() diff --git a/test/unittest/cacao/cacao_test.go b/test/unittest/cacao/cacao_test.go index bf3bf224..5564e255 100644 --- a/test/unittest/cacao/cacao_test.go +++ b/test/unittest/cacao/cacao_test.go @@ -4,7 +4,7 @@ import ( "fmt" "io" "os" - "soarca/models/cacao" + "soarca/models/decoder" "testing" "time" @@ -41,7 +41,7 @@ func TestCacaoDecode(t *testing.T) { return } - var workflow = cacao.Decode(byteValue) + var workflow = decoder.DecodeValidate(byteValue) // fmt.Println(workflow) @@ -136,4 +136,20 @@ func TestCacaoDecode(t *testing.T) { assert.Equal(t, workflow.AuthenticationInfoDefinitions["http-basic--76c26f7f-9a15-40ff-a90a-7b19e23372ae"].UserId, "admin") assert.Equal(t, workflow.AuthenticationInfoDefinitions["http-basic--76c26f7f-9a15-40ff-a90a-7b19e23372ae"].Password, "super-secure-password") + // Assert the variables are mapped correctly on the playbook level + assert.Equal(t, workflow.PlaybookVariables["__var1__"].Name, "__var1__") + assert.Equal(t, workflow.PlaybookVariables["__var1__"].Type, "string") + assert.Equal(t, workflow.PlaybookVariables["__var1__"].Description, "Some nice description") + assert.Equal(t, workflow.PlaybookVariables["__var1__"].Value, "A random string") + assert.Equal(t, workflow.PlaybookVariables["__var1__"].Constant, false) + assert.Equal(t, workflow.PlaybookVariables["__var1__"].External, false) + + // Assert the variables are mapped correctly on the step level + assert.Equal(t, workflow.Workflow["action--9fcc5c3b-0b70-4d73-b922-cf5491dcd1a4"].StepVariables["__bia_address__"].Name, "__bia_address__") + assert.Equal(t, workflow.Workflow["action--9fcc5c3b-0b70-4d73-b922-cf5491dcd1a4"].StepVariables["__bia_address__"].Type, "ipv4-addr") + assert.Equal(t, workflow.Workflow["action--9fcc5c3b-0b70-4d73-b922-cf5491dcd1a4"].StepVariables["__bia_address__"].Description, "Bia address") + assert.Equal(t, workflow.Workflow["action--9fcc5c3b-0b70-4d73-b922-cf5491dcd1a4"].StepVariables["__bia_address__"].Value, "192.168.0.1") + assert.Equal(t, workflow.Workflow["action--9fcc5c3b-0b70-4d73-b922-cf5491dcd1a4"].StepVariables["__bia_address__"].Constant, true) + assert.Equal(t, workflow.Workflow["action--9fcc5c3b-0b70-4d73-b922-cf5491dcd1a4"].StepVariables["__bia_address__"].External, false) + } diff --git a/test/unittest/cacao/playbooks/playbook.json b/test/unittest/cacao/playbooks/playbook.json index 0a019c5c..9851477e 100644 --- a/test/unittest/cacao/playbooks/playbook.json +++ b/test/unittest/cacao/playbooks/playbook.json @@ -41,9 +41,9 @@ "individual--6b23a237-ade8-4d00-9aa1-75999732d557": { "name": "banana rama", "banana": "rama", - "contact" : { - "email" : { - "work" : "banana@business.com" + "contact": { + "email": { + "work": "banana@business.com" } } } @@ -56,6 +56,15 @@ "url": "http://tno.nl/cst" } ], + "playbook_variables": { + "__var1__": { + "type": "string", + "description": "Some nice description", + "value": "A random string", + "constant": false, + "external": false + } + }, "workflow_start": "action--a76dbc32-b739-427b-ae13-4ec703d5797e", "workflow_exception": "end--40131926-89e9-44df-a018-5f92f2df7914", "workflow": { @@ -83,7 +92,16 @@ "command": "GET http://__bia_address__/analysisreport/__cve__" } ], - "agent": "individual--6b23a237-ade8-4d00-9aa1-75999732d557" + "agent": "individual--6b23a237-ade8-4d00-9aa1-75999732d557", + "step_variables": { + "__bia_address__": { + "type": "ipv4-addr", + "description": "Bia address", + "value": "192.168.0.1", + "constant": true, + "external": false + } + } }, "action--09b97fab-56a1-45dc-a88f-be3cde3eac33": { "type": "action", diff --git a/test/unittest/cacao/variables_test.go b/test/unittest/cacao/variables_test.go index 55fd4f7f..af7a08a6 100644 --- a/test/unittest/cacao/variables_test.go +++ b/test/unittest/cacao/variables_test.go @@ -7,84 +7,176 @@ import ( "github.com/go-playground/assert/v2" ) -func TestVariableMapMerging(t *testing.T) { - base := cacao.VariableMap{ - "__var0__": { - Value: "OLD", - }, +func TestVariablesFind(t *testing.T) { + variables := make(cacao.Variables) + inserted := cacao.Variable{ + Name: "__var0__", + Value: "value", } + variables["__var0__"] = inserted + variable, found := variables.Find("__var0__") + assert.Equal(t, found, true) + assert.Equal(t, variable, inserted) +} - new := cacao.VariableMap{ - "__var1__": { - Value: "NEW", - }, - } +func TestVariablesInsertNew(t *testing.T) { + vars := cacao.NewVariables() + inserted := vars.Insert(cacao.Variable{ + Name: "__var0__", + Value: "value", + }) - merged := base.Merge(new) - assert.Equal(t, merged["__var0__"].Value, "OLD") - assert.Equal(t, merged["__var1__"].Value, "NEW") + assert.Equal(t, inserted, true) + assert.Equal(t, vars["__var0__"].Value, "value") } -func TestVariableMapMergingWithUpdate(t *testing.T) { - base := cacao.VariableMap{ - "__var__": { - Value: "OLD", - }, - } +func TestVariablesInsertExisting(t *testing.T) { + vars := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "old", + }) + inserted := vars.Insert(cacao.Variable{ + Name: "__var0__", + Value: "new", + }) + + assert.Equal(t, inserted, false) + assert.Equal(t, vars["__var0__"].Value, "old") +} - new := cacao.VariableMap{ - "__var__": { - Value: "NEW", - }, - } +func TestVariablesInsertOrReplace(t *testing.T) { + vars := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "old", + }) + replaced := vars.InsertOrReplace(cacao.Variable{ + Name: "__var0__", + Value: "new", + }) + + assert.Equal(t, replaced, true) + assert.Equal(t, vars["__var0__"].Value, "new") +} - merged := base.Merge(new) - assert.Equal(t, merged["__var__"].Value, "OLD") +func TestVariablesInsertRange(t *testing.T) { + vars := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "old", + }) + otherRange := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "new", + }, cacao.Variable{ + Name: "__var1__", + Value: "new2", + }) + vars.InsertRange(otherRange) + + assert.Equal(t, vars["__var0__"].Value, "old") + assert.Equal(t, vars["__var1__"].Value, "new2") } -func TestVariableMapMergingWithConstant(t *testing.T) { - base := cacao.VariableMap{ - "__var__": { - Value: "OLD", - }, - } +func TestVariablesMerge(t *testing.T) { + base := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "OLD", + }) - new := cacao.VariableMap{ - "__var__": { - Value: "NEW", - Constant: true, - }, - } + new := cacao.NewVariables(cacao.Variable{ + Name: "__var1__", + Value: "NEW", + }) + + base.Merge(new) + + assert.Equal(t, base["__var0__"].Value, "OLD") + assert.Equal(t, new["__var0__"].Value, "") + assert.Equal(t, base["__var1__"].Value, "NEW") + assert.Equal(t, new["__var1__"].Value, "NEW") +} + +func TestVariablesMergeWithUpdate(t *testing.T) { + base := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "OLD", + }) + + new := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "NEW", + }) - merged := base.Merge(new) - assert.Equal(t, merged["__var__"].Value, "NEW") + base.Merge(new) + + assert.Equal(t, base["__var0__"].Value, "NEW") } -func TestVariableMapStringReplace(t *testing.T) { +func TestVariablesStringInterpolation(t *testing.T) { original := "__var0__:value is __var0__:value" - vars := cacao.VariableMap{ - "__var0__": { - Value: "GO", - }, - } + vars := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "GO", + }) - replaced := vars.Replace(original) + replaced := vars.Interpolate(original) assert.Equal(t, replaced, "GO is GO") } -func TestVariableMapStringReplaceMultiple(t *testing.T) { +func TestVariablesStringInterpolationEmptyString(t *testing.T) { + original := "" + + vars := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "GO", + }) + + replaced := vars.Interpolate(original) + assert.Equal(t, replaced, "") +} + +func TestVariablesStringInterpolateMultiple(t *testing.T) { original := "__var0__:value is __var1__:value" - vars := cacao.VariableMap{ - "__var0__": { - Value: "GO", - }, - "__var1__": { - Value: "COOL", - }, - } + vars := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "GO", + }, cacao.Variable{ + Name: "__var1__", + Value: "COOL", + }) - replaced := vars.Replace(original) + replaced := vars.Interpolate(original) assert.Equal(t, replaced, "GO is COOL") } + +func TestVariablesStringInterpolateMultipleAndUnkown(t *testing.T) { + original := "__var0__:value is __var1_unknown__:value" + + vars := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "GO", + }, cacao.Variable{ + Name: "__var1__", + Value: "COOL", + }) + + replaced := vars.Interpolate(original) + assert.Equal(t, replaced, "GO is __var1_unknown__:value") +} + +func TestVariablesSelect(t *testing.T) { + vars := cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "GO", + }, cacao.Variable{ + Name: "__var1__", + Value: "COOL", + }) + filteredVars := vars.Select([]string{"__var0__", "__unknown__"}) + + assert.Equal(t, filteredVars, cacao.NewVariables(cacao.Variable{ + Name: "__var0__", + Value: "GO", + })) +} diff --git a/test/unittest/decomposer/decomposer_test.go b/test/unittest/decomposer/decomposer_test.go index ae6b2da3..cf6ac636 100644 --- a/test/unittest/decomposer/decomposer_test.go +++ b/test/unittest/decomposer/decomposer_test.go @@ -36,7 +36,7 @@ func TestExecutePlaybook(t *testing.T) { Type: "ssh", ID: "action--test", Name: "ssh-tests", - StepVariables: cacao.VariableMap{expectedVariables.Name: expectedVariables}, + StepVariables: cacao.Variables{expectedVariables.Name: expectedVariables}, Commands: []cacao.Command{expectedCommand}, Cases: map[string]string{}, OnCompletion: "end--test", @@ -85,8 +85,8 @@ func TestExecutePlaybook(t *testing.T) { mock_executer.On("Execute", metaStep1, expectedCommand, expectedAuth, expectedTarget, - cacao.VariableMap{"var1": expectedVariables}, - expectedAgent).Return(executionId, cacao.VariableMap{}, nil) + cacao.Variables{"var1": expectedVariables}, + expectedAgent).Return(executionId, cacao.Variables{}, nil) returnedId, err := decomposer.Execute(playbook) uuid_mock.AssertExpectations(t) @@ -129,7 +129,7 @@ func TestExecutePlaybookMultiStep(t *testing.T) { Type: "ssh", ID: "action--test", Name: "ssh-tests", - StepVariables: cacao.VariableMap{expectedVariables.Name: expectedVariables}, + StepVariables: cacao.Variables{expectedVariables.Name: expectedVariables}, Commands: []cacao.Command{expectedCommand}, Cases: map[string]string{}, OnCompletion: "action--test2", @@ -142,7 +142,7 @@ func TestExecutePlaybookMultiStep(t *testing.T) { Type: "ssh", ID: "action--test2", Name: "ssh-tests", - StepVariables: cacao.VariableMap{expectedVariables2.Name: expectedVariables2}, + StepVariables: cacao.Variables{expectedVariables2.Name: expectedVariables2}, Commands: []cacao.Command{expectedCommand2}, Cases: map[string]string{}, Agent: "agent1", @@ -153,7 +153,7 @@ func TestExecutePlaybookMultiStep(t *testing.T) { Type: "ssh", ID: "action--test3", Name: "ssh-tests", - StepVariables: cacao.VariableMap{expectedVariables.Name: expectedVariables}, + StepVariables: cacao.Variables{expectedVariables.Name: expectedVariables}, Commands: []cacao.Command{expectedCommand}, Agent: "agent1", // Targets: []string{"target1"}, @@ -200,15 +200,15 @@ func TestExecutePlaybookMultiStep(t *testing.T) { expectedCommand, expectedAuth, expectedTarget, - cacao.VariableMap{"var1": expectedVariables}, - expectedAgent).Return(executionId, cacao.VariableMap{}, nil) + cacao.Variables{"var1": expectedVariables}, + expectedAgent).Return(executionId, cacao.Variables{}, nil) mock_executer.On("Execute", metaStep2, expectedCommand2, expectedAuth, expectedTarget, - cacao.VariableMap{"var2": expectedVariables2}, - expectedAgent).Return(executionId, cacao.VariableMap{}, nil) + cacao.Variables{"var2": expectedVariables2}, + expectedAgent).Return(executionId, cacao.Variables{}, nil) returnedId, err := decomposer.Execute(playbook) uuid_mock.AssertExpectations(t) @@ -255,7 +255,7 @@ func TestExecuteEmptyMultiStep(t *testing.T) { ID: "action--test", Name: "ssh-tests", Agent: "agent1", - StepVariables: cacao.VariableMap{expectedVariables.Name: expectedVariables}, + StepVariables: cacao.Variables{expectedVariables.Name: expectedVariables}, Commands: []cacao.Command{expectedCommand}, Cases: map[string]string{}, OnCompletion: "", @@ -307,7 +307,7 @@ func TestExecuteIllegalMultiStep(t *testing.T) { Type: "ssh", ID: "action--test", Name: "ssh-tests", - StepVariables: cacao.VariableMap{expectedVariables.Name: expectedVariables}, + StepVariables: cacao.Variables{expectedVariables.Name: expectedVariables}, Commands: []cacao.Command{expectedCommand}, Cases: map[string]string{}, OnCompletion: "action-some-non-existing", diff --git a/test/unittest/executor/executor_test.go b/test/unittest/executor/executor_test.go index 1cf33fde..1a4da495 100644 --- a/test/unittest/executor/executor_test.go +++ b/test/unittest/executor/executor_test.go @@ -56,15 +56,15 @@ func TestExecuteStep(t *testing.T) { expectedCommand, expectedAuth, expectedTarget, - cacao.VariableMap{expectedVariables.Name: expectedVariables}). - Return(cacao.VariableMap{expectedVariables.Name: expectedVariables}, + cacao.Variables{expectedVariables.Name: expectedVariables}). + Return(cacao.Variables{expectedVariables.Name: expectedVariables}, nil) _, _, err := executerObject.Execute(metadata, expectedCommand, expectedAuth, expectedTarget, - cacao.VariableMap{expectedVariables.Name: expectedVariables}, + cacao.Variables{expectedVariables.Name: expectedVariables}, agent) assert.Equal(t, err, nil) @@ -113,7 +113,7 @@ func TestNonExistingCapabilityStep(t *testing.T) { expectedCommand, expectedAuth, expectedTarget, - cacao.VariableMap{expectedVariables.Name: expectedVariables}, + cacao.Variables{expectedVariables.Name: expectedVariables}, agent) assert.Equal(t, err, errors.New("executor is not available in soarca")) diff --git a/test/unittest/mocks/mock_capability/mock_capability.go b/test/unittest/mocks/mock_capability/mock_capability.go index 8da2a6bb..a2b4730d 100644 --- a/test/unittest/mocks/mock_capability/mock_capability.go +++ b/test/unittest/mocks/mock_capability/mock_capability.go @@ -15,9 +15,9 @@ func (capability *Mock_Capability) Execute(metadata execution.Metadata, command cacao.Command, authentication cacao.AuthenticationInformation, target cacao.AgentTarget, - variables cacao.VariableMap) (cacao.VariableMap, error) { + variables cacao.Variables) (cacao.Variables, error) { args := capability.Called(metadata, command, authentication, target, variables) - return args.Get(0).(cacao.VariableMap), args.Error(1) + return args.Get(0).(cacao.Variables), args.Error(1) } func (capability *Mock_Capability) GetType() string { diff --git a/test/unittest/mocks/mock_executor/mock_executor.go b/test/unittest/mocks/mock_executor/mock_executor.go index 71b7c85e..4014704e 100644 --- a/test/unittest/mocks/mock_executor/mock_executor.go +++ b/test/unittest/mocks/mock_executor/mock_executor.go @@ -10,7 +10,7 @@ import ( type Mock_Executor struct { mock.Mock - OnCompletionCallback func(executionId uuid.UUID, output cacao.VariableMap) + OnCompletionCallback func(executionId uuid.UUID, output cacao.Variables) } func (executer *Mock_Executor) Execute( @@ -18,10 +18,10 @@ func (executer *Mock_Executor) Execute( command cacao.Command, authentication cacao.AuthenticationInformation, target cacao.AgentTarget, - variable cacao.VariableMap, + variable cacao.Variables, agent cacao.AgentTarget) (uuid.UUID, - cacao.VariableMap, + cacao.Variables, error) { args := executer.Called(metadata, command, authentication, target, variable, agent) - return args.Get(0).(uuid.UUID), args.Get(1).(cacao.VariableMap), args.Error(2) + return args.Get(0).(uuid.UUID), args.Get(1).(cacao.Variables), args.Error(2) }