From 2eb71ee618105e5f4f6f53cfdf653e7b07b706e0 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 29 Jan 2026 12:01:48 -0600 Subject: [PATCH 001/105] feat(pfsense): update pfsense filter --- .../20260129002_update_filter_pfsense.xml | 372 ++++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 374 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260129002_update_filter_pfsense.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260129002_update_filter_pfsense.xml b/backend/src/main/resources/config/liquibase/changelog/20260129002_update_filter_pfsense.xml new file mode 100644 index 000000000..9316b44d6 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260129002_update_filter_pfsense.xml @@ -0,0 +1,372 @@ + + + + + + + ' + - fieldName: log.syslogVersion + pattern: '{{.integer}}' + - fieldName: log.deviceTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}{{.space}}{{.time}}{{.iso8601Timezone}}' + - fieldName: log.syslogHost + pattern: '{{.hostname}}' + - fieldName: log.msgAll + pattern: '{{.greedy}}' + source: raw + where: regexMatch("raw", "\\d{4}-\\d{2}-\\d{2}") + + # Parsing syslog format date (OPNsense/pfSense) + - grok: + patterns: + - fieldName: log.priority + pattern: '\<{{.integer}}\>' + - fieldName: log.deviceTime + pattern: '{{.monthName}}{{.space}}{{.monthDay}}{{.space}}{{.time}}{{.space}}' + - fieldName: log.syslogHost + pattern: '{{.hostname}}{{.space}}' + - fieldName: log.msgAll + pattern: '{{.greedy}}' + source: raw + where: regexMatch("raw", "<\\d+>[A-Z][a-z]{2}\\s+\\d{1,2}\\s+\\d{2}") + + #......................................................................# + # Removing unnecessary characters of the syslogHeader + #......................................................................# + - trim: + function: prefix + substring: "<" + fields: + - log.priority + - trim: + function: suffix + substring: ">" + fields: + - log.priority + + #......................................................................# + # Checking that the msgAll field exists + #......................................................................# + - grok: + patterns: + - fieldName: log.eventType + pattern: '{{.word}}' + - fieldName: log.pid + pattern: '(\[)?({{.integer}}?)(\])?(- -|:)' + - fieldName: log.csvMsg + pattern: '{{.greedy}}' + source: log.msgAll + + #......................................................................# + # Removing unnecessary characters + #......................................................................# + - trim: + function: prefix + substring: "[" + fields: + - log.pid + - trim: + function: suffix + substring: "]:" + fields: + - log.pid + + # ..........................................................................# + # Remove issues fileds + # ..........................................................................# + - delete: + fields: + - log.msgAll + + #......................................................................# + # Using csv to parse the message + #......................................................................# + - csv: + source: log.csvMsg + separator: "," + headers: + - log.ruleNumber + - log.subRuleNumber + - log.anchor + - log.tracker + - log.realInterface + - log.reason + - log.action + - log.direction + - log.ipVersion + - log.ipv4Tos + - log.ipv4Ecn + - log.ipv4Ttl + - log.ipv4Id + - log.ipv4Offset + - log.ipv4Flags + - log.ipv4ProtocolId + - log.proto + - log.ipLength + - log.srcIp + - log.dstIp + - log.srcPort + - log.dstPort + - log.dataLength + - log.tcpFlags + - log.sequenceNumber + - log.ackNumber + - log.tcpWindow + - log.urg + - log.tcpOptions + where: regexMatch("log.csvMsg", "(.+),(\\s)?(match|\\w+),(block|pass),(in|out),(4|6),(.+)(tcp|TCP|Tcp)") + + # .......................................................................# + - csv: + source: log.csvMsg + separator: "," + headers: + - log.ruleNumber + - log.subRuleNumber + - log.anchor + - log.tracker + - log.realInterface + - log.reason + - log.action + - log.direction + - log.ipVersion + - log.ipv4Tos + - log.ipv4Ecn + - log.ipv4Ttl + - log.ipv4Id + - log.ipv4Offset + - log.ipv4Flags + - log.ipv4ProtocolId + - log.proto + - log.ipLength + - log.srcIp + - log.dstIp + - log.srcPort + - log.dstPort + - log.dataLength + where: regexMatch("log.csvMsg", "(.+),(\\s)?(match|\\w+),(block|pass),(in|out),(4|6),(.+)(udp|UDP|Udp)") + + #......................................................................# + - csv: + source: log.csvMsg + separator: "," + headers: + - log.ruleNumber + - log.subRuleNumber + - log.anchor + - log.tracker + - log.realInterface + - log.reason + - log.action + - log.direction + - log.ipVersion + - log.ipv4Tos + - log.ipv4Ecn + - log.ipv4Ttl + - log.ipv4Id + - log.ipv4Offset + - log.ipv4Flags + - log.ipv4ProtocolId + - log.proto + - log.ipLength + - log.srcIp + - log.dstIp + - log.icmpType + - log.icmpData1 + - log.icmpData2 + - log.icmpData3 + - log.icmpData4 + - log.icmpData5 + where: regexMatch("log.csvMsg", "(.+),(\\s)?(match|\\w+),(block|pass),(in|out),(4|6),(.+)(icmp|ICMP|Icmp)") + + #......................................................................# + - csv: + source: log.csvMsg + separator: "," + headers: + - log.ruleNumber + - log.subRuleNumber + - log.anchor + - log.tracker + - log.realInterface + - log.reason + - log.action + - log.direction + - log.ipVersion + - log.ipv6Class + - log.ipv6FlowLabel + - log.ipv6HopLimit + - log.proto + - log.ipv6ProtocolId + - log.ipLength + - log.srcIp + - log.dstIp + - log.srcPort + - log.dstPort + - log.dataLength + - log.tcpFlags + - log.sequenceNumber + - log.ackNumber + - log.tcpWindow + - log.urg + - log.tcpOptions + where: regexMatch("log.csvMsg", "(.+),(\\s)?(match|\\w+),(block|pass),(in|out),(6|17),(.+)(tcp|TCP|Tcp)") + + #......................................................................# + - csv: + source: log.csvMsg + separator: "," + headers: + - log.ruleNumber + - log.subRuleNumber + - log.anchor + - log.tracker + - log.realInterface + - log.reason + - log.action + - log.direction + - log.ipVersion + - log.ipv6Class + - log.ipv6FlowLabel + - log.ipv6HopLimit + - log.proto + - log.ipv6ProtocolId + - log.ipLength + - log.srcIp + - log.dstIp + - log.srcPort + - log.dstPort + - log.dataLength + where: regexMatch("log.csvMsg", "(.+),(match|\\w+),(block|pass),(in|out),6,(.+)(udp|UDP|Udp)") + + #......................................................................# + - csv: + source: log.csvMsg + separator: "," + headers: + - log.ruleNumber + - log.subRuleNumber + - log.anchor + - log.tracker + - log.realInterface + - log.reason + - log.action + - log.direction + - log.ipVersion + - log.ipv6Class + - log.ipv6FlowLabel + - log.ipv6HopLimit + - log.proto + - log.ipv6ProtocolId + - log.ipLength + - log.srcIp + - log.dstIp + - log.icmpType + - log.icmpData1 + - log.icmpData2 + - log.icmpData3 + - log.icmpData4 + - log.icmpData5 + where: regexMatch("log.csvMsg", "(.+),(match|\\w+),(block|pass),(in|out),(6|17),(.+)(icmp|ICMP|Icmp)") + + # ................................................# + # Rename fields + # ................................................# + - rename: + from: + - log.action + to: action + + - rename: + from: + - log.proto + to: protocol + + - rename: + from: + - log.srcIp + to: origin.ip + + - rename: + from: + - log.dstIp + to: target.ip + + - rename: + from: + - log.srcPort + to: origin.port + + - rename: + from: + - log.dstPort + to: target.port + + # ................................................# + # Fileds conversions + # ................................................# + - cast: + fields: + - origin.port + - target.port + to: int + + # Adding geolocation to origin.ip + - dynamic: + plugin: com.utmstack.geolocation + params: + source: origin.ip + destination: origin.geolocation + where: exists("origin.ip") + + # Adding geolocation to target.ip + - dynamic: + plugin: com.utmstack.geolocation + params: + source: target.ip + destination: target.geolocation + where: exists("target.ip") + + # ..........................................................................# + # Remove issues fileds + # ..........................................................................# + - delete: + fields: + - log.csvMsg + +$$ + WHERE id=1522; + ]]> + + + \ No newline at end of file diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 71b5ddba4..13634f749 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -327,6 +327,8 @@ + + From fe30f0bacaf9db808ba9618c139a9e114d62ccbf Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 29 Jan 2026 12:12:15 -0600 Subject: [PATCH 002/105] fix: update query suggestions to remove redundant log prefix Signed-off-by: Manuel Abascal --- .../query-suggestions/query-suggestions.constants.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/shared/components/code-editor/components/query-suggestions/query-suggestions.constants.ts b/frontend/src/app/shared/components/code-editor/components/query-suggestions/query-suggestions.constants.ts index 289184c58..63efc2888 100644 --- a/frontend/src/app/shared/components/code-editor/components/query-suggestions/query-suggestions.constants.ts +++ b/frontend/src/app/shared/components/code-editor/components/query-suggestions/query-suggestions.constants.ts @@ -6,7 +6,7 @@ export interface QuerySuggestion { export const QUERY_SUGGESTIONS: QuerySuggestion[] = [ { label: 'Selecting Fields', - query: `SELECT lastEvent.log.action, severity + query: `SELECT lastEvent.action, severity FROM v11-alert-* LIMIT 20;` }, @@ -32,13 +32,13 @@ export const QUERY_SUGGESTIONS: QuerySuggestion[] = [ }, { label: 'Selecting Nested Field', - query: `SELECT lastEvent.log.action AS action + query: `SELECT lastEvent.action AS action FROM v11-alert-* LIMIT 5;` }, { label: 'Using Aggregations (COUNT)', - query: `SELECT lastEvent.log.action AS action, COUNT(*) AS total + query: `SELECT lastEvent.action AS action, COUNT(*) AS total FROM v11-alert-* WHERE @timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 30 DAY) AND NOW() GROUP BY action @@ -47,7 +47,7 @@ export const QUERY_SUGGESTIONS: QuerySuggestion[] = [ }, { label: 'Using Aggregations (MAX)', - query: `SELECT lastEvent.log.action AS action, MAX(@timestamp) AS lastSeen + query: `SELECT lastEvent.action AS action, MAX(@timestamp) AS lastSeen FROM v11-alert-* GROUP BY action;` }, @@ -68,7 +68,7 @@ export const QUERY_SUGGESTIONS: QuerySuggestion[] = [ label: 'Using Subqueries', query: `SELECT action, total FROM ( - SELECT lastEvent.log.action AS action, COUNT(*) AS total + SELECT lastEvent.action AS action, COUNT(*) AS total FROM v11-alert-* WHERE @timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 30 DAY) AND NOW() GROUP BY action From f9491e7bae33241592c10dc1e07d348df6d9fbcd Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 30 Jan 2026 15:38:39 -0600 Subject: [PATCH 003/105] feat: replace hardcoded search limit with MAX_SEARCH_RESULTS constant Signed-off-by: Manuel Abascal --- .../alert-management/alert-view/alert-view.component.ts | 5 ++--- .../alert-echoes-timeline/alert-echoes-timeline.component.ts | 3 ++- .../shared/components/alert-echoes/alert-echoes.component.ts | 3 ++- .../alert-rule-create/alert-rule-create.component.ts | 3 ++- .../file-management/file-view/file-view.component.ts | 3 ++- .../app/incident-response/shared/services/alert.service.ts | 3 ++- frontend/src/app/shared/constants/global.constant.ts | 2 ++ frontend/src/environments/environment.ts | 2 +- 8 files changed, 15 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts index c69534c38..97b2cbe09 100644 --- a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts +++ b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts @@ -8,7 +8,6 @@ import {NgxSpinnerService} from 'ngx-spinner'; import {LocalStorageService} from 'ngx-webstorage'; import {Observable, Subject} from 'rxjs'; import {filter, takeUntil, tap} from 'rxjs/operators'; -import {TimelineItem} from 'src/app/shared/types/utm-timeline-item'; import {UtmToastService} from '../../../shared/alert/utm-toast.service'; import { ElasticFilterDefaultTime @@ -30,7 +29,7 @@ import { ALERT_ECHOES_FIELD } from '../../../shared/constants/alert/alert-field.constant'; import {AUTOMATIC_REVIEW, IGNORED} from '../../../shared/constants/alert/alert-status.constant'; -import {ADMIN_ROLE} from '../../../shared/constants/global.constant'; +import {ADMIN_ROLE, MAX_SEARCH_RESULTS} from '../../../shared/constants/global.constant'; import {MAIN_INDEX_PATTERN} from '../../../shared/constants/main-index-pattern.constant'; import {ITEMS_PER_PAGE} from '../../../shared/constants/pagination.constants'; import {SortDirection} from '../../../shared/directives/sortable/type/sort-direction.type'; @@ -340,7 +339,7 @@ export class AlertViewComponent implements OnInit, OnDestroy { getAlert(calledFrom?: string, filtersParam?: ElasticFilterType[]) { this.elasticDataService.search(this.page, this.itemsPerPage, - 100000000, this.dataNature, + MAX_SEARCH_RESULTS, this.dataNature, sanitizeFilters(this.filters), this.sortBy, true).subscribe( (res: HttpResponse) => { this.totalItems = Number(res.headers.get('X-Total-Count')); diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-echoes-timeline/alert-echoes-timeline.component.ts b/frontend/src/app/data-management/alert-management/shared/components/alert-echoes-timeline/alert-echoes-timeline.component.ts index 5828950f6..66217fc75 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-echoes-timeline/alert-echoes-timeline.component.ts +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-echoes-timeline/alert-echoes-timeline.component.ts @@ -15,6 +15,7 @@ import {ElasticFilterType} from '../../../../../shared/types/filter/elastic-filt import {TimelineItem} from '../../../../../shared/types/utm-timeline-item'; import {sanitizeFilters} from '../../../../../shared/util/elastic-filter.util'; import {AlertEchoesTimelineService, TimelineGroup} from './alert-echoes-timeline.service'; +import {MAX_SEARCH_RESULTS} from "../../../../../shared/constants/global.constant"; @Component({ @@ -178,7 +179,7 @@ export class AlertEchoesTimelineComponent implements OnInit { loadData() { this.loading = true; this.elasticDataService.search(this.page, this.pageSize, - 100000000, DataNatureTypeEnum.ALERT, + MAX_SEARCH_RESULTS, DataNatureTypeEnum.ALERT, sanitizeFilters(this.filters), this.sortBy, true) .subscribe( (res: HttpResponse) => { diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-echoes/alert-echoes.component.ts b/frontend/src/app/data-management/alert-management/shared/components/alert-echoes/alert-echoes.component.ts index 998692c10..1197692f9 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-echoes/alert-echoes.component.ts +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-echoes/alert-echoes.component.ts @@ -17,6 +17,7 @@ import {UtmAlertType} from '../../../../../shared/types/alert/utm-alert.type'; import {ElasticFilterType} from '../../../../../shared/types/filter/elastic-filter.type'; import {sanitizeFilters} from '../../../../../shared/util/elastic-filter.util'; import {EventDataTypeEnum} from '../../enums/event-data-type.enum'; +import {MAX_SEARCH_RESULTS} from "../../../../../shared/constants/global.constant"; @Component({ selector: 'app-alert-echoes', @@ -61,7 +62,7 @@ export class AlertEchoesComponent implements OnInit { loadChildrenAlerts() { this.loading = true; this.elasticDataService.search(this.page, this.itemsPerPage, - 100000000, this.dataNature, + MAX_SEARCH_RESULTS, this.dataNature, sanitizeFilters(this.filters), this.sortBy, true).subscribe( (res: HttpResponse) => { this.totalItems = Number(res.headers.get('X-Total-Count')); diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.ts b/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.ts index 94c79dce9..19c596d7c 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.ts +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-rule-create/alert-rule-create.component.ts @@ -54,6 +54,7 @@ import {AlertRulesService} from '../../services/alert-rules.service'; import {AlertTagService} from '../../services/alert-tag.service'; import {setAlertPropertyValue} from '../../util/alert-util-function'; import {AlertActionRefreshService} from "../../services/alert-action-refresh.service"; +import {MAX_SEARCH_RESULTS} from "../../../../../shared/constants/global.constant"; @Component({ selector: 'app-alert-rule-create', @@ -415,7 +416,7 @@ export class AlertRuleCreateComponent implements OnInit, OnDestroy { this.elasticDataService.search( this.alertRequest.page, this.alertRequest.size, - 100000000, + MAX_SEARCH_RESULTS, this.alertRequest.dataNature, sanitizeFilters(this.alertRequest.filters), this.alertRequest.sort).subscribe( diff --git a/frontend/src/app/data-management/file-management/file-view/file-view.component.ts b/frontend/src/app/data-management/file-management/file-view/file-view.component.ts index 900eb279a..5db27bcd0 100644 --- a/frontend/src/app/data-management/file-management/file-view/file-view.component.ts +++ b/frontend/src/app/data-management/file-management/file-view/file-view.component.ts @@ -31,6 +31,7 @@ import { import {AccessMaskEnum} from '../shared/enum/access-mask.enum'; import {FileFieldEnum} from '../shared/enum/file-field.enum'; import {FileQueryParamEnum} from '../shared/enum/file-query-param.enum'; +import {MAX_SEARCH_RESULTS} from "../../../shared/constants/global.constant"; @Component({ selector: 'app-file-view', @@ -150,7 +151,7 @@ export class FileViewComponent implements OnInit { getFiles(calledFrom?: string) { this.elasticDataService.search(this.page, this.itemsPerPage, - 100000000, LOG_INDEX_WINLOGBEAT, + MAX_SEARCH_RESULTS, LOG_INDEX_WINLOGBEAT, sanitizeFilters(this.filters), this.sortBy).subscribe( (res: HttpResponse) => { this.totalItems = Number(res.headers.get('X-Total-Count')); diff --git a/frontend/src/app/incident-response/shared/services/alert.service.ts b/frontend/src/app/incident-response/shared/services/alert.service.ts index dd7427b87..47f0e32b7 100644 --- a/frontend/src/app/incident-response/shared/services/alert.service.ts +++ b/frontend/src/app/incident-response/shared/services/alert.service.ts @@ -6,6 +6,7 @@ import {UtmToastService} from '../../../shared/alert/utm-toast.service'; import {ElasticDataService} from '../../../shared/services/elasticsearch/elastic-data.service'; import {RefreshDataService} from '../../../shared/services/util/refresh-data.service'; import {sanitizeFilters} from '../../../shared/util/elastic-filter.util'; +import {MAX_SEARCH_RESULTS} from "../../../shared/constants/global.constant"; @Injectable( { @@ -23,7 +24,7 @@ export class AlertService extends RefreshDataService { return this.elasticDataService.search( request.page, request.size, - 100000000, + MAX_SEARCH_RESULTS, request.dataNature, sanitizeFilters(request.filters), request.sort) diff --git a/frontend/src/app/shared/constants/global.constant.ts b/frontend/src/app/shared/constants/global.constant.ts index 928e9118a..79a792de2 100644 --- a/frontend/src/app/shared/constants/global.constant.ts +++ b/frontend/src/app/shared/constants/global.constant.ts @@ -5,3 +5,5 @@ export const DEMO_URL = 'https://demo.utmstack.com/'; export const LOG_SOURCE_DASHBOARD_NAME = 'Log source system'; export const SAAS_DEFAULT_PASSWORD = 'DefaultPa$$word!'; export const ONLINE_DOCUMENTATION_BASE = 'https://docs.utmstack.com/UTMStackDocumentationSite/'; +export const MAX_SEARCH_RESULTS = 100000; + diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index c14489291..066a347a4 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -4,7 +4,7 @@ export const environment = { production: false, - SERVER_API_URL: 'https://192.168.1.18/', + SERVER_API_URL: 'https://10.11.12.113/', //SERVER_API_URL: 'http://localhost:8080/', SERVER_API_CONTEXT: '', SESSION_AUTH_TOKEN: window.location.host.split(':')[0].toLocaleUpperCase(), From 9b7cf317767bb30aee4d063e485c502e93a1bc38 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 30 Jan 2026 15:56:04 -0600 Subject: [PATCH 004/105] feat: replace hardcoded search limit with MAX_SEARCH_RESULTS constant Signed-off-by: Manuel Abascal --- .../alert-report-view/alert-report-view.component.ts | 4 ++-- .../alert-reports/alert-reports.component.ts | 4 ++-- .../components/save-report/save-report.component.ts | 1 - .../alert-events-related.component.ts | 4 ++-- .../file-save-data/file-save-data.component.ts | 4 ++-- .../log-analyzer-field-detail.component.ts | 4 ++-- .../log-analyzer-view/log-analyzer-view.component.ts | 10 ++++------ frontend/src/app/shared/constants/global.constant.ts | 2 +- .../src/app/shared/constants/log-analyzer.constant.ts | 1 - 9 files changed, 15 insertions(+), 19 deletions(-) delete mode 100644 frontend/src/app/shared/constants/log-analyzer.constant.ts diff --git a/frontend/src/app/data-management/alert-management/alert-report-view/alert-report-view.component.ts b/frontend/src/app/data-management/alert-management/alert-report-view/alert-report-view.component.ts index d5c0928ac..02a4a2234 100644 --- a/frontend/src/app/data-management/alert-management/alert-report-view/alert-report-view.component.ts +++ b/frontend/src/app/data-management/alert-management/alert-report-view/alert-report-view.component.ts @@ -2,7 +2,7 @@ import {HttpResponse} from '@angular/common/http'; import {Component, OnInit} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; import * as moment from 'moment'; -import {LOG_ANALYZER_TOTAL_ITEMS} from '../../../shared/constants/log-analyzer.constant'; +import {MAX_SEARCH_RESULTS} from "../../../shared/constants/global.constant"; import {SortEvent} from '../../../shared/directives/sortable/type/sort-event'; import {DataNatureTypeEnum} from '../../../shared/enums/nature-data.enum'; import {ElasticDataExportService} from '../../../shared/services/elasticsearch/elastic-data-export.service'; @@ -78,7 +78,7 @@ export class AlertReportViewComponent implements OnInit { columns: this.report.columns, dataOrigin: DataNatureTypeEnum.ALERT, filters: this.report.filters, - top: LOG_ANALYZER_TOTAL_ITEMS + top: MAX_SEARCH_RESULTS }; this.elasticDataExportService.exportCsv(params, 'UTM ALERTS').then(() => { this.generateReport = false; diff --git a/frontend/src/app/data-management/alert-management/alert-reports/alert-reports.component.ts b/frontend/src/app/data-management/alert-management/alert-reports/alert-reports.component.ts index 23bcd521e..383b7ea37 100644 --- a/frontend/src/app/data-management/alert-management/alert-reports/alert-reports.component.ts +++ b/frontend/src/app/data-management/alert-management/alert-reports/alert-reports.component.ts @@ -6,7 +6,6 @@ import {AccountService} from '../../../core/auth/account.service'; import {User} from '../../../core/user/user.model'; import {UtmToastService} from '../../../shared/alert/utm-toast.service'; import {ModalConfirmationComponent} from '../../../shared/components/utm/util/modal-confirmation/modal-confirmation.component'; -import {LOG_ANALYZER_TOTAL_ITEMS} from '../../../shared/constants/log-analyzer.constant'; import {ITEMS_PER_PAGE} from '../../../shared/constants/pagination.constants'; import {SortEvent} from '../../../shared/directives/sortable/type/sort-event'; import {DataNatureTypeEnum} from '../../../shared/enums/nature-data.enum'; @@ -17,6 +16,7 @@ import {SortByType} from '../../../shared/types/sort-by.type'; import {AlertTagService} from '../shared/services/alert-tag.service'; import {AlertReportFilterComponent} from './shared/components/alert-report-filter/alert-report-filter.component'; import {AlertReportService} from './shared/services/report.service'; +import {MAX_SEARCH_RESULTS} from "../../../shared/constants/global.constant"; @Component({ selector: 'app-alert-saved-reports', @@ -128,7 +128,7 @@ export class AlertReportsComponent implements OnInit { columns: report.columns, dataOrigin: DataNatureTypeEnum.ALERT, filters: report.filters, - top: LOG_ANALYZER_TOTAL_ITEMS + top: MAX_SEARCH_RESULTS }; this.elasticDataExportService.exportCsv(params, 'UTM ALERTS').then(() => { this.generateReport = false; diff --git a/frontend/src/app/data-management/alert-management/alert-reports/shared/components/save-report/save-report.component.ts b/frontend/src/app/data-management/alert-management/alert-reports/shared/components/save-report/save-report.component.ts index 33c2be55e..7be5925ec 100644 --- a/frontend/src/app/data-management/alert-management/alert-reports/shared/components/save-report/save-report.component.ts +++ b/frontend/src/app/data-management/alert-management/alert-reports/shared/components/save-report/save-report.component.ts @@ -1,7 +1,6 @@ import {Component, Input, OnInit} from '@angular/core'; import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap'; import {UtmToastService} from '../../../../../../shared/alert/utm-toast.service'; -import {LOG_ANALYZER_TOTAL_ITEMS} from '../../../../../../shared/constants/log-analyzer.constant'; import {ALERT_INDEX_PATTERN} from '../../../../../../shared/constants/main-index-pattern.constant'; import {ElasticDataExportService} from '../../../../../../shared/services/elasticsearch/elastic-data-export.service'; import {AlertReportType} from '../../../../../../shared/types/alert/alert-report.type'; diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-events-related/alert-events-related.component.ts b/frontend/src/app/data-management/alert-management/shared/components/alert-events-related/alert-events-related.component.ts index cdf3a6b61..d7417d051 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-events-related/alert-events-related.component.ts +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-events-related/alert-events-related.component.ts @@ -2,10 +2,10 @@ import {Component, Input, OnInit} from '@angular/core'; import { UtmTableDetailComponent } from '../../../../../shared/components/utm/table/utm-table/utm-table-detail/utm-table-detail.component'; -import {LOG_ANALYZER_TOTAL_ITEMS} from '../../../../../shared/constants/log-analyzer.constant'; import {ITEMS_PER_PAGE} from '../../../../../shared/constants/pagination.constants'; import {ElasticDataTypesEnum} from '../../../../../shared/enums/elastic-data-types.enum'; import {UtmFieldType} from '../../../../../shared/types/table/utm-field.type'; +import {MAX_SEARCH_RESULTS} from "../../../../../shared/constants/global.constant"; @Component({ selector: 'app-alert-events-related', @@ -20,7 +20,7 @@ export class AlertEventsRelatedComponent implements OnInit { @Input() events: any[] = []; displayedLogs: any[] = []; page = 1; - readonly totalItems = LOG_ANALYZER_TOTAL_ITEMS; + readonly totalItems = MAX_SEARCH_RESULTS; itemsPerPage = ITEMS_PER_PAGE; readonly componentDetail = UtmTableDetailComponent; sortField = ''; diff --git a/frontend/src/app/data-management/file-management/shared/components/file-save-data/file-save-data.component.ts b/frontend/src/app/data-management/file-management/shared/components/file-save-data/file-save-data.component.ts index 518f729e5..76ac61874 100644 --- a/frontend/src/app/data-management/file-management/shared/components/file-save-data/file-save-data.component.ts +++ b/frontend/src/app/data-management/file-management/shared/components/file-save-data/file-save-data.component.ts @@ -1,10 +1,10 @@ import {Component, Input, OnInit} from '@angular/core'; import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap'; -import {LOG_ANALYZER_TOTAL_ITEMS} from '../../../../../shared/constants/log-analyzer.constant'; import {DataNatureTypeEnum} from '../../../../../shared/enums/nature-data.enum'; import {ElasticDataExportService} from '../../../../../shared/services/elasticsearch/elastic-data-export.service'; import {ElasticFilterType} from '../../../../../shared/types/filter/elastic-filter.type'; import {UtmFieldType} from '../../../../../shared/types/table/utm-field.type'; +import {MAX_SEARCH_RESULTS} from "../../../../../shared/constants/global.constant"; @Component({ selector: 'app-file-save-data', @@ -30,7 +30,7 @@ export class FileSaveDataComponent implements OnInit { columns: this.fields, dataOrigin: DataNatureTypeEnum.EVENT, filters: this.filters, - top: LOG_ANALYZER_TOTAL_ITEMS + top: MAX_SEARCH_RESULTS }; this.elasticDataExportService.exportCsv(params, 'UTM FILE CLASSIFICATION').then(() => { this.generateReport = false; diff --git a/frontend/src/app/log-analyzer/explorer/log-analyzer-field/log-analyzer-field-card/log-analyzer-field-detail/log-analyzer-field-detail.component.ts b/frontend/src/app/log-analyzer/explorer/log-analyzer-field/log-analyzer-field-card/log-analyzer-field-detail/log-analyzer-field-detail.component.ts index e0464b536..6f579c7a9 100644 --- a/frontend/src/app/log-analyzer/explorer/log-analyzer-field/log-analyzer-field-card/log-analyzer-field-detail/log-analyzer-field-detail.component.ts +++ b/frontend/src/app/log-analyzer/explorer/log-analyzer-field/log-analyzer-field-card/log-analyzer-field-detail/log-analyzer-field-detail.component.ts @@ -1,7 +1,7 @@ import {Component, Input, OnDestroy, OnInit} from '@angular/core'; import {Observable} from 'rxjs'; import {UtmFilterBehavior} from '../../../../../shared/components/utm/filters/utm-elastic-filter/shared/behavior/utm-filter.behavior'; -import {LOG_ANALYZER_TOTAL_ITEMS} from '../../../../../shared/constants/log-analyzer.constant'; +import {MAX_SEARCH_RESULTS} from "../../../../../shared/constants/global.constant"; import {ElasticDataTypesEnum} from '../../../../../shared/enums/elastic-data-types.enum'; import {ElasticOperatorsEnum} from '../../../../../shared/enums/elastic-operators.enum'; import {ElasticSearchFieldInfoType} from '../../../../../shared/types/elasticsearch/elastic-search-field-info.type'; @@ -22,7 +22,7 @@ export class LogAnalyzerFieldDetailComponent implements OnInit, OnDestroy { filters: ElasticFilterType[] = []; fieldTopValues: LogAnalyzerFieldDetailType; loading = true; - total = LOG_ANALYZER_TOTAL_ITEMS; + total = MAX_SEARCH_RESULTS; public logObservable: Observable<{ filter: ElasticFilterType[], sort: string }>; subscription: any; private sort = '@timestamp,desc'; diff --git a/frontend/src/app/log-analyzer/explorer/log-analyzer-view/log-analyzer-view.component.ts b/frontend/src/app/log-analyzer/explorer/log-analyzer-view/log-analyzer-view.component.ts index 872f68a17..7ca10ec75 100644 --- a/frontend/src/app/log-analyzer/explorer/log-analyzer-view/log-analyzer-view.component.ts +++ b/frontend/src/app/log-analyzer/explorer/log-analyzer-view/log-analyzer-view.component.ts @@ -7,10 +7,9 @@ import * as moment from 'moment'; import {Observable, Subject} from 'rxjs'; import {filter, takeUntil} from 'rxjs/operators'; -import {ADMIN_ROLE} from '../../../shared/constants/global.constant'; +import {ADMIN_ROLE, MAX_SEARCH_RESULTS} from '../../../shared/constants/global.constant'; import {ALERT_INDEX_PATTERN, LOG_INDEX_PATTERN} from '../../../shared/constants/main-index-pattern.constant'; import {ITEMS_PER_PAGE} from '../../../shared/constants/pagination.constants'; -import {LOG_ANALYZER_TOTAL_ITEMS} from '../../../shared/constants/log-analyzer.constant'; import {ElasticDataTypesEnum} from '../../../shared/enums/elastic-data-types.enum'; import {ElasticOperatorsEnum} from '../../../shared/enums/elastic-operators.enum'; @@ -38,7 +37,6 @@ import {IndexFieldController} from '../../shared/behaviors/index-field-controlle import {IndexPatternBehavior} from '../../shared/behaviors/index-pattern.behavior'; import {LogFilterBehavior} from '../../shared/behaviors/log-filter.behavior'; import {QueryRunBehavior} from '../../shared/behaviors/query-run.behavior'; -import {TabService} from '../../shared/services/tab.service'; import {LogAnalyzerQueryType} from '../../shared/type/log-analyzer-query.type'; @Component({ @@ -54,7 +52,7 @@ export class LogAnalyzerViewComponent implements OnInit, OnDestroy { rows: any[] = []; page = 1; itemsPerPage = ITEMS_PER_PAGE; - totalItems = LOG_ANALYZER_TOTAL_ITEMS; + totalItems = MAX_SEARCH_RESULTS; view: 'table' | 'chart' = 'table'; filters: ElasticFilterType[] = [{ field: '@timestamp', @@ -254,7 +252,7 @@ export class LogAnalyzerViewComponent implements OnInit, OnDestroy { this.logAnalyzerService.search( this.page, this.itemsPerPage, - LOG_ANALYZER_TOTAL_ITEMS, + MAX_SEARCH_RESULTS, this.pattern.pattern, this.filters, this.sortBy).subscribe( @@ -380,7 +378,7 @@ export class LogAnalyzerViewComponent implements OnInit, OnDestroy { columns: this.fields, indexPattern: this.pattern.pattern, filters: this.filters, - top: LOG_ANALYZER_TOTAL_ITEMS + top: MAX_SEARCH_RESULTS }; this.elasticDataExportService.exportCsv(params, 'UTM LOG EXPLORER').then(() => { this.csvExport = false; diff --git a/frontend/src/app/shared/constants/global.constant.ts b/frontend/src/app/shared/constants/global.constant.ts index 79a792de2..0558e31c3 100644 --- a/frontend/src/app/shared/constants/global.constant.ts +++ b/frontend/src/app/shared/constants/global.constant.ts @@ -5,5 +5,5 @@ export const DEMO_URL = 'https://demo.utmstack.com/'; export const LOG_SOURCE_DASHBOARD_NAME = 'Log source system'; export const SAAS_DEFAULT_PASSWORD = 'DefaultPa$$word!'; export const ONLINE_DOCUMENTATION_BASE = 'https://docs.utmstack.com/UTMStackDocumentationSite/'; -export const MAX_SEARCH_RESULTS = 100000; +export const MAX_SEARCH_RESULTS = 10000; diff --git a/frontend/src/app/shared/constants/log-analyzer.constant.ts b/frontend/src/app/shared/constants/log-analyzer.constant.ts deleted file mode 100644 index a89f32a48..000000000 --- a/frontend/src/app/shared/constants/log-analyzer.constant.ts +++ /dev/null @@ -1 +0,0 @@ -export const LOG_ANALYZER_TOTAL_ITEMS = 10000; From 2ba7785635807b548700d231bf1ee1d6924a20c2 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 30 Jan 2026 17:07:46 -0600 Subject: [PATCH 005/105] feat: replace hardcoded search limit with MAX_SEARCH_RESULTS constant Signed-off-by: Manuel Abascal --- .../alert-report-view/alert-report-view.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/data-management/alert-management/alert-report-view/alert-report-view.component.ts b/frontend/src/app/data-management/alert-management/alert-report-view/alert-report-view.component.ts index 02a4a2234..da20fce80 100644 --- a/frontend/src/app/data-management/alert-management/alert-report-view/alert-report-view.component.ts +++ b/frontend/src/app/data-management/alert-management/alert-report-view/alert-report-view.component.ts @@ -2,7 +2,7 @@ import {HttpResponse} from '@angular/common/http'; import {Component, OnInit} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; import * as moment from 'moment'; -import {MAX_SEARCH_RESULTS} from "../../../shared/constants/global.constant"; +import {MAX_SEARCH_RESULTS} from '../../../shared/constants/global.constant'; import {SortEvent} from '../../../shared/directives/sortable/type/sort-event'; import {DataNatureTypeEnum} from '../../../shared/enums/nature-data.enum'; import {ElasticDataExportService} from '../../../shared/services/elasticsearch/elastic-data-export.service'; From 060eed01680369d0b6ad06caa66429b62ee8492f Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Mon, 2 Feb 2026 13:28:56 +0000 Subject: [PATCH 006/105] docs(README): fix broken tags, improve clarity, and correct grammar --- README.md | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1f14d3b77..391b08831 100644 --- a/README.md +++ b/README.md @@ -4,21 +4,19 @@

