From abec59e7d3e91e2dd2afa209201fc998cf6476dd Mon Sep 17 00:00:00 2001 From: "Freddy R. Laffita Almaguer" Date: Thu, 1 Feb 2024 11:15:00 +0200 Subject: [PATCH 1/2] Adding default_agent field to utm_alert_response_rule, to use its value as default if the alert log is not coming from an agent. Adding new logic to UtmAlertResponseRuleService to manage the default agent to execute the incident response when the alert log is not coming from an agent. --- .../UtmAlertResponseRule.java | 14 ++++ .../UtmAlertResponseRuleService.java | 66 +++++++++++++++---- .../service/dto/UtmAlertResponseRuleDTO.java | 11 ++++ ..._default_agent_utm_alert_response_rule.xml | 14 ++++ .../resources/config/liquibase/master.xml | 2 + 5 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20240131001_add_default_agent_utm_alert_response_rule.xml diff --git a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java index 3c8f547c1..3df026eda 100644 --- a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java +++ b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java @@ -9,8 +9,10 @@ import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import javax.persistence.*; +import javax.validation.constraints.Size; import java.io.Serializable; import java.time.Instant; @@ -38,6 +40,9 @@ public class UtmAlertResponseRule implements Serializable { private String agentPlatform; @Column(name = "excluded_agents") private String excludedAgents; + @Size(max = 500) + @Column(name = "default_agent" , length = 500) + private String defaultAgent; @CreatedBy @Column(name = "created_by", nullable = false, length = 50, updatable = false) private String createdBy; @@ -62,6 +67,7 @@ public UtmAlertResponseRule(UtmAlertResponseRuleDTO dto) { this.ruleCmd = dto.getCommand(); this.ruleActive = dto.getActive(); this.agentPlatform = dto.getAgentPlatform(); + this.defaultAgent = dto.getDefaultAgent(); if (!CollectionUtils.isEmpty(dto.getExcludedAgents())) this.excludedAgents = String.join(",", dto.getExcludedAgents()); else @@ -132,6 +138,14 @@ public void setExcludedAgents(String excludedAgents) { this.excludedAgents = excludedAgents; } + public String getDefaultAgent() { + return defaultAgent; + } + + public void setDefaultAgent(String defaultAgent) { + this.defaultAgent = defaultAgent; + } + public String getCreatedBy() { return createdBy; } diff --git a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java index 2b17708d3..c199b88b2 100644 --- a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java +++ b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java @@ -129,19 +129,26 @@ public void evaluateRules(List alerts) { return; List rules = alertResponseRuleRepository.findAllByRuleActiveIsTrue(); - if (CollectionUtils.isEmpty(rules)) - return; // Excluding alerts tagged as false positive alerts = alerts.stream().filter(a -> (CollectionUtils.isEmpty(a.getTags()) || !a.getTags().contains("False positive"))) .collect(Collectors.toList()); + // Do nothing if there is no valid alerts to check + if (CollectionUtils.isEmpty(alerts)) + return; + String alertJsonArray = new Gson().toJson(alerts); for (UtmAlertResponseRule rule : rules) { List conditions = new ArrayList<>(); List agentNames = networkScanRepository.findAgentNamesByPlatform(rule.getAgentPlatform()); + if (!CollectionUtils.isEmpty(agentNames)) - conditions.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_ONE_OF, agentNames)); + continue; + + // Conditions for matching agents (these are the alerts made from logs coming from an agent) + //------------------------------------------------------------------------------------------ + conditions.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_ONE_OF, agentNames)); if (StringUtils.hasText(rule.getRuleConditions())) conditions.addAll(new Gson().fromJson(rule.getRuleConditions(), TypeToken.getParameterized(List.class, FilterType.class).getType())); @@ -152,19 +159,50 @@ public void evaluateRules(List alerts) { Filter filter = buildFilters(conditions); List matches = UtilJson.read("$[?]", alertJsonArray, filter); - if (CollectionUtils.isEmpty(matches)) - continue; + if (!CollectionUtils.isEmpty(matches)) { - for (Object match : matches) { - String matchAsJson = new Gson().toJson(match); + for (Object match : matches) { + String matchAsJson = new Gson().toJson(match); - UtmAlertResponseRuleExecution exe = new UtmAlertResponseRuleExecution(); - exe.setAgent(UtilJson.read("$.dataSource", matchAsJson)); - exe.setAlertId(UtilJson.read("$.id", matchAsJson)); - exe.setRuleId(rule.getId()); - exe.setCommand(buildCommand(rule.getRuleCmd(), matchAsJson)); - exe.setExecutionStatus(RuleExecutionStatus.PENDING); - alertResponseRuleExecutionRepository.save(exe); + UtmAlertResponseRuleExecution exe = new UtmAlertResponseRuleExecution(); + exe.setAgent(UtilJson.read("$.dataSource", matchAsJson)); + exe.setAlertId(UtilJson.read("$.id", matchAsJson)); + exe.setRuleId(rule.getId()); + exe.setCommand(buildCommand(rule.getRuleCmd(), matchAsJson)); + exe.setExecutionStatus(RuleExecutionStatus.PENDING); + alertResponseRuleExecutionRepository.save(exe); + } + } + + // Then the alerts that match the filters but aren't from an agent, gets executed using the default agent if there is one + //----------------------------------------------------------------------------------------------------------------------- + if (StringUtils.hasText(rule.getDefaultAgent())) { + List conditionsNonAgents = new ArrayList<>(); + conditionsNonAgents.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_NOT_ONE_OF, agentNames)); + + if (StringUtils.hasText(rule.getRuleConditions())) + conditionsNonAgents.addAll(new Gson().fromJson(rule.getRuleConditions(), TypeToken.getParameterized(List.class, FilterType.class).getType())); + + if (StringUtils.hasText(rule.getExcludedAgents())) + conditionsNonAgents.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_NOT_ONE_OF, List.of(rule.getExcludedAgents().split(",")))); + + filter = buildFilters(conditionsNonAgents); + matches = UtilJson.read("$[?]", alertJsonArray, filter); + + if (!CollectionUtils.isEmpty(matches)) { + + for (Object match : matches) { + String matchAsJson = new Gson().toJson(match); + + UtmAlertResponseRuleExecution exe = new UtmAlertResponseRuleExecution(); + exe.setAgent(rule.getDefaultAgent()); + exe.setAlertId(UtilJson.read("$.id", matchAsJson)); + exe.setRuleId(rule.getId()); + exe.setCommand(buildCommand(rule.getRuleCmd(), matchAsJson)); + exe.setExecutionStatus(RuleExecutionStatus.PENDING); + alertResponseRuleExecutionRepository.save(exe); + } + } } } } catch (Exception e) { diff --git a/backend/src/main/java/com/park/utmstack/service/dto/UtmAlertResponseRuleDTO.java b/backend/src/main/java/com/park/utmstack/service/dto/UtmAlertResponseRuleDTO.java index 037afd98c..a8b369a16 100644 --- a/backend/src/main/java/com/park/utmstack/service/dto/UtmAlertResponseRuleDTO.java +++ b/backend/src/main/java/com/park/utmstack/service/dto/UtmAlertResponseRuleDTO.java @@ -31,6 +31,8 @@ public class UtmAlertResponseRuleDTO { private Boolean active; @NotBlank private String agentPlatform; + @Size(max = 500) + private String defaultAgent; private List excludedAgents = new ArrayList<>(); @JsonProperty(access = JsonProperty.Access.READ_ONLY) private String createdBy; @@ -52,6 +54,7 @@ public UtmAlertResponseRuleDTO(UtmAlertResponseRule rule) { this.command = rule.getRuleCmd(); this.active = rule.getRuleActive(); this.agentPlatform = rule.getAgentPlatform(); + this.defaultAgent = rule.getDefaultAgent(); if (StringUtils.hasText(rule.getExcludedAgents())) this.excludedAgents.addAll(Arrays.asList(rule.getExcludedAgents().split(","))); this.createdBy = rule.getCreatedBy(); @@ -112,6 +115,14 @@ public String getAgentPlatform() { return agentPlatform; } + public String getDefaultAgent() { + return defaultAgent; + } + + public void setDefaultAgent(String defaultAgent) { + this.defaultAgent = defaultAgent; + } + public void setAgentPlatform(String agentPlatform) { this.agentPlatform = agentPlatform; } diff --git a/backend/src/main/resources/config/liquibase/changelog/20240131001_add_default_agent_utm_alert_response_rule.xml b/backend/src/main/resources/config/liquibase/changelog/20240131001_add_default_agent_utm_alert_response_rule.xml new file mode 100644 index 000000000..994b0a742 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20240131001_add_default_agent_utm_alert_response_rule.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 6cb2c778f..6823824e7 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -32,4 +32,6 @@ + + From 9c50817458035fd3fbbac0fc3b3c8ee78af8f9c4 Mon Sep 17 00:00:00 2001 From: "Freddy R. Laffita Almaguer" Date: Thu, 1 Feb 2024 16:59:30 +0200 Subject: [PATCH 2/2] Reformat code to implement better solution. --- .../UtmAlertResponseRuleService.java | 97 +++++++++---------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java index c199b88b2..9038a4068 100644 --- a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java +++ b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java @@ -140,75 +140,74 @@ public void evaluateRules(List alerts) { String alertJsonArray = new Gson().toJson(alerts); for (UtmAlertResponseRule rule : rules) { - List conditions = new ArrayList<>(); List agentNames = networkScanRepository.findAgentNamesByPlatform(rule.getAgentPlatform()); - if (!CollectionUtils.isEmpty(agentNames)) + if (CollectionUtils.isEmpty(agentNames)) continue; - // Conditions for matching agents (these are the alerts made from logs coming from an agent) + // Matching agents (these are the alerts made from logs coming from an agent) //------------------------------------------------------------------------------------------ - conditions.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_ONE_OF, agentNames)); - - if (StringUtils.hasText(rule.getRuleConditions())) - conditions.addAll(new Gson().fromJson(rule.getRuleConditions(), TypeToken.getParameterized(List.class, FilterType.class).getType())); - - if (StringUtils.hasText(rule.getExcludedAgents())) - conditions.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_NOT_ONE_OF, List.of(rule.getExcludedAgents().split(",")))); - - Filter filter = buildFilters(conditions); - List matches = UtilJson.read("$[?]", alertJsonArray, filter); - - if (!CollectionUtils.isEmpty(matches)) { - - for (Object match : matches) { - String matchAsJson = new Gson().toJson(match); - - UtmAlertResponseRuleExecution exe = new UtmAlertResponseRuleExecution(); - exe.setAgent(UtilJson.read("$.dataSource", matchAsJson)); - exe.setAlertId(UtilJson.read("$.id", matchAsJson)); - exe.setRuleId(rule.getId()); - exe.setCommand(buildCommand(rule.getRuleCmd(), matchAsJson)); - exe.setExecutionStatus(RuleExecutionStatus.PENDING); - alertResponseRuleExecutionRepository.save(exe); - } - } + createResponseRuleExecution(rule,alertJsonArray,agentNames,true); // Then the alerts that match the filters but aren't from an agent, gets executed using the default agent if there is one //----------------------------------------------------------------------------------------------------------------------- if (StringUtils.hasText(rule.getDefaultAgent())) { - List conditionsNonAgents = new ArrayList<>(); - conditionsNonAgents.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_NOT_ONE_OF, agentNames)); + createResponseRuleExecution(rule,alertJsonArray,agentNames,false); + } + } + } catch (Exception e) { + String msg = ctx + ": " + e.getLocalizedMessage(); + log.error(msg); + eventService.createEvent(msg, ApplicationEventType.ERROR); + } + } - if (StringUtils.hasText(rule.getRuleConditions())) - conditionsNonAgents.addAll(new Gson().fromJson(rule.getRuleConditions(), TypeToken.getParameterized(List.class, FilterType.class).getType())); + private void createResponseRuleExecution (UtmAlertResponseRule rule, String alertJsonArray, List agentNames, boolean isAgent) throws Exception { + final String ctx = CLASSNAME + ".createResponseRuleExecution"; + List conditions = new ArrayList<>(); + try { + // Common conditions + if (StringUtils.hasText(rule.getRuleConditions())) + conditions.addAll(new Gson().fromJson(rule.getRuleConditions(), TypeToken.getParameterized(List.class, FilterType.class).getType())); - if (StringUtils.hasText(rule.getExcludedAgents())) - conditionsNonAgents.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_NOT_ONE_OF, List.of(rule.getExcludedAgents().split(",")))); + if (StringUtils.hasText(rule.getExcludedAgents())) + conditions.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_NOT_ONE_OF, List.of(rule.getExcludedAgents().split(",")))); - filter = buildFilters(conditionsNonAgents); - matches = UtilJson.read("$[?]", alertJsonArray, filter); + // Specific condition for agent and non agents + if (isAgent) { + conditions.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_ONE_OF, agentNames)); + } else { + conditions.add(new FilterType(Constants.alertDataSourceKeyword, OperatorType.IS_NOT_ONE_OF, agentNames)); + } - if (!CollectionUtils.isEmpty(matches)) { + // Processing the alerts and generating the rule executions + Filter filter = buildFilters(conditions); + List matches = UtilJson.read("$[?]", alertJsonArray, filter); - for (Object match : matches) { - String matchAsJson = new Gson().toJson(match); + if (!CollectionUtils.isEmpty(matches)) { - UtmAlertResponseRuleExecution exe = new UtmAlertResponseRuleExecution(); - exe.setAgent(rule.getDefaultAgent()); - exe.setAlertId(UtilJson.read("$.id", matchAsJson)); - exe.setRuleId(rule.getId()); - exe.setCommand(buildCommand(rule.getRuleCmd(), matchAsJson)); - exe.setExecutionStatus(RuleExecutionStatus.PENDING); - alertResponseRuleExecutionRepository.save(exe); - } + for (Object match : matches) { + String matchAsJson = new Gson().toJson(match); + + UtmAlertResponseRuleExecution exe = new UtmAlertResponseRuleExecution(); + // Execution agent takes the rule's default agent if the alert was generated by logs from non agent datasource + if (isAgent) { + exe.setAgent(UtilJson.read("$.dataSource", matchAsJson)); + } else { + exe.setAgent(rule.getDefaultAgent()); } + + exe.setAlertId(UtilJson.read("$.id", matchAsJson)); + exe.setRuleId(rule.getId()); + exe.setCommand(buildCommand(rule.getRuleCmd(), matchAsJson)); + exe.setExecutionStatus(RuleExecutionStatus.PENDING); + alertResponseRuleExecutionRepository.save(exe); } } + } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); - log.error(msg); - eventService.createEvent(msg, ApplicationEventType.ERROR); + throw new Exception(msg); } }