Skip to content

Commit 0e68280

Browse files
DET-728: Added support for overriding sumo rules
1 parent 6322a46 commit 0e68280

19 files changed

+1085
-18
lines changed

sumologic/resource_sumologic_cse_aggregation_rule.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ func resourceSumologicCSEAggregationRule() *schema.Resource {
6969
Optional: true,
7070
},
7171
"match_expression": {
72-
Type: schema.TypeString,
73-
Required: true,
72+
Type: schema.TypeString,
73+
Required: true,
74+
DiffSuppressFunc: suppressSpaceDiff,
7475
},
7576
"name": {
7677
Type: schema.TypeString,
@@ -206,13 +207,20 @@ func resourceSumologicCSEAggregationRuleCreate(d *schema.ResourceData, meta inte
206207
}
207208

208209
func resourceSumologicCSEAggregationRuleUpdate(d *schema.ResourceData, meta interface{}) error {
210+
ruleSource := getRuleSource(d.Id(), meta)
209211
CSEAggregationRule, err := resourceToCSEAggregationRule(d)
210212
if err != nil {
211213
return err
212214
}
213215

214216
c := meta.(*Client)
215-
if err = c.UpdateCSEAggregationRule(CSEAggregationRule); err != nil {
217+
if ruleSource == "user" {
218+
err = c.UpdateCSEAggregationRule(CSEAggregationRule)
219+
} else {
220+
err = c.OverrideCSEAggregationRule(CSEAggregationRule)
221+
}
222+
223+
if err != nil {
216224
return err
217225
}
218226

sumologic/resource_sumologic_cse_aggregation_rule_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,52 @@ func TestAccSumologicCSEAggregationRule_createAndUpdateWithCustomWindowSize(t *t
5656
})
5757
}
5858

59+
func TestAccSumologicCSEAggregationRule_Override(t *testing.T) {
60+
SkipCseTest(t)
61+
62+
var aggregationRule CSEAggregationRule
63+
descriptionExpression := "This rule detects when a user has utilized multiple distinct User Agents when performing authentication through Okta. This activity could potentially indicate credential theft or a general session anomaly. Examine other Okta related events surrounding the time period for this signal, pivoting off the username value to examine if any other suspicious activity has taken place. If this rule is generating false positives, adjust the threshold value and consider excluding certain user accounts via tuning expression."
64+
65+
resourceName := "sumologic_cse_aggregation_rule.sumo_aggregation_rule_test"
66+
resource.Test(t, resource.TestCase{
67+
PreCheck: func() { testAccPreCheck(t) },
68+
Providers: testAccProviders,
69+
CheckDestroy: testAccCSEAggregationRuleDestroy,
70+
Steps: []resource.TestStep{
71+
{
72+
Config: testOverrideCSEAggregationRuleConfig(descriptionExpression),
73+
ResourceName: resourceName,
74+
ImportState: true,
75+
ImportStateId: "AGGREGATION-S00009",
76+
ImportStateVerify: false,
77+
ImportStateVerifyIgnore: []string{"name"}, // Ignore fields that might differ
78+
ImportStatePersist: true,
79+
},
80+
{
81+
Config: testOverrideCSEAggregationRuleConfig(fmt.Sprintf("Updated %s", descriptionExpression)),
82+
Check: resource.ComposeTestCheckFunc(
83+
testCheckCSEAggregationRuleExists(resourceName, &aggregationRule),
84+
testCheckAggregationRuleOverrideValues(&aggregationRule, fmt.Sprintf("Updated %s", descriptionExpression)),
85+
resource.TestCheckResourceAttrSet(resourceName, "id"),
86+
resource.TestCheckResourceAttr(resourceName, "id", "AGGREGATION-S00009"),
87+
),
88+
},
89+
{
90+
Config: testOverrideCSEAggregationRuleConfig(descriptionExpression),
91+
Check: resource.ComposeTestCheckFunc(
92+
testCheckCSEAggregationRuleExists(resourceName, &aggregationRule),
93+
testCheckAggregationRuleOverrideValues(&aggregationRule, descriptionExpression),
94+
resource.TestCheckResourceAttrSet(resourceName, "id"),
95+
resource.TestCheckResourceAttr(resourceName, "id", "AGGREGATION-S00009"),
96+
),
97+
},
98+
{
99+
Config: getAggregationRuleRemovedBlock(),
100+
},
101+
},
102+
})
103+
}
104+
59105
func TestAccSumologicCSEAggregationRule_createAndUpdateToCustomWindowSize(t *testing.T) {
60106
SkipCseTest(t)
61107

@@ -171,6 +217,62 @@ func testAccCSEAggregationRuleDestroy(s *terraform.State) error {
171217
return nil
172218
}
173219

220+
func testOverrideCSEAggregationRuleConfig(descriptionExpression string) string {
221+
return fmt.Sprintf(`
222+
resource "sumologic_cse_aggregation_rule" "sumo_aggregation_rule_test" {
223+
description_expression = "%s"
224+
enabled = true
225+
group_by_entity = true
226+
group_by_fields = []
227+
is_prototype = true
228+
match_expression = <<-EOT
229+
metadata_vendor = "Okta"
230+
and metadata_deviceEventId = "user.authentication.sso"
231+
EOT
232+
name = "Okta - Session Anomaly (Multiple User Agents)"
233+
name_expression = "Okta - Session Anomaly (Multiple User Agents) for user: {{user_username}}"
234+
summary_expression = "{{user_username}} has utilized a number of distinct User Agents which has crossed the threshold (4) value within a 30-minute time period to perform Okta authentication."
235+
tags = [
236+
"_mitreAttackTactic:TA0001",
237+
"_mitreAttackTechnique:T1078.004",
238+
]
239+
trigger_expression = "distinct_userAgents > 4"
240+
window_size = "T30M"
241+
242+
aggregation_functions {
243+
arguments = [
244+
"fields[\"client.userAgent.rawUserAgent\"]",
245+
]
246+
function = "count_distinct"
247+
name = "distinct_userAgents"
248+
}
249+
250+
entity_selectors {
251+
entity_type = "_username"
252+
expression = "user_username"
253+
}
254+
255+
severity_mapping {
256+
default = 1
257+
field = null
258+
type = "constant"
259+
}
260+
}
261+
`, descriptionExpression)
262+
}
263+
264+
func getAggregationRuleRemovedBlock() string {
265+
return fmt.Sprintf(`
266+
removed {
267+
from = sumologic_cse_aggregation_rule.sumo_aggregation_rule_test
268+
269+
lifecycle {
270+
destroy = false
271+
}
272+
}
273+
`)
274+
}
275+
174276
func testCreateCSEAggregationRuleConfig(t *testing.T, payload *CSEAggregationRule) string {
175277
resourceTemplate := `
176278
resource "sumologic_cse_aggregation_rule" "aggregation_rule" {
@@ -303,3 +405,12 @@ func testCheckCSEAggregationRuleValues(t *testing.T, expected *CSEAggregationRul
303405
return nil
304406
}
305407
}
408+
409+
func testCheckAggregationRuleOverrideValues(aggregationRule *CSEAggregationRule, descriptionExpression string) resource.TestCheckFunc {
410+
return func(s *terraform.State) error {
411+
if aggregationRule.DescriptionExpression != descriptionExpression {
412+
return fmt.Errorf("bad descriptionExpression, expected \"%s\", got %#v", descriptionExpression, aggregationRule.DescriptionExpression)
413+
}
414+
return nil
415+
}
416+
}

sumologic/resource_sumologic_cse_chain_rule.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ func resourceSumologicCSEChainRule() *schema.Resource {
3434
Elem: &schema.Resource{
3535
Schema: map[string]*schema.Schema{
3636
"expression": {
37-
Type: schema.TypeString,
38-
Required: true,
37+
Type: schema.TypeString,
38+
Required: true,
39+
DiffSuppressFunc: suppressSpaceDiff,
3940
},
4041
"limit": {
4142
Type: schema.TypeInt,
@@ -184,13 +185,20 @@ func resourceSumologicCSEChainRuleCreate(d *schema.ResourceData, meta interface{
184185
}
185186

186187
func resourceSumologicCSEChainRuleUpdate(d *schema.ResourceData, meta interface{}) error {
188+
ruleSource := getRuleSource(d.Id(), meta)
187189
CSEChainRule, err := resourceToCSEChainRule(d)
188190
if err != nil {
189191
return err
190192
}
191193

192194
c := meta.(*Client)
193-
if err = c.UpdateCSEChainRule(CSEChainRule); err != nil {
195+
if ruleSource == "user" {
196+
err = c.UpdateCSEChainRule(CSEChainRule)
197+
} else {
198+
err = c.OverrideCSEChainRule(CSEChainRule)
199+
}
200+
201+
if err != nil {
194202
return err
195203
}
196204

sumologic/resource_sumologic_cse_chain_rule_test.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,52 @@ func TestAccSumologicCSEChainRule_createAndUpdateWithCustomWindowSize(t *testing
5656
})
5757
}
5858

59+
func TestAccSumologicCSEChainRule_Override(t *testing.T) {
60+
SkipCseTest(t)
61+
62+
var ChainRule CSEChainRule
63+
descriptionExpression := "This rule utilizes Jamf telemetry and looks for osascript execution with a suspicious parent process indicating execution from a shell or terminal in addition to the osascript process making network connections to an external IP address."
64+
65+
resourceName := "sumologic_cse_chain_rule.sumo_chain_rule_test"
66+
resource.Test(t, resource.TestCase{
67+
PreCheck: func() { testAccPreCheck(t) },
68+
Providers: testAccProviders,
69+
CheckDestroy: testAccCSEChainRuleDestroy,
70+
Steps: []resource.TestStep{
71+
{
72+
Config: testOverrideCSEChainRuleConfig(descriptionExpression),
73+
ResourceName: resourceName,
74+
ImportState: true,
75+
ImportStateId: "CHAIN-S00016",
76+
ImportStateVerify: false,
77+
ImportStateVerifyIgnore: []string{"name"}, // Ignore fields that might differ
78+
ImportStatePersist: true,
79+
},
80+
{
81+
Config: testOverrideCSEChainRuleConfig(fmt.Sprintf("Updated %s", descriptionExpression)),
82+
Check: resource.ComposeTestCheckFunc(
83+
testCheckCSEChainRuleExists(resourceName, &ChainRule),
84+
testCheckChainRuleOverrideValues(&ChainRule, fmt.Sprintf("Updated %s", descriptionExpression)),
85+
resource.TestCheckResourceAttrSet(resourceName, "id"),
86+
resource.TestCheckResourceAttr(resourceName, "id", "CHAIN-S00016"),
87+
),
88+
},
89+
{
90+
Config: testOverrideCSEChainRuleConfig(descriptionExpression),
91+
Check: resource.ComposeTestCheckFunc(
92+
testCheckCSEChainRuleExists(resourceName, &ChainRule),
93+
testCheckChainRuleOverrideValues(&ChainRule, descriptionExpression),
94+
resource.TestCheckResourceAttrSet(resourceName, "id"),
95+
resource.TestCheckResourceAttr(resourceName, "id", "CHAIN-S00016"),
96+
),
97+
},
98+
{
99+
Config: getChainRuleRemovedBlock(),
100+
},
101+
},
102+
})
103+
}
104+
59105
func TestAccSumologicCSEChainRule_createAndUpdateToCustomWindowSize(t *testing.T) {
60106
SkipCseTest(t)
61107

@@ -219,6 +265,66 @@ func testCreateCSEChainRuleConfig(t *testing.T, payload *CSEChainRule) string {
219265
return buffer.String()
220266
}
221267

268+
func testOverrideCSEChainRuleConfig(descriptionExpression string) string {
269+
return fmt.Sprintf(`
270+
resource "sumologic_cse_chain_rule" "sumo_chain_rule_test" {
271+
description = "%s"
272+
enabled = true
273+
group_by_fields = [
274+
"user_username",
275+
]
276+
is_prototype = true
277+
name = "macOS - Suspicious Osascript Execution and Network Activity"
278+
ordered = false
279+
severity = 3
280+
summary_expression = "User: {{user_username}} has created and deleted an agent pool in a short period of time - inaminadika10"
281+
tags = [
282+
"_mitreAttackTactic:TA0002",
283+
"_mitreAttackTechnique:T1059.002",
284+
]
285+
window_size = "T05M"
286+
287+
entity_selectors {
288+
entity_type = "_hostname"
289+
expression = "device_hostname"
290+
}
291+
292+
expressions_and_limits {
293+
expression = <<-EOT
294+
baseImage = "/usr/bin/osascript"
295+
and !isNull(commandLine)
296+
and parentBaseImage matches /(\/bin)/
297+
EOT
298+
limit = 1
299+
}
300+
expressions_and_limits {
301+
expression = <<-EOT
302+
metadata_vendor = "Jamf"
303+
and metadata_product = "Jamf"
304+
and metadata_deviceEventId = "AUE_CONNECT"
305+
and parentBaseImage matches /(\/bin)/
306+
and baseImage = "/usr/bin/osascript"
307+
and !isNull(dstDevice_ip)
308+
and dstDevice_ip_isInternal = false
309+
EOT
310+
limit = 1
311+
}
312+
}
313+
`, descriptionExpression)
314+
}
315+
316+
func getChainRuleRemovedBlock() string {
317+
return fmt.Sprintf(`
318+
removed {
319+
from = sumologic_cse_chain_rule.sumo_chain_rule_test
320+
321+
lifecycle {
322+
destroy = false
323+
}
324+
}
325+
`)
326+
}
327+
222328
func getCSEChainRuleTestPayload() CSEChainRule {
223329
return CSEChainRule{
224330
Description: "Test description",
@@ -282,3 +388,12 @@ func testCheckCSEChainRuleValues(t *testing.T, expected *CSEChainRule, actual *C
282388
return nil
283389
}
284390
}
391+
392+
func testCheckChainRuleOverrideValues(chainRule *CSEChainRule, descriptionExpression string) resource.TestCheckFunc {
393+
return func(s *terraform.State) error {
394+
if chainRule.Description != descriptionExpression {
395+
return fmt.Errorf("bad descriptionExpression, expected \"%s\", got %#v", descriptionExpression, chainRule.Description)
396+
}
397+
return nil
398+
}
399+
}

sumologic/resource_sumologic_cse_first_seen_rule.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ func resourceSumologicCSEFirstSeenRule() *schema.Resource {
3636
},
3737
"entity_selectors": getEntitySelectorsSchema(),
3838
"filter_expression": {
39-
Type: schema.TypeString,
40-
Required: true,
39+
Type: schema.TypeString,
40+
Required: true,
41+
DiffSuppressFunc: suppressSpaceDiff,
4142
},
4243
"group_by_fields": {
4344
Type: schema.TypeList,
@@ -183,13 +184,20 @@ func resourceSumologicCSEFirstSeenRuleCreate(d *schema.ResourceData, meta interf
183184
}
184185

185186
func resourceSumologicCSEFirstSeenRuleUpdate(d *schema.ResourceData, meta interface{}) error {
187+
ruleSource := getRuleSource(d.Id(), meta)
186188
CSEFirstSeenRule, err := resourceToCSEFirstSeenRule(d)
187189
if err != nil {
188190
return err
189191
}
190192

191193
c := meta.(*Client)
192-
if err = c.UpdateCSEFirstSeenRule(CSEFirstSeenRule); err != nil {
194+
if ruleSource == "user" {
195+
err = c.UpdateCSEFirstSeenRule(CSEFirstSeenRule)
196+
} else {
197+
err = c.OverrideCSEFirstSeenRule(CSEFirstSeenRule)
198+
}
199+
200+
if err != nil {
193201
return err
194202
}
195203

0 commit comments

Comments
 (0)