- - - - - - + + + + + +

Enterprise-ready SIEM and XDR powered by Real-Time correlation and Threat Intelligence

## Introduction -Welcome to the UTMStack open-source project! UTMStack is a unified threat management platform that merges SIEM (Security Information and Event Management) and XDR (Extended Detection and Response) technologies. Our unique approach allows real-time correlation of log data, threat intelligence, and malware activity patterns from multiple sources, enabling the identification and halting of complex threats that use stealthy techniques. Visit an [online demo here.](https://utmstack.com/demo) - -We have a [dedicated repository](https://github.com/utmstack/rules) for correlation rules, contributors are welcome to submit a pull request. +Welcome to the UTMStack open-source project! UTMStack is a unified threat management platform that merges SIEM (Security Information and Event Management) and XDR (Extended Detection and Response) technologies. Our unique approach allows real-time correlation of log data, threat intelligence, and malware activity patterns from multiple sources, enabling the identification and halting of complex threats that use stealthy techniques. Visit an [online demo here.](https://utmstack.com/demo)

UTMStack UTMStack

@@ -80,7 +78,7 @@ Definitions: - Cold log storage: archived data that should be restored before accessing it. - Data source: any individual source of logs, for example, devices, agents, SaaS integrations. -Resources needed for one month of hot log storage. +Required resources for one month of hot log storage. - For 50 data sources (120 GB) of hot log storage you will need 4 Cores, 16 GB RAM, 150 GB Disk Space - For 120 data sources (250 GB) of hot log storage you will need 8 Cores, 16 GB RAM, 250 GB Disk Space - For 240 data sources (500 GB) of hot log storage you will need 16 Cores, 32 GB RAM, 500 GB Disk Space @@ -109,16 +107,16 @@ Once UTMStack is installed, use admin as the user and the password generated dur Note: Use HTTPS in front of your server name or IP to access the login page. ### Required ports -- 22/TCP Secure Shell (We recommend to create a firewall rule to allow it only from admin workstation) -- 80/TCP UTMStack Web-based Graphical User Interface Redirector (We recommend to create a firewall rule to allow it only from admin and security analyst workstations) -- 443/TCP UTMStack Web-based Graphical User Interface (We recommend to create a firewall rule to allow it only from admin and security analyst workstations) -- 9090/TCP Cockpit Web-based Graphical Interface for Servers (We recommend to create a firewall rule to allow it only from admin workstation) -- Others ports will be required during the configuration of UTMStack's integrations in order to receive logs. (Please follow the security recommendations given on the integration guide if exists) +- 22/TCP Secure Shell (We recommend creating a firewall rule to allow it only from admins workstations) +- 80/TCP UTMStack Web-based Graphical User Interface Redirector (We recommend creating a firewall rule to allow it only from admin and security analyst workstations) +- 443/TCP UTMStack Web-based Graphical User Interface (We recommend creating a firewall rule to allow it only from admin and security analyst workstations) +- 9090/TCP Cockpit Web-based Graphical Interface for Servers (We recommend creating a firewall rule to allow it only from admin workstation) +- Others ports will be required during the configuration of UTMStack's integrations to receive logs. (Please follow the security recommendations given on the integration guide if exists) # FAQ - Is this based on Grafana, Kibana, or a similar reporting tool? Answer: It is not. UTMStack has been built from the ground up to be a simple and intuitive SIEM/XDR. - Does UTMStack use ELK for log correlation? Answer: It does not. UTMStack correlation engine was built from scratch to analyze data before ingestion and maximize real-time correlation. -- What is the difference between the Open Source and Enterprise version? +- What is the difference between the Open Source and Enterprise versions? The enterprise version includes features that would typically benefit enterprises and MSPs. For example, support, faster correlation, frequent threat intelligence updates, and Artificial Intelligence. From 667e65dc61a55e4a3dc347cb517ab14ec9a9ceaa Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 2 Feb 2026 10:03:09 -0600 Subject: [PATCH 007/105] feat: add Linux Auditing Daemon update to changelog --- .../20260202001_update_linux_auditing_module.xml | 16 ++++++++++++++++ .../main/resources/config/liquibase/master.xml | 2 ++ 2 files changed, 18 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260202001_update_linux_auditing_module.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260202001_update_linux_auditing_module.xml b/backend/src/main/resources/config/liquibase/changelog/20260202001_update_linux_auditing_module.xml new file mode 100644 index 000000000..f9c0a6b3a --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260202001_update_linux_auditing_module.xml @@ -0,0 +1,16 @@ + + + + + + + + + module_name = 'AUDITD' + + + + \ No newline at end of file diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 71b5ddba4..f5a33e540 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -327,6 +327,8 @@ + + From 2544d9196b1b361f3a939f8dfd76957454bbd76b Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 2 Feb 2026 10:11:51 -0600 Subject: [PATCH 008/105] feat: update architecture names for clarity in Linux agent guide Signed-off-by: Manuel Abascal --- .../guide-linux-agent/guide-linux-agent.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/app-module/guides/guide-linux-agent/guide-linux-agent.component.ts b/frontend/src/app/app-module/guides/guide-linux-agent/guide-linux-agent.component.ts index 3dd3b133d..a143e9ed1 100644 --- a/frontend/src/app/app-module/guides/guide-linux-agent/guide-linux-agent.component.ts +++ b/frontend/src/app/app-module/guides/guide-linux-agent/guide-linux-agent.component.ts @@ -78,23 +78,23 @@ export class GuideLinuxAgentComponent implements OnInit { private loadArchitectures() { this.architectures = [ { - id: 1, name: 'Ubuntu 16/18/20+', + id: 1, name: 'Ubuntu / Debian', install: this.getCommandUbuntu('utmstack_agent_service'), uninstall: this.getUninstallCommand('utmstack_agent_service'), shell: '' }, { - id: 2, name: 'Centos 7/Red Hat Enterprise Linux', + id: 2, name: 'Fedora / RedHat', install: this.getCommandCentos7RedHat('utmstack_agent_service'), uninstall: this.getUninstallCommand('utmstack_agent_service'), shell: '' - }, + }/*, { id: 3, name: 'Centos 8/AlmaLinux', install: this.getCommandCentos8Almalinux('utmstack_agent_service'), uninstall: this.getUninstallCommand('utmstack_agent_service'), shell: '' - } + }*/ ]; } } From 2433faa773b5452761238ad7c3c60c7e126dcf8f Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 2 Feb 2026 10:24:35 -0600 Subject: [PATCH 009/105] feat: enhance version info handling in API documentation component Signed-off-by: Manuel Abascal --- .../utm-api-doc/utm-api-doc.component.html | 2 +- .../utm-api-doc/utm-api-doc.component.ts | 22 ++++++++++++++++--- .../app/shared/constants/global.constant.ts | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.html b/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.html index d21f11edd..a9d639a76 100644 --- a/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.html +++ b/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.html @@ -6,7 +6,7 @@

Version: - +

