From 08b36154e662e917585c4827e014f310d97385c7 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 12 Nov 2025 14:20:35 -0600 Subject: [PATCH 1/2] feat: implement service to automatically assign asset groups to alerts --- .../aop/logging/impl/AlertLoggingAspect.java | 36 ++++----- .../domain/reports/types/IncidentType.java | 8 +- .../alert/{AlertType.java => UtmAlert.java} | 6 +- .../UtmNetworkScanRepository.java | 6 ++ .../park/utmstack/service/MailService.java | 8 +- .../utmstack/service/UtmAlertService.java | 4 +- .../service/UtmAlertTagRuleService.java | 80 +++++++++++++++---- .../service/UtmDataInputStatusService.java | 4 +- .../UtmAlertResponseRuleService.java | 4 +- .../elasticsearch/ElasticsearchService.java | 6 +- .../service/impl/UtmAlertServiceImpl.java | 18 ++--- .../service/incident/UtmIncidentService.java | 4 +- .../network_scan/AlertAssetGroupService.java | 53 ++++++++++++ .../service/reports/CustomReportService.java | 12 +-- .../customs/threatActivityForAlerts.html | 2 +- 15 files changed, 181 insertions(+), 70 deletions(-) rename backend/src/main/java/com/park/utmstack/domain/shared_types/alert/{AlertType.java => UtmAlert.java} (97%) create mode 100644 backend/src/main/java/com/park/utmstack/service/network_scan/AlertAssetGroupService.java diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/impl/AlertLoggingAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/AlertLoggingAspect.java index 2c2103686..405710b18 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/impl/AlertLoggingAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/AlertLoggingAspect.java @@ -5,7 +5,7 @@ import com.park.utmstack.domain.chart_builder.types.query.FilterType; import com.park.utmstack.domain.chart_builder.types.query.OperatorType; import com.park.utmstack.domain.index_pattern.enums.SystemIndexPattern; -import com.park.utmstack.domain.shared_types.alert.AlertType; +import com.park.utmstack.domain.shared_types.alert.UtmAlert; import com.park.utmstack.security.SecurityUtils; import com.park.utmstack.service.UtmAlertLogService; import com.park.utmstack.service.elasticsearch.ElasticsearchService; @@ -87,14 +87,14 @@ public Object logManualAlertStatusChange(ProceedingJoinPoint joinPoint) throws T Integer status = (Integer) args[1]; String statusObservation = (String) args[2]; - List alerts = getAlerts(alertIds); + List alerts = getAlerts(alertIds); joinPoint.proceed(); if (CollectionUtils.isEmpty(alerts)) return null; - for (AlertType alert : alerts) { + for (UtmAlert alert : alerts) { UtmAlertLog alertLog = new UtmAlertLog(); alertLog.setAlertId(alert.getId()); alertLog.setLogUser(SecurityUtils.getCurrentUserLogin().orElse("system")); @@ -146,19 +146,19 @@ public Object logAutomaticAlertStatusChange(ProceedingJoinPoint joinPoint) throw .size(Constants.LOG_ANALYZER_TOTAL_RESULTS) .query(query)); - HitsMetadata response = elasticsearchService.search(sr, AlertType.class).hits(); + HitsMetadata response = elasticsearchService.search(sr, UtmAlert.class).hits(); joinPoint.proceed(); if (response.total().value() <= 0) return null; - List alerts = response.hits().stream().map(Hit::source).collect(Collectors.toList()); + List alerts = response.hits().stream().map(Hit::source).collect(Collectors.toList()); if (CollectionUtils.isEmpty(alerts)) return null; - for (AlertType alert : alerts) { + for (UtmAlert alert : alerts) { UtmAlertLog alertLog = new UtmAlertLog(); alertLog.setAlertId(alert.getId()); alertLog.setLogUser("system"); @@ -197,7 +197,7 @@ public Object logManualAlertTagsChange(ProceedingJoinPoint joinPoint) throws Thr List alertIds = (List) args[0]; List tags = (List) args[1]; - List alerts = getAlerts(alertIds); + List alerts = getAlerts(alertIds); joinPoint.proceed(); @@ -206,7 +206,7 @@ public Object logManualAlertTagsChange(ProceedingJoinPoint joinPoint) throws Thr String user = SecurityUtils.getCurrentUserLogin().orElse("system"); - for (AlertType alert : alerts) { + for (UtmAlert alert : alerts) { UtmAlertLog alertLog = new UtmAlertLog(); alertLog.setAlertId(alert.getId()); alertLog.setLogUser(user); @@ -247,21 +247,21 @@ public Object logAutomaticAlertTagsChange(ProceedingJoinPoint joinPoint) throws SearchRequest request = SearchRequest.of(s -> s.query(query) .size(Constants.LOG_ANALYZER_TOTAL_RESULTS).index(indexPattern)); - HitsMetadata hits = elasticsearchService.search(request, AlertType.class).hits(); + HitsMetadata hits = elasticsearchService.search(request, UtmAlert.class).hits(); joinPoint.proceed(); if (hits.total().value() <= 0) return null; - List alerts = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); + List alerts = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); if (CollectionUtils.isEmpty(alerts)) return null; String user = SecurityUtils.getCurrentUserLogin().orElse("system"); - for (AlertType alert : alerts) { + for (UtmAlert alert : alerts) { UtmAlertLog alertLog = new UtmAlertLog(); alertLog.setAlertId(alert.getId()); alertLog.setLogUser(user); @@ -294,7 +294,7 @@ public Object logManualAlertNotesChange(ProceedingJoinPoint joinPoint) throws Th String alertId = (String) args[0]; String notes = (String) args[1]; - List alerts = getAlerts(Collections.singletonList(alertId)); + List alerts = getAlerts(Collections.singletonList(alertId)); joinPoint.proceed(); @@ -303,7 +303,7 @@ public Object logManualAlertNotesChange(ProceedingJoinPoint joinPoint) throws Th String user = SecurityUtils.getCurrentUserLogin().orElse("system"); - for (AlertType alert : alerts) { + for (UtmAlert alert : alerts) { UtmAlertLog alertLog = new UtmAlertLog(); alertLog.setAlertId(alert.getId()); alertLog.setLogUser(user); @@ -347,19 +347,19 @@ public Object logConvertToIncident(ProceedingJoinPoint joinPoint) throws Throwab SearchRequest request = SearchRequest.of(s -> s.size(Constants.LOG_ANALYZER_TOTAL_RESULTS) .query(query).index(indexPattern)); - HitsMetadata hits = elasticsearchService.search(request, AlertType.class).hits(); + HitsMetadata hits = elasticsearchService.search(request, UtmAlert.class).hits(); joinPoint.proceed(); if (hits.total().value() <= 0) return null; - List alerts = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); + List alerts = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); if (CollectionUtils.isEmpty(alerts)) return null; - for (AlertType alert : alerts) { + for (UtmAlert alert : alerts) { UtmAlertLog alertLog = new UtmAlertLog(); alertLog.setAlertId(alert.getId()); alertLog.setLogUser(incidentCreatedBy); @@ -393,14 +393,14 @@ public Object logConvertToIncident(ProceedingJoinPoint joinPoint) throws Throwab * @return * @throws Exception */ - private List getAlerts(List ids) throws Exception { + private List getAlerts(List ids) throws Exception { final String ctx = CLASS_NAME + ".getAlerts"; try { List filters = new ArrayList<>(); filters.add(new FilterType(Constants.alertIdKeyword, OperatorType.IS_ONE_OF_TERMS, ids)); SearchRequest request = SearchRequest.of(s -> s.query(SearchUtil.toQuery(filters)) .index(Constants.SYS_INDEX_PATTERN.get(SystemIndexPattern.ALERTS))); - HitsMetadata hits = elasticsearchService.search(request, AlertType.class).hits(); + HitsMetadata hits = elasticsearchService.search(request, UtmAlert.class).hits(); if (hits.total().value() <= 0) return Collections.emptyList(); return hits.hits().stream().map(Hit::source).collect(Collectors.toList()); diff --git a/backend/src/main/java/com/park/utmstack/domain/reports/types/IncidentType.java b/backend/src/main/java/com/park/utmstack/domain/reports/types/IncidentType.java index 5519d1999..f7ae469b8 100644 --- a/backend/src/main/java/com/park/utmstack/domain/reports/types/IncidentType.java +++ b/backend/src/main/java/com/park/utmstack/domain/reports/types/IncidentType.java @@ -1,20 +1,20 @@ package com.park.utmstack.domain.reports.types; import com.park.utmstack.domain.incident_response.UtmIncidentJob; -import com.park.utmstack.domain.shared_types.alert.AlertType; +import com.park.utmstack.domain.shared_types.alert.UtmAlert; import java.util.List; public class IncidentType { - private AlertType incident; + private UtmAlert incident; private List srcResponses; private List destResponses; - public AlertType getIncident() { + public UtmAlert getIncident() { return incident; } - public void setIncident(AlertType incident) { + public void setIncident(UtmAlert incident) { this.incident = incident; } diff --git a/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/AlertType.java b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/UtmAlert.java similarity index 97% rename from backend/src/main/java/com/park/utmstack/domain/shared_types/alert/AlertType.java rename to backend/src/main/java/com/park/utmstack/domain/shared_types/alert/UtmAlert.java index 63c4de951..362b3f2bb 100644 --- a/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/AlertType.java +++ b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/UtmAlert.java @@ -18,7 +18,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @Getter @Setter -public class AlertType { +public class UtmAlert { @JsonProperty("@timestamp") private String timestamp; @@ -106,6 +106,10 @@ public class AlertType { @JsonProperty("logs") private List logs; + private String assetGroupName; + + private Long assetGroupId; + public Instant getTimestampAsInstant() { if (StringUtils.hasText(timestamp)) return Instant.parse(timestamp); diff --git a/backend/src/main/java/com/park/utmstack/repository/network_scan/UtmNetworkScanRepository.java b/backend/src/main/java/com/park/utmstack/repository/network_scan/UtmNetworkScanRepository.java index c35e80e12..8a7168dc0 100644 --- a/backend/src/main/java/com/park/utmstack/repository/network_scan/UtmNetworkScanRepository.java +++ b/backend/src/main/java/com/park/utmstack/repository/network_scan/UtmNetworkScanRepository.java @@ -123,4 +123,10 @@ void updateGroup(@Param("assetIds") List assetIds, @Query(nativeQuery = true, value = "select n.asset_name from utm_network_scan n where n.asset_name is not null and n.is_agent is true and n.asset_alive is true and n.asset_status <> 'MISSING' and n.asset_os_platform = :platform") List findAgentNamesByPlatform(@Param("platform") String platform); + + @Query("SELECT ns.assetName, ns.groupId, ag.groupName " + + "FROM UtmNetworkScan ns " + + "JOIN UtmAssetGroup ag ON ns.groupId = ag.id " + + "WHERE ns.groupId IS NOT NULL") + List findAllAssetGroupMappings(); } diff --git a/backend/src/main/java/com/park/utmstack/service/MailService.java b/backend/src/main/java/com/park/utmstack/service/MailService.java index e273f4675..ba07eaf0f 100644 --- a/backend/src/main/java/com/park/utmstack/service/MailService.java +++ b/backend/src/main/java/com/park/utmstack/service/MailService.java @@ -5,7 +5,7 @@ import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.domain.incident.UtmIncident; import com.park.utmstack.domain.mail_sender.MailConfig; -import com.park.utmstack.domain.shared_types.alert.AlertType; +import com.park.utmstack.domain.shared_types.alert.UtmAlert; import com.park.utmstack.domain.shared_types.LogType; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.mail_sender.BaseMailSender; @@ -250,7 +250,7 @@ public void sendPasswordResetMail(User user) { } @Async - public void sendAlertEmail(List emailsTo, AlertType alert, List relatedLogs) { + public void sendAlertEmail(List emailsTo, UtmAlert alert, List relatedLogs) { final String ctx = CLASS_NAME + ".sendAlertEmail"; try { JavaMailSender javaMailSender = getJavaMailSender(); @@ -289,7 +289,7 @@ public void sendAlertEmail(List emailsTo, AlertType alert, List } @Async - public void sendIncidentEmail(List emailsTo, List alerts, UtmIncident incident) { + public void sendIncidentEmail(List emailsTo, List alerts, UtmIncident incident) { final String ctx = CLASS_NAME + ".sendIncidentEmail"; try { JavaMailSender javaMailSender = getJavaMailSender(); @@ -332,7 +332,7 @@ public void sendIncidentEmail(List emailsTo, List alerts, Utm * @return ByteArrayResource object with attachment to alert mail * @throws Exception In case of any error */ - private ByteArrayResource buildAlertEmailAttachment(Context context, AlertType alert, + private ByteArrayResource buildAlertEmailAttachment(Context context, UtmAlert alert, List relatedLogs) throws Exception { final String ctx = CLASS_NAME + ".buildAlertEmailAttachment"; try { diff --git a/backend/src/main/java/com/park/utmstack/service/UtmAlertService.java b/backend/src/main/java/com/park/utmstack/service/UtmAlertService.java index f88d69a88..51caa3164 100644 --- a/backend/src/main/java/com/park/utmstack/service/UtmAlertService.java +++ b/backend/src/main/java/com/park/utmstack/service/UtmAlertService.java @@ -1,7 +1,7 @@ package com.park.utmstack.service; import com.park.utmstack.domain.chart_builder.types.query.FilterType; -import com.park.utmstack.domain.shared_types.alert.AlertType; +import com.park.utmstack.domain.shared_types.alert.UtmAlert; import com.park.utmstack.domain.shared_types.static_dashboard.CardType; import com.park.utmstack.util.exceptions.DashboardOverviewException; import com.park.utmstack.util.exceptions.ElasticsearchIndexDocumentUpdateException; @@ -26,7 +26,7 @@ void updateStatus(List alertIds, int status, String statusObservation) t void convertToIncident(List eventIds, String incidentName, Integer incidentId, String incidentSource) throws ElasticsearchIndexDocumentUpdateException; - List getAlertsByIds(List ids) throws UtmElasticsearchException; + List getAlertsByIds(List ids) throws UtmElasticsearchException; void updateStatusAndTag(List alertIds, int status, String statusObservation) throws UtmElasticsearchException, IOException, ElasticsearchIndexDocumentUpdateException; diff --git a/backend/src/main/java/com/park/utmstack/service/UtmAlertTagRuleService.java b/backend/src/main/java/com/park/utmstack/service/UtmAlertTagRuleService.java index dba239492..ec228bc30 100644 --- a/backend/src/main/java/com/park/utmstack/service/UtmAlertTagRuleService.java +++ b/backend/src/main/java/com/park/utmstack/service/UtmAlertTagRuleService.java @@ -12,10 +12,12 @@ import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.elasticsearch.ElasticsearchService; import com.park.utmstack.service.elasticsearch.SearchUtil; +import com.park.utmstack.service.network_scan.AlertAssetGroupService; import com.park.utmstack.util.AlertUtil; import com.park.utmstack.util.enums.AlertStatus; import com.park.utmstack.util.events.RulesEvaluationEndEvent; import com.park.utmstack.web.rest.vm.AlertTagRuleFilterVM; +import lombok.RequiredArgsConstructor; import org.hibernate.jpa.TypedParameterValue; import org.hibernate.type.BooleanType; import org.hibernate.type.LongType; @@ -38,6 +40,7 @@ import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; @@ -46,6 +49,7 @@ */ @Service @Transactional +@RequiredArgsConstructor public class UtmAlertTagRuleService { private final Logger log = LoggerFactory.getLogger(UtmAlertTagRuleService.class); @@ -58,22 +62,8 @@ public class UtmAlertTagRuleService { private final AlertPointcut alertPointcut; private final UtmAlertTagService alertTagService; private final ElasticsearchService elasticsearchService; + private final AlertAssetGroupService alertAssetGroupService; - public UtmAlertTagRuleService(UtmAlertTagRuleRepository alertTagRuleRepository, - AlertUtil alertUtil, - ApplicationEventPublisher publisher, - ApplicationEventService eventService, - AlertPointcut alertPointcut, - UtmAlertTagService alertTagService, - ElasticsearchService elasticsearchService) { - this.alertTagRuleRepository = alertTagRuleRepository; - this.alertUtil = alertUtil; - this.publisher = publisher; - this.eventService = eventService; - this.alertPointcut = alertPointcut; - this.alertTagService = alertTagService; - this.elasticsearchService = elasticsearchService; - } /** * Save a utmTagRule. @@ -148,6 +138,9 @@ public void automaticReview() { if (alertUtil.countAllAlertsByStatus(AlertStatus.AUTOMATIC_REVIEW.getCode()) == 0) return; + // Assigning asset groups to alerts in automatic review + this.assignAssetGroupsToReviewAlerts(); + // Getting all registered rules List tagRules = alertTagRuleRepository.findAll(); @@ -247,4 +240,61 @@ private void applyTagRule(List rules, Instant rulesEvaluationSt } } } + + + private void assignAssetGroupsToReviewAlerts() { + final String ctx = CLASSNAME + ".assignAssetGroupsToReviewAlerts"; + try { + + Map> assetGroups = + alertAssetGroupService.getAssetGroupsMapForAlerts(); + + if (assetGroups.isEmpty()) { + log.debug("{}: No asset-group mappings found", ctx); + return; + } + + StringBuilder scriptBuilder = new StringBuilder(); + scriptBuilder.append("if (ctx._source.containsKey('dataSource') && ctx._source.dataSource != null) {\n"); + + for (Map.Entry> entry : assetGroups.entrySet()) { + String assetName = entry.getKey(); + Long groupId = (Long) entry.getValue().get("id"); + String groupName = (String) entry.getValue().get("name"); + + scriptBuilder.append(String.format( + """ + if (ctx._source.dataSource == '%s') { + ctx._source.assetGroupId = %dL; + ctx._source.assetGroupName = '%s'; + } + """, + assetName.replace("'", "\\'"), // Escapar comillas simples + groupId, + groupName.replace("'", "\\'") + )); + } + + scriptBuilder.append("}"); + String script = scriptBuilder.toString(); + + + List filters = new ArrayList<>(); + filters.add(new FilterType(Constants.alertStatus, OperatorType.IS, + AlertStatus.AUTOMATIC_REVIEW.getCode())); + filters.add(new FilterType("dataSource", OperatorType.IS_NOT, null)); + + Query query = SearchUtil.toQuery(filters); + String indexPattern = Constants.SYS_INDEX_PATTERN.get(SystemIndexPattern.ALERTS); + + elasticsearchService.updateByQuery(query, indexPattern, script); + + log.info("{}: Asset groups assigned to {} alerts", ctx, assetGroups.size()); + + } catch (Exception e) { + String msg = ctx + ": " + e.getMessage(); + eventService.createEvent(msg, ApplicationEventType.ERROR); + log.error(msg, e); + } + } } diff --git a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java index 26d3f9bef..f4da74953 100644 --- a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java +++ b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java @@ -10,7 +10,7 @@ import com.park.utmstack.domain.network_scan.UtmNetworkScan; import com.park.utmstack.domain.network_scan.enums.AssetStatus; import com.park.utmstack.domain.network_scan.enums.UpdateLevel; -import com.park.utmstack.domain.shared_types.alert.AlertType; +import com.park.utmstack.domain.shared_types.alert.UtmAlert; import com.park.utmstack.repository.UtmDataInputStatusRepository; import com.park.utmstack.repository.correlation.config.UtmDataTypesRepository; import com.park.utmstack.repository.network_scan.UtmNetworkScanRepository; @@ -326,7 +326,7 @@ public void checkDatasourceDown() { * Create the alert object for datasource down * * @param input: Datasource information - * @return A ${@link AlertType} to index + * @return A ${@link UtmAlert} to index */ private Map createAlertForDatasourceDown(UtmDataInputStatus input) { List cloudTypes = Arrays.asList("aws", "o365", "office365", "azure", "gcp", "google", "nids", "netflow"); 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 607c26240..07b3cdd36 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 @@ -15,7 +15,7 @@ import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.domain.chart_builder.types.query.FilterType; import com.park.utmstack.domain.chart_builder.types.query.OperatorType; -import com.park.utmstack.domain.shared_types.alert.AlertType; +import com.park.utmstack.domain.shared_types.alert.UtmAlert; import com.park.utmstack.repository.alert_response_rule.UtmAlertResponseActionTemplateRepository; import com.park.utmstack.repository.alert_response_rule.UtmAlertResponseRuleExecutionRepository; import com.park.utmstack.repository.alert_response_rule.UtmAlertResponseRuleHistoryRepository; @@ -120,7 +120,7 @@ public Map> resolveFilterValues() { } @Async - public void evaluateRules(List alerts) { + public void evaluateRules(List alerts) { final String ctx = CLASSNAME + ".evaluateRules"; try { if (CollectionUtils.isEmpty(alerts)) diff --git a/backend/src/main/java/com/park/utmstack/service/elasticsearch/ElasticsearchService.java b/backend/src/main/java/com/park/utmstack/service/elasticsearch/ElasticsearchService.java index 3b7296ca7..b7d078842 100644 --- a/backend/src/main/java/com/park/utmstack/service/elasticsearch/ElasticsearchService.java +++ b/backend/src/main/java/com/park/utmstack/service/elasticsearch/ElasticsearchService.java @@ -20,14 +20,12 @@ import com.utmstack.opensearch_connector.types.ElasticCluster; import com.utmstack.opensearch_connector.types.IndexSort; import org.elasticsearch.index.query.BoolQueryBuilder; +import org.opensearch.client.json.JsonData; import org.opensearch.client.opensearch._types.SortOrder; import org.opensearch.client.opensearch._types.query_dsl.Query; import org.opensearch.client.opensearch.cat.CountResponse; import org.opensearch.client.opensearch.cat.indices.IndicesRecord; -import org.opensearch.client.opensearch.core.CountRequest; -import org.opensearch.client.opensearch.core.IndexResponse; -import org.opensearch.client.opensearch.core.SearchRequest; -import org.opensearch.client.opensearch.core.SearchResponse; +import org.opensearch.client.opensearch.core.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.support.PagedListHolder; diff --git a/backend/src/main/java/com/park/utmstack/service/impl/UtmAlertServiceImpl.java b/backend/src/main/java/com/park/utmstack/service/impl/UtmAlertServiceImpl.java index 7190039d2..a7e6b6da4 100644 --- a/backend/src/main/java/com/park/utmstack/service/impl/UtmAlertServiceImpl.java +++ b/backend/src/main/java/com/park/utmstack/service/impl/UtmAlertServiceImpl.java @@ -9,7 +9,7 @@ import com.park.utmstack.domain.chart_builder.types.query.OperatorType; import com.park.utmstack.domain.index_pattern.enums.SystemIndexPattern; import com.park.utmstack.domain.shared_types.ApplicationLayer; -import com.park.utmstack.domain.shared_types.alert.AlertType; +import com.park.utmstack.domain.shared_types.alert.UtmAlert; import com.park.utmstack.domain.shared_types.LogType; import com.park.utmstack.domain.shared_types.static_dashboard.CardType; import com.park.utmstack.repository.UtmAlertLastRepository; @@ -91,12 +91,12 @@ public void checkForNewAlerts() { SearchRequest build = srb.build(); - HitsMetadata hitsMetadata = elasticsearchService.search(build, AlertType.class).hits(); + HitsMetadata hitsMetadata = elasticsearchService.search(build, UtmAlert.class).hits(); - if (hitsMetadata.total().value() <= 0) + if (hitsMetadata.total() != null && hitsMetadata.total().value() <= 0) return; - List alerts = hitsMetadata.hits().stream().map(Hit::source).collect(Collectors.toList()); + List alerts = hitsMetadata.hits().stream().map(Hit::source).collect(Collectors.toList()); if (CollectionUtils.isEmpty(alerts)) return; @@ -104,7 +104,7 @@ public void checkForNewAlerts() { initialDate.setLastAlertTimestamp(alerts.get(alerts.size() - 1).getTimestampAsInstant()); lastAlertRepository.save(initialDate); - for (AlertType alert : alerts) { + for (UtmAlert alert : alerts) { List relatedLogs; try { relatedLogs = getRelatedAlerts(alert.getLogs()); @@ -129,7 +129,7 @@ public void checkForNewAlerts() { alertResponseRuleService.evaluateRules(alerts); if (moduleService.isModuleActive(ModuleName.SOC_AI)) - socAIService.requestSocAiProcess(alerts.stream().map(AlertType::getId).collect(Collectors.toList())); + socAIService.requestSocAiProcess(alerts.stream().map(UtmAlert::getId).collect(Collectors.toList())); } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); @@ -362,7 +362,7 @@ public void convertToIncident(List alertIds, String incidentName, Intege } } - public List getAlertsByIds(List alertIds) throws UtmElasticsearchException { + public List getAlertsByIds(List alertIds) throws UtmElasticsearchException { final String ctx = CLASS_NAME + ".getAlertsByIds"; try { if (CollectionUtils.isEmpty(alertIds)) @@ -376,12 +376,12 @@ public List getAlertsByIds(List alertIds) throws UtmElasticse .index(Constants.SYS_INDEX_PATTERN.get(SystemIndexPattern.ALERTS)) .size(Constants.LOG_ANALYZER_TOTAL_RESULTS)); - HitsMetadata hits = elasticsearchService.search(request, AlertType.class).hits(); + HitsMetadata hits = elasticsearchService.search(request, UtmAlert.class).hits(); if (hits.total().value() <= 0) return new ArrayList<>(); - List alerts = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); + List alerts = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); if (CollectionUtils.isEmpty(alerts)) return new ArrayList<>(); diff --git a/backend/src/main/java/com/park/utmstack/service/incident/UtmIncidentService.java b/backend/src/main/java/com/park/utmstack/service/incident/UtmIncidentService.java index cdc7154db..642799d8d 100644 --- a/backend/src/main/java/com/park/utmstack/service/incident/UtmIncidentService.java +++ b/backend/src/main/java/com/park/utmstack/service/incident/UtmIncidentService.java @@ -6,7 +6,7 @@ import com.park.utmstack.domain.incident.UtmIncidentAlert; import com.park.utmstack.domain.incident.enums.IncidentHistoryActionEnum; import com.park.utmstack.domain.incident.enums.IncidentStatusEnum; -import com.park.utmstack.domain.shared_types.alert.AlertType; +import com.park.utmstack.domain.shared_types.alert.UtmAlert; import com.park.utmstack.repository.incident.UtmIncidentRepository; import com.park.utmstack.service.MailService; import com.park.utmstack.service.UserService; @@ -275,7 +275,7 @@ public void delete(Long id) { private void sendIncidentsEmail(List alertIds, UtmIncident utmIncident) { final String ctx = CLASSNAME + ".sendIncidentsEmail"; try { - List alerts = utmAlertService.getAlertsByIds(alertIds); + List alerts = utmAlertService.getAlertsByIds(alertIds); if (CollectionUtils.isEmpty(alerts)) return; diff --git a/backend/src/main/java/com/park/utmstack/service/network_scan/AlertAssetGroupService.java b/backend/src/main/java/com/park/utmstack/service/network_scan/AlertAssetGroupService.java new file mode 100644 index 000000000..da4784da0 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/service/network_scan/AlertAssetGroupService.java @@ -0,0 +1,53 @@ +package com.park.utmstack.service.network_scan; + +import com.park.utmstack.domain.network_scan.UtmAssetGroup; +import com.park.utmstack.repository.network_scan.UtmAssetGroupRepository; +import com.park.utmstack.repository.network_scan.UtmNetworkScanRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +@Slf4j +public class AlertAssetGroupService { + + private static final String CLASSNAME = "AlertAssetGroupService"; + + private final UtmNetworkScanRepository networkScanRepository; + + + @Transactional(readOnly = true) + public Map> getAssetGroupsMapForAlerts() { + final String ctx = CLASSNAME + ".getAssetGroupsMapForAlerts"; + + try { + List results = networkScanRepository.findAllAssetGroupMappings(); + + Map> assetGroupsMap = new HashMap<>(); + + for (Object[] row : results) { + String assetName = (String) row[0]; + Long groupId = (Long) row[1]; + String groupName = (String) row[2]; + + Map groupInfo = new HashMap<>(); + groupInfo.put("id", groupId); + groupInfo.put("name", groupName); + + assetGroupsMap.put(assetName, groupInfo); + } + + return assetGroupsMap; + + } catch (Exception e) { + log.error("{}: Error retrieving asset groups map: {}", ctx, e.getMessage()); + return new HashMap<>(); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/park/utmstack/service/reports/CustomReportService.java b/backend/src/main/java/com/park/utmstack/service/reports/CustomReportService.java index 044d96bfa..879c01da0 100644 --- a/backend/src/main/java/com/park/utmstack/service/reports/CustomReportService.java +++ b/backend/src/main/java/com/park/utmstack/service/reports/CustomReportService.java @@ -6,7 +6,7 @@ import com.park.utmstack.domain.index_pattern.enums.SystemIndexPattern; import com.park.utmstack.domain.network_scan.NetworkScanFilter; import com.park.utmstack.domain.reports.types.IncidentType; -import com.park.utmstack.domain.shared_types.alert.AlertType; +import com.park.utmstack.domain.shared_types.alert.UtmAlert; import com.park.utmstack.domain.shared_types.enums.ImageShortName; import com.park.utmstack.service.UtmImagesService; import com.park.utmstack.service.elasticsearch.ElasticsearchService; @@ -75,12 +75,12 @@ public Optional buildThreatActivityForAlerts(Instant from .query(SearchUtil.toQuery(filters)); SearchUtil.applyPaginationAndSort(srb, page, top); - HitsMetadata hits = elasticsearchService.search(srb.build(), AlertType.class).hits(); + HitsMetadata hits = elasticsearchService.search(srb.build(), UtmAlert.class).hits(); if (hits.total().value() <= 0) return Optional.empty(); - List alerts = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); + List alerts = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); Map vars = new HashMap<>(); vars.put("alerts", alerts); @@ -106,16 +106,16 @@ public Optional buildThreatActivityForIncidents(Instant f .query(SearchUtil.toQuery(filters)); SearchUtil.applyPaginationAndSort(srb, page, top); - HitsMetadata hits = elasticsearchService.search(srb.build(), AlertType.class).hits(); + HitsMetadata hits = elasticsearchService.search(srb.build(), UtmAlert.class).hits(); if (hits.total().value() <= 0) return Optional.empty(); - List incidentDocs = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); + List incidentDocs = hits.hits().stream().map(Hit::source).collect(Collectors.toList()); List incidents = new ArrayList<>(); - for (AlertType incident : incidentDocs) { + for (UtmAlert incident : incidentDocs) { IncidentType incidentType = new IncidentType(); incidentType.setIncident(incident); diff --git a/backend/src/main/resources/templates/reports/customs/threatActivityForAlerts.html b/backend/src/main/resources/templates/reports/customs/threatActivityForAlerts.html index 778a6d2df..30f3b573a 100644 --- a/backend/src/main/resources/templates/reports/customs/threatActivityForAlerts.html +++ b/backend/src/main/resources/templates/reports/customs/threatActivityForAlerts.html @@ -14,7 +14,7 @@

STACK

- +
From 98f3c0156370d00e47a504e00897897197dd195b Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 12 Nov 2025 15:30:56 -0600 Subject: [PATCH 2/2] feat: add asset group fields to alert constants and configuration Signed-off-by: Manuel Abascal --- .../app/shared/constants/alert/alert-field.constant.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/shared/constants/alert/alert-field.constant.ts b/frontend/src/app/shared/constants/alert/alert-field.constant.ts index 0d102a2d9..19e33b1ea 100644 --- a/frontend/src/app/shared/constants/alert/alert-field.constant.ts +++ b/frontend/src/app/shared/constants/alert/alert-field.constant.ts @@ -14,6 +14,8 @@ export const ALERT_SEVERITY_FIELD = 'severity'; export const ALERT_IMPACT_FIELD = 'impact'; export const ALERT_SEVERITY_FIELD_LABEL = 'severityLabel'; export const ALERT_TAGS_FIELD = 'tags'; +export const ALERT_ASSETS_GROUP_NAME_FIELD = 'assetGroupName'; +export const ALERT_ASSETS_GROUP_ID_FIELD = 'assetGroupId'; export const ALERT_NOTE_FIELD = 'notes'; export const ALERT_OBSERVATION_FIELD = 'statusObservation'; export const ALERT_TIMESTAMP_FIELD = '@timestamp'; @@ -438,6 +440,12 @@ export const ALERT_FILTERS_FIELDS: UtmFieldType[] = [ customStyle: 'text-blue-800', visible: true, }, + { + label: 'Datasource Group', + field: ALERT_ASSETS_GROUP_NAME_FIELD, + type: ElasticDataTypesEnum.STRING, + visible: true, + }, { label: 'ID', field: ALERT_CASE_ID_FIELD, @@ -605,7 +613,7 @@ export const ALERT_FILTERS_FIELDS: UtmFieldType[] = [ field: ALERT_TAGS_FIELD, type: ElasticDataTypesEnum.STRING, visible: true, - }, + } ]; export const EVENT_FIELDS: UtmFieldType[] = [