diff --git a/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.ts b/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.ts index 56fc76b2e..ad5d30e78 100644 --- a/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.ts +++ b/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.ts @@ -1,16 +1,32 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {Subject} from 'rxjs'; +import {takeUntil} from 'rxjs/operators'; +import {VersionInfoService} from '../../shared/services/version/version-info.service'; +import {AppVersionInfo} from '../../shared/types/updates/updates.type'; @Component({ selector: 'app-utm-api-doc', templateUrl: './utm-api-doc.component.html', styleUrls: ['./utm-api-doc.component.scss'] }) -export class UtmApiDocComponent implements OnInit { +export class UtmApiDocComponent implements OnInit, OnDestroy { - constructor() { + versionInfo: AppVersionInfo; + destroy$ = new Subject(); + + constructor(private versionTypeService: VersionInfoService) { } ngOnInit() { + + this.versionTypeService.appVersionInfo$ + .pipe(takeUntil(this.destroy$)) + .subscribe((versionInfo: AppVersionInfo) => this.versionInfo = versionInfo); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/frontend/src/app/shared/constants/global.constant.ts b/frontend/src/app/shared/constants/global.constant.ts index 0558e31c3..5efa49043 100644 --- a/frontend/src/app/shared/constants/global.constant.ts +++ b/frontend/src/app/shared/constants/global.constant.ts @@ -4,6 +4,6 @@ export const ADMIN_DEFAULT_EMAIL = 'admin@localhost'; export const DEMO_URL = 'https://demo.utmstack.com/'; export const LOG_SOURCE_DASHBOARD_NAME = 'Log source system'; export const SAAS_DEFAULT_PASSWORD = 'DefaultPa$$word!'; -export const ONLINE_DOCUMENTATION_BASE = 'https://docs.utmstack.com/UTMStackDocumentationSite/'; +export const ONLINE_DOCUMENTATION_BASE = 'https://docs.utmstack.com'; export const MAX_SEARCH_RESULTS = 10000; From 77b61fd50f78629ffc9ec07597b873cce6cb43cc Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 2 Feb 2026 10:45:15 -0600 Subject: [PATCH 010/105] feat: add Crowdstrike filter update to changelog --- .../20260202002_update_filter_crowdstrike.xml | 330 ++++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 332 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260202002_update_filter_crowdstrike.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260202002_update_filter_crowdstrike.xml b/backend/src/main/resources/config/liquibase/changelog/20260202002_update_filter_crowdstrike.xml new file mode 100644 index 000000000..57f700bf3 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260202002_update_filter_crowdstrike.xml @@ -0,0 +1,330 @@ + + + + + + + + + + \ No newline at end of file diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index f5a33e540..41ed68e30 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -329,6 +329,8 @@ + + From 175e4e67b86a42f83f380d96a3478cca53484f5b Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 2 Feb 2026 10:47:16 -0600 Subject: [PATCH 011/105] feat: update layout styles for API documentation component Signed-off-by: Manuel Abascal --- .../utm-api-doc/utm-api-doc.component.scss | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.scss b/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.scss index 5ea1bf8ae..54cc37d9d 100644 --- a/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.scss +++ b/frontend/src/app/app-management/utm-api-doc/utm-api-doc.component.scss @@ -1,6 +1,9 @@ -.utm-api-doc { - //max-height: 800px; - //overflow-y: scroll; +:host { + display: flex; + flex-direction: column; + flex: 1 1 auto; + min-height: 0; + height: 100%; } span.method { From 6f3e0e8cae9b8831ae8b232f5681c8b69c92e8b6 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 2 Feb 2026 10:51:36 -0600 Subject: [PATCH 012/105] feat: comment out security scheme definitions in OpenApiConfiguration --- .../java/com/park/utmstack/config/OpenApiConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/config/OpenApiConfiguration.java b/backend/src/main/java/com/park/utmstack/config/OpenApiConfiguration.java index dd1414d6d..f3fcba61e 100644 --- a/backend/src/main/java/com/park/utmstack/config/OpenApiConfiguration.java +++ b/backend/src/main/java/com/park/utmstack/config/OpenApiConfiguration.java @@ -34,7 +34,7 @@ public OpenAPI customOpenAPI() { .addList(securitySchemeApiInternalKey) .addList(securitySchemeApiKey)) .components(new Components() - .addSecuritySchemes(securitySchemeBearer, + /*.addSecuritySchemes(securitySchemeBearer, new SecurityScheme() .name(securitySchemeBearer) .type(SecurityScheme.Type.HTTP) @@ -43,7 +43,7 @@ public OpenAPI customOpenAPI() { .addSecuritySchemes(securitySchemeApiInternalKey, new SecurityScheme() .name("Utm-Internal-Key") .type(SecurityScheme.Type.APIKEY) - .in(SecurityScheme.In.HEADER)) + .in(SecurityScheme.In.HEADER))*/ .addSecuritySchemes(securitySchemeApiKey, new SecurityScheme() .name(Constants.API_KEY_HEADER) .type(SecurityScheme.Type.APIKEY) From acf3eadad53e7de467e6616d8d07e82d60874f6e Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 2 Feb 2026 11:05:54 -0600 Subject: [PATCH 013/105] feat: update alert field type to string and adjust sorting logic in alert view Signed-off-by: Manuel Abascal --- .../alert-management/alert-view/alert-view.component.html | 6 +++--- .../alert-management/alert-view/alert-view.component.ts | 2 ++ .../src/app/shared/constants/alert/alert-field.constant.ts | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html index 62dad69d5..863052961 100644 --- a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html +++ b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html @@ -106,11 +106,11 @@
tooltipClass="utm-tooltip-top"> {{item.label}} diff --git a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts index 97b2cbe09..96a135d59 100644 --- a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts +++ b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts @@ -55,6 +55,7 @@ import {AlertActionRefreshService} from '../shared/services/alert-action-refresh import {AlertTagService} from '../shared/services/alert-tag.service'; import {OPEN_ALERTS_KEY, OpenAlertsService} from '../shared/services/open-alerts.service'; import {getCurrentAlertStatus, getStatusName} from '../shared/util/alert-util-function'; +import {ElasticDataTypesEnum} from "../../../shared/enums/elastic-data-types.enum"; @Component({ selector: 'app-alert-view', @@ -139,6 +140,7 @@ export class AlertViewComponent implements OnInit, OnDestroy { pageSizeChildren = ITEMS_PER_PAGE * 4; readonly ALERT_STATUS_FIELD = ALERT_STATUS_FIELD; readonly Math = Math; + readonly ElasticDataTypesEnum = ElasticDataTypesEnum; ngOnInit() { this.openAlerts = this.localStorage.retrieve(OPEN_ALERTS_KEY); 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 e8335c8db..2e91dff5e 100644 --- a/frontend/src/app/shared/constants/alert/alert-field.constant.ts +++ b/frontend/src/app/shared/constants/alert/alert-field.constant.ts @@ -129,7 +129,7 @@ export const ALERT_FIELDS: UtmFieldType[] = [ { label: 'Severity', field: ALERT_SEVERITY_FIELD_LABEL, - type: ElasticDataTypesEnum.NUMBER, + type: ElasticDataTypesEnum.STRING, visible: true, }, { From 4d3d3f9bf8f7be5476d3c890b88f8a9ada8518d4 Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Mon, 2 Feb 2026 19:09:35 +0000 Subject: [PATCH 014/105] chore(plugins): update dependencies across plugins - Updated versions for `github.com/threatwinds/go-sdk`, `github.com/gabriel-vasile/mimetype`, `github.com/google/cel-go`, and others in `go.mod` files. - Removed unused dependency `github.com/stoewer/go-strcase`. --- plugins/alerts/go.mod | 11 +++++------ plugins/alerts/go.sum | 23 ++++++++++------------- plugins/config/go.mod | 13 ++++++------- plugins/config/go.sum | 27 ++++++++++++--------------- plugins/events/go.mod | 11 +++++------ plugins/events/go.sum | 23 ++++++++++------------- plugins/geolocation/go.mod | 11 +++++------ plugins/geolocation/go.sum | 23 ++++++++++------------- plugins/stats/go.mod | 11 +++++------ plugins/stats/go.sum | 23 ++++++++++------------- 10 files changed, 78 insertions(+), 98 deletions(-) diff --git a/plugins/alerts/go.mod b/plugins/alerts/go.mod index bfcbdb687..d48c7c6dc 100644 --- a/plugins/alerts/go.mod +++ b/plugins/alerts/go.mod @@ -4,7 +4,7 @@ go 1.25.5 require ( github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.9 + github.com/threatwinds/go-sdk v1.1.10 github.com/tidwall/gjson v1.18.0 google.golang.org/protobuf v1.36.11 ) @@ -16,7 +16,7 @@ require ( github.com/bytedance/sonic v1.15.0 // indirect github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect - github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-gonic/gin v1.11.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -24,7 +24,7 @@ require ( github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.2 // indirect - github.com/google/cel-go v0.26.1 // indirect + github.com/google/cel-go v0.27.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect @@ -36,7 +36,6 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect @@ -48,8 +47,8 @@ require ( golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b // indirect google.golang.org/grpc v1.78.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect diff --git a/plugins/alerts/go.sum b/plugins/alerts/go.sum index 9978feb4f..0c6ce469a 100644 --- a/plugins/alerts/go.sum +++ b/plugins/alerts/go.sum @@ -14,8 +14,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= -github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= @@ -38,8 +38,8 @@ github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= -github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= +github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -77,8 +77,6 @@ github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SA github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -86,13 +84,12 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/threatwinds/go-sdk v1.1.9 h1:4i8UZczXyGbRJsUEHRgaS2oQ03VTRjh/DYyTtGdSfRA= -github.com/threatwinds/go-sdk v1.1.9/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.10 h1:joqXa0K8DiiJ135isRi6eOzVTtlPBY2i2yAS2ia2wNU= +github.com/threatwinds/go-sdk v1.1.10/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= @@ -142,10 +139,10 @@ golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b h1:SGYyueaEovpqmWmtTvwtVgo638V/QFE2zlTCnRrR3jg= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b h1:GZxXGdFaHX27ZSMHudWc4FokdD+xl8BC2UJm1OVIEzs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/plugins/config/go.mod b/plugins/config/go.mod index 2ba8d1179..db172777d 100644 --- a/plugins/config/go.mod +++ b/plugins/config/go.mod @@ -3,8 +3,8 @@ module github.com/utmstack/UTMStack/plugins/config go 1.25.5 require ( - github.com/lib/pq v1.10.9 - github.com/threatwinds/go-sdk v1.1.9 + github.com/lib/pq v1.11.1 + github.com/threatwinds/go-sdk v1.1.10 gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/yaml v1.6.0 ) @@ -16,7 +16,7 @@ require ( github.com/bytedance/sonic v1.15.0 // indirect github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect - github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-gonic/gin v1.11.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -24,7 +24,7 @@ require ( github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.2 // indirect - github.com/google/cel-go v0.26.1 // indirect + github.com/google/cel-go v0.27.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -37,7 +37,6 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect @@ -50,8 +49,8 @@ require ( golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b // indirect google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/plugins/config/go.sum b/plugins/config/go.sum index a0ab31a8b..98dd2873b 100644 --- a/plugins/config/go.sum +++ b/plugins/config/go.sum @@ -14,8 +14,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= -github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= @@ -38,8 +38,8 @@ github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= -github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= +github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -57,8 +57,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI= +github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -79,8 +79,6 @@ github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SA github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -88,13 +86,12 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/threatwinds/go-sdk v1.1.9 h1:4i8UZczXyGbRJsUEHRgaS2oQ03VTRjh/DYyTtGdSfRA= -github.com/threatwinds/go-sdk v1.1.9/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.10 h1:joqXa0K8DiiJ135isRi6eOzVTtlPBY2i2yAS2ia2wNU= +github.com/threatwinds/go-sdk v1.1.10/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= @@ -144,10 +141,10 @@ golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b h1:SGYyueaEovpqmWmtTvwtVgo638V/QFE2zlTCnRrR3jg= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b h1:GZxXGdFaHX27ZSMHudWc4FokdD+xl8BC2UJm1OVIEzs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/plugins/events/go.mod b/plugins/events/go.mod index 1e9ac27e5..4c508d248 100644 --- a/plugins/events/go.mod +++ b/plugins/events/go.mod @@ -3,7 +3,7 @@ module github.com/utmstack/UTMStack/plugins/events go 1.25.5 require ( - github.com/threatwinds/go-sdk v1.1.9 + github.com/threatwinds/go-sdk v1.1.10 github.com/tidwall/gjson v1.18.0 ) @@ -14,7 +14,7 @@ require ( github.com/bytedance/sonic v1.15.0 // indirect github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect - github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-gonic/gin v1.11.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -22,7 +22,7 @@ require ( github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.2 // indirect - github.com/google/cel-go v0.26.1 // indirect + github.com/google/cel-go v0.27.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -35,7 +35,6 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect @@ -47,8 +46,8 @@ require ( golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b // indirect google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/plugins/events/go.sum b/plugins/events/go.sum index 9978feb4f..0c6ce469a 100644 --- a/plugins/events/go.sum +++ b/plugins/events/go.sum @@ -14,8 +14,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= -github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= @@ -38,8 +38,8 @@ github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= -github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= +github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -77,8 +77,6 @@ github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SA github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -86,13 +84,12 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/threatwinds/go-sdk v1.1.9 h1:4i8UZczXyGbRJsUEHRgaS2oQ03VTRjh/DYyTtGdSfRA= -github.com/threatwinds/go-sdk v1.1.9/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.10 h1:joqXa0K8DiiJ135isRi6eOzVTtlPBY2i2yAS2ia2wNU= +github.com/threatwinds/go-sdk v1.1.10/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= @@ -142,10 +139,10 @@ golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b h1:SGYyueaEovpqmWmtTvwtVgo638V/QFE2zlTCnRrR3jg= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b h1:GZxXGdFaHX27ZSMHudWc4FokdD+xl8BC2UJm1OVIEzs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/plugins/geolocation/go.mod b/plugins/geolocation/go.mod index 82e6ad8fc..0ac2ba241 100644 --- a/plugins/geolocation/go.mod +++ b/plugins/geolocation/go.mod @@ -3,7 +3,7 @@ module github.com/utmstack/UTMStack/plugins/geolocation go 1.25.5 require ( - github.com/threatwinds/go-sdk v1.1.9 + github.com/threatwinds/go-sdk v1.1.10 github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 ) @@ -15,7 +15,7 @@ require ( github.com/bytedance/sonic v1.15.0 // indirect github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect - github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-gonic/gin v1.11.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -23,7 +23,7 @@ require ( github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.2 // indirect - github.com/google/cel-go v0.26.1 // indirect + github.com/google/cel-go v0.27.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -36,7 +36,6 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect @@ -48,8 +47,8 @@ require ( golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b // indirect google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/plugins/geolocation/go.sum b/plugins/geolocation/go.sum index 96b622d6e..f69c3240f 100644 --- a/plugins/geolocation/go.sum +++ b/plugins/geolocation/go.sum @@ -14,8 +14,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= -github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= @@ -38,8 +38,8 @@ github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= -github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= +github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -77,8 +77,6 @@ github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SA github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -86,13 +84,12 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/threatwinds/go-sdk v1.1.9 h1:4i8UZczXyGbRJsUEHRgaS2oQ03VTRjh/DYyTtGdSfRA= -github.com/threatwinds/go-sdk v1.1.9/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.10 h1:joqXa0K8DiiJ135isRi6eOzVTtlPBY2i2yAS2ia2wNU= +github.com/threatwinds/go-sdk v1.1.10/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -143,10 +140,10 @@ golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b h1:SGYyueaEovpqmWmtTvwtVgo638V/QFE2zlTCnRrR3jg= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b h1:GZxXGdFaHX27ZSMHudWc4FokdD+xl8BC2UJm1OVIEzs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/plugins/stats/go.mod b/plugins/stats/go.mod index 178d81425..acbcafcbf 100644 --- a/plugins/stats/go.mod +++ b/plugins/stats/go.mod @@ -4,7 +4,7 @@ go 1.25.5 require ( github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.9 + github.com/threatwinds/go-sdk v1.1.10 google.golang.org/protobuf v1.36.11 ) @@ -15,7 +15,7 @@ require ( github.com/bytedance/sonic v1.15.0 // indirect github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect - github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-gonic/gin v1.11.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -23,7 +23,7 @@ require ( github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.2 // indirect - github.com/google/cel-go v0.26.1 // indirect + github.com/google/cel-go v0.27.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect @@ -35,7 +35,6 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect @@ -48,8 +47,8 @@ require ( golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b // indirect google.golang.org/grpc v1.78.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect diff --git a/plugins/stats/go.sum b/plugins/stats/go.sum index 9978feb4f..0c6ce469a 100644 --- a/plugins/stats/go.sum +++ b/plugins/stats/go.sum @@ -14,8 +14,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= -github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= @@ -38,8 +38,8 @@ github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= -github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= +github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -77,8 +77,6 @@ github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SA github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -86,13 +84,12 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/threatwinds/go-sdk v1.1.9 h1:4i8UZczXyGbRJsUEHRgaS2oQ03VTRjh/DYyTtGdSfRA= -github.com/threatwinds/go-sdk v1.1.9/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.10 h1:joqXa0K8DiiJ135isRi6eOzVTtlPBY2i2yAS2ia2wNU= +github.com/threatwinds/go-sdk v1.1.10/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= @@ -142,10 +139,10 @@ golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b h1:SGYyueaEovpqmWmtTvwtVgo638V/QFE2zlTCnRrR3jg= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b h1:GZxXGdFaHX27ZSMHudWc4FokdD+xl8BC2UJm1OVIEzs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= From 2eb57415ac39710f49deba59fc979c65caaff61a Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Mon, 2 Feb 2026 19:21:35 +0000 Subject: [PATCH 015/105] docs(README): enhance formatting and improve badge layout --- README.md | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index b4c14ab6d..553f1624f 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,32 @@ -

-
- UTMStack -

+# UTMStack

- - - - - - + + UTMStack +

-

Enterprise-ready SIEM and XDR powered by Real-Time correlation and Threat Intelligence

+[![Contributors](https://img.shields.io/github/contributors-anon/utmstack/utmstack)](https://github.com/utmstack/UTMStack/graphs/contributors) +[![Release](https://img.shields.io/github/release/utmstack/utmstack)](https://github.com/utmstack/UTMStack/releases/) +[![Issues](https://img.shields.io/github/issues-raw/utmstack/utmstack)](https://github.com/utmstack/UTMStack/issues) +[![Commit Activity](https://img.shields.io/github/commit-activity/m/utmstack/utmstack)](https://github.com/utmstack/UTMStack/commits/main) +[![License](https://img.shields.io/github/license/ad-aures/castopod?color=blue)](https://github.com/utmstack/UTMStack/blob/master/LICENSE) +[![Discord](https://img.shields.io/discord/1154016563775672400.svg?logo=discord)](https://discord.gg/ZznvZ8xcHh) + +#### Enterprise-ready SIEM and XDR powered by Real-Time correlation and Threat Intelligence ## Introduction Welcome to the UTMStack open-source project! UTMStack is a unified threat management platform that merges SIEM (Security Information and Event Management) and XDR (Extended Detection and Response) technologies. Our unique approach allows real-time correlation of log data, threat intelligence, and malware activity patterns from multiple sources, enabling the identification and halting of complex threats that use stealthy techniques. Visit an [online demo here.](https://utmstack.com/demo) -

-UTMStack UTMStack

+

+ + UTMStack + + + UTMStack + +

## Features @@ -31,7 +38,6 @@ Welcome to the UTMStack open-source project! UTMStack is a unified threat manage - SOC AI-Powered Analysis - Security Compliance - ## Why UTMStack? UTMStack stands out in threat prevention by surpassing the boundaries of traditional systems. Our software platform can swiftly analyze log data to identify and halt threats at their source in real-time, even if the threat was not directly detected on the server itself. This seamless integration of SIEM and XDR capabilities sets UTMStack apart from competitors, providing organizations with an effective, holistic cybersecurity suite that enhances threat detection, response, and remediation across clients’ valuable digital infrastructure. Correlation happens before data ingestion, reducing workload and improving response times. From ace8513f59efca59281a0b3b520d3330d985059f Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 2 Feb 2026 18:00:03 -0600 Subject: [PATCH 016/105] fix(notification): improve email sending logic and error handling --- frontend/package.json | 2 +- .../adversary-alerts-graph.component.ts | 37 ++++++++++++------- frontend/src/environments/environment.ts | 2 +- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index aba386502..ee52bf852 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,7 +3,7 @@ "version": "7.1.0", "scripts": { "ng": "ng", - "start": "ng serve --host 0.0.0.0", + "start": "ng serve --host 0.0.0.0 --disable-host-check", "build": "ng build --prod --base-href /", "test": "ng test", "lint": "ng lint", diff --git a/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts b/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts index b2163cb91..cd0faedfd 100644 --- a/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts +++ b/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts @@ -1,5 +1,4 @@ import {Component, Input, OnChanges} from '@angular/core'; -import {ECharts} from 'echarts'; import {UtmAlertType} from '../../../shared/types/alert/utm-alert.type'; import {Side} from '../../../shared/types/event/event'; import {EventDataTypeEnum} from '../../alert-management/shared/enums/event-data-type.enum'; @@ -50,7 +49,7 @@ export class AdversaryAlertsGraphComponent implements OnChanges { }); } - private getGraphicElements() { + private getGraphicElements(hasAnyChildren: boolean) { const sankey = { left: 10, right: 180, @@ -60,22 +59,28 @@ export class AdversaryAlertsGraphComponent implements OnChanges { const chartElement = document.querySelector('.chart-container'); const chartContainerWidth = chartElement ? chartElement.clientWidth : 1000; - const columnWidth = chartContainerWidth / 3; - const depthPositions = [ - { left: sankey.left, width: columnWidth }, - { left: sankey.left + columnWidth, width: columnWidth }, - { left: sankey.left + (columnWidth * 2), width: columnWidth } - ]; + // Si no hay hijos → solo 2 columnas + const columnCount = hasAnyChildren ? 3 : 2; + const columnWidth = chartContainerWidth / columnCount; + + const depthPositions = Array.from({ length: columnCount }).map((_, i) => ({ + left: sankey.left + columnWidth * i, + width: columnWidth + })); + + const labels = hasAnyChildren + ? ['Adversary', 'Alerts', 'Echoes'] + : ['Adversary', 'Alerts']; - const graphicElements: any[] = []; - const labels = ['Adversary', 'Alerts', 'Echoes']; const colors = [ { fill: 'rgba(31, 119, 180, 0.06)', text: '#1F77B4' }, { fill: 'rgba(255, 127, 14, 0.06)', text: '#FF7F0E' }, { fill: 'rgba(44, 160, 44, 0.06)', text: '#2CA02C' } ]; + const graphicElements: any[] = []; + depthPositions.forEach((pos, index) => { const color = colors[index]; @@ -83,7 +88,10 @@ export class AdversaryAlertsGraphComponent implements OnChanges { type: 'rect', left: pos.left, top: sankey.top, - shape: { width: pos.width - 10, height: this.chartHeight - sankey.top - sankey.bottom }, + shape: { + width: pos.width - 10, + height: this.chartHeight - sankey.top - sankey.bottom + }, style: { fill: color.fill, stroke: 'none' @@ -92,7 +100,7 @@ export class AdversaryAlertsGraphComponent implements OnChanges { graphicElements.push({ type: 'text', - left: pos.left + (pos.width / 2), + left: pos.left + pos.width / 2, top: 0, style: { text: labels[index], @@ -181,7 +189,7 @@ export class AdversaryAlertsGraphComponent implements OnChanges { meta: { alert: { ...alert, - hasChildren: true + hasChildren: childCount > 0 }, severity: alert.severityLabel, timestamp: alert.timestamp, @@ -249,6 +257,9 @@ export class AdversaryAlertsGraphComponent implements OnChanges { const totalNodes = nodes.length; this.chartHeight = this.baseHeight + totalNodes * this.nodeGap; + console.log('totalNodes', nodes); + console.log('Links', links); + return { tooltip: { trigger: 'item', diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index 066a347a4..c14489291 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -4,7 +4,7 @@ export const environment = { production: false, - SERVER_API_URL: 'https://10.11.12.113/', + SERVER_API_URL: 'https://192.168.1.18/', //SERVER_API_URL: 'http://localhost:8080/', SERVER_API_CONTEXT: '', SESSION_AUTH_TOKEN: window.location.host.split(':')[0].toLocaleUpperCase(), From 1ef7e24cbdd9eac3554385cd997354024f732849 Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Tue, 3 Feb 2026 14:21:18 +0000 Subject: [PATCH 017/105] feat(config): add state change detection for dynamic updates - Introduced `ConfigState` to track timestamp updates for assets, rules, filters, and patterns. - Implemented `hasChanges` to detect modifications in database configurations and trigger updates accordingly. --- plugins/config/main.go | 56 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/plugins/config/main.go b/plugins/config/main.go index c15b6c4ac..3db45fca5 100644 --- a/plugins/config/main.go +++ b/plugins/config/main.go @@ -73,6 +73,13 @@ type ExpressionBackend struct { Value interface{} `yaml:"value"` } +type ConfigState struct { + AssetsLastUpdate time.Time + RulesLastUpdate time.Time + FiltersLastUpdate time.Time + PatternsLastUpdate time.Time +} + func (b *ExpressionBackend) ToExpression() Expression { return Expression{ Field: b.Field, @@ -267,6 +274,8 @@ func main() { return } + state := &ConfigState{} + for { func() { db, err := connect() @@ -284,6 +293,17 @@ func main() { } }() + changed, newState, err := hasChanges(db, state) + if err != nil { + _ = catcher.Error("failed to check for changes", err, map[string]any{"process": "plugin_com.utmstack.config"}) + time.Sleep(30 * time.Second) + return + } + + if !changed { + return + } + filters, err := getFilters(db) if err != nil { _ = catcher.Error("failed to get filters", err, map[string]any{"process": "plugin_com.utmstack.config"}) @@ -398,12 +418,46 @@ func main() { time.Sleep(30 * time.Second) return } + + *state = newState }() - time.Sleep(5 * time.Minute) + time.Sleep(30 * time.Second) } } +func hasChanges(db *sql.DB, state *ConfigState) (bool, ConfigState, error) { + newState := ConfigState{} + changed := false + + queries := []struct { + query string + target *time.Time + old time.Time + }{ + {"SELECT MAX(last_update) FROM utm_tenant_config", &newState.AssetsLastUpdate, state.AssetsLastUpdate}, + {"SELECT MAX(rule_last_update) FROM utm_correlation_rules", &newState.RulesLastUpdate, state.RulesLastUpdate}, + {"SELECT MAX(updated_at) FROM utm_logstash_filter", &newState.FiltersLastUpdate, state.FiltersLastUpdate}, + {"SELECT MAX(last_update) FROM utm_regex_pattern", &newState.PatternsLastUpdate, state.PatternsLastUpdate}, + } + + for _, q := range queries { + var lastUpdate sql.NullTime + err := db.QueryRow(q.query).Scan(&lastUpdate) + if err != nil { + return false, newState, err + } + if lastUpdate.Valid { + *q.target = lastUpdate.Time + } + if (*q.target).After(q.old) { + changed = true + } + } + + return changed, newState, nil +} + // connect to postgres database func connect() (*sql.DB, error) { pCfg := plugins.PluginCfg("com.utmstack") From b007feda04e442479262488a4e0d991f9cc34fcd Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Tue, 3 Feb 2026 14:44:08 +0000 Subject: [PATCH 018/105] refactor(alerts): streamline `AlertFields` struct and initialization - Removed unused fields from the `AlertFields` struct. - Simplified initialization logic by directly setting required fields. --- plugins/alerts/main.go | 75 ++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/plugins/alerts/main.go b/plugins/alerts/main.go index cd49f6d40..8df59ba22 100644 --- a/plugins/alerts/main.go +++ b/plugins/alerts/main.go @@ -24,35 +24,22 @@ type IncidentDetail struct { } type AlertFields struct { - Timestamp string `json:"@timestamp"` - ID string `json:"id"` - ParentID *string `json:"parentId,omitempty"` - Status int `json:"status"` - StatusLabel string `json:"statusLabel"` - StatusObservation string `json:"statusObservation"` - IsIncident bool `json:"isIncident"` - IncidentDetail IncidentDetail `json:"incidentDetail"` - Name string `json:"name"` - Category string `json:"category"` - Severity int `json:"severity"` - SeverityLabel string `json:"severityLabel"` - Description string `json:"description"` - Solution string `json:"solution"` - Technique string `json:"technique"` - Reference []string `json:"reference"` - DataType string `json:"dataType"` - Impact *plugins.Impact `json:"impact"` - ImpactScore uint32 `json:"impactScore"` - DataSource string `json:"dataSource"` - Adversary *plugins.Side `json:"adversary"` - Target *plugins.Side `json:"target"` - Events []*plugins.Event `json:"events"` - LastEvent *plugins.Event `json:"lastEvent"` - Tags []string `json:"tags"` - Notes string `json:"notes"` - TagRulesApplied []int `json:"tagRulesApplied"` - DeduplicatedBy []string `json:"deduplicatedBy"` - GroupedBy []string `json:"groupedBy"` + Status int `json:"status"` + StatusLabel string `json:"statusLabel"` + StatusObservation string `json:"statusObservation"` + IsIncident bool `json:"isIncident"` + IncidentDetail IncidentDetail `json:"incidentDetail"` + Severity int `json:"severity"` + SeverityLabel string `json:"severityLabel"` + Solution string `json:"solution"` + Reference []string `json:"reference"` + LastEvent *plugins.Event `json:"lastEvent"` + Tags []string `json:"tags"` + Notes string `json:"notes"` + TagRulesApplied []int `json:"tagRulesApplied"` + DeduplicatedBy []string `json:"deduplicatedBy"` + GroupedBy []string `json:"groupedBy"` + plugins.Alert } func main() { @@ -329,22 +316,11 @@ func newAlert(alert *plugins.Alert, parentId *string) error { } a := AlertFields{ - Timestamp: alert.Timestamp, - ID: alert.Id, - ParentID: parentId, Status: 1, StatusLabel: "Automatic review", - Name: alert.Name, - Category: alert.Category, Severity: severityN, SeverityLabel: severityLabel, - Description: alert.Description, - Technique: alert.Technique, Reference: alert.References, - DataType: alert.DataType, - DataSource: alert.DataSource, - Adversary: alert.Adversary, - Target: alert.Target, LastEvent: func() *plugins.Event { l := len(alert.Events) if l == 0 { @@ -352,13 +328,26 @@ func newAlert(alert *plugins.Alert, parentId *string) error { } return alert.Events[l-1] }(), - Events: alert.Events, - Impact: alert.Impact, - ImpactScore: alert.ImpactScore, DeduplicatedBy: alert.DeduplicateBy, GroupedBy: alert.GroupBy, } + a.Timestamp = alert.Timestamp + a.Id = alert.Id + a.ParentId = alert.ParentId + a.Name = alert.Name + a.Category = alert.Category + a.Description = alert.Description + a.Technique = alert.Technique + a.DataSource = alert.DataSource + a.DataType = alert.DataType + a.Adversary = alert.Adversary + a.Target = alert.Target + a.Events = alert.Events + a.Impact = alert.Impact + a.ImpactScore = alert.ImpactScore + a.Errors = alert.Errors + // Retry logic for indexing operation maxRetries := 3 retryDelay := 2 * time.Second From 6fbc0fa70218fba3bcc10a48cbe5567198a5ecf5 Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Tue, 3 Feb 2026 14:53:50 +0000 Subject: [PATCH 019/105] chore(tests): remove integration tests and update SDK to v1.1.11 - Deleted `integration_test.go` file as part of test cleanup. - Upgraded `github.com/threatwinds/go-sdk` to v1.1.11 in `go.mod` and `go.sum`. --- plugins/alerts/go.mod | 4 +- plugins/alerts/go.sum | 4 +- plugins/alerts/integration_test.go | 414 ----------------------------- 3 files changed, 4 insertions(+), 418 deletions(-) delete mode 100644 plugins/alerts/integration_test.go diff --git a/plugins/alerts/go.mod b/plugins/alerts/go.mod index d48c7c6dc..091402ac7 100644 --- a/plugins/alerts/go.mod +++ b/plugins/alerts/go.mod @@ -3,8 +3,7 @@ module github.com/utmstack/UTMStack/plugins/alerts go 1.25.5 require ( - github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.10 + github.com/threatwinds/go-sdk v1.1.11 github.com/tidwall/gjson v1.18.0 google.golang.org/protobuf v1.36.11 ) @@ -25,6 +24,7 @@ require ( github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/google/cel-go v0.27.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect diff --git a/plugins/alerts/go.sum b/plugins/alerts/go.sum index 0c6ce469a..52dbfb2d2 100644 --- a/plugins/alerts/go.sum +++ b/plugins/alerts/go.sum @@ -88,8 +88,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/threatwinds/go-sdk v1.1.10 h1:joqXa0K8DiiJ135isRi6eOzVTtlPBY2i2yAS2ia2wNU= -github.com/threatwinds/go-sdk v1.1.10/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.11 h1:3S/sgT9qZaNK+YthSQ5b7D8ucJu70LhQg6yEFSaSqy8= +github.com/threatwinds/go-sdk v1.1.11/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= diff --git a/plugins/alerts/integration_test.go b/plugins/alerts/integration_test.go deleted file mode 100644 index 308ff1d5a..000000000 --- a/plugins/alerts/integration_test.go +++ /dev/null @@ -1,414 +0,0 @@ -package main - -import ( - "context" - "fmt" - "os" - "strings" - "testing" - "time" - - "github.com/google/uuid" - sdkos "github.com/threatwinds/go-sdk/os" - "github.com/threatwinds/go-sdk/plugins" - "google.golang.org/protobuf/types/known/structpb" -) - -func TestAlertsIntegration(t *testing.T) { - // 1. Setup OpenSearch connection - nodesEnv := os.Getenv("NODES") - if nodesEnv == "" { - t.Skip("NODES env var not set, skipping integration test") - } - - // Fix for http vs https mismatch in test environment - if strings.HasPrefix(nodesEnv, "https://") { - nodesEnv = strings.Replace(nodesEnv, "https://", "http://", 1) - } - if !strings.HasPrefix(nodesEnv, "http://") && !strings.HasPrefix(nodesEnv, "https://") { - nodesEnv = "http://" + nodesEnv - } - - err := sdkos.Connect([]string{nodesEnv}, os.Getenv("USER"), os.Getenv("PASSWORD")) - if err != nil { - t.Fatalf("Failed to connect to OpenSearch: %v", err) - } - - // Helper to create a test alert - createAlert := func(name, user string, dedup []string) *plugins.Alert { - return &plugins.Alert{ - Id: uuid.NewString(), - Name: name, - Description: "Integration Test Alert", - Timestamp: time.Now().UTC().Format(time.RFC3339Nano), - Severity: "low", - Events: []*plugins.Event{ - { - Log: map[string]*structpb.Value{ - "user": structpb.NewStringValue(user), - }, - }, - }, - DeduplicateBy: dedup, - GroupBy: dedup, // Default to using same fields for GroupBy in legacy tests - } - } - - // Helper to clean up - alertIndex := sdkos.BuildIndexPattern("v11", "alert") - runID := uuid.NewString() - - t.Run("Deduplication_Basic", func(t *testing.T) { - alertName := "DedupTest-" + runID - dedupFields := []string{"name"} - - // 1. Send First Alert - alert1 := createAlert(alertName, "user1", dedupFields) - _, err := correlate(context.Background(), alert1) - if err != nil { - t.Fatalf("First correlate failed: %v", err) - } - - // Allow OS to index - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - // 2. Send Second Alert (DIFFERENT user so it's not a duplicate, but SAME name so it's grouped) - alert2 := createAlert(alertName, "user2", dedupFields) - alert2.DeduplicateBy = []string{"name", "events.0.log.user"} - alert2.GroupBy = []string{"name"} - - _, err = correlate(context.Background(), alert2) - if err != nil { - t.Fatalf("Second correlate failed: %v", err) - } - - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - // Fetch alert2 - a2Doc, err := getAlertFromOS(alert2.Id) - if err != nil { - t.Fatalf("Failed to fetch alert 2: %v", err) - } - - if a2Doc.ParentID == nil { - t.Errorf("Expected Alert 2 to have ParentID, got nil") - } else if *a2Doc.ParentID != alert1.Id { - t.Errorf("Expected Alert 2 ParentID to be %s, got %s", alert1.Id, *a2Doc.ParentID) - } - }) - - t.Run("Deduplication_GroupBy_Fields", func(t *testing.T) { - alertName := "GroupByTest-" + runID - groupBy := []string{"name"} - dedupFields := []string{"name", "events.0.log.user"} - - // 1. User A - First Alert - alertA1 := createAlert(alertName, "userA", dedupFields) - alertA1.GroupBy = groupBy - - _, err := correlate(context.Background(), alertA1) - if err != nil { - t.Fatal(err) - } - - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - // 2. User A - Second Alert -> DUPLICATE -> Should be dropped - alertA2_dup := createAlert(alertName, "userA", dedupFields) - alertA2_dup.GroupBy = groupBy - _, err = correlate(context.Background(), alertA2_dup) - if err != nil { - t.Fatal(err) - } - - // 3. User B - First Alert -> DIFFERENT for Dedup, SAME for GroupBy -> Should be linked - alertB1 := createAlert(alertName, "userB", dedupFields) - alertB1.GroupBy = groupBy - _, err = correlate(context.Background(), alertB1) - if err != nil { - t.Fatal(err) - } - - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - // Verify A2_dup is NOT in OS - _, err = getAlertFromOS(alertA2_dup.Id) - if err == nil { - t.Errorf("Expected A2_dup to be dropped") - } - - // Verify B1 has parent A1 - docB1, err := getAlertFromOS(alertB1.Id) - if err != nil { - t.Fatalf("Failed to get B1: %v", err) - } - if docB1.ParentID == nil || *docB1.ParentID != alertA1.Id { - t.Errorf("Expected B1 parent to be A1. Got: %v", docB1.ParentID) - } - }) - - // Helper to get hit from OS - getHit := func(id string) (*sdkos.Hit, error) { - query := sdkos.SearchRequest{ - Query: &sdkos.Query{ - Term: map[string]map[string]interface{}{ - "id.keyword": { - "value": id, - }, - }, - }, - Size: 1, - } - hits, err := query.WideSearchIn(context.Background(), []string{sdkos.BuildIndexPattern("v11", "alert")}) - if err != nil { - return nil, err - } - if hits.Hits.Total.Value == 0 { - return nil, fmt.Errorf("not found") - } - return &hits.Hits.Hits[0], nil - } - - t.Run("Deduplication_Reopen_Closed", func(t *testing.T) { - alertName := "ReopenTest-" + runID - dedupFields := []string{"name"} - - // 1. Create Parent Alert - alertParent := createAlert(alertName, "user1", dedupFields) - _, err := correlate(context.Background(), alertParent) - if err != nil { - t.Fatal(err) - } - - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - // 2. Manually Close the Parent Alert - hit, err := getHit(alertParent.Id) - if err != nil { - t.Fatal(err) - } - - var a AlertFields - hit.Source.ParseSource(&a) - a.Status = 5 - hit.Source.SetSource(a) - - err = hit.Save(context.Background()) - if err != nil { - t.Fatal("Failed to save closed alert:", err) - } - - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - // 3. Create Child Alert (DIFFERENT user so it's not a duplicate, but SAME name so it's grouped) - alertChild := createAlert(alertName, "user2", dedupFields) - alertChild.DeduplicateBy = []string{"name", "events.0.log.user"} - alertChild.GroupBy = []string{"name"} - _, err = correlate(context.Background(), alertChild) - if err != nil { - t.Fatal(err) - } - - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - // 4. Verify Parent is Re-opened (Status 2) - docParentReopened, err := getAlertFromOS(alertParent.Id) - if err != nil { - t.Fatal(err) - } - - if docParentReopened.Status != 2 { - t.Errorf("Expected Parent Alert to be re-opened (Status 2), but got %d", docParentReopened.Status) - } - - // Verify Grouping - docChild, _ := getAlertFromOS(alertChild.Id) - if docChild.ParentID == nil || *docChild.ParentID != alertParent.Id { - t.Errorf("Expected Child to group with Parent %s, got %v", alertParent.Id, docChild.ParentID) - } - }) - - t.Run("Deduplication_Missing_Field", func(t *testing.T) { - alertName := "MissingFieldTest-" + runID - dedupFields := []string{"name", "events.0.log.user"} - - alertNoUser := &plugins.Alert{ - Id: uuid.NewString(), - Name: alertName, - Timestamp: time.Now().UTC().Format(time.RFC3339Nano), - Severity: "low", - Events: []*plugins.Event{{Log: map[string]*structpb.Value{}}}, - DeduplicateBy: dedupFields, - GroupBy: dedupFields, - } - - alertWithUser := createAlert(alertName, "bob", dedupFields) - - _, err := correlate(context.Background(), alertNoUser) - if err != nil { - t.Fatal(err) - } - - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - _, err = correlate(context.Background(), alertWithUser) - if err != nil { - t.Fatal(err) - } - - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - docWithUser, _ := getAlertFromOS(alertWithUser.Id) - if docWithUser.ParentID != nil && *docWithUser.ParentID == alertNoUser.Id { - t.Errorf("Alert with User='bob' incorrectly deduped with Alert (User=Missing)") - } - - alertNoUser2 := &plugins.Alert{ - Id: uuid.NewString(), - Name: alertName, - Timestamp: time.Now().UTC().Format(time.RFC3339Nano), - Severity: "low", - Events: []*plugins.Event{{Log: map[string]*structpb.Value{"diff": structpb.NewStringValue("yes")}}}, - DeduplicateBy: dedupFields, - GroupBy: []string{"name"}, - } - - _, err = correlate(context.Background(), alertNoUser2) - if err != nil { - t.Fatal(err) - } - - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - docNoUser2, err := getAlertFromOS(alertNoUser2.Id) - if err != nil { - t.Logf("Alert(NoUser2) correctly dropped or not found yet: %v", err) - } else if docNoUser2.ParentID == nil { - t.Errorf("Expected Alert(NoUser2) to find a parent") - } - }) - - t.Run("Deduplication_Empty_List", func(t *testing.T) { - alertName := "EmptyDedup-" + runID - alert1 := createAlert(alertName, "user1", []string{}) - _, err := correlate(context.Background(), alert1) - if err != nil { - t.Fatal(err) - } - alert2 := createAlert(alertName, "user1", []string{}) - _, err = correlate(context.Background(), alert2) - if err != nil { - t.Fatal(err) - } - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - doc1, _ := getAlertFromOS(alert1.Id) - doc2, _ := getAlertFromOS(alert2.Id) - if doc1 != nil && doc1.ParentID != nil { - t.Errorf("Alert 1 should be parent") - } - if doc2 != nil && doc2.ParentID != nil { - t.Errorf("Alert 2 should be separate parent") - } - }) - - t.Run("Deduplication_vs_GroupBy", func(t *testing.T) { - alertName := "DedupVsGroup-" + runID - groupBy := []string{"name"} - dedupBy := []string{"name", "events.0.log.user"} - - alert1 := &plugins.Alert{ - Id: uuid.NewString(), - Name: alertName, - Timestamp: time.Now().UTC().Format(time.RFC3339Nano), - Events: []*plugins.Event{{Log: map[string]*structpb.Value{"user": structpb.NewStringValue("userA")}}}, - DeduplicateBy: dedupBy, - GroupBy: groupBy, - } - _, err := correlate(context.Background(), alert1) - if err != nil { - t.Fatal(err) - } - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - alert2 := &plugins.Alert{ - Id: uuid.NewString(), - Name: alertName, - Timestamp: time.Now().UTC().Format(time.RFC3339Nano), - Events: []*plugins.Event{{Log: map[string]*structpb.Value{"user": structpb.NewStringValue("userA")}}}, - DeduplicateBy: dedupBy, - GroupBy: groupBy, - } - _, err = correlate(context.Background(), alert2) - if err != nil { - t.Fatal(err) - } - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - - _, err = getAlertFromOS(alert2.Id) - if err == nil { - t.Errorf("Alert 2 (Duplicate) should not have been indexed") - } - - alert3 := &plugins.Alert{ - Id: uuid.NewString(), - Name: alertName, - Timestamp: time.Now().UTC().Format(time.RFC3339Nano), - Events: []*plugins.Event{{Log: map[string]*structpb.Value{"user": structpb.NewStringValue("userB")}}}, - DeduplicateBy: dedupBy, - GroupBy: groupBy, - } - _, err = correlate(context.Background(), alert3) - if err != nil { - t.Fatal(err) - } - time.Sleep(1 * time.Second) - sdkos.RefreshIndex(context.Background(), alertIndex) - doc3, err := getAlertFromOS(alert3.Id) - if err != nil { - t.Fatalf("Failed to get Alert 3: %v", err) - } - if doc3.ParentID == nil || *doc3.ParentID != alert1.Id { - t.Errorf("Alert 3 should have been grouped with Alert 1. Got: %v", doc3.ParentID) - } - }) -} - -func getAlertFromOS(id string) (*AlertFields, error) { - query := sdkos.SearchRequest{ - Query: &sdkos.Query{ - Term: map[string]map[string]interface{}{ - "id.keyword": { - "value": id, - }, - }, - }, - Size: 1, - } - ctx := context.Background() - hits, err := query.WideSearchIn(ctx, []string{sdkos.BuildIndexPattern("v11", "alert")}) - if err != nil { - return nil, err - } - if hits.Hits.Total.Value == 0 { - return nil, fmt.Errorf("alert not found") - } - var a AlertFields - err = hits.Hits.Hits[0].Source.ParseSource(&a) - if err != nil { - return nil, err - } - return &a, nil -} From d27805edd3178103edc87ddbebf4a535041ba942 Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Tue, 3 Feb 2026 15:09:31 +0000 Subject: [PATCH 020/105] docs(README): add detailed plugin overviews for alerts and config components --- plugins/alerts/README.md | 43 ++++++++++++++++ plugins/config/README.md | 103 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) diff --git a/plugins/alerts/README.md b/plugins/alerts/README.md index 631c4b320..2f917a1e8 100644 --- a/plugins/alerts/README.md +++ b/plugins/alerts/README.md @@ -1 +1,44 @@ # UTMStack Alerts Plugin + +This plugin handles alert correlation and deduplication for UTMStack. It processes incoming alerts, identifies duplicates, and groups related alerts to maintain a clean and manageable alert environment. + +## Features + +- **Alert Deduplication**: Automatically detects and skips duplicate alerts based on configured fields. +- **Alert Correlation**: Groups related alerts by linking them to a parent alert using specified fields. +- **Status Management**: Updates the status of parent alerts when new related events occur (e.g., re-opening completed alerts). +- **OpenSearch Integration**: Stores and updates alert documents directly in OpenSearch. +- **Resilience**: Implements retry logic for search and indexing operations and recovers from panics during alert processing. + +## Configuration + +The plugin requires the following configuration (usually provided via `org.opensearch` configuration): + +- `opensearch`: The URL of the OpenSearch cluster to connect to. + +## How it works + +1. **Initialization**: Connects to OpenSearch and registers itself as a correlation plugin. +2. **Correlation Logic**: + - Checks if the incoming alert is a duplicate based on `DeduplicateBy` fields. + - If not a duplicate, searches for a previous alert to link to based on `GroupBy` fields. + - If a parent alert is found, it updates the parent's status to "Open" if it was previously "Completed". +3. **Indexing**: Creates a new alert document in the `alert` index with metadata like severity, status, and related events. + +## Installation + +This plugin is part of the UTMStack ecosystem and is typically deployed as a containerized service. + +## Development + +To build the plugin: + +```bash +go build -o alerts-plugin main.go +``` + +## Dependencies + +- `github.com/threatwinds/go-sdk` +- `github.com/tidwall/gjson` +- `google.golang.org/protobuf` diff --git a/plugins/config/README.md b/plugins/config/README.md index cf4c0ba15..6ea788d8c 100644 --- a/plugins/config/README.md +++ b/plugins/config/README.md @@ -1 +1,104 @@ # UTMStack Config Plugin + +A lightweight service that keeps UTMStack detection content (rules, filters, patterns, and tenants) in sync from PostgreSQL to the local plugin work directory used by the ThreatWinds SDK. + +It periodically checks the database for updates and, when changes are detected, rewrites the YAML files that power the correlation and pipeline layers. + +## What it does +- Connects to PostgreSQL using settings under the `com.utmstack` configuration group. +- Detects changes in these tables (by their last-update timestamps): + - `utm_tenant_config` (assets/tenants) + - `utm_correlation_rules` (rules) + - `utm_logstash_filter` (pipeline filters) + - `utm_regex_pattern` (Grok/regex patterns) +- Writes files into the plugin work directory (`plugins.WorkDir`): + - `pipeline/tenants.yaml` — tenant and assets configuration + - `pipeline/patterns.yaml` — regex/grok patterns + - `pipeline/filters/.yaml` — Logstash/Log pipeline snippets, one file per active filter + - `rules/utmstack/.yaml` — correlation rules, one file per active rule +- Removes files that no longer exist in the database (safe cleanup per folder). +- Uses an inter-process lock so only one writer runs at a time. + +The loop runs every 30 seconds. If the environment mode is set to `playground`, the program exits immediately (disabled mode). + +## Configuration +This plugin relies on ThreatWinds Go SDK configuration. Two config namespaces are used: + +1) Database credentials: `com.utmstack` +``` +com: + utmstack: + postgresql: + server: 127.0.0.1 + port: 5432 + database: utmstack + user: utmstack + password: change_me +``` + +2) Plugin runtime options: `plugin_com.utmstack.config` +``` +plugin_com: + utmstack: + config: + env: + mode: prod # set to "playground" to disable the loop +``` + +Notes: +- The exact loading mechanism (file path, env) is provided by the ThreatWinds SDK; ensure the process can read these settings. +- `plugins.WorkDir` is also provided by the SDK. By default it points to the plugin’s writable work directory. + +## Database expectations +- Filters are read when `utm_logstash_filter.is_active = true` and written as `.yaml`. +- Rules are read when `utm_correlation_rules.rule_active = true` and written as `.yaml`. +- Rule data types are resolved from `utm_group_rules_data_type` and `utm_data_types`. +- Assets come from `utm_tenant_config` and are exported into `pipeline/tenants.yaml` as a single default tenant. +- Patterns come from `utm_regex_pattern` and are exported into `pipeline/patterns.yaml`. + +## Build +Requires Go (version as specified in `go.mod`). +``` +go build -o utmstack-config-plugin . +``` + +## Run +Ensure configuration is available to the process (see Configuration section), then run: +``` +./utmstack-config-plugin +``` +Behavior: +- On startup, the service connects to PostgreSQL and checks for changes. +- On change, it regenerates the output files under `plugins.WorkDir`. +- It sleeps for ~30s between checks. + +To temporarily disable processing (e.g., during local development), set the plugin env mode to `playground`. + +## Output layout (relative to plugins.WorkDir) +``` +./pipeline/ + tenants.yaml + patterns.yaml + /filters/ + .yaml +./rules/ + /utmstack/ + .yaml +``` + +## Error handling & logging +- Database, I/O, and serialization errors are wrapped with context via the `catcher` package. +- Locking is handled by the SDK (`plugins.AcquireLock`/`plugins.ReleaseLock`). + +## Development notes +Key functions are implemented in `main.go`: +- Database access: `connect`, `getFilters`, `getRules`, `getPatterns`, `getAssets`, `getRuleDataTypes`. +- Change detection: `hasChanges` (uses `MAX(updated_at)`/`MAX(last_update)` timestamps). +- Writers: `writeFilters`, `writeRules`, `writePatterns`, `writeTenant`. +- Cleanup: `cleanUpFilters`, `cleanUpRules`. + +## Troubleshooting +- Verify that the `com.utmstack` database credentials are correct and reachable. +- Ensure the process has write permissions to the SDK work directory (`plugins.WorkDir`). +- Confirm the plugin mode is not set to `playground`. +- Check logs for messages emitted by the `catcher` error wrapper. From bf646216b91edd3e607b66ec0b1926d310df21d6 Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Tue, 3 Feb 2026 16:03:33 +0000 Subject: [PATCH 021/105] feat(alerts): add `Timestamp` field to `AlertFields` struct - Introduced `Timestamp` field to store alert timestamps. - Updated initialization logic to handle the new field directly. --- plugins/alerts/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/alerts/main.go b/plugins/alerts/main.go index 8df59ba22..1c28e998c 100644 --- a/plugins/alerts/main.go +++ b/plugins/alerts/main.go @@ -24,6 +24,7 @@ type IncidentDetail struct { } type AlertFields struct { + Timestamp string `json:"@timestamp"` Status int `json:"status"` StatusLabel string `json:"statusLabel"` StatusObservation string `json:"statusObservation"` @@ -316,6 +317,7 @@ func newAlert(alert *plugins.Alert, parentId *string) error { } a := AlertFields{ + Timestamp: alert.Timestamp, Status: 1, StatusLabel: "Automatic review", Severity: severityN, @@ -332,7 +334,6 @@ func newAlert(alert *plugins.Alert, parentId *string) error { GroupedBy: alert.GroupBy, } - a.Timestamp = alert.Timestamp a.Id = alert.Id a.ParentId = alert.ParentId a.Name = alert.Name From 1ec490cfda1d4251ae93277d5776f6ae1c2902f3 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 3 Feb 2026 10:50:25 -0600 Subject: [PATCH 022/105] feat(adversary-alerts): enhance chart rendering logic with dynamic column count Signed-off-by: Manuel Abascal --- .../adversary-alerts-graph.component.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts b/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts index cd0faedfd..246ec8be7 100644 --- a/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts +++ b/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts @@ -60,7 +60,6 @@ export class AdversaryAlertsGraphComponent implements OnChanges { const chartElement = document.querySelector('.chart-container'); const chartContainerWidth = chartElement ? chartElement.clientWidth : 1000; - // Si no hay hijos → solo 2 columnas const columnCount = hasAnyChildren ? 3 : 2; const columnWidth = chartContainerWidth / columnCount; @@ -133,6 +132,7 @@ export class AdversaryAlertsGraphComponent implements OnChanges { ]; let colorIndex = 0; + const hasAnyChildren = adversaryAlerts.some( g => g.alerts.some( a => a.children.length > 0 ) ); const nodeKey = (id: string, name: string) => `${id}::${name}`; const truncate = (text: string, max = 30) => text.length > max ? text.slice(0, max) + '…' : text; const getNodeSize = (count: number, base = 10, max = 30) => Math.min(base + count * 2, max); @@ -257,9 +257,6 @@ export class AdversaryAlertsGraphComponent implements OnChanges { const totalNodes = nodes.length; this.chartHeight = this.baseHeight + totalNodes * this.nodeGap; - console.log('totalNodes', nodes); - console.log('Links', links); - return { tooltip: { trigger: 'item', @@ -276,7 +273,7 @@ export class AdversaryAlertsGraphComponent implements OnChanges { `; } }, - graphic: this.getGraphicElements(), + graphic: this.getGraphicElements(hasAnyChildren), series: [{ type: 'sankey', orient: 'horizontal', From d3b2dd9d912725f19e1dfd096db34975654c2409 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 3 Feb 2026 10:50:32 -0600 Subject: [PATCH 023/105] feat(log-analyzer): update SQL editor container styles for responsive design Signed-off-by: Manuel Abascal --- .../log-analyzer-view.component.html | 2 +- .../log-analyzer-view.component.scss | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/log-analyzer/explorer/log-analyzer-view/log-analyzer-view.component.html b/frontend/src/app/log-analyzer/explorer/log-analyzer-view/log-analyzer-view.component.html index ea773e5e3..975fa5f6c 100644 --- a/frontend/src/app/log-analyzer/explorer/log-analyzer-view/log-analyzer-view.component.html +++ b/frontend/src/app/log-analyzer/explorer/log-analyzer-view/log-analyzer-view.component.html @@ -43,7 +43,7 @@ -
+
Date: Tue, 3 Feb 2026 10:51:50 -0600 Subject: [PATCH 024/105] fix(package): restore package.json Signed-off-by: Manuel Abascal --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index ee52bf852..aba386502 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,7 +3,7 @@ "version": "7.1.0", "scripts": { "ng": "ng", - "start": "ng serve --host 0.0.0.0 --disable-host-check", + "start": "ng serve --host 0.0.0.0", "build": "ng build --prod --base-href /", "test": "ng test", "lint": "ng lint", From 5b08e5277d9f6a6d8efff4dedaddc2d72100f9f9 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 3 Feb 2026 11:03:12 -0600 Subject: [PATCH 025/105] feat: update last update timestamp when activating or deactivating a rule --- .../service/correlation/rules/UtmCorrelationRulesService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/java/com/park/utmstack/service/correlation/rules/UtmCorrelationRulesService.java b/backend/src/main/java/com/park/utmstack/service/correlation/rules/UtmCorrelationRulesService.java index 6ae4c80b6..984d1b9dd 100644 --- a/backend/src/main/java/com/park/utmstack/service/correlation/rules/UtmCorrelationRulesService.java +++ b/backend/src/main/java/com/park/utmstack/service/correlation/rules/UtmCorrelationRulesService.java @@ -125,6 +125,7 @@ public void setRuleActivation(Long ruleId, boolean setActive) throws Exception { .orElseThrow(() -> new RuntimeException(ctx + ": The rule you're trying to activate or deactivate is not present in database.")); try { rule.setRuleActive(setActive); + rule.setRuleLastUpdate(Instant.now(Clock.systemUTC())); this.utmCorrelationRulesRepository.save(rule); } catch (Exception ex) { throw new RuntimeException(ctx + ": An error occurred while adding a rule.", ex); From 39360d8456fcb865dc21642a6017492a7691daf1 Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Tue, 3 Feb 2026 19:34:13 +0000 Subject: [PATCH 026/105] refactor(alerts): update `ParentId` initialization to handle nullable values --- plugins/alerts/main.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/alerts/main.go b/plugins/alerts/main.go index 1c28e998c..9245d6b53 100644 --- a/plugins/alerts/main.go +++ b/plugins/alerts/main.go @@ -335,7 +335,12 @@ func newAlert(alert *plugins.Alert, parentId *string) error { } a.Id = alert.Id - a.ParentId = alert.ParentId + a.ParentId = func() string { + if parentId != nil { + return *parentId + } + return "" + }() a.Name = alert.Name a.Category = alert.Category a.Description = alert.Description From 2ee1e050cdf7553b509b9d78c8adbf6cb57df6b4 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 3 Feb 2026 14:27:27 -0600 Subject: [PATCH 027/105] fix: update timestamp handling in correlation rules and logstash filter services --- .../service/correlation/rules/UtmCorrelationRulesService.java | 2 +- .../service/logstash_filter/UtmLogstashFilterService.java | 2 ++ .../utmstack/web/rest/logstash_filter/UtmFilterResource.java | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/correlation/rules/UtmCorrelationRulesService.java b/backend/src/main/java/com/park/utmstack/service/correlation/rules/UtmCorrelationRulesService.java index 984d1b9dd..97033db62 100644 --- a/backend/src/main/java/com/park/utmstack/service/correlation/rules/UtmCorrelationRulesService.java +++ b/backend/src/main/java/com/park/utmstack/service/correlation/rules/UtmCorrelationRulesService.java @@ -125,7 +125,7 @@ public void setRuleActivation(Long ruleId, boolean setActive) throws Exception { .orElseThrow(() -> new RuntimeException(ctx + ": The rule you're trying to activate or deactivate is not present in database.")); try { rule.setRuleActive(setActive); - rule.setRuleLastUpdate(Instant.now(Clock.systemUTC())); + rule.setRuleLastUpdate(Instant.now()); this.utmCorrelationRulesRepository.save(rule); } catch (Exception ex) { throw new RuntimeException(ctx + ": An error occurred while adding a rule.", ex); diff --git a/backend/src/main/java/com/park/utmstack/service/logstash_filter/UtmLogstashFilterService.java b/backend/src/main/java/com/park/utmstack/service/logstash_filter/UtmLogstashFilterService.java index f3d7e5b67..4afb5dd94 100644 --- a/backend/src/main/java/com/park/utmstack/service/logstash_filter/UtmLogstashFilterService.java +++ b/backend/src/main/java/com/park/utmstack/service/logstash_filter/UtmLogstashFilterService.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.Instant; import java.util.List; import java.util.Optional; @@ -36,6 +37,7 @@ public UtmLogstashFilterService(UtmLogstashFilterRepository logstashFilterReposi public UtmLogstashFilter save(UtmLogstashFilter logstashFilter) { final String ctx = CLASSNAME + ".save"; try { + logstashFilter.setUpdatedAt(Instant.now()); logstashFilter.setSystemOwner(false); return logstashFilterRepository.save(logstashFilter); } catch (Exception e) { diff --git a/backend/src/main/java/com/park/utmstack/web/rest/logstash_filter/UtmFilterResource.java b/backend/src/main/java/com/park/utmstack/web/rest/logstash_filter/UtmFilterResource.java index b43929aa7..12060773c 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/logstash_filter/UtmFilterResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/logstash_filter/UtmFilterResource.java @@ -72,7 +72,7 @@ public ResponseEntity createLogstashFilter(@Valid @RequestBod // If you provide a pipelineId we create relation, otherwise only create the filter if (pipelineId!=null) { Optional pipeline = pipelineService.findOne(pipelineId); - if (!pipeline.isPresent()) { + if (pipeline.isEmpty()) { throw new Exception("The pipeline with ID (" + pipelineId + ") not exists"); } UtmLogstashFilter filter = logstashFilterService.save(logstashFilter); From 0ca5bfbc17bfa20ec6aa0c102a810a1b43184427 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Tue, 3 Feb 2026 15:40:33 -0500 Subject: [PATCH 028/105] fix[ci/cd]: solved multiple instance problem in actions --- .github/workflows/v11-deployment-pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/v11-deployment-pipeline.yml b/.github/workflows/v11-deployment-pipeline.yml index 8ac8a5e94..65d09e245 100644 --- a/.github/workflows/v11-deployment-pipeline.yml +++ b/.github/workflows/v11-deployment-pipeline.yml @@ -522,7 +522,7 @@ jobs: -H "Content-Type: application/json" \ -H "id: $auth_id" \ -H "key: $auth_key" \ - -d "{\"instance_id\": \"$instance_id\", \"version\": \"$TAG\"}") + -d "{\"instances_ids\": [\"$instance_id\"], \"version\": \"$TAG\"}") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') From 576874f6a2de4dd53a81f1d3fd4f9229abf72912 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 3 Feb 2026 19:23:59 -0600 Subject: [PATCH 029/105] feat(rule-view): enhance YAML export functionality and improve UI layout Signed-off-by: Manuel Abascal --- .../see-rule/rule-view.component.html | 26 ++++--- .../see-rule/rule-view.component.scss | 76 +++++++++++++++++++ .../see-rule/rule-view.component.ts | 61 ++++++++++++--- .../shared/rule-yaml.mapper.ts | 23 ++++++ 4 files changed, 163 insertions(+), 23 deletions(-) create mode 100644 frontend/src/app/shared/components/utm/util/utm-file-upload/shared/rule-yaml.mapper.ts diff --git a/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.html b/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.html index d326d5738..3780f27e1 100644 --- a/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.html +++ b/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.html @@ -1,17 +1,21 @@ + - +
+
+ {{ 'YAML - ' + rowDocument.name }} +
- +
+ - - -
- + +
-
- - +
+

 
diff --git a/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.scss b/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.scss index 2f8409575..2025c4be1 100644 --- a/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.scss +++ b/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.scss @@ -34,3 +34,79 @@ app-utm-modal-header { transform: scale(1.05); } } + +.yaml-container { + width: 100%; + background: #ffffff; + padding: 16px; + border-radius: 6px; + border: 1px solid #e5e7eb; +} + +.yaml-preview { + white-space: pre-wrap; + font-family: "Fira Code", monospace; + font-size: 14px; +} + +::ng-deep .yaml-key { + color: #0277bd; + font-weight: 600; +} + +::ng-deep .yaml-value { + color: #FF6B6B; +} + +.sub-header { + background: transparent; + border: none; + border-bottom: 1px solid #f0f0f0; + padding: 12px 0; + margin-bottom: 16px; + box-shadow: none; +} + +.sub-header-title { + font-size: 13px; + font-weight: 600; + color: #666666; + letter-spacing: 0.3px; + text-transform: uppercase; + max-width: 70%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.sub-btn { + background: #232f3e; + color: #ffffff; + padding: .375rem .75rem; + font-size: .75rem !important; + border: none; + font-weight: 400; + cursor: pointer; + transition: all 0.2s ease; + display: inline-flex; + align-items: center; + gap: 6px; + white-space: nowrap; + border-radius: .25rem; + + &:hover { + background: #364556; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + transform: translateY(-1px); + } + + &:active { + transform: translateY(0); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15); + } + + i { + font-size: .75rem !important; + } +} + diff --git a/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.ts b/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.ts index 126886e59..d9c0696ba 100644 --- a/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.ts +++ b/frontend/src/app/rule-management/app-rule/components/see-rule/rule-view.component.ts @@ -1,34 +1,71 @@ - -import { Component, Input } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Rule } from '../../../models/rule.model'; +import {Component, Input, OnInit} from '@angular/core'; import * as yaml from 'js-yaml'; +import {mapRuleToYaml} from '../../../../shared/components/utm/util/utm-file-upload/shared/rule-yaml.mapper'; +import {Rule} from '../../../models/rule.model'; @Component({ selector: 'app-rule-view', templateUrl: './rule-view.component.html', styleUrls: ['./rule-view.component.scss'], }) -export class RuleViewComponent { - @Input() rowDocument: Rule; +export class RuleViewComponent implements OnInit { + @Input() rowDocument: Rule; copied = false; + ngOnInit() { + console.log('Rule received:', this.rowDocument); + } + get yamlString(): string { try { - return yaml.dump(this.rowDocument, { indent: 2 }); + const yamlModel = mapRuleToYaml(this.rowDocument); + + return yaml.dump(yamlModel, { + indent: 2, + lineWidth: -1, + styles: { + '!!null': 'empty' + } + }); } catch (e) { - return 'Error parsing YAML'; + return 'Error generating YAML'; } } + get yamlHighlighted(): string { + return this.yamlString + .replace(/^(\s*)([a-zA-Z0-9_]+):/gm, '$1$2:') + .replace(/: (.*)/g, ': $1') + .replace(/-\s+(.*)/g, '- $1') + .replace(/^\s{2,}(.+)/gm, ' $1'); + } + + exportYaml() { + const yamlContent = this.yamlString; + + const blob = new Blob([yamlContent], { + type: 'text/yaml;charset=utf-8' + }); + + const url = window.URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + + a.download = `${this.rowDocument.name || 'rule'}.yml` + .replace(/\s+/g, '-') + .toLowerCase(); + a.click(); + + window.URL.revokeObjectURL(url); + } + + copyToClipboard() { - window.navigator['clipboard'].writeText(this.yamlString).then(() => { + (navigator as any).clipboard.writeText(this.yamlString).then(() => { this.copied = true; setTimeout(() => (this.copied = false), 1500); }); } } - - - diff --git a/frontend/src/app/shared/components/utm/util/utm-file-upload/shared/rule-yaml.mapper.ts b/frontend/src/app/shared/components/utm/util/utm-file-upload/shared/rule-yaml.mapper.ts new file mode 100644 index 000000000..31f8fe053 --- /dev/null +++ b/frontend/src/app/shared/components/utm/util/utm-file-upload/shared/rule-yaml.mapper.ts @@ -0,0 +1,23 @@ +import {Rule} from '../../../../../../rule-management/models/rule.model'; + + +export function mapRuleToYaml(rule: Rule): any { + return { + name: rule.name, + dataTypes: rule.dataTypes ? rule.dataTypes.map(d => d.dataType) : [], + impact: { + confidentiality: rule.confidentiality, + integrity: rule.integrity, + availability: rule.availability + }, + category: rule.category, + technique: rule.technique, + adversary: rule.adversary, + references: rule.references, + description: rule.description, + where: rule.definition, + afterEvents: rule.afterEvents && rule.afterEvents.length ? rule.afterEvents : [], + groupBy: rule.groupBy && rule.groupBy.length ? rule.groupBy : [], + deduplicateBy: rule.deduplicateBy && rule.deduplicateBy.length ? rule.deduplicateBy : [] + }; +} From ad0da80af1435dd1aa9f3e20bad953fd13c252ed Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Feb 2026 07:56:57 -0600 Subject: [PATCH 030/105] fix: rename assetIpMacName to searchQuery in agent sidebar data loading Signed-off-by: Manuel Abascal --- .../shared/component/agent-sidebar/agent-sidebar.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/incident-response/shared/component/agent-sidebar/agent-sidebar.component.ts b/frontend/src/app/incident-response/shared/component/agent-sidebar/agent-sidebar.component.ts index 320d6d518..b1e39fd91 100644 --- a/frontend/src/app/incident-response/shared/component/agent-sidebar/agent-sidebar.component.ts +++ b/frontend/src/app/incident-response/shared/component/agent-sidebar/agent-sidebar.component.ts @@ -27,7 +27,7 @@ export class AgentSidebarComponent implements OnInit { this.agentSidebarService.loadData({ ...this.request, page: 0, - assetIpMacName: $event.toString() + searchQuery: $event.toString() }); } From 6002ad7330e8f484ed2e06f5b057630d313515d8 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Feb 2026 12:34:48 -0600 Subject: [PATCH 031/105] fix: clean up alert detail view logic and HTML formatting Signed-off-by: Manuel Abascal --- .../alert-management/alert-view/alert-view.component.html | 3 +-- .../alert-management/alert-view/alert-view.component.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html index 863052961..208b44871 100644 --- a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html +++ b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html @@ -219,8 +219,7 @@
+ (click)="viewDetailAlert(alert, td)"> 0) { + /*if (alert.echoes > 0) { alert.expanded = true; this.loadChildrenAlerts(alert); - } + }*/ this.alertDetail = alert; this.viewAlertDetail = true; } From 4a2f429f2a5ea8a49be561d7d1540fe362b197a3 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Feb 2026 12:46:38 -0600 Subject: [PATCH 032/105] feat: add constructors to ComplianceFileResponse for improved object initialization --- .../service/compliance/ComplianceFileResponse.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceFileResponse.java b/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceFileResponse.java index 0beccfa66..79653e459 100644 --- a/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceFileResponse.java +++ b/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceFileResponse.java @@ -1,17 +1,19 @@ package com.park.utmstack.service.compliance; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data @Builder +@NoArgsConstructor +@AllArgsConstructor public class ComplianceFileResponse { - // Success fields private byte[] pdfBytes; - - // Error fields private boolean error; private String message; private String details; } + From 6784002b9d527e79361847571135125c99277b6f Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Feb 2026 13:48:17 -0600 Subject: [PATCH 033/105] refactor: improve ComplianceMailService and PdfService for better error handling and code clarity --- .../compliance/ComplianceFileResponse.java | 19 ------ .../compliance/ComplianceMailService.java | 56 ++++++++++------- .../UtmComplianceReportScheduleService.java | 63 ++++++++++++------- .../utmstack/service/util/PdfService.java | 54 +--------------- 4 files changed, 74 insertions(+), 118 deletions(-) delete mode 100644 backend/src/main/java/com/park/utmstack/service/compliance/ComplianceFileResponse.java diff --git a/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceFileResponse.java b/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceFileResponse.java deleted file mode 100644 index 79653e459..000000000 --- a/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceFileResponse.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.park.utmstack.service.compliance; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ComplianceFileResponse { - private byte[] pdfBytes; - private boolean error; - private String message; - private String details; -} - - diff --git a/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceMailService.java b/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceMailService.java index b553d14ba..4567fc274 100644 --- a/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceMailService.java +++ b/backend/src/main/java/com/park/utmstack/service/compliance/ComplianceMailService.java @@ -1,46 +1,56 @@ package com.park.utmstack.service.compliance; import com.park.utmstack.service.MailService; +import com.park.utmstack.service.dto.web_pdf.PdfServiceResponse; import com.park.utmstack.service.util.PdfService; +import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.time.Clock; import java.time.Instant; -/** - * Service Implementation for managing Compliance PDF Delivery. - */ @Service -@Transactional +@RequiredArgsConstructor public class ComplianceMailService { - private final Logger log = LoggerFactory.getLogger(ComplianceMailService.class); + + private static final Logger log = LoggerFactory.getLogger(ComplianceMailService.class); private static final String CLASSNAME = "ComplianceMailService"; + private final MailService mailService; private final PdfService pdfService; - public ComplianceMailService(MailService mailService, - PdfService pdfService) { - this.mailService = mailService; - this.pdfService = pdfService; - } - - /** - * Method to generate dashboard in PDF format and send via email - */ - public void sendComplianceByMail(String url, String userEmail) throws Exception { + public void sendComplianceByMail(String url, String userEmail) { final String ctx = CLASSNAME + ".sendComplianceByMail"; String accessKey = System.getenv("INTERNAL_KEY"); - byte[] pdfInBytes = pdfService.getPdf(url, accessKey,PdfService.PdfAccessTypes.PDF_TYPE_INTERNAL.get()); - if (pdfInBytes != null && pdfInBytes.length > 0) { - mailService.sendComplianceReportEmail(userEmail, "UTMStack Compliance Report Delivery" - , "This is a scheduled email delivery of a Compliance Report, please do not answer this email. ", - "Compliance_Report_" + Instant.now(Clock.systemUTC()) + ".pdf", pdfInBytes); - } else { - log.error(ctx + ": We couldn't send the email, reason: No data returned from PDF service"); + + if (accessKey == null || accessKey.isBlank()) { + log.error("{}: INTERNAL_KEY environment variable is missing", ctx); + return; } + + PdfServiceResponse response = + pdfService.downloadPdf(url, accessKey, PdfService.PdfAccessTypes.PDF_TYPE_INTERNAL.get()); + + if (response.getPdfBytes() == null || response.getPdfBytes().length == 0) { + log.error("{}: PDF service returned empty content for URL {}", ctx, url); + return; + } + + String filename = "Compliance_Report_" + Instant.now(Clock.systemUTC()) + ".pdf"; + + mailService.sendComplianceReportEmail( + userEmail, + "UTMStack Compliance Report Delivery", + "This is a scheduled email delivery of a Compliance Report, please do not answer this email.", + filename, + response.getPdfBytes() + ); + + log.info("{}: Email successfully sent to {}", ctx, userEmail); + } } + diff --git a/backend/src/main/java/com/park/utmstack/service/compliance/UtmComplianceReportScheduleService.java b/backend/src/main/java/com/park/utmstack/service/compliance/UtmComplianceReportScheduleService.java index 9e98a4cd3..67a09fd7e 100644 --- a/backend/src/main/java/com/park/utmstack/service/compliance/UtmComplianceReportScheduleService.java +++ b/backend/src/main/java/com/park/utmstack/service/compliance/UtmComplianceReportScheduleService.java @@ -11,12 +11,14 @@ import com.park.utmstack.service.UserService; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.dto.compliance.UtmComplianceReportScheduleCriteria; +import com.park.utmstack.util.exceptions.ApiException; import com.park.utmstack.web.rest.errors.BadRequestAlertException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; +import org.springframework.http.HttpStatus; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.support.CronExpression; import org.springframework.stereotype.Service; @@ -128,7 +130,7 @@ public Optional findByComplianceReportValues(UtmCom log.debug("Request to get UtmComplianceReportSchedule : {}", reportSchedule); User user = userService.getCurrentUserLogin(); return utmComplianceReportScheduleRepository.findFirstByUserIdAndComplianceIdAndScheduleString(user.getId(), - reportSchedule.getComplianceId(),reportSchedule.getScheduleString()); + reportSchedule.getComplianceId(), reportSchedule.getScheduleString()); } /** @@ -144,31 +146,43 @@ public void delete(Long id) { /** * Scheduled method to execute the compliance report pdf generation and email delivery - * */ + * + */ @Scheduled(fixedDelay = 5000, initialDelay = 30000) public void scheduleComplianceReport() { - final String ctx = CLASSNAME + ".scheduleComplianceReport"; List schedulesList = findAll(); + schedulesList.forEach(this::processSchedule); + + } + + private void processSchedule(UtmComplianceReportSchedule schedule) { + + Optional userOpt = userService.getUserWithAuthorities(schedule.getUserId()); + + if (userOpt.isEmpty()) { + log.error("Schedule {} skipped: user {} not found", schedule.getId(), schedule.getUserId()); + return; + } + + User user = userOpt.get(); + + Instant now = Instant.now(Clock.systemUTC()); + Instant next = getNext(schedule.getScheduleString(), schedule.getLastExecutionTime(), now); + + if (!isTimeToExecute(next, now)) { + return; + } + + complianceMailService.sendComplianceByMail(schedule.getUrlWithParams(), user.getEmail()); + markExecuted(schedule, next); - schedulesList.forEach(current -> { - Optional user = userService.getUserWithAuthorities(current.getUserId()); - try { - Instant currentDate = Instant.now(Clock.systemUTC()); - Instant next = getNext(current.getScheduleString(), current.getLastExecutionTime(), currentDate); - if (isTimeToExecute(next, currentDate)) { - // Set the next execution time (Base time seed) - current.setLastExecutionTime(next); - utmComplianceReportScheduleRepository.save(current); - complianceMailService.sendComplianceByMail(current.getUrlWithParams(), user.get().getEmail()); - } - - } catch (Exception e) { - String msg = ctx + ": " + e.getLocalizedMessage(); - log.error(msg); - applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - } - }); + } + + @Transactional + public void markExecuted(UtmComplianceReportSchedule schedule, Instant next) { + schedule.setLastExecutionTime(next); + utmComplianceReportScheduleRepository.save(schedule); } /*** @@ -180,7 +194,8 @@ private boolean isTimeToExecute(Instant next, Instant currentDate) { /** * Method to know the next valid Instant to execute the task, even if the system was shut down for a while - * */ + * + */ private Instant getNext(String cronExpresion, Instant lastExecution, Instant currentDate) { CronExpression parse = CronExpression.parse(cronExpresion); Instant possibleNext = Objects.requireNonNull(parse.next(lastExecution.atZone(ZoneOffset.UTC))).toInstant(); @@ -194,7 +209,7 @@ private Instant getNext(String cronExpresion, Instant lastExecution, Instant cur // near next execution to avoid extra executions, because the general scheduler that calls these methods, // is every 5 seconds Long diffBetweenCurrentAndPossibleNext = currentSecs - possibleNext.getEpochSecond(); - Integer rate = Long.valueOf(diffBetweenCurrentAndPossibleNext/diffBetweenLastAndNext).intValue(); + Integer rate = Long.valueOf(diffBetweenCurrentAndPossibleNext / diffBetweenLastAndNext).intValue(); Instant resultNext = lastExecution.atZone(ZoneOffset.UTC).toInstant().plusSeconds(diffBetweenLastAndNext * rate); return resultNext.atZone(ZoneOffset.UTC).toInstant(); } @@ -205,7 +220,7 @@ private Specification createSpecification(UtmCompli User user = userService.getCurrentUserLogin(); Specification specification = Specification.where((root, query, criteriaBuilder) -> - criteriaBuilder.equal(root.get("userId"), user.getId())); + criteriaBuilder.equal(root.get("userId"), user.getId())); if (criteria != null) { if (criteria.getName() != null) { specification = specification.and(buildSpecification(criteria.getName(), diff --git a/backend/src/main/java/com/park/utmstack/service/util/PdfService.java b/backend/src/main/java/com/park/utmstack/service/util/PdfService.java index 5441694b0..758ef9d5f 100644 --- a/backend/src/main/java/com/park/utmstack/service/util/PdfService.java +++ b/backend/src/main/java/com/park/utmstack/service/util/PdfService.java @@ -1,11 +1,10 @@ package com.park.utmstack.service.util; import com.park.utmstack.config.Constants; -import com.park.utmstack.service.compliance.ComplianceFileResponse; import com.park.utmstack.service.dto.web_pdf.PdfServiceResponse; -import com.park.utmstack.service.federation_service.UtmFederationServiceClientService; import com.park.utmstack.service.web_clients.rest_template.RestTemplateService; import com.park.utmstack.util.exceptions.ApiException; +import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -14,66 +13,17 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.util.UriComponentsBuilder; -import java.util.Objects; - /** * Service Implementation for PDF generation. */ @Service +@RequiredArgsConstructor @Transactional public class PdfService { private final Logger log = LoggerFactory.getLogger(PdfService.class); private static final String CLASSNAME = "PdfService"; - private final UtmFederationServiceClientService fsService; private final RestTemplateService restTemplateService; - private final String COMPLIANCE_EXPORT_URL = "/dashboard/export-compliance/"; - - public PdfService(UtmFederationServiceClientService fsService, - RestTemplateService restTemplateService) { - this.fsService = fsService; - this.restTemplateService = restTemplateService; - } - - /** - * Get pdf report in bytes array. - * - * @param url the url of the compliance report. - * @return the pdf report in bytes array. - */ - @Transactional(readOnly = true) - public ResponseEntity getPdfReportByUrlInBytes(String url, String accessKey, String accessType) throws Exception{ - log.debug("Request to get UtmComplianceReportSchedule : {}", url); - return ResponseEntity.ok().body(getPdf(Constants.FRONT_BASE_URL + url, accessKey, accessType)); - } - - /** - * Method to get pdf in bytes - */ - public byte[] getPdf(String url, String accessKey, String accessType) throws Exception { - final String ctx = CLASSNAME + ".getPdf"; - - String urlService = UriComponentsBuilder.fromUriString(Constants.PDF_SERVICE_URL) - .queryParam("baseUrl", Constants.FRONT_BASE_URL) - .queryParam("url", url) - .queryParam("accessKey", accessKey) - .queryParam("accessType", accessType) - .build().toUriString(); - - ResponseEntity rs = restTemplateService.get(urlService, ComplianceFileResponse.class); - log.info("Requesting PDF creation to URL : {}", Constants.PDF_SERVICE_URL + "?url=" + url); - if (!rs.getStatusCode().is2xxSuccessful()) { - log.error(ctx + ": {}", restTemplateService.extractErrorMessage(rs)); - } else { - byte[] pdfInBytes = Objects.requireNonNull(rs.getBody()).getPdfBytes(); - if (pdfInBytes != null && pdfInBytes.length > 0) { - return pdfInBytes; - } else { - log.error(ctx + ": We couldn't generate the pdf, reason: No data returned from PDF service"); - } - } - return null; - } public PdfServiceResponse downloadPdf(String url, String accessKey, String accessType) { final String ctx = CLASSNAME + ".getPdf"; From 723c82cb11ec78da9ff644cf200c5e4dd6e382a1 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Wed, 4 Feb 2026 23:15:55 +0300 Subject: [PATCH 034/105] refactor(o365-filter): improve the office365 filter to properly complement the correlation rules --- filters/office365/o365.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/filters/office365/o365.yml b/filters/office365/o365.yml index 742e0af55..6ccc68ca9 100755 --- a/filters/office365/o365.yml +++ b/filters/office365/o365.yml @@ -1,4 +1,4 @@ -# Microsoft 365 filter, version 1.0.2 +# Microsoft 365 filter, version 1.0.3 # Based on Official documentation # See https://learn.microsoft.com/en-us/compliance/assurance/assurance-microsoft-365-audit-log-collection @@ -72,6 +72,26 @@ pipeline: - log.UserId to: origin.user + - rename: + from: + - log.Folder.Path + to: log.folderPath + + - rename: + from: + - log.Folder.Id + to: log.folderId + + - rename: + from: + - log.DestFolder.Id + to: log.destFolderId + + - rename: + from: + - log.DestFolder.Path + to: log.destFolderPath + # Drop unnecessary events - drop: where: "['SupervisionRuleMatch', 'SupervisoryReviewTag', 'ComplianceManagerAutomationChange', 'LabelContentExplorerAccessedItem', 'CreateCopilotPlugin', 'CreateCopilotPromptBook', 'DeleteCopilotPlugin', 'DeleteCopilotPromptBook', 'DisableCopilotPlugin', 'DisableCopilotPromptBook', 'EnableCopilotPlugin', 'EnableCopilotPromptBook', 'CopilotInteraction', 'UpdateCopilotPlugin', 'UpdateCopilotPromptBook', 'UpdateCopilotSettings', 'ApproveDisposal', 'ExtendRetention', 'RelabelItem', 'SearchUpdated', 'CaseUpdated', 'SearchPermissionUpdated', 'HoldUpdated', 'PreviewItemDownloaded', 'PreviewItemListed', 'SearchCreated', 'CaseAdded', 'HoldCreated', 'SearchRemoved', 'HoldRemoved', 'SearchExportDownloaded', 'SearchPreviewed', 'SearchResultsPurged', 'RemovedSearchResultsSentToZoom', 'RemovedSearchExported', 'RemovedSearchPreviewed', 'RemovedSearchResultsPurged', 'SearchReportRemoved', 'SearchResultsSentToZoom', 'SearchStarted', 'SearchExported', 'SearchReport', 'SearchStopped', 'SearchViewed', 'ViewedSearchExported', 'ViewedSearchPreviewed', 'AddRemediatedData', 'BurnJob', 'CreateWorkingSet', 'CreateWorkingSetSearch', 'CreateTag', 'DeleteWorkingSetSearch', 'DeleteTag', 'DownloadDocument', 'UpdateTag', 'ExportJob', 'UpdateWorkingSetSearch', 'PreviewWorkingSetSearch', 'ErrorRemediationJob', 'TagFiles', 'TagJob', 'ViewDocument', 'Copy', 'Create', 'ApplyRecordLabel', 'HardDelete', 'Send', 'Update', 'FileAccessed', 'FileAccessedExtended', 'ComplianceSettingChanged', 'LockRecord', 'UnlockRecord', 'FileCheckedIn', 'FileCheckedOut', 'FileCopied', 'FileDeletedFirstStageRecycleBin', 'FileDeletedSecondStageRecycleBin', 'RecordDelete', 'DocumentSensitivityMismatchDetected', 'FileCheckOutDiscarded', 'FileDownloaded', 'FileModifiedExtended', 'FilePreviewed', 'SearchQueryPerformed', 'FileRecycled', 'FolderRecycled', 'FileVersionsAllMinorsRecycled', 'FileVersionsAllRecycled', 'FileVersionRecycled', 'FileRestored', 'FileUploaded', 'PageViewed', 'PageViewedExtended', 'ClientViewSignaled', 'PagePrefetched', 'FolderCopied', 'FolderCreated', 'FolderDeletedFirstStageRecycleBin', 'FolderDeletedSecondStageRecycleBin', 'FolderRestored', 'InformationBarriersInsightsReportCompleted', 'InformationBarriersInsightsReportOneDriveSectionQueried', 'InformationBarriersInsightsReportSchedule', 'InformationBarriersInsightsReportSharePointSectionQueried', 'updateddeviceconfiguration', 'UpdatedPolicyConfigPriority', 'BackupPolicyActivated', 'RestoreTaskActivated', 'BackupItemAdded', 'BackupItemRemoved', 'RestoreTaskCompleted', 'DraftRestoreTaskCreated', 'NewBackupPolicyCreated', 'DraftRestoreTaskDeleted', 'DraftRestoreTaskEdited', 'BackupPolicyPaused', 'GetBackupItem', 'ViewBackupPolicyDetails', 'GetRestoreTaskDetails', 'ListAllBackupPolicies', 'ListAllBackupItemsInPolicies', 'ListAllBackupItemsInTenant', 'ListAllBackupItemsInWorkload', 'GetAllRestoreArtifactsInTask', 'ListAllRestorePoints', 'ListAllRestoreTasks', 'BackupItemRestoreCompleted', 'BackupItemRestoreTriggered', 'SetAdvancedFeatures', 'RunAntiVirusScan', 'LogsCollection', 'TaggingConfigurationUpdated', 'AlertExcelDownloaded', 'RemediationActionAdded', 'RemediationActionUpdated', 'SensorCreated', 'SensorDeploymentAccessKeyReceived', 'SensorDeploymentAccessKeyUpdated', 'SensorActivationMethodConfigurationUpdated', 'DomainControllerCoverageExcelDownloaded', 'MonitoringAlertUpdated', 'ReportDownloaded', 'AlertNotificationsRecipientAdded', 'MonitoringAlertNotificationRecipientAdded', 'WorkspaceCreated', 'AddCommentToIncident.', 'AssignUserToIncident', 'UpdateIncidentStatus', 'AddTagsToIncident', 'RemoveTagsFromIncident', 'CreateComment', 'CreateForm', 'MoveForm', 'ViewForm', 'PreviewForm', 'ExportForm', 'AllowShareFormForCopy', 'DisallowShareFormForCopy', 'AddFormCoauthor', 'RemoveFormCoauthor', 'ViewRuntimeForm', 'CreateResponse', 'UpdateResponse', 'ViewResponses', 'ViewResponse', 'GetSummaryLink', 'DeleteSummaryLink', 'ProInvitation', 'ListForms', 'SubmitResponse', 'ConnectToExcelWorkbook', 'CollectionCreated', 'CollectionUpdated', 'CollectionHardDeleted', 'CollectionSoftDeleted', 'CollectionRenamed', 'MovedFormIntoCollection', 'MovedFormOutofCollection', 'PlanCopied', 'TaskAssigned', 'TaskCompleted', 'PlanListRead', 'TaskListRead', 'ProjectCreated', 'RoadmapCreated', 'RoadmapItemCreated', 'TaskCreated', 'ProjectListAccessed', 'RoadmapAccessed', 'RoadmapItemAccessed', 'TaskAccessed', 'AuditSearchCreated', 'AuditSearchCompleted', 'AuditSearchCancelled', 'AuditSearchExportJobCreated', 'AuditSearchExportJobCompleted', 'AuditSearchExportResultsDownloaded', 'EntityCreated', 'ClassificationAdded', 'ClassificationDefinitionCreated', 'GlossaryTermAssigned', 'GlossaryTermCreated', 'BotAddedToTeam', 'ChannelAdded', 'ConnectorAdded', 'MeetingDetail', 'MeetingParticipantDetail', 'MemberAdded', 'TabAdded', 'SensitivityLabelApplied', 'SensitivityLabelChanged', 'ChatCreated', 'TeamCreated', 'MessageDeleted', 'MessageEditedHasLink', 'MessagesExported', 'RecordingExported', 'TranscriptsExported', 'FailedValidation', 'ChatRetrieved', 'MessageHostedContentsListed', 'PerformedCardAction', 'MessageSent', 'AINotesUpdate', 'LiveNotesUpdate', 'AppPublishedToCatalog', 'MessageRead', 'InviteeResponded', 'ChannelOwnerResponded', 'MessagesListed', 'MessageCreatedHasLink', 'MessageCreatedNotification', 'MessageDeletedNotification', 'MessageUpdatedNotification', 'InviteSent', 'SubscribedToMessages', 'AppUpdatedInCatalog', 'ChatUpdated', 'MessageUpdated', 'TabUpdated', 'AppUpgraded', 'MessageSent', 'ScheduleGroupAdded', 'ShiftAdded', 'TimeOffAdded', 'OpenShiftAdded', 'ScheduleShared', 'ClockedIn', 'ClockedOut', 'BreakEnded', 'TimeClockEntryAdded', 'RequestAdded', 'RequestRespondedTo', 'WorkforceIntegrationAdded', 'OffShiftDialogAccepted', 'CreateUpdateRequest', 'EditUpdateRequest', 'SubmitUpdate', 'ViewUpdate', 'AcceptedSharingLinkOnFolder', 'FolderSharingLinkShared', 'LinkedEntityCreated', 'SubTaskCreated', 'TaskCreated', 'TaskRead', 'TaskListCreated', 'TaskListRead', 'AccessedOdataLink', 'CanceledQuery', 'DeletedResult', 'DownloadedReport', 'ExecutedQuery', 'UploadedOrgData', 'ViewedExplore', 'QuarantineReleaseRequestDeny', 'QuarantinePreview', 'QuarantineReleaseRequest', 'QuarantineViewHeader', 'UpdateUsageReportsPrivacySetting', 'NewAdaptiveScope', 'NewComplianceTag', 'NewRetentionCompliancePolicy', 'RemoveAdaptiveScope', 'RemoveComplianceTag', 'SetRestrictiveRetentionUI', 'ExchangeDataProactivelyPreserved', 'SharePointDataProactivelyPreserved', 'ListCreated', 'ListColumnCreated', 'ListContentTypeCreated', 'ListItemCreated', 'SiteColumnCreated', 'SiteContentTypeCreated', 'ListContentTypeDeleted', 'SiteColumnDeleted', 'SiteContentTypeDeleted', 'ListItemRecycled', 'ListItemRestored', 'ListColumnUpdated', 'ListContentTypeUpdated', 'SiteColumnUpdated', 'SiteContentTypeUpdated', 'SharingInvitationCreated', 'AccessRequestUpdated', 'SharingInvitationUpdated', 'SharingInvitationRevoked', 'AllowedDataLocationAdded', 'SiteGeoMoveCancelled', 'MigrationJobCompleted', 'SiteGeoMoveCompleted', 'SiteCollectionCreated', 'HubSiteOrphanHubDeleted', 'PreviewModeEnabledSet', 'LegacyWorkflowEnabledSet', 'OfficeOnDemandSet', 'PeopleResultsScopeSet', 'NewsFeedEnabledSet', 'HubSiteJoined', 'SiteCollectionQuotaModified', 'HubSiteRegistered', 'SiteGeoMoveScheduled', 'GeoQuotaAllocated', 'SiteAdminChangeRequest', 'ManagedSyncClientAllowed', 'FileSyncDownloadedFull', 'FileSyncUploadedFull', 'DataShareCreated', 'DataShareDeleted', 'GenerateCopyOfLakeData', 'DownloadCopyOfLakeData', 'SoftDeleteSettingsUpdated', 'CloseConversation', 'OpenConversation', 'MessageCreation', 'MessageDeleted', 'FileDownloaded', 'DataExport', 'ThreadAccessFailure', 'MarkedMessageChanged', 'RemoveCuratedTopic', 'UsagePolicyAcceptance', 'AdminThreadMuted', 'AdminThreadUnmuted', 'FileUpdateDescription', 'MessageUpdated', 'FileVisited', 'ThreadViewed', 'PulseSubmit', 'PulseCreate', 'PulseExtendDeadline', 'PulseInvite', 'PulseCancel', 'PulseShareResults', 'PulseCreateDraft', 'PulseDeleteDraft'].exists_one(v, v == action)" From 1a226a52952380fb1b24f6a2ef5e6b288b5378ef Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Wed, 4 Feb 2026 23:18:03 +0300 Subject: [PATCH 035/105] refactor(sonic_wall-filter): improve the sonic_wall filter to correctly parse the new types of events received --- filters/sonicwall/sonic_wall.yml | 73 ++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/filters/sonicwall/sonic_wall.yml b/filters/sonicwall/sonic_wall.yml index aa685a16b..05ab41c48 100644 --- a/filters/sonicwall/sonic_wall.yml +++ b/filters/sonicwall/sonic_wall.yml @@ -1,4 +1,4 @@ -# SonicWall Firewall, version 3.0.1 +# SonicWall Firewall, version 3.2.0 # Based on docs # Support Syslog CEF format # @@ -48,6 +48,30 @@ pipeline: pattern: '{{.greedy}}' source: raw + - grok: + patterns: + - fieldName: log.priority + pattern: '\<{{.data}}\>' + - fieldName: log.irrelevant + pattern: '{{.data}}\=' + - fieldName: log.device + pattern: '{{.word}}' + - fieldName: log.irrelevant1 + pattern: '{{.data}}\=' + - fieldName: log.sn + pattern: '{{.word}}' + - fieldName: log.irrelevant2 + pattern: '{{.data}}\"' + - fieldName: log.dvcTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}{{.space}}{{.time}}' + - fieldName: log.irrelevant3 + pattern: '{{.data}}\=' + - fieldName: log.srcIp + pattern: '{{.ipv4}}' + - fieldName: log.msgAll + pattern: '{{.greedy}}' + source: raw + # ......................................................................# # Checking if the log is in CEF format #......................................................................# @@ -70,7 +94,7 @@ pipeline: - fieldName: log.cefMsgAll pattern: '{{.greedy}}' source: log.msgAll - where: log.msgAll.contains("CEF:") + where: contains("log.msgAll", "CEF:") - grok: patterns: @@ -85,7 +109,7 @@ pipeline: - fieldName: log.cefMsgAll pattern: '{{.greedy}}' source: log.msgAll - where: log.msgAll.contains("CEF:") + where: contains("log.msgAll", "CEF:") #......................................................................# # Removing unnecessary characters of the syslogHeader @@ -317,119 +341,119 @@ pipeline: params: key: log.groupCategory value: 'System' - where: safe("log.gcat", "") == "1" + where: equals("log.gcat", "1") - add: function: 'string' params: key: log.groupCategory value: 'Log' - where: safe("log.gcat", "") == "2" + where: equals("log.gcat", "2") - add: function: 'string' params: key: log.groupCategory value: 'Security Services' - where: safe("log.gcat", "") == "3" + where: equals("log.gcat", "3") - add: function: 'string' params: key: log.groupCategory value: 'Users' - where: safe("log.gcat", "") == "4" + where: equals("log.gcat", "4") - add: function: 'string' params: key: log.groupCategory value: 'Firewall Settings' - where: safe("log.gcat", "") == "5" + where: equals("log.gcat", "5") - add: function: 'string' params: key: log.groupCategory value: 'Network' - where: safe("log.gcat", "") == "6" + where: equals("log.gcat", "6") - add: function: 'string' params: key: log.groupCategory value: 'VPN' - where: safe("log.gcat", "") == "7" + where: equals("log.gcat", "7") - add: function: 'string' params: key: log.groupCategory value: 'High Availability' - where: safe("log.gcat", "") == "8" + where: equals("log.gcat", "8") - add: function: 'string' params: key: log.groupCategory value: '3G/4G, Modem, and Module' - where: safe("log.gcat", "") == "9" + where: equals("log.gcat", "9") - add: function: 'string' params: key: log.groupCategory value: 'Firewall' - where: safe("log.gcat", "") == "10" + where: equals("log.gcat", "10") - add: function: 'string' params: key: log.groupCategory value: 'Wireless' - where: safe("log.gcat", "") == "11" + where: equals("log.gcat", "11") - add: function: 'string' params: key: log.groupCategory value: 'VoIP' - where: safe("log.gcat", "") == "12" + where: equals("log.gcat", "12") - add: function: 'string' params: key: log.groupCategory value: 'SSL VPN' - where: safe("log.gcat", "") == "13" + where: equals("log.gcat", "13") - add: function: 'string' params: key: log.groupCategory value: 'Anti-Spam' - where: safe("log.gcat", "") == "14" + where: equals("log.gcat", "14") - add: function: 'string' params: key: log.groupCategory value: 'WAN Acceleration' - where: safe("log.gcat", "") == "15" + where: equals("log.gcat", "15") - add: function: 'string' params: key: log.groupCategory value: 'SD-WAN' - where: safe("log.gcat", "") == "16" + where: equals("log.gcat", "16") - add: function: 'string' params: key: log.groupCategory value: 'Multi-Tenancy' - where: safe("log.gcat", "") == "17" + where: equals("log.gcat", "17") # Adding geolocation to origin.ip - dynamic: @@ -455,19 +479,19 @@ pipeline: params: key: severity value: 'high' - where: safe("log.pri", "") in ["0", "1", "2", "3"] + where: oneOf("log.pri", ["0", "1", "2", "3"]) - add: function: 'string' params: key: severity value: 'medium' - where: safe("log.pri", "") == "4" + where: equals("log.pri", "4") - add: function: 'string' params: key: severity value: 'low' - where: safe("log.pri", "") in ["5", "6", "7"] + where: oneOf("log.pri", ["5", "6", "7"]) # ..........................................................................# # Removing unnuse fields @@ -475,6 +499,9 @@ pipeline: - delete: fields: - log.irrelevant + - log.irrelevant1 + - log.irrelevant2 + - log.irrelevant3 - log.msgAll - log.cefMsgAll - log.pri From e5877afeffd891773a4014feb643608d1eafa272 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Feb 2026 15:06:06 -0600 Subject: [PATCH 036/105] fix: enhance target validation in alert label retrieval Signed-off-by: Manuel Abascal --- .../adversary-alerts-graph/adversary-alerts-graph.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts b/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts index 246ec8be7..0753e35fc 100644 --- a/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts +++ b/frontend/src/app/data-management/adversary-management/adversary-alerts-graph/adversary-alerts-graph.component.ts @@ -311,7 +311,7 @@ export class AdversaryAlertsGraphComponent implements OnChanges { } private getLabel(alert: UtmAlertType) { - if (alert.target) { + if (alert.target && Object.keys(alert.target).length > 0) { const target: any = alert.target as Side; if (target.ip) { return target.ip; From ee7345f25e68242c0b38e068ada2420237bcc879 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Feb 2026 15:31:53 -0600 Subject: [PATCH 037/105] fix: improve isEmpty checks for alert target and log objects Signed-off-by: Manuel Abascal --- .../alert-host-detail/alert-host-detail.component.ts | 11 ++++++++++- .../alert-view-detail.component.html | 2 +- .../alert-view-detail/alert-view-detail.component.ts | 11 ++++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-host-detail/alert-host-detail.component.ts b/frontend/src/app/data-management/alert-management/shared/components/alert-host-detail/alert-host-detail.component.ts index 932e465fc..74a158436 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-host-detail/alert-host-detail.component.ts +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-host-detail/alert-host-detail.component.ts @@ -31,9 +31,18 @@ export class AlertHostDetailComponent implements OnInit { } isEmpty(): boolean { - return !this.alert || !this.alert[this.type] || Object.keys(this.alert[this.type]).length === 0; + const value = this.alert[this.type]; + + if (value == null) { return true; } + + if (typeof value !== 'object') { return true; } + + if (Array.isArray(value)) { return value.length === 0; } + + return Object.keys(value).length === 0 && value.constructor === Object; } + getFields(obj: any, prefix = ''): { id: string; fieldType: UtmFieldType }[] { let fields: { id: string, fieldType: UtmFieldType}[] = []; diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.html b/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.html index 01e704b1c..8ce80d2f6 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.html +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.html @@ -158,7 +158,7 @@
- +
diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.ts b/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.ts index 2e5ad6098..b87478f4b 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.ts +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.ts @@ -161,7 +161,16 @@ export class AlertViewDetailComponent implements OnInit { return Object.entries(this.log).length === 0; } - protected readonly ALERT_ADVERSARY_FIELD = ALERT_ADVERSARY_FIELD; + isEmpty(obj: any): boolean { + if (obj == null) { return true; } + + if (typeof obj !== 'object') { return true; } + + if (Array.isArray(obj)) { return obj.length === 0; } + + return Object.keys(obj).length === 0 && obj.constructor === Object; + } + } export enum AlertDetailTabEnum { From 2ab8afe5a896af526585e6de04fc4c1dfe4b1a75 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Feb 2026 16:40:41 -0600 Subject: [PATCH 038/105] feat: update integration filters for Microsoft 365 and SonicWall --- ...60204001_update_filter_o35_integration.xml | 125 ++++ ...2_update_filter_sonic_wall_integration.xml | 549 ++++++++++++++++++ .../resources/config/liquibase/master.xml | 4 + 3 files changed, 678 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260204001_update_filter_o35_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260204002_update_filter_sonic_wall_integration.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260204001_update_filter_o35_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260204001_update_filter_o35_integration.xml new file mode 100644 index 000000000..a2041510d --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260204001_update_filter_o35_integration.xml @@ -0,0 +1,125 @@ + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260204002_update_filter_sonic_wall_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260204002_update_filter_sonic_wall_integration.xml new file mode 100644 index 000000000..dfa6f5070 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260204002_update_filter_sonic_wall_integration.xml @@ -0,0 +1,549 @@ + + + + + + ' + - fieldName: log.syslogVersion + pattern: '{{.integer}}' + - fieldName: log.dvcTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}{{.space}}{{.time}}' + - fieldName: log.syslogHost + pattern: '{{.hostname}}' + - fieldName: log.irrelevant + pattern: '{{.word}}\=' + - fieldName: log.id + pattern: '{{.integer}}' + - fieldName: log.msgAll + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.dvcTime + pattern: '{{.monthName}}{{.space}}{{.monthDay}}{{.space}}{{.time}}' + - fieldName: log.srcIp + pattern: '{{.ipv4}}' + - fieldName: log.msgAll + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.priority + pattern: '\<{{.data}}\>' + - fieldName: log.irrelevant + pattern: '{{.data}}\=' + - fieldName: log.device + pattern: '{{.word}}' + - fieldName: log.irrelevant1 + pattern: '{{.data}}\=' + - fieldName: log.sn + pattern: '{{.word}}' + - fieldName: log.irrelevant2 + pattern: '{{.data}}\"' + - fieldName: log.dvcTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}{{.space}}{{.time}}' + - fieldName: log.irrelevant3 + pattern: '{{.data}}\=' + - fieldName: log.srcIp + pattern: '{{.ipv4}}' + - fieldName: log.msgAll + pattern: '{{.greedy}}' + source: raw + + # ......................................................................# + # Checking if the log is in CEF format + #......................................................................# + # Using grok to parse syslogHeader of the message + # ......................................................................# + - grok: + patterns: + - fieldName: log.priority + pattern: '\<{{.data}}\>' + - fieldName: log.syslogVersion + pattern: '{{.integer}}' + - fieldName: log.dvcTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}{{.space}}{{.time}}' + - fieldName: log.syslogHost + pattern: '{{.hostname}}' + - fieldName: log.formatType + pattern: '(CEF:)' + - fieldName: log.formatVersion + pattern: '(\s)?{{.integer}}' + - fieldName: log.cefMsgAll + pattern: '{{.greedy}}' + source: log.msgAll + where: contains("log.msgAll", "CEF:") + + - grok: + patterns: + - fieldName: log.dvcTime + pattern: '((?i)\b(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:tember)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\b){{.space}}{{.monthDay}}{{.space}}{{.year}}{{.space}}{{.time}}' + - fieldName: log.sn + pattern: '{{.word}}' + - fieldName: log.formatType + pattern: '(CEF:)' + - fieldName: log.formatVersion + pattern: '{{.integer}}' + - fieldName: log.cefMsgAll + pattern: '{{.greedy}}' + source: log.msgAll + where: contains("log.msgAll", "CEF:") + + #......................................................................# + # Removing unnecessary characters of the syslogHeader + #......................................................................# + - trim: + function: prefix + substring: "<" + fields: + - log.priority + - trim: + function: suffix + substring: ">" + fields: + - log.priority + - trim: + function: suffix + substring: ":" + fields: + - log.formatType + + #......................................................................# + # Using grok to parse CEF fields + #......................................................................# + - grok: + patterns: + - fieldName: log.dvcVendor + pattern: '\|{{.data}}\|' + - fieldName: log.dvcProduct + pattern: '{{.data}}\|' + - fieldName: log.dvcVersion + pattern: '{{.data}}\|' + - fieldName: log.eventId + pattern: '{{.data}}\|' + - fieldName: log.eventName + pattern: '{{.data}}\|' + - fieldName: log.severity + pattern: '{{.data}}\|' + - fieldName: log.msgAll + pattern: '{{.greedy}}' + source: log.cefMsgAll + + - trim: + function: prefix + substring: "|" + fields: + - log.dvcVendor + + - trim: + function: suffix + substring: "|" + fields: + - log.dvcVendor + - log.dvcProduct + - log.dvcVersion + - log.eventId + - log.eventName + - log.severity + + # Using grok to extract values with spaces in fields + - grok: + patterns: + - fieldName: log.trash + pattern: '{{.data}}msg=' + - fieldName: log.message + pattern: '\"{{.data}}\"' + - fieldName: log.trash2 + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.trash1 + pattern: '{{.data}}ipscat=' + - fieldName: log.ipscat1 + pattern: '\"{{.data}}\"' + - fieldName: log.trash3 + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.trash4 + pattern: '{{.data}}cs6=' + - fieldName: log.cs61 + pattern: '\"{{.data}}\"' + - fieldName: log.trash5 + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.trash6 + pattern: '{{.data}}rule=' + - fieldName: log.rule1 + pattern: '\"{{.data}}\"' + - fieldName: log.trash7 + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.trash8 + pattern: '{{.data}}Category=' + - fieldName: log.category1 + pattern: '\"{{.data}}\"' + - fieldName: log.trash9 + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.trash10 + pattern: '{{.data}}note=' + - fieldName: log.note1 + pattern: '\"{{.data}}\"' + - fieldName: log.trash11 + pattern: '{{.greedy}}' + source: raw + + - trim: + function: prefix + substring: '"' + fields: + - log.message + - log.note1 + - log.ipscat1 + - log.cs61 + - log.rule1 + - log.category1 + - log.fwaction + + - trim: + function: suffix + substring: '"' + fields: + - log.message + - log.note1 + - log.ipscat1 + - log.cs61 + - log.rule1 + - log.category1 + - log.fwaction + + #......................................................................# + # Using kv to parse msgAll components from syslog format only + #......................................................................# + - kv: + fieldSplit: " " + valueSplit: "=" + source: log.msgAll + + # ................................................# + # Rename fields + # ................................................# + - rename: + from: + - log.dst + to: target.ip + - rename: + from: + - log.dstname + to: target.host + - rename: + from: + - log.proto + to: protocol + - rename: + from: + - log.src + to: origin.ip + - rename: + from: + - log.fwaction + to: action + - rename: + from: + - log.dstMac + to: target.mac + - rename: + from: + - log.srcMac + to: origin.mac + - rename: + from: + - log.dmac + to: target.mac + - rename: + from: + - log.smac + to: origin.mac + - rename: + from: + - log.message + to: log.msg + - rename: + from: + - log.note1 + to: log.note + - rename: + from: + - log.ipscat1 + to: log.ipscat + - rename: + from: + - log.cs61 + to: log.cs6 + - rename: + from: + - log.rule1 + to: log.rule + - rename: + from: + - log.category1 + to: log.category + + # .......................................................................# + # Fields conversions + # .......................................................................# + - cast: + fields: + - log.gcat + to: string + + #......................................................................# + # Define Syslog Group Category (gcat) Values + #......................................................................# + - add: + function: 'string' + params: + key: log.groupCategory + value: 'System' + where: equals("log.gcat", "1") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'Log' + where: equals("log.gcat", "2") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'Security Services' + where: equals("log.gcat", "3") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'Users' + where: equals("log.gcat", "4") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'Firewall Settings' + where: equals("log.gcat", "5") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'Network' + where: equals("log.gcat", "6") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'VPN' + where: equals("log.gcat", "7") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'High Availability' + where: equals("log.gcat", "8") + + - add: + function: 'string' + params: + key: log.groupCategory + value: '3G/4G, Modem, and Module' + where: equals("log.gcat", "9") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'Firewall' + where: equals("log.gcat", "10") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'Wireless' + where: equals("log.gcat", "11") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'VoIP' + where: equals("log.gcat", "12") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'SSL VPN' + where: equals("log.gcat", "13") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'Anti-Spam' + where: equals("log.gcat", "14") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'WAN Acceleration' + where: equals("log.gcat", "15") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'SD-WAN' + where: equals("log.gcat", "16") + + - add: + function: 'string' + params: + key: log.groupCategory + value: 'Multi-Tenancy' + where: equals("log.gcat", "17") + + # Adding geolocation to origin.ip + - dynamic: + plugin: com.utmstack.geolocation + params: + source: origin.ip + destination: origin.geolocation + where: exists("origin.ip") + + # Adding geolocation to target.ip + - dynamic: + plugin: com.utmstack.geolocation + params: + source: target.ip + destination: target.geolocation + where: exists("target.ip") + + # .......................................................................# + # Adding severity based on log.pri + # .......................................................................# + - add: + function: 'string' + params: + key: severity + value: 'high' + where: oneOf("log.pri", ["0", "1", "2", "3"]) + - add: + function: 'string' + params: + key: severity + value: 'medium' + where: equals("log.pri", "4") + - add: + function: 'string' + params: + key: severity + value: 'low' + where: oneOf("log.pri", ["5", "6", "7"]) + + # ..........................................................................# + # Removing unnuse fields + #.........................................................................# + - delete: + fields: + - log.irrelevant + - log.irrelevant1 + - log.irrelevant2 + - log.irrelevant3 + - log.msgAll + - log.cefMsgAll + - log.pri + - log.gcat + - log.message + - log.note1 + - log.ipscat1 + - log.cs61 + - log.rule1 + - log.category1 + - log.trash + - log.trash1 + - log.trash2 + - log.trash3 + - log.trash4 + - log.trash5 + - log.trash6 + - log.trash7 + - log.trash8 + - log.trash9 + - log.trash10 + - log.trash11 + +$$ + + WHERE id=1511 + + ]]> + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 41ed68e30..4c4d002ff 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -331,6 +331,10 @@ + + + + From 5f02b0beae833377c95097dc53af4e58697f7793 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 5 Feb 2026 10:32:06 -0600 Subject: [PATCH 039/105] feat: add error handling and response mapping for module configuration validation --- .../config/RestTemplateConfiguration.java | 3 +- .../impl/ModuleConfigurationValidator.java | 3 +- .../ModuleConfigurationValidationService.java | 76 ++++++++++++------- .../dto/application_modules/CSError.java | 9 +++ .../service/dto/application_modules/Meta.java | 9 +++ .../ModuleConfigValidationErrorMapper.java | 25 ++++++ .../ModuleConfigValidationErrorResponse.java | 13 ++++ .../rest_template/RestTemplateService.java | 4 + 8 files changed, 111 insertions(+), 31 deletions(-) create mode 100644 backend/src/main/java/com/park/utmstack/service/dto/application_modules/CSError.java create mode 100644 backend/src/main/java/com/park/utmstack/service/dto/application_modules/Meta.java create mode 100644 backend/src/main/java/com/park/utmstack/service/dto/application_modules/ModuleConfigValidationErrorMapper.java create mode 100644 backend/src/main/java/com/park/utmstack/service/dto/application_modules/ModuleConfigValidationErrorResponse.java diff --git a/backend/src/main/java/com/park/utmstack/config/RestTemplateConfiguration.java b/backend/src/main/java/com/park/utmstack/config/RestTemplateConfiguration.java index 5af8523ff..b08dc1764 100644 --- a/backend/src/main/java/com/park/utmstack/config/RestTemplateConfiguration.java +++ b/backend/src/main/java/com/park/utmstack/config/RestTemplateConfiguration.java @@ -5,6 +5,7 @@ import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; @@ -44,7 +45,7 @@ public RestTemplate rawRestTemplate() { RestTemplate rest = new RestTemplate(); rest.setErrorHandler(new DefaultResponseErrorHandler() { @Override - public boolean hasError(ClientHttpResponse response) { + public boolean hasError(@NotNull ClientHttpResponse response) { return false; } }); diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/validators/impl/ModuleConfigurationValidator.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/validators/impl/ModuleConfigurationValidator.java index 63811ffdd..0bdfb8e36 100644 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/validators/impl/ModuleConfigurationValidator.java +++ b/backend/src/main/java/com/park/utmstack/domain/application_modules/validators/impl/ModuleConfigurationValidator.java @@ -32,8 +32,7 @@ public boolean isValid(GroupConfigurationDTO dto, ConstraintValidatorContext con return module.validateConfiguration(utmModule, dto.getKeys()); } catch (Exception e) { context.disableDefaultConstraintViolation(); - context.buildConstraintViolationWithTemplate("Invalid configuration for selected module.") - .addPropertyNode("keys") + context.buildConstraintViolationWithTemplate(e.getMessage()) .addConstraintViolation(); return false; } diff --git a/backend/src/main/java/com/park/utmstack/service/application_modules/connectors/ModuleConfigurationValidationService.java b/backend/src/main/java/com/park/utmstack/service/application_modules/connectors/ModuleConfigurationValidationService.java index 9c9ca1124..c34c15cbc 100644 --- a/backend/src/main/java/com/park/utmstack/service/application_modules/connectors/ModuleConfigurationValidationService.java +++ b/backend/src/main/java/com/park/utmstack/service/application_modules/connectors/ModuleConfigurationValidationService.java @@ -1,6 +1,9 @@ package com.park.utmstack.service.application_modules.connectors; +import com.fasterxml.jackson.databind.JsonNode; import com.park.utmstack.config.Constants; +import com.park.utmstack.service.dto.application_modules.ModuleConfigValidationErrorMapper; +import com.park.utmstack.service.dto.application_modules.ModuleConfigValidationErrorResponse; import com.park.utmstack.service.dto.application_modules.UtmModuleGroupConfWrapperDTO; import com.park.utmstack.service.web_clients.rest_template.RestTemplateService; import com.park.utmstack.util.exceptions.ApiException; @@ -24,44 +27,61 @@ public class ModuleConfigurationValidationService { public boolean validateModuleConfiguration(String module, UtmModuleGroupConfWrapperDTO configurations) { final String ctx = CLASSNAME + ".ModuleConfigurationValidationService"; + HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/json"); headers.add("Accept", "*/*"); headers.set(Constants.EVENT_PROCESSOR_INTERNAL_KEY_HEADER, System.getenv(Constants.ENV_INTERNAL_KEY)); - String baseUrl = "http://" + System.getenv(Constants.ENV_EVENT_PROCESSOR_HOST) + ":" + System.getenv(Constants.ENV_EVENT_PROCESSOR_PORT); + String baseUrl = "http://" + System.getenv(Constants.ENV_EVENT_PROCESSOR_HOST) + ":" + System.getenv(Constants.ENV_EVENT_PROCESSOR_PORT); String endPoint = baseUrl + "/api/v1/modules-config/validate?nameShort=" + module; - try{ - ResponseEntity response = restTemplateService.post( - endPoint, - configurations, - String.class, - headers - ); - - if (!response.getStatusCode().is2xxSuccessful()) { - List errors = response.getHeaders().get("X-UtmStack-error"); - String errorMessage = (errors != null && !errors.isEmpty()) - ? String.join(", ", errors) - : "Unknown error occurred during module configuration validation."; - - log.error("{}: Module configuration validation failed for module: {} with status: {}. Cause: {}", - ctx, module, response.getStatusCode(), errorMessage); - throw new ApiException( - String.format("Module configuration validation failed for module: %s. Cause: %s", module, errorMessage), - response.getStatusCode() - ); + + ResponseEntity response = restTemplateService.postRaw( + endPoint, + configurations, + JsonNode.class, + headers + ); + + JsonNode body = response.getBody(); + + if (response.getStatusCode().is2xxSuccessful() && body != null && body.has("status")) { + return true; + } + + if (body != null && body.has("error")) { + String errorText = body.get("error").asText(); + + if (errorText.contains("{\"meta\"")) { + ModuleConfigValidationErrorResponse structured = ModuleConfigValidationErrorMapper.parse(errorText); + + if (structured != null) { + String traceId = structured.getMeta().getTraceId(); + String message = structured.getErrors().get(0).getMessage(); + + log.error("{}: External provider validation failed for module {}. TraceId: {}. Message: {}", + ctx, module, traceId, message); + + throw new ApiException( + "External provider validation failed: " + message + " (traceId=" + traceId + ")", + HttpStatus.UNAUTHORIZED + ); + } } - return true; + log.error("{}: Module configuration validation failed for module {}. Cause: {}", + ctx, module, errorText); - } catch (ApiException e) { - throw e; - } catch (Exception e) { - log.error("{}: An error occurred while validating module configuration for module: {}. Cause: {}", - ctx, module, e.getMessage(), e); - throw new ApiException("An error occurred while validating module configuration", HttpStatus.INTERNAL_SERVER_ERROR); + throw new ApiException(errorText, HttpStatus.BAD_REQUEST); } + + log.error("{}: Unexpected response validating module {}.", ctx, module); + throw new ApiException( + String.format("%s: Unexpected response validating module %s.", ctx, module), + HttpStatus.INTERNAL_SERVER_ERROR + ); } + + } diff --git a/backend/src/main/java/com/park/utmstack/service/dto/application_modules/CSError.java b/backend/src/main/java/com/park/utmstack/service/dto/application_modules/CSError.java new file mode 100644 index 000000000..901eab291 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/service/dto/application_modules/CSError.java @@ -0,0 +1,9 @@ +package com.park.utmstack.service.dto.application_modules; + +import lombok.Data; + +@Data +public class CSError { + private int code; + private String message; +} diff --git a/backend/src/main/java/com/park/utmstack/service/dto/application_modules/Meta.java b/backend/src/main/java/com/park/utmstack/service/dto/application_modules/Meta.java new file mode 100644 index 000000000..940645656 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/service/dto/application_modules/Meta.java @@ -0,0 +1,9 @@ +package com.park.utmstack.service.dto.application_modules; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class Meta { + @JsonProperty("trace_id") private String traceId; +} diff --git a/backend/src/main/java/com/park/utmstack/service/dto/application_modules/ModuleConfigValidationErrorMapper.java b/backend/src/main/java/com/park/utmstack/service/dto/application_modules/ModuleConfigValidationErrorMapper.java new file mode 100644 index 000000000..9ec10ea1d --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/service/dto/application_modules/ModuleConfigValidationErrorMapper.java @@ -0,0 +1,25 @@ +package com.park.utmstack.service.dto.application_modules; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class ModuleConfigValidationErrorMapper { + + private static final ObjectMapper mapper = new ObjectMapper(); + + public static ModuleConfigValidationErrorResponse parse(String errorText) { + try { + ObjectMapper mapper = new ObjectMapper(); + + int start = errorText.indexOf("{\"meta\""); + if (start == -1) return null; + + String innerJson = errorText.substring(start); + + return mapper.readValue(innerJson, ModuleConfigValidationErrorResponse.class); + + } catch (Exception e) { + return null; + } + } + +} diff --git a/backend/src/main/java/com/park/utmstack/service/dto/application_modules/ModuleConfigValidationErrorResponse.java b/backend/src/main/java/com/park/utmstack/service/dto/application_modules/ModuleConfigValidationErrorResponse.java new file mode 100644 index 000000000..eb7fa8604 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/service/dto/application_modules/ModuleConfigValidationErrorResponse.java @@ -0,0 +1,13 @@ +package com.park.utmstack.service.dto.application_modules; + +import lombok.Data; + +import java.util.List; + +@Data +public class ModuleConfigValidationErrorResponse { + private Meta meta; + private List errors; +} + + diff --git a/backend/src/main/java/com/park/utmstack/service/web_clients/rest_template/RestTemplateService.java b/backend/src/main/java/com/park/utmstack/service/web_clients/rest_template/RestTemplateService.java index 3cdbfa5be..233249c3b 100644 --- a/backend/src/main/java/com/park/utmstack/service/web_clients/rest_template/RestTemplateService.java +++ b/backend/src/main/java/com/park/utmstack/service/web_clients/rest_template/RestTemplateService.java @@ -54,6 +54,10 @@ public ResponseEntity getRaw(String url, Class type) { return rawRestTemplate.exchange(url, HttpMethod.GET, requestEntity, type); } + public ResponseEntity postRaw(String url, T body, Class type, HttpHeaders headers) { + HttpEntity requestEntity = new HttpEntity<>(body, headers); + return rawRestTemplate.exchange(url, HttpMethod.POST, requestEntity, type); + } public ResponseEntity get(String url, Class type, HttpHeaders headers) throws Exception { final String ctx = CLASSNAME + ".get"; From a2ca1a605c5c68237c9664f254e6abca402c63d1 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 5 Feb 2026 11:30:59 -0600 Subject: [PATCH 040/105] fix: add property node to constraint violation for better error reporting in module configuration validation --- .../validators/impl/ModuleConfigurationValidator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/validators/impl/ModuleConfigurationValidator.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/validators/impl/ModuleConfigurationValidator.java index 0bdfb8e36..7efc54cb9 100644 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/validators/impl/ModuleConfigurationValidator.java +++ b/backend/src/main/java/com/park/utmstack/domain/application_modules/validators/impl/ModuleConfigurationValidator.java @@ -33,6 +33,7 @@ public boolean isValid(GroupConfigurationDTO dto, ConstraintValidatorContext con } catch (Exception e) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(e.getMessage()) + .addPropertyNode("keys") .addConstraintViolation(); return false; } From 1ae95e22763a8ab38f12ba60fd4475932f5b709f Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 5 Feb 2026 21:54:08 +0300 Subject: [PATCH 041/105] fix(crowdstrike-plugin): handle cloud region URL parsing to prevent wrong region selection --- plugins/crowdstrike/main.go | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/plugins/crowdstrike/main.go b/plugins/crowdstrike/main.go index 020cd0eff..8ced226be 100644 --- a/plugins/crowdstrike/main.go +++ b/plugins/crowdstrike/main.go @@ -129,10 +129,18 @@ func (p *CrowdStrikeProcessor) createClient() (*client.CrowdStrikeAPISpecificati errors.New("client ID or client secret is empty"), map[string]any{"process": "plugin_com.utmstack.crowdstrike"}) } + cloudType, err := extractCloudFromURL(p.Cloud) + if err != nil { + return nil, catcher.Error("invalid cloud region configuration", err, map[string]any{ + "process": "plugin_com.utmstack.crowdstrike", + "cloud_value": p.Cloud, + }) + } + client, err := falcon.NewClient(&falcon.ApiConfig{ ClientId: p.ClientID, ClientSecret: p.ClientSecret, - Cloud: falcon.Cloud(p.Cloud), + Cloud: cloudType, Context: context.Background(), }) if err != nil { @@ -142,6 +150,29 @@ func (p *CrowdStrikeProcessor) createClient() (*client.CrowdStrikeAPISpecificati return client, nil } +func extractCloudFromURL(cloudValue string) (falcon.CloudType, error) { + trimmed := strings.TrimSpace(cloudValue) + + urlToRegion := map[string]string{ + "api.crowdstrike.com": "us-1", + "api.us-2.crowdstrike.com": "us-2", + "api.eu-1.crowdstrike.com": "eu-1", + "api.laggar.gcw.crowdstrike.com": "us-gov-1", + "api.us-gov-2.crowdstrike.mil": "us-gov-2", + } + + if strings.Contains(trimmed, "://") || strings.Contains(trimmed, ".crowdstrike.") { + for host, region := range urlToRegion { + if strings.Contains(trimmed, host) { + return falcon.CloudValidate(region) + } + } + return 0, fmt.Errorf("unrecognized CrowdStrike URL: %s", trimmed) + } + + return falcon.CloudValidate(trimmed) +} + func (p *CrowdStrikeProcessor) getEvents() ([]string, error) { client, err := p.createClient() if err != nil { From 7bf9918cc99eabd06838d2912f9187588cb660e9 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 5 Feb 2026 21:55:02 +0300 Subject: [PATCH 042/105] fix(modules-config): add cloud region URL parsing for CrowdStrike configuration --- .../modules-config/validations/crowdstrike.go | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/plugins/modules-config/validations/crowdstrike.go b/plugins/modules-config/validations/crowdstrike.go index 5d02d7816..9eb684098 100644 --- a/plugins/modules-config/validations/crowdstrike.go +++ b/plugins/modules-config/validations/crowdstrike.go @@ -3,6 +3,7 @@ package validations import ( "context" "fmt" + "strings" "time" "github.com/crowdstrike/gofalcon/falcon" @@ -43,10 +44,15 @@ func ValidateCrowdstrikeConfig(config *config.ModuleGroup) error { return fmt.Errorf("App Name is required in CROWDSTRIKE configuration") } + cloudType, err := extractCloudFromURL(cloud) + if err != nil { + return fmt.Errorf("invalid cloud region configuration: %w", err) + } + client, err := falcon.NewClient(&falcon.ApiConfig{ ClientId: clientID, ClientSecret: clientSecret, - Cloud: falcon.Cloud(cloud), + Cloud: cloudType, Context: context.Background(), }) if err != nil { @@ -74,3 +80,26 @@ func ValidateCrowdstrikeConfig(config *config.ModuleGroup) error { return nil } + +func extractCloudFromURL(cloudValue string) (falcon.CloudType, error) { + trimmed := strings.TrimSpace(cloudValue) + + urlToRegion := map[string]string{ + "api.crowdstrike.com": "us-1", + "api.us-2.crowdstrike.com": "us-2", + "api.eu-1.crowdstrike.com": "eu-1", + "api.laggar.gcw.crowdstrike.com": "us-gov-1", + "api.us-gov-2.crowdstrike.mil": "us-gov-2", + } + + if strings.Contains(trimmed, "://") || strings.Contains(trimmed, ".crowdstrike.") { + for host, region := range urlToRegion { + if strings.Contains(trimmed, host) { + return falcon.CloudValidate(region) + } + } + return 0, fmt.Errorf("unrecognized CrowdStrike URL: %s", trimmed) + } + + return falcon.CloudValidate(trimmed) +} From ac1ffc98727b1c1edb0def2f5e90e6b3bffdf904 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 5 Feb 2026 14:15:13 -0600 Subject: [PATCH 043/105] refactor(import-rule): enhance step indicators and improve rule display logic Signed-off-by: Manuel Abascal --- .../import-rules/import-rule.component.html | 209 ++++++++++++++---- .../import-rules/import-rule.component.ts | 130 ++++++----- 2 files changed, 233 insertions(+), 106 deletions(-) diff --git a/frontend/src/app/rule-management/app-rule/components/import-rules/import-rule.component.html b/frontend/src/app/rule-management/app-rule/components/import-rules/import-rule.component.html index 44b69acd9..a94bd2fe4 100644 --- a/frontend/src/app/rule-management/app-rule/components/import-rules/import-rule.component.html +++ b/frontend/src/app/rule-management/app-rule/components/import-rules/import-rule.component.html @@ -1,80 +1,117 @@ +