From 29426c95ab922d7e7766cd613ad835b77ed9cdf9 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 11 Feb 2026 13:13:18 -0600 Subject: [PATCH 001/115] feat(assets-view): refactor asset detail handling and improve status display Signed-off-by: Manuel Abascal --- .../assets-view/assets-view.component.html | 9 +++---- .../assets-view/assets-view.component.ts | 24 +++++++++++-------- .../shared/types/net-scan.type.ts | 2 ++ .../utm-agent-connect.component.ts | 3 ++- .../utm-agent-detail.component.ts | 2 ++ 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/assets-discover/assets-view/assets-view.component.html b/frontend/src/app/assets-discover/assets-view/assets-view.component.html index 2823407bb..aae1ae3bf 100644 --- a/frontend/src/app/assets-discover/assets-view/assets-view.component.html +++ b/frontend/src/app/assets-discover/assets-view/assets-view.component.html @@ -264,13 +264,14 @@
-
+
- Agent {{ agent }} detail + {{ assetSelected.isAgent }} + {{ assetSelected.isAgent ? 'Agent' : 'Source' }} {{ assetName }} details
diff --git a/frontend/src/app/assets-discover/assets-view/assets-view.component.ts b/frontend/src/app/assets-discover/assets-view/assets-view.component.ts index e5cd403d0..d85f9c3ee 100644 --- a/frontend/src/app/assets-discover/assets-view/assets-view.component.ts +++ b/frontend/src/app/assets-discover/assets-view/assets-view.component.ts @@ -55,7 +55,7 @@ export class AssetsViewComponent implements OnInit, OnDestroy { page = 0; loading = false; itemsPerPage = ITEMS_PER_PAGE; - viewAssetDetail: NetScanType; + assetSelected: NetScanType; sortBy = AssetFieldEnum.ASSET_IS_ALIVE + ',DESC'; assetsFields: UtmFieldType[] = ASSETS_FIELDS; checkbox: any; @@ -83,7 +83,7 @@ export class AssetsViewComponent implements OnInit, OnDestroy { deleting: string[] = []; agentConsole: NetScanType; reasonRun: IncidentCommandType; - agent: string; + assetName: string; noData = false; destroy$ = new Subject(); @@ -280,7 +280,7 @@ export class AssetsViewComponent implements OnInit, OnDestroy { case AssetFieldEnum.ASSET_METRICS: break; default: - this.viewAssetDetail = asset; + this.assetSelected = asset; } } @@ -391,8 +391,12 @@ export class AssetsViewComponent implements OnInit, OnDestroy { getLastInput(asset: NetScanType) { if (asset.dataInputList.length > 0) { - const lastInput = asset.dataInputList[asset.dataInputList.length - 1].timestamp; + + const lastInputStatus = asset.dataInputList[asset.dataInputList.length - 1]; + const lastInput = lastInputStatus.timestamp; + asset.lastInputTimestamp = lastInput; + asset.status = lastInputStatus.down; return this.formatTimestampToDate(lastInput); } @@ -406,17 +410,17 @@ export class AssetsViewComponent implements OnInit, OnDestroy { } toggleAsset(asset: NetScanType) { - if (this.viewAssetDetail && this.viewAssetDetail.id === asset.id) { - this.viewAssetDetail = undefined; + if (this.assetSelected && this.assetSelected.id === asset.id) { + this.assetSelected = undefined; } else { - this.viewAssetDetail = asset; + this.assetSelected = asset; } } viwAgentDetail(event: Event, asset: NetScanType) { event.stopPropagation(); - this.viewAssetDetail = asset; - this.agent = asset.assetName; + this.assetSelected = asset; + this.assetName = asset.assetName && asset.assetName !== '' ? asset.assetName : asset.assetIp; } isSourceConnected(asset: NetScanType, source: UtmDataInputStatus): boolean { @@ -439,7 +443,7 @@ export class AssetsViewComponent implements OnInit, OnDestroy { } closeDetail() { - this.agent = undefined; + this.assetName = undefined; this.reasonRun.reason = ''; } diff --git a/frontend/src/app/assets-discover/shared/types/net-scan.type.ts b/frontend/src/app/assets-discover/shared/types/net-scan.type.ts index 42212b4e4..2a30d2588 100644 --- a/frontend/src/app/assets-discover/shared/types/net-scan.type.ts +++ b/frontend/src/app/assets-discover/shared/types/net-scan.type.ts @@ -39,4 +39,6 @@ export class NetScanType { sortKey?: string; lastInput: string; lastInputTimestamp: number; + status?: boolean; + isAgent?: boolean; } diff --git a/frontend/src/app/shared/components/utm/util/utm-agent-connect/utm-agent-connect.component.ts b/frontend/src/app/shared/components/utm/util/utm-agent-connect/utm-agent-connect.component.ts index dfdc55190..ab4414ce9 100644 --- a/frontend/src/app/shared/components/utm/util/utm-agent-connect/utm-agent-connect.component.ts +++ b/frontend/src/app/shared/components/utm/util/utm-agent-connect/utm-agent-connect.component.ts @@ -23,6 +23,7 @@ export class UtmAgentConnectComponent implements OnInit, OnChanges { } ngOnInit() { + console.log('init'); this.hasNoReason = this.websocketCommand.reason === '' || !this.websocketCommand.reason; } @@ -47,7 +48,7 @@ export class UtmAgentConnectComponent implements OnInit, OnChanges { ip: this.asset.assetIp, hostname: this.asset.assetName, os: this.asset.assetOs, - status: AgentStatusEnum.OFFLINE, + status: !this.asset.status ? AgentStatusEnum.ONLINE : AgentStatusEnum.OFFLINE, platform: this.asset.assetOsPlatform, version: this.asset.assetOsMinorVersion, agentKey: '', diff --git a/frontend/src/app/shared/components/utm/util/utm-agent-detail/utm-agent-detail.component.ts b/frontend/src/app/shared/components/utm/util/utm-agent-detail/utm-agent-detail.component.ts index 7e229177b..5dd9b8c9d 100644 --- a/frontend/src/app/shared/components/utm/util/utm-agent-detail/utm-agent-detail.component.ts +++ b/frontend/src/app/shared/components/utm/util/utm-agent-detail/utm-agent-detail.component.ts @@ -28,6 +28,8 @@ export class UtmAgentDetailComponent implements OnInit { this.agentIp = this.agent.ip; this.ips = this.agent.addresses !== '' ? this.agent.addresses.split(',') : []; this.macs = this.agent.mac !== '' ? this.agent.mac.split(',') : []; + + console.log(this.agent); } public getAgentIcon(): string { From 9eecd501e07aced9a9d5651b4b0d25d49c9e33f9 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 11 Feb 2026 16:41:38 -0600 Subject: [PATCH 002/115] feat(RequestDsl): enhance search request handling for LIST_CHART visualization --- .../requests/RequestDsl.java | 79 +++++++++++++++---- 1 file changed, 62 insertions(+), 17 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/util/chart_builder/elasticsearch_dsl/requests/RequestDsl.java b/backend/src/main/java/com/park/utmstack/util/chart_builder/elasticsearch_dsl/requests/RequestDsl.java index cb49b9df1..a5c35aae6 100644 --- a/backend/src/main/java/com/park/utmstack/util/chart_builder/elasticsearch_dsl/requests/RequestDsl.java +++ b/backend/src/main/java/com/park/utmstack/util/chart_builder/elasticsearch_dsl/requests/RequestDsl.java @@ -18,10 +18,7 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; public class RequestDsl { @@ -74,7 +71,25 @@ private void applyPagination(Pageable pageable, int top) { if (pageable != null && pageable.isPaged()) { SearchUtil.applyPaginationAndSort(searchRequestBuilder, pageable, top); } else { - searchRequestBuilder.size(10000); + if (visualization.getChartType() == ChartType.LIST_CHART) { + + searchRequestBuilder.size(10000); + + Set fields = new HashSet<>(); + AggregationType agg = visualization.getAggregationType(); + if (agg != null && agg.getBucket() != null) { + collectBucketFields(agg.getBucket(), fields); + } + + List includeList = new ArrayList<>(fields) + .stream() + .map(this::normalizeField) + .toList(); + + searchRequestBuilder.source(s -> s.filter(f -> f.excludes("*"))); + searchRequestBuilder.source(s -> s.filter(f + -> f.includes(includeList))); + } } } @@ -98,7 +113,6 @@ public SearchRequest.Builder getSearchSourceBuilderForCount() throws UtmElastics } - /** * Build an aggregation section for an elasticsearch dsl request * @@ -157,19 +171,19 @@ private Map buildBucketAggregation switch (bucket.getAggregation()) { case TERMS: TermsAggregation term = new TermsAggregation.Builder().field(bucket.getField()) - .size(bucket.getTerms().getSize()).order(List.of(Map.of(bucket.getTerms().getSortBy(), - bucket.getTerms().getAsc() ? SortOrder.Asc : SortOrder.Desc))).build(); + .size(bucket.getTerms().getSize()).order(List.of(Map.of(bucket.getTerms().getSortBy(), + bucket.getTerms().getAsc() ? SortOrder.Asc : SortOrder.Desc))).build(); bucketAggregations.put(bucket.toString(), new Aggregation.Builder().terms(term)); break; case RANGE: RangeAggregation range = new RangeAggregation.Builder().field(bucket.getField()) - .ranges(bucket.getRanges().stream().map(r -> AggregationRange.of(a -> a.from(String.valueOf(r.getFrom())) - .to(String.valueOf(r.getTo())))).collect(Collectors.toList())).build(); + .ranges(bucket.getRanges().stream().map(r -> AggregationRange.of(a -> a.from(String.valueOf(r.getFrom())) + .to(String.valueOf(r.getTo())))).collect(Collectors.toList())).build(); bucketAggregations.put(bucket.toString(), new Aggregation.Builder().range(range)); break; case DATE_HISTOGRAM: DateHistogramAggregation.Builder histogram = new DateHistogramAggregation.Builder().field(bucket.getField()) - .timeZone(TimezoneUtil.getAppTimezone().toString()); + .timeZone(TimezoneUtil.getAppTimezone().toString()); String interval = bucket.getDateHistogram().getInterval(); if (bucket.getDateHistogram().isFixedInterval()) histogram.fixedInterval(i -> i.time(interval)); @@ -204,24 +218,24 @@ private Map buildMetricAggregation(List metrics) { switch (metric.getAggregation()) { case AVERAGE: metricAggregations.put(metric.getId(), Aggregation.of(agg -> - agg.avg(avg -> avg.field(metric.getField())))); + agg.avg(avg -> avg.field(metric.getField())))); break; case MAX: metricAggregations.put(metric.getId(), Aggregation.of(agg -> - agg.max(max -> max.field(metric.getField())))); + agg.max(max -> max.field(metric.getField())))); break; case MIN: metricAggregations.put(metric.getId(), Aggregation.of(agg -> - agg.min(min -> min.field(metric.getField())))); + agg.min(min -> min.field(metric.getField())))); break; case SUM: metricAggregations.put(metric.getId(), Aggregation.of(agg -> - agg.sum(sum -> sum.field(metric.getField())))); + agg.sum(sum -> sum.field(metric.getField())))); break; case MEDIAN: metricAggregations.put(metric.getId(), Aggregation.of(agg -> - agg.percentiles(percentile -> percentile.field(metric.getField()) - .keyed(false).percents(50D)))); + agg.percentiles(percentile -> percentile.field(metric.getField()) + .keyed(false).percents(50D)))); break; } } @@ -230,4 +244,35 @@ private Map buildMetricAggregation(List metrics) { throw new RuntimeException(ctx + ": " + e.getMessage()); } } + + private void collectBucketFields(Bucket bucket, Set fields) { + if (bucket == null) return; + + // 1. Field of this bucket (covers terms, ranges, date histogram) + if (bucket.getField() != null && !bucket.getField().isEmpty()) { + fields.add(bucket.getField()); + } + + // 2. Date histogram (field is in the bucket, not in DateHistogramBucket) + if (bucket.getDateHistogram() != null && + bucket.getField() != null && + !bucket.getField().isEmpty()) { + fields.add(bucket.getField()); + } + + // 3. Recurse into subBucket + if (bucket.getSubBucket() != null) { + collectBucketFields(bucket.getSubBucket(), fields); + } + } + + + private String normalizeField(String field) { + if (field.endsWith(".keyword")) { + return field.substring(0, field.length() - ".keyword".length()); + } + return field; + } + + } From f91ac27251084aa4a6ee703ab54050b27baa9b09 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 11 Feb 2026 16:41:51 -0600 Subject: [PATCH 003/115] feat(RequestDsl): enhance search request handling for LIST_CHART visualization --- .../park/utmstack/domain/chart_builder/UtmVisualization.java | 2 +- .../src/main/java/com/park/utmstack/util/UtilSerializer.java | 2 +- .../utmstack/util/exceptions/UtmSerializationException.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/domain/chart_builder/UtmVisualization.java b/backend/src/main/java/com/park/utmstack/domain/chart_builder/UtmVisualization.java index d3f0b0875..0f999ce11 100644 --- a/backend/src/main/java/com/park/utmstack/domain/chart_builder/UtmVisualization.java +++ b/backend/src/main/java/com/park/utmstack/domain/chart_builder/UtmVisualization.java @@ -219,7 +219,7 @@ public void setChartConfig(String chartConfig) { this.chartConfig = chartConfig; } - public AggregationType getAggregationType() throws UtmSerializationException { + public AggregationType getAggregationType() { if (StringUtils.hasText(aggregation)) aggregationType = UtilSerializer.jsonDeserialize(AggregationType.class, aggregation); return aggregationType; diff --git a/backend/src/main/java/com/park/utmstack/util/UtilSerializer.java b/backend/src/main/java/com/park/utmstack/util/UtilSerializer.java index 7eec18a6a..9784a321a 100644 --- a/backend/src/main/java/com/park/utmstack/util/UtilSerializer.java +++ b/backend/src/main/java/com/park/utmstack/util/UtilSerializer.java @@ -67,7 +67,7 @@ public static String jsonSerialize(T obj) throws UtmSerializationException { } } - public static T jsonDeserialize(Class type, String json) throws UtmSerializationException { + public static T jsonDeserialize(Class type, String json) { String ctx = CLASS_NAME + ".jsonDeserialize"; try { ObjectMapper om = new ObjectMapper(); diff --git a/backend/src/main/java/com/park/utmstack/util/exceptions/UtmSerializationException.java b/backend/src/main/java/com/park/utmstack/util/exceptions/UtmSerializationException.java index 7b0aed56b..d92ebd4dc 100644 --- a/backend/src/main/java/com/park/utmstack/util/exceptions/UtmSerializationException.java +++ b/backend/src/main/java/com/park/utmstack/util/exceptions/UtmSerializationException.java @@ -1,6 +1,6 @@ package com.park.utmstack.util.exceptions; -public class UtmSerializationException extends Exception { +public class UtmSerializationException extends RuntimeException { public UtmSerializationException(String message) { super(message); } From 69157b84e6b7fdf59b0503d19f5cb62b9d8695d9 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 11 Feb 2026 16:43:22 -0600 Subject: [PATCH 004/115] feat: add updates for Windows visualizations and default time range adjustments --- ...60211004_update_windows_visualizations.xml | 49 +++++++++++++++++++ ...005_update_default_time_visualizations.xml | 28 +++++++++++ .../resources/config/liquibase/master.xml | 4 ++ 3 files changed, 81 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260211005_update_default_time_visualizations.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml new file mode 100644 index 000000000..ca13dffdc --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260211005_update_default_time_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260211005_update_default_time_visualizations.xml new file mode 100644 index 000000000..33d13b25c --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260211005_update_default_time_visualizations.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index a1ba2df2f..fb0f51e27 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -411,6 +411,10 @@ + + + + From d2f2dea1ad970f7f5b5355bd3616a2dacbb693d4 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 12 Feb 2026 16:03:35 +0300 Subject: [PATCH 005/115] feat(netflow-filter): add IANA protocol mapping and improve field processing --- filters/netflow/netflow.yml | 947 ++++++++++++++++++++++++++++++++++-- 1 file changed, 917 insertions(+), 30 deletions(-) diff --git a/filters/netflow/netflow.yml b/filters/netflow/netflow.yml index a92c36581..402618f72 100644 --- a/filters/netflow/netflow.yml +++ b/filters/netflow/netflow.yml @@ -1,4 +1,4 @@ -# Netflow firewall module filter, version 3.1.0 +# Netflow firewall module filter, version 3.1.1 # Based in docs and Netflow Generator (Solarwinds) for send log # # Documentations @@ -80,8 +80,8 @@ pipeline: - grok: patterns: - fieldName: log.irrelevant4 - pattern: '{{.data}}proto="\[' - - fieldName: protocol + pattern: '{{.data}}proto="/\[' + - fieldName: log.protocol pattern: '{{.data}}\]"' source: raw @@ -158,31 +158,8 @@ pipeline: from: - log.dest_ip to: target.ip - - rename: - from: - - log.srcPortg - to: origin.port - - rename: - from: - - log.src_port - to: origin.port - - rename: - from: - - log.dstPortg - to: target.port - - rename: - from: - - log.dest_port - to: target.port - - # Fields conversions - - cast: - to: 'int' - fields: - - origin.port - - target.port - # Removing unused caracters + # Remove unused caracters and estandarize fields - trim: function: prefix substring: '"' @@ -198,6 +175,7 @@ pipeline: - log.version - target.ip - origin.ip + - trim: function: suffix substring: '"' @@ -222,15 +200,62 @@ pipeline: - log.totalPackets - log.inEthernet - log.outEthernet - - protocol - - origin.port - - target.port + - log.protocol + - log.srcPortg + - log.src_port + - log.dstPortg + - log.dest_port - log.destMask - log.sourceMask - log.tcpFlgs - log.destAs - log.sourceAs + - trim: + function: prefix + substring: '0' + fields: + - log.srcPortg + - log.src_port + - log.dstPortg + - log.dest_port + + - trim: + function: substring + substring: " " + fields: + - log.srcPortg + - log.src_port + - log.dstPortg + - log.dest_port + + # Fields conversions + - cast: + to: 'int' + fields: + - log.srcPortg + - log.src_port + - log.dstPortg + - log.dest_port + + # Rename fields to fields base + - rename: + from: + - log.srcPortg + to: origin.port + - rename: + from: + - log.src_port + to: origin.port + - rename: + from: + - log.dstPortg + to: target.port + - rename: + from: + - log.dest_port + to: target.port + # Adding geolocation to origin.ip - dynamic: plugin: com.utmstack.geolocation @@ -246,6 +271,866 @@ pipeline: source: target.ip destination: target.geolocation where: exists("target.ip") + + # Adding protocol field based on IANA protocol numbers + - add: + function: "string" + params: + key: protocol + value: "HOPOPT" + where: equals("log.protocol", "0") + - add: + function: "string" + params: + key: protocol + value: "ICMP" + where: equals("log.protocol", "1") + - add: + function: "string" + params: + key: protocol + value: "IGMP" + where: equals("log.protocol", "2") + - add: + function: "string" + params: + key: protocol + value: "GGP" + where: equals("log.protocol", "3") + - add: + function: "string" + params: + key: protocol + value: "IP-in-IP" + where: equals("log.protocol", "4") + - add: + function: "string" + params: + key: protocol + value: "ST" + where: equals("log.protocol", "5") + - add: + function: "string" + params: + key: protocol + value: "TCP" + where: equals("log.protocol", "6") + - add: + function: "string" + params: + key: protocol + value: "CBT" + where: equals("log.protocol", "7") + - add: + function: "string" + params: + key: protocol + value: "EGP" + where: equals("log.protocol", "8") + - add: + function: "string" + params: + key: protocol + value: "IGP" + where: equals("log.protocol", "9") + - add: + function: "string" + params: + key: protocol + value: "BBN-RCC-MON" + where: equals("log.protocol", "10") + - add: + function: "string" + params: + key: protocol + value: "NVP-II" + where: equals("log.protocol", "11") + - add: + function: "string" + params: + key: protocol + value: "PUP" + where: equals("log.protocol", "12") + - add: + function: "string" + params: + key: protocol + value: "ARGUS" + where: equals("log.protocol", "13") + - add: + function: "string" + params: + key: protocol + value: "EMCON" + where: equals("log.protocol", "14") + - add: + function: "string" + params: + key: protocol + value: "XNET" + where: equals("log.protocol", "15") + - add: + function: "string" + params: + key: protocol + value: "CHAOS" + where: equals("log.protocol", "16") + - add: + function: "string" + params: + key: protocol + value: "UDP" + where: equals("log.protocol", "17") + - add: + function: "string" + params: + key: protocol + value: "MUX" + where: equals("log.protocol", "18") + - add: + function: "string" + params: + key: protocol + value: "DCN-MEAS" + where: equals("log.protocol", "19") + - add: + function: "string" + params: + key: protocol + value: "HMP" + where: equals("log.protocol", "20") + - add: + function: "string" + params: + key: protocol + value: "PRM" + where: equals("log.protocol", "21") + - add: + function: "string" + params: + key: protocol + value: "XNS-IDP" + where: equals("log.protocol", "22") + - add: + function: "string" + params: + key: protocol + value: "TRUNK-1" + where: equals("log.protocol", "23") + - add: + function: "string" + params: + key: protocol + value: "TRUNK-2" + where: equals("log.protocol", "24") + - add: + function: "string" + params: + key: protocol + value: "LEAF-1" + where: equals("log.protocol", "25") + - add: + function: "string" + params: + key: protocol + value: "LEAF-2" + where: equals("log.protocol", "26") + - add: + function: "string" + params: + key: protocol + value: "RDP" + where: equals("log.protocol", "27") + - add: + function: "string" + params: + key: protocol + value: "IRTP" + where: equals("log.protocol", "28") + - add: + function: "string" + params: + key: protocol + value: "ISO-TP4" + where: equals("log.protocol", "29") + - add: + function: "string" + params: + key: protocol + value: "NETBLT" + where: equals("log.protocol", "30") + - add: + function: "string" + params: + key: protocol + value: "MFE-NSP" + where: equals("log.protocol", "31") + - add: + function: "string" + params: + key: protocol + value: "MERIT-INP" + where: equals("log.protocol", "32") + - add: + function: "string" + params: + key: protocol + value: "DCCP" + where: equals("log.protocol", "33") + - add: + function: "string" + params: + key: protocol + value: "3PC" + where: equals("log.protocol", "34") + - add: + function: "string" + params: + key: protocol + value: "IDPR" + where: equals("log.protocol", "35") + - add: + function: "string" + params: + key: protocol + value: "XTP" + where: equals("log.protocol", "36") + - add: + function: "string" + params: + key: protocol + value: "DDP" + where: equals("log.protocol", "37") + - add: + function: "string" + params: + key: protocol + value: "IDPR-CMTP" + where: equals("log.protocol", "38") + - add: + function: "string" + params: + key: protocol + value: "TP++" + where: equals("log.protocol", "39") + - add: + function: "string" + params: + key: protocol + value: "IL" + where: equals("log.protocol", "40") + - add: + function: "string" + params: + key: protocol + value: "IPv6" + where: equals("log.protocol", "41") + - add: + function: "string" + params: + key: protocol + value: "SDRP" + where: equals("log.protocol", "42") + - add: + function: "string" + params: + key: protocol + value: "IPv6-Route" + where: equals("log.protocol", "43") + - add: + function: "string" + params: + key: protocol + value: "IPv6-Frag" + where: equals("log.protocol", "44") + - add: + function: "string" + params: + key: protocol + value: "IDRP" + where: equals("log.protocol", "45") + - add: + function: "string" + params: + key: protocol + value: "RSVP" + where: equals("log.protocol", "46") + - add: + function: "string" + params: + key: protocol + value: "GRE" + where: equals("log.protocol", "47") + - add: + function: "string" + params: + key: protocol + value: "DSR" + where: equals("log.protocol", "48") + - add: + function: "string" + params: + key: protocol + value: "BNA" + where: equals("log.protocol", "49") + - add: + function: "string" + params: + key: protocol + value: "ESP" + where: equals("log.protocol", "50") + - add: + function: "string" + params: + key: protocol + value: "AH" + where: equals("log.protocol", "51") + - add: + function: "string" + params: + key: protocol + value: "I-NLSP" + where: equals("log.protocol", "52") + - add: + function: "string" + params: + key: protocol + value: "SwIPe" + where: equals("log.protocol", "53") + - add: + function: "string" + params: + key: protocol + value: "NARP" + where: equals("log.protocol", "54") + - add: + function: "string" + params: + key: protocol + value: "MOBILE" + where: equals("log.protocol", "55") + - add: + function: "string" + params: + key: protocol + value: "TLSP" + where: equals("log.protocol", "56") + - add: + function: "string" + params: + key: protocol + value: "SKIP" + where: equals("log.protocol", "57") + - add: + function: "string" + params: + key: protocol + value: "IPv6-ICMP" + where: equals("log.protocol", "58") + - add: + function: "string" + params: + key: protocol + value: "IPv6-NoNxt" + where: equals("log.protocol", "59") + - add: + function: "string" + params: + key: protocol + value: "IPv6-Opts" + where: equals("log.protocol", "60") + - add: + function: "string" + params: + key: protocol + value: "CFTP" + where: equals("log.protocol", "62") + - add: + function: "string" + params: + key: protocol + value: "SAT-EXPAK" + where: equals("log.protocol", "64") + - add: + function: "string" + params: + key: protocol + value: "KRYPTOLAN" + where: equals("log.protocol", "65") + - add: + function: "string" + params: + key: protocol + value: "RVD" + where: equals("log.protocol", "66") + - add: + function: "string" + params: + key: protocol + value: "IPPC" + where: equals("log.protocol", "67") + - add: + function: "string" + params: + key: protocol + value: "SAT-MON" + where: equals("log.protocol", "69") + - add: + function: "string" + params: + key: protocol + value: "VISA" + where: equals("log.protocol", "70") + - add: + function: "string" + params: + key: protocol + value: "IPCU" + where: equals("log.protocol", "71") + - add: + function: "string" + params: + key: protocol + value: "CPNX" + where: equals("log.protocol", "72") + - add: + function: "string" + params: + key: protocol + value: "CPHB" + where: equals("log.protocol", "73") + - add: + function: "string" + params: + key: protocol + value: "WSN" + where: equals("log.protocol", "74") + - add: + function: "string" + params: + key: protocol + value: "PVP" + where: equals("log.protocol", "75") + - add: + function: "string" + params: + key: protocol + value: "BR-SAT-MON" + where: equals("log.protocol", "76") + - add: + function: "string" + params: + key: protocol + value: "SUN-ND" + where: equals("log.protocol", "77") + - add: + function: "string" + params: + key: protocol + value: "WB-MON" + where: equals("log.protocol", "78") + - add: + function: "string" + params: + key: protocol + value: "WB-EXPAK" + where: equals("log.protocol", "79") + - add: + function: "string" + params: + key: protocol + value: "ISO-IP" + where: equals("log.protocol", "80") + - add: + function: "string" + params: + key: protocol + value: "VMTP" + where: equals("log.protocol", "81") + - add: + function: "string" + params: + key: protocol + value: "SECURE-VMTP" + where: equals("log.protocol", "82") + - add: + function: "string" + params: + key: protocol + value: "VINES" + where: equals("log.protocol", "83") + - add: + function: "string" + params: + key: protocol + value: "IPTM" + where: equals("log.protocol", "84") + - add: + function: "string" + params: + key: protocol + value: "NSFNET-IGP" + where: equals("log.protocol", "85") + - add: + function: "string" + params: + key: protocol + value: "DGP" + where: equals("log.protocol", "86") + - add: + function: "string" + params: + key: protocol + value: "TCF" + where: equals("log.protocol", "87") + - add: + function: "string" + params: + key: protocol + value: "EIGRP" + where: equals("log.protocol", "88") + - add: + function: "string" + params: + key: protocol + value: "OSPF" + where: equals("log.protocol", "89") + - add: + function: "string" + params: + key: protocol + value: "Sprite-RPC" + where: equals("log.protocol", "90") + - add: + function: "string" + params: + key: protocol + value: "LARP" + where: equals("log.protocol", "91") + - add: + function: "string" + params: + key: protocol + value: "MTP" + where: equals("log.protocol", "92") + - add: + function: "string" + params: + key: protocol + value: "AX.25" + where: equals("log.protocol", "93") + - add: + function: "string" + params: + key: protocol + value: "OS" + where: equals("log.protocol", "94") + - add: + function: "string" + params: + key: protocol + value: "MICP" + where: equals("log.protocol", "95") + - add: + function: "string" + params: + key: protocol + value: "SCC-SP" + where: equals("log.protocol", "96") + - add: + function: "string" + params: + key: protocol + value: "ETHERIP" + where: equals("log.protocol", "97") + - add: + function: "string" + params: + key: protocol + value: "ENCAP" + where: equals("log.protocol", "98") + - add: + function: "string" + params: + key: protocol + value: "GMTP" + where: equals("log.protocol", "100") + - add: + function: "string" + params: + key: protocol + value: "IFMP" + where: equals("log.protocol", "101") + - add: + function: "string" + params: + key: protocol + value: "PNNI" + where: equals("log.protocol", "102") + - add: + function: "string" + params: + key: protocol + value: "PIM" + where: equals("log.protocol", "103") + - add: + function: "string" + params: + key: protocol + value: "ARIS" + where: equals("log.protocol", "104") + - add: + function: "string" + params: + key: protocol + value: "SCPS" + where: equals("log.protocol", "105") + - add: + function: "string" + params: + key: protocol + value: "QNX" + where: equals("log.protocol", "106") + - add: + function: "string" + params: + key: protocol + value: "A/N" + where: equals("log.protocol", "107") + - add: + function: "string" + params: + key: protocol + value: "IPComp" + where: equals("log.protocol", "108") + - add: + function: "string" + params: + key: protocol + value: "SNP" + where: equals("log.protocol", "109") + - add: + function: "string" + params: + key: protocol + value: "Compaq-Peer" + where: equals("log.protocol", "110") + - add: + function: "string" + params: + key: protocol + value: "IPX-in-IP" + where: equals("log.protocol", "111") + - add: + function: "string" + params: + key: protocol + value: "VRRP" + where: equals("log.protocol", "112") + - add: + function: "string" + params: + key: protocol + value: "PGM" + where: equals("log.protocol", "113") + - add: + function: "string" + params: + key: protocol + value: "L2TP" + where: equals("log.protocol", "115") + - add: + function: "string" + params: + key: protocol + value: "DDX" + where: equals("log.protocol", "116") + - add: + function: "string" + params: + key: protocol + value: "IATP" + where: equals("log.protocol", "117") + - add: + function: "string" + params: + key: protocol + value: "STP" + where: equals("log.protocol", "118") + - add: + function: "string" + params: + key: protocol + value: "SRP" + where: equals("log.protocol", "119") + - add: + function: "string" + params: + key: protocol + value: "UTI" + where: equals("log.protocol", "120") + - add: + function: "string" + params: + key: protocol + value: "SMP" + where: equals("log.protocol", "121") + - add: + function: "string" + params: + key: protocol + value: "SM" + where: equals("log.protocol", "122") + - add: + function: "string" + params: + key: protocol + value: "PTP" + where: equals("log.protocol", "123") + - add: + function: "string" + params: + key: protocol + value: "IS-IS" + where: equals("log.protocol", "124") + - add: + function: "string" + params: + key: protocol + value: "FIRE" + where: equals("log.protocol", "125") + - add: + function: "string" + params: + key: protocol + value: "CRTP" + where: equals("log.protocol", "126") + - add: + function: "string" + params: + key: protocol + value: "CRUDP" + where: equals("log.protocol", "127") + - add: + function: "string" + params: + key: protocol + value: "SSCOPMCE" + where: equals("log.protocol", "128") + - add: + function: "string" + params: + key: protocol + value: "IPLT" + where: equals("log.protocol", "129") + - add: + function: "string" + params: + key: protocol + value: "SPS" + where: equals("log.protocol", "130") + - add: + function: "string" + params: + key: protocol + value: "PIPE" + where: equals("log.protocol", "131") + - add: + function: "string" + params: + key: protocol + value: "SCTP" + where: equals("log.protocol", "132") + - add: + function: "string" + params: + key: protocol + value: "FC" + where: equals("log.protocol", "133") + - add: + function: "string" + params: + key: protocol + value: "RSVP-E2E-IGNORE" + where: equals("log.protocol", "134") + - add: + function: "string" + params: + key: protocol + value: "Mobility-Header" + where: equals("log.protocol", "135") + - add: + function: "string" + params: + key: protocol + value: "UDPLite" + where: equals("log.protocol", "136") + - add: + function: "string" + params: + key: protocol + value: "MPLS-in-IP" + where: equals("log.protocol", "137") + - add: + function: "string" + params: + key: protocol + value: "manet" + where: equals("log.protocol", "138") + - add: + function: "string" + params: + key: protocol + value: "HIP" + where: equals("log.protocol", "139") + - add: + function: "string" + params: + key: protocol + value: "Shim6" + where: equals("log.protocol", "140") + - add: + function: "string" + params: + key: protocol + value: "WESP" + where: equals("log.protocol", "141") + - add: + function: "string" + params: + key: protocol + value: "ROHC" + where: equals("log.protocol", "142") + - add: + function: "string" + params: + key: protocol + value: "Ethernet" + where: equals("log.protocol", "143") + - add: + function: "string" + params: + key: protocol + value: "AGGFRAG" + where: equals("log.protocol", "144") + - add: + function: "string" + params: + key: protocol + value: "NSH" + where: equals("log.protocol", "145") + - add: + function: "string" + params: + key: protocol + value: "Homa" + where: equals("log.protocol", "146") + - add: + function: "string" + params: + key: protocol + value: "BIT-EMU" + where: equals("log.protocol", "147") # Removing unused fields - delete: @@ -261,6 +1146,8 @@ pipeline: - log.dstAs - log.srcAs - log.dstPort + - log.proto + - log.protocol - log.irrelevant - log.irrelevant1 - log.irrelevant2 From 2c8bd19361bd8da3d149f128206692d314477456 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 12 Feb 2026 07:48:11 -0600 Subject: [PATCH 006/115] feat(assets-view): refactor asset detail handling and improve status display Signed-off-by: Manuel Abascal --- .../assets-view/assets-view.component.html | 49 +++++++++++++++++-- .../assets-view/assets-view.component.scss | 4 ++ .../assets-view/assets-view.component.ts | 3 +- .../utm-agent-connect.component.ts | 1 - .../utm-agent-detail.component.ts | 2 - 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/assets-discover/assets-view/assets-view.component.html b/frontend/src/app/assets-discover/assets-view/assets-view.component.html index aae1ae3bf..c2fc7bdd1 100644 --- a/frontend/src/app/assets-discover/assets-view/assets-view.component.html +++ b/frontend/src/app/assets-discover/assets-view/assets-view.component.html @@ -270,8 +270,7 @@
- {{ assetSelected.isAgent }} - {{ assetSelected.isAgent ? 'Agent' : 'Source' }} {{ assetName }} details + {{ assetSelected.isAgent ? 'Agent' : 'Data Source' }} — {{ assetName }} Details
-
+
+ + +
+ Name:  + {{assetSelected.displayName}} +
+
+ MAC:  +
+ + {{card}} + +
+
+
+ Status:  + + + {{ !assetSelected.status ? 'ONLINE' : 'OFFLINE' }} + +
+
+ Type:  + + +
+
+ Group Source:  + + +
+
+ Comment:  + +
+
+ Last seen:  + {{ assetSelected.lastInput | date }} +
+
diff --git a/frontend/src/app/assets-discover/assets-view/assets-view.component.scss b/frontend/src/app/assets-discover/assets-view/assets-view.component.scss index e14e1b008..37338a59a 100644 --- a/frontend/src/app/assets-discover/assets-view/assets-view.component.scss +++ b/frontend/src/app/assets-discover/assets-view/assets-view.component.scss @@ -13,3 +13,7 @@ height: 100%; min-height: 0; } + +.min-w-70px { + min-width: 70px; +} diff --git a/frontend/src/app/assets-discover/assets-view/assets-view.component.ts b/frontend/src/app/assets-discover/assets-view/assets-view.component.ts index d85f9c3ee..b66991669 100644 --- a/frontend/src/app/assets-discover/assets-view/assets-view.component.ts +++ b/frontend/src/app/assets-discover/assets-view/assets-view.component.ts @@ -19,7 +19,6 @@ import {SortEvent} from '../../shared/directives/sortable/type/sort-event'; import {ChartValueSeparator} from '../../shared/enums/chart-value-separator'; import {ElasticOperatorsEnum} from '../../shared/enums/elastic-operators.enum'; import {IncidentOriginTypeEnum} from '../../shared/enums/incident-response/incident-origin-type.enum'; -import {UtmDatePipe} from '../../shared/pipes/date.pipe'; import {IncidentCommandType} from '../../shared/types/incident/incident-command.type'; import {UtmFieldType} from '../../shared/types/table/utm-field.type'; import {TimeFilterType} from '../../shared/types/time-filter.type'; @@ -44,7 +43,7 @@ import {SourceDataTypeConfigComponent} from '../source-data-type-config/source-d changeDetection: ChangeDetectionStrategy.OnPush }) export class AssetsViewComponent implements OnInit, OnDestroy { - assets$: Observable; + assets: NetScanType[]; // defaultTime: ElasticFilterDefaultTime = new ElasticFilterDefaultTime('now-30d', 'now'); pageWidth = window.innerWidth; diff --git a/frontend/src/app/shared/components/utm/util/utm-agent-connect/utm-agent-connect.component.ts b/frontend/src/app/shared/components/utm/util/utm-agent-connect/utm-agent-connect.component.ts index ab4414ce9..d6448b479 100644 --- a/frontend/src/app/shared/components/utm/util/utm-agent-connect/utm-agent-connect.component.ts +++ b/frontend/src/app/shared/components/utm/util/utm-agent-connect/utm-agent-connect.component.ts @@ -23,7 +23,6 @@ export class UtmAgentConnectComponent implements OnInit, OnChanges { } ngOnInit() { - console.log('init'); this.hasNoReason = this.websocketCommand.reason === '' || !this.websocketCommand.reason; } diff --git a/frontend/src/app/shared/components/utm/util/utm-agent-detail/utm-agent-detail.component.ts b/frontend/src/app/shared/components/utm/util/utm-agent-detail/utm-agent-detail.component.ts index 5dd9b8c9d..7e229177b 100644 --- a/frontend/src/app/shared/components/utm/util/utm-agent-detail/utm-agent-detail.component.ts +++ b/frontend/src/app/shared/components/utm/util/utm-agent-detail/utm-agent-detail.component.ts @@ -28,8 +28,6 @@ export class UtmAgentDetailComponent implements OnInit { this.agentIp = this.agent.ip; this.ips = this.agent.addresses !== '' ? this.agent.addresses.split(',') : []; this.macs = this.agent.mac !== '' ? this.agent.mac.split(',') : []; - - console.log(this.agent); } public getAgentIcon(): string { From 5945ee193bc4d1ce2bbaa95eeb1ccbc1556b248a Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 12 Feb 2026 08:59:44 -0600 Subject: [PATCH 007/115] feat(netflow-filter): add update for Netflow filter version 3.1.1 and enhance field processing --- .../20260212001_update_filter_netflow.xml | 1182 +++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 1184 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260212001_update_filter_netflow.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260212001_update_filter_netflow.xml b/backend/src/main/resources/config/liquibase/changelog/20260212001_update_filter_netflow.xml new file mode 100644 index 000000000..4e0b88a0b --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260212001_update_filter_netflow.xml @@ -0,0 +1,1182 @@ + + + + + + + + + + \ 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 fb0f51e27..988412b6e 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -415,6 +415,8 @@ + + From 413ea27ecae99a891d42cf0e7daed72e638ac56f Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 12 Feb 2026 19:24:07 +0300 Subject: [PATCH 008/115] refactor(azure-plugin): extracts event processing logic into separate functions to handle JSON format detection (array vs object) --- plugins/azure/main.go | 146 ++++++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 61 deletions(-) diff --git a/plugins/azure/main.go b/plugins/azure/main.go index 1d53a0280..28712e7c6 100644 --- a/plugins/azure/main.go +++ b/plugins/azure/main.go @@ -419,67 +419,7 @@ func processPartition(ctx context.Context, pc *azeventhubs.ProcessorPartitionCli } for _, event := range events { - var logData map[string]any - if err := json.Unmarshal(event.Body, &logData); err != nil { - _ = catcher.Error("cannot parse event body", err, map[string]any{ - "group": groupName, - "partitionID": pc.PartitionID(), - "process": "plugin_com.utmstack.azure", - }) - continue - } - - if records, ok := logData["records"].([]any); ok && len(records) > 0 { - for _, record := range records { - recordMap, ok := record.(map[string]any) - if !ok { - _ = catcher.Error("invalid record format in records array", nil, map[string]any{ - "group": groupName, - "partitionID": pc.PartitionID(), - "process": "plugin_com.utmstack.azure", - }) - continue - } - - jsonLog, err := json.Marshal(recordMap) - if err != nil { - _ = catcher.Error("cannot encode record to JSON", err, map[string]any{ - "group": groupName, - "partitionID": pc.PartitionID(), - "process": "plugin_com.utmstack.azure", - }) - continue - } - - _ = plugins.EnqueueLog(&plugins.Log{ - Id: uuid.New().String(), - TenantId: defaultTenant, - DataType: "azure", - DataSource: groupName, - Timestamp: time.Now().UTC().Format(time.RFC3339Nano), - Raw: string(jsonLog), - }, "com.utmstack.azure") - } - } else { - jsonLog, err := json.Marshal(logData) - if err != nil { - _ = catcher.Error("cannot encode log to JSON", err, map[string]any{ - "group": groupName, - "partitionID": pc.PartitionID(), - "process": "plugin_com.utmstack.azure", - }) - continue - } - - _ = plugins.EnqueueLog(&plugins.Log{ - Id: uuid.New().String(), - TenantId: defaultTenant, - DataType: "azure", - DataSource: groupName, - Timestamp: time.Now().UTC().Format(time.RFC3339Nano), - Raw: string(jsonLog), - }, "com.utmstack.azure") - } + processEvent(event.Body, groupName) } if err := pc.UpdateCheckpoint(context.Background(), events[len(events)-1], nil); err != nil { @@ -518,6 +458,90 @@ func getAzureProcessor(group *config.ModuleGroup) AzureConfig { return azurePro } +func processEvent(eventBody []byte, groupName string) { + var firstByte byte + for _, b := range eventBody { + if b != ' ' && b != '\t' && b != '\n' && b != '\r' { + firstByte = b + break + } + } + + switch firstByte { + case '[': + processArrayEvent(eventBody, groupName) + case '{': + processObjectEvent(eventBody, groupName) + default: + _ = catcher.Error("invalid JSON format: expected array or object", nil, map[string]any{ + "group": groupName, + "process": "plugin_com.utmstack.azure", + }) + } +} + +func processArrayEvent(eventBody []byte, groupName string) { + var records []map[string]any + if err := json.Unmarshal(eventBody, &records); err != nil { + _ = catcher.Error("cannot parse event body as array", err, map[string]any{ + "group": groupName, + "process": "plugin_com.utmstack.azure", + }) + return + } + + for _, record := range records { + enqueueRecord(record, groupName) + } +} + +func processObjectEvent(eventBody []byte, groupName string) { + var logData map[string]any + if err := json.Unmarshal(eventBody, &logData); err != nil { + _ = catcher.Error("cannot parse event body as object", err, map[string]any{ + "group": groupName, + "process": "plugin_com.utmstack.azure", + }) + return + } + + if records, ok := logData["records"].([]any); ok && len(records) > 0 { + for _, record := range records { + recordMap, ok := record.(map[string]any) + if !ok { + _ = catcher.Error("invalid record format in records array", nil, map[string]any{ + "group": groupName, + "process": "plugin_com.utmstack.azure", + }) + continue + } + enqueueRecord(recordMap, groupName) + } + } else { + enqueueRecord(logData, groupName) + } +} + +func enqueueRecord(record map[string]any, groupName string) { + jsonLog, err := json.Marshal(record) + if err != nil { + _ = catcher.Error("cannot encode record to JSON", err, map[string]any{ + "group": groupName, + "process": "plugin_com.utmstack.azure", + }) + return + } + + _ = plugins.EnqueueLog(&plugins.Log{ + Id: uuid.New().String(), + TenantId: defaultTenant, + DataType: "azure", + DataSource: groupName, + Timestamp: time.Now().UTC().Format(time.RFC3339Nano), + Raw: string(jsonLog), + }, "com.utmstack.azure") +} + func connectionChecker(url string) error { checkConn := func() error { if err := checkConnection(url); err != nil { From a1b91701f040122227bfde3a9042bd28123f4de9 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 12 Feb 2026 12:40:36 -0600 Subject: [PATCH 009/115] feat(windows-visualizations): update outdated fields in Windows visualizations and normalize field names --- ...60211004_update_windows_visualizations.xml | 341 +++++++++++++++++- ...60212002_update_windows_visualizations.xml | 126 +++++++ .../resources/config/liquibase/master.xml | 2 + 3 files changed, 457 insertions(+), 12 deletions(-) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260212002_update_windows_visualizations.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml index ca13dffdc..843ca606c 100644 --- a/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml +++ b/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml @@ -2,7 +2,8 @@ + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog + http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"> @@ -10,36 +11,352 @@ diff --git a/backend/src/main/resources/config/liquibase/changelog/20260212002_update_windows_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260212002_update_windows_visualizations.xml new file mode 100644 index 000000000..551e89fa1 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260212002_update_windows_visualizations.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 988412b6e..0eae6b4ee 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -417,6 +417,8 @@ + + From 6c0c23c343402bceac6d2b8405cf8eb78cf08c71 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 12 Feb 2026 14:39:37 -0600 Subject: [PATCH 010/115] feat(windows-visualizations): update field names in Windows visualizations for consistency --- ...60212003_update_windows_visualizations.xml | 413 ++++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 415 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260212003_update_windows_visualizations.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260212003_update_windows_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260212003_update_windows_visualizations.xml new file mode 100644 index 000000000..3a31a149f --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260212003_update_windows_visualizations.xml @@ -0,0 +1,413 @@ + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 0eae6b4ee..fc8d65232 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -419,6 +419,8 @@ + + From 9e88c992b0fe0a4b06cd69972bb4b436d824f395 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 12 Feb 2026 15:01:24 -0600 Subject: [PATCH 011/115] feat(open-alerts): optimize open alerts handling and improve local storage updates Signed-off-by: Manuel Abascal --- .../shared/services/open-alerts.service.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/data-management/alert-management/shared/services/open-alerts.service.ts b/frontend/src/app/data-management/alert-management/shared/services/open-alerts.service.ts index 81afe4da0..880718277 100644 --- a/frontend/src/app/data-management/alert-management/shared/services/open-alerts.service.ts +++ b/frontend/src/app/data-management/alert-management/shared/services/open-alerts.service.ts @@ -32,10 +32,12 @@ export class OpenAlertsService implements OnDestroy { takeUntil(this.destroy$), switchMap(() => this.alertOpenStatusService.getOpenAlert()), tap((response) => { - this.openAlerts = response.body; - this.localStorage.store(OPEN_ALERTS_KEY, this.openAlerts); - if (this.openAlerts >= 0 && this.openAlerts !== this.openAlerts) { - this.openAlertsBehaviorSubject.next(this.openAlerts); + const newValue = response.body; + + if (newValue !== this.openAlerts) { + this.openAlerts = newValue; + this.openAlertsBehaviorSubject.next(newValue); + this.localStorage.store(OPEN_ALERTS_KEY, newValue); } }), catchError((err) => { From 03eee3bfb7c137292188c90b5d01d20e94d652e8 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 12 Feb 2026 15:02:54 -0600 Subject: [PATCH 012/115] feat(visualization-list): integrate UtmToastService for error handling in visualization fetching Signed-off-by: Manuel Abascal --- .../visualization-list/visualization-list.component.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/graphic-builder/visualization/visualization-list/visualization-list.component.ts b/frontend/src/app/graphic-builder/visualization/visualization-list/visualization-list.component.ts index 05adf6938..67f760466 100644 --- a/frontend/src/app/graphic-builder/visualization/visualization-list/visualization-list.component.ts +++ b/frontend/src/app/graphic-builder/visualization/visualization-list/visualization-list.component.ts @@ -18,6 +18,7 @@ import {VisualizationService} from '../shared/services/visualization.service'; import {VisualizationCreateComponent} from '../visualization-create/visualization-create.component'; import {VisualizationDeleteComponent} from '../visualization-delete/visualization-delete.component'; import {VisualizationImportComponent} from '../visualization-import/visualization-import.component'; +import {UtmToastService} from "../../../shared/alert/utm-toast.service"; @Component({ selector: 'app-visualization-list', @@ -54,7 +55,8 @@ export class VisualizationListComponent implements OnInit { private visualizationService: VisualizationService, private spinner: NgxSpinnerService, private router: Router, - private activatedRoute: ActivatedRoute) { + private activatedRoute: ActivatedRoute, + private toastService: UtmToastService) { } ngOnInit() { @@ -118,7 +120,7 @@ export class VisualizationListComponent implements OnInit { getVisualizationList() { this.visualizationService.query(this.requestParams).subscribe( (res: HttpResponse) => this.onSuccess(res.body, res.headers), - (res: HttpResponse) => this.onError(res.body) + (res: HttpResponse) => this.onError(res) ); } @@ -200,7 +202,8 @@ export class VisualizationListComponent implements OnInit { } private onError(error) { - // this.alertService.error(error.error, error.message, null); + this.toastService.showError('Error', 'An error occurred while fetching visualizations'); + this.loading = false; } } From 841428fc098143c084ca844337387462029ffcd2 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 12 Feb 2026 15:49:01 -0600 Subject: [PATCH 013/115] feat(windows-visualizations): update field names in Windows visualizations for consistency --- ...60212004_update_windows_visualizations.xml | 228 ++++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 230 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260212004_update_windows_visualizations.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260212004_update_windows_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260212004_update_windows_visualizations.xml new file mode 100644 index 000000000..ded05c74a --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260212004_update_windows_visualizations.xml @@ -0,0 +1,228 @@ + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index fc8d65232..254211c36 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -421,6 +421,8 @@ + + From 146e6a375a2f9f3eab2dfb23bd192bdfa713610d Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 13 Feb 2026 07:45:10 -0600 Subject: [PATCH 014/115] feat(windows-visualizations): update field names in Windows visualizations for consistency --- ...60211004_update_windows_visualizations.xml | 377 +++++++++--------- ...60212002_update_windows_visualizations.xml | 44 +- ...60212005_update_windows_visualizations.xml | 309 ++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 4 files changed, 532 insertions(+), 200 deletions(-) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260212005_update_windows_visualizations.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml index 843ca606c..7bd925e57 100644 --- a/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml +++ b/backend/src/main/resources/config/liquibase/changelog/20260211004_update_windows_visualizations.xml @@ -10,95 +10,101 @@ + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 254211c36..09b3b629f 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -423,6 +423,8 @@ + + From 2b8a6f3b3256943dfc70f6aea2ab93941035d582 Mon Sep 17 00:00:00 2001 From: Yadian Llada Lopez Date: Fri, 13 Feb 2026 10:00:53 -0500 Subject: [PATCH 015/115] feat(bitdefender-gz): add renaming for log.deviceIps, log.dvchost, and log.act fields --- filters/antivirus/bitdefender_gz.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/filters/antivirus/bitdefender_gz.yml b/filters/antivirus/bitdefender_gz.yml index e8605dc75..fa430da73 100644 --- a/filters/antivirus/bitdefender_gz.yml +++ b/filters/antivirus/bitdefender_gz.yml @@ -183,16 +183,31 @@ pipeline: - log.src to: origin.ip + - rename: + from: + - log.deviceIps + to: origin.ip + + - rename: + from: + - log.dvchost + to: target.host + - rename: from: - log.sproc - to: origin.path + to: target.path - rename: from: - log.filePath to: origin.path + - rename: + from: + - log.act + to: action + # Removing unnecessary characters - trim: function: prefix From cafc3a1df56994a4bd28c4a92c7d7ce1ef4ddd5e Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 13 Feb 2026 09:23:35 -0600 Subject: [PATCH 016/115] feat(bitdefender-visualizations): normalize field names in Bitdefender GZ visualizations --- ...001_update_bit_defender_visualizations.xml | 66 +++++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 68 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260213001_update_bit_defender_visualizations.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260213001_update_bit_defender_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260213001_update_bit_defender_visualizations.xml new file mode 100644 index 000000000..89f6c8542 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260213001_update_bit_defender_visualizations.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 09b3b629f..ee3760120 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -425,6 +425,8 @@ + + From a2e52de2133f9bf8684553ac7ecdb7ec97783feb Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 13 Feb 2026 09:39:57 -0600 Subject: [PATCH 017/115] feat(vmware-visualizations): normalize field names in VMware visualizations --- ...260213002_update_vmware_visualizations.xml | 51 +++++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 53 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260213002_update_vmware_visualizations.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260213002_update_vmware_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260213002_update_vmware_visualizations.xml new file mode 100644 index 000000000..2d6732334 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260213002_update_vmware_visualizations.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index ee3760120..44637eacc 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -427,6 +427,8 @@ + + From a2d52aae01acaec40fcfbdda9b79a2afe11711f6 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 13 Feb 2026 09:47:56 -0600 Subject: [PATCH 018/115] feat(bitdefender-filter): add Bitdefender GravityZone filter update with field renaming and cleanup --- ...20260213003_update_filter_bit_defender.xml | 301 ++++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 303 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260213003_update_filter_bit_defender.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260213003_update_filter_bit_defender.xml b/backend/src/main/resources/config/liquibase/changelog/20260213003_update_filter_bit_defender.xml new file mode 100644 index 000000000..cfb7be90c --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260213003_update_filter_bit_defender.xml @@ -0,0 +1,301 @@ + + + + + + + ' + - fieldName: log.syslogVersion + pattern: '{{.integer}}' + - fieldName: log.syslogDeviceTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}\w{{.time}}\w' + - fieldName: log.syslogHostIP + pattern: '{{.ipv4}}|{{.ipv6}}|{{.word}}' + - fieldName: log.notDefined + pattern: '{{.integer}}' + - fieldName: log.0trash + pattern: '{{.word}}\:{{.integer}}' + - fieldName: log.restData + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.syslogPriority + pattern: '\<{{.data}}\>' + - fieldName: log.syslogVersion + pattern: '{{.integer}}' + - fieldName: log.syslogDeviceTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}\w{{.time}}\w' + - fieldName: log.hostId + pattern: '{{.word}}' + - fieldName: log.0trash + pattern: '{{.word}}' + - fieldName: log.processPid + pattern: '\[{{.integer}}\]' + - fieldName: log.1trash + pattern: '{{.word}}\:{{.integer}}' + - fieldName: log.restData + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.syslogPriority + pattern: '\<{{.data}}\>' + - fieldName: log.syslogDeviceTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}\w{{.time}}\w' + - fieldName: log.hostId + pattern: '{{.word}}' + - fieldName: log.0trash + pattern: '{{.word}}' + - fieldName: log.processPid + pattern: '\[{{.integer}}\]' + - fieldName: log.1trash + pattern: '{{.word}}\:{{.integer}}' + - fieldName: log.restData + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.syslogPriority + pattern: '\<{{.data}}\>' + - fieldName: log.syslogVersion + pattern: '{{.integer}}' + - fieldName: log.syslogDeviceTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}\w{{.time}}\w' + - fieldName: log.syslogHostIP + pattern: '{{.ipv4}}|{{.ipv6}}|{{.word}}' + - fieldName: log.0trash + pattern: '{{.word}}\:{{.integer}}' + - fieldName: log.restData + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.syslogPriority + pattern: '\<{{.data}}\>' + - fieldName: log.syslogVersion + pattern: '{{.integer}}' + - fieldName: log.syslogDeviceTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}\w{{.time}}\w' + - fieldName: log.restData + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.syslogPriority + pattern: '\<{{.data}}\>' + - fieldName: log.0trash + pattern: '{{.word}}\:{{.integer}}' + - fieldName: log.restData + pattern: '{{.greedy}}' + source: raw + + - grok: + patterns: + - fieldName: log.cefVersion + pattern: 'CEF\:{{.integer}}' + - fieldName: log.restData + pattern: '{{.greedy}}' + source: raw + + # Using grok to parse components of the cef_message + - grok: + patterns: + - fieldName: log.productVendor + pattern: '\|{{.data}}\|' + - fieldName: log.product + pattern: '{{.data}}\|' + - fieldName: log.productVersion + pattern: '{{.data}}\|' + - fieldName: log.signatureID + pattern: '{{.data}}\|' + - fieldName: log.eventType + pattern: '{{.data}}\|' + - fieldName: log.severity + pattern: '{{.data}}\|' + - fieldName: log.restData + pattern: '{{.greedy}}' + source: log.restData + + # Using grok to parse kv issued fields with space + - grok: + patterns: + - fieldName: log.2trash + pattern: '{{.data}}dvc=' + - fieldName: log.dvcToParse + pattern: '{{.data}}{{.word}}\=' + - fieldName: log.irrelevant + pattern: '{{.greedy}}' + source: log.restData + + - grok: + patterns: + - fieldName: log.2trash + pattern: '{{.data}}request=' + - fieldName: log.requestToParse + pattern: '{{.data}}{{.word}}\=' + - fieldName: log.irrelevant + pattern: '{{.greedy}}' + source: log.restData + + # Applying grok to remove unnecessary data + - grok: + patterns: + - fieldName: log.deviceIps + pattern: '{{.greedy}}{{.space}}' + - fieldName: log.irrelevant + pattern: '{{.greedy}}' + source: log.dvcToParse + + - grok: + patterns: + - fieldName: log.requested + pattern: '{{.greedy}}{{.space}}' + - fieldName: log.irrelevant + pattern: '{{.greedy}}' + source: log.requestToParse + + # Using the kv filter with default config, usefull in key-value logs + - kv: + fieldSplit: " " + valueSplit: "=" + source: log.restData + + # Renaming useful fields + - rename: + from: + - log.spt + to: origin.port + + - rename: + from: + - log.src + to: origin.ip + + - rename: + from: + - log.deviceIps + to: origin.ip + + - rename: + from: + - log.dvchost + to: target.host + + - rename: + from: + - log.sproc + to: target.path + + - rename: + from: + - log.filePath + to: origin.path + + - rename: + from: + - log.act + to: action + + # Removing unnecessary characters + - trim: + function: prefix + substring: '|' + fields: + - log.productVendor + + - trim: + function: suffix + substring: '|' + fields: + - log.productVendor + - log.product + - log.productVersion + - log.signatureID + - log.eventType + - log.severity + + - trim: + function: prefix + substring: '<' + fields: + - log.syslogPriority + + - trim: + function: suffix + substring: '>' + fields: + - log.syslogPriority + + - trim: + function: prefix + substring: '[' + fields: + - log.processPid + + - trim: + function: suffix + substring: ']' + fields: + - log.processPid + + # Adding geolocation to origin ip + - dynamic: + plugin: com.utmstack.geolocation + params: + source: origin.ip + destination: origin.geolocation + where: exists("origin.ip") + + # Reformat and field conversions + - cast: + fields: + - origin.port + to: int + + # Removing unused fields + - delete: + fields: + - log.0trash + - log.1trash + - log.2trash + - log.restData + - log.irrelevant + - log.spt + - log.src + - log.sproc + - log.filePath + - log.dvc + - log.request + - log.dvcToParse + - log.cefVersion$$ + WHERE id = 1514; + ]]> + + + \ 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 44637eacc..f559743dd 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -429,6 +429,8 @@ + + From f2a012c5c09e3cf8f5a56405c98bd60c4aa7c383 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 13 Feb 2026 10:24:10 -0600 Subject: [PATCH 019/115] feat(dashboard-render): improve dashboard loading logic and enhance error handling for filters Signed-off-by: Manuel Abascal --- .../dashboard-render.component.html | 8 ++--- .../dashboard-render.component.ts | 29 ++++++++++++++----- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/dashboard/dashboard-render/dashboard-render.component.html b/frontend/src/app/dashboard/dashboard-render/dashboard-render.component.html index f99dcb24d..5372cdeec 100644 --- a/frontend/src/app/dashboard/dashboard-render/dashboard-render.component.html +++ b/frontend/src/app/dashboard/dashboard-render/dashboard-render.component.html @@ -1,5 +1,5 @@
-
+
{{dashboard.name}}
@@ -17,9 +17,9 @@
{{dashboard.name}}
- + {{dashboard.name}}
-
{ - this.dashboard = this.layoutService.dashboard; - this.filters = this.dashboard && this.dashboard.filters ? JSON.parse(this.dashboard.filters) : []; - this.loadingVisualizations = false; - }), - map(data => data.response) + + this.layout$ = this.activatedRoute.data.pipe( + map(data => { + const response = data && data.response ? data.response : []; + + this.dashboard = this.layoutService.dashboard ? this.layoutService.dashboard : null; + + try { + this.filters = this.dashboard && this.dashboard.filters + ? JSON.parse(this.dashboard.filters) + : []; + } catch { + this.filters = []; + } + + this.loadingVisualizations = false; + + return response; + }) ); + this.dashboardBehavior.$filterDashboard.subscribe(dashboardFilter => { if (dashboardFilter) { mergeParams(dashboardFilter.filter, this.filtersValues).then(newFilters => { From 9cb31c57f43cd7b3779fd8e5a0df6887daa83588 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Mon, 16 Feb 2026 19:32:21 +0300 Subject: [PATCH 020/115] update vmware-esxi filter --- filters/vmware/vmware-esxi.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/filters/vmware/vmware-esxi.yml b/filters/vmware/vmware-esxi.yml index 7c533dd72..6b6f5b6b0 100644 --- a/filters/vmware/vmware-esxi.yml +++ b/filters/vmware/vmware-esxi.yml @@ -75,6 +75,14 @@ pipeline: - fieldName: log.subModuleIdentifier pattern: '{{.word}}\]' source: log.originIdComponent + + - grok: + patterns: + - fieldName: log.irrelevant2 + pattern: '{{.data}}level{{.space}}=' + - fieldName: log.level + pattern: '{{.integer}}' + source: log.message # Removing unused caracters - trim: From f5efbf9b9139b9c5e960424ff4e9807f00c35e3f Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Mon, 16 Feb 2026 19:45:41 +0300 Subject: [PATCH 021/115] update version vmware-esxi filter --- filters/vmware/vmware-esxi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filters/vmware/vmware-esxi.yml b/filters/vmware/vmware-esxi.yml index 6b6f5b6b0..a415d2e1f 100644 --- a/filters/vmware/vmware-esxi.yml +++ b/filters/vmware/vmware-esxi.yml @@ -1,4 +1,4 @@ -# VMWare-ESXi, version 3.0.1 +# VMWare-ESXi, version 3.0.2 # # Based on docs and real logs provided # Support VMWare-ESXi log From 44e543d25afd60ae5b2dd81a4dc79ce5194e44c1 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 16 Feb 2026 11:48:23 -0600 Subject: [PATCH 022/115] feat(vmware-esxi-filter): add update for VMware ESXi filter with enhanced parsing and cleanup --- .../20260216001_update_filter_vmware_esxi.xml | 157 ++++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 159 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260216001_update_filter_vmware_esxi.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260216001_update_filter_vmware_esxi.xml b/backend/src/main/resources/config/liquibase/changelog/20260216001_update_filter_vmware_esxi.xml new file mode 100644 index 000000000..941f18aa3 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260216001_update_filter_vmware_esxi.xml @@ -0,0 +1,157 @@ + + + + + + + ' + - fieldName: log.deviceTime + pattern: '{{.year}}(-){{.monthNumber}}(-){{.monthDay}}(T){{.time}}(Z)' + - fieldName: origin.hostname + pattern: '{{.hostname}}' + - fieldName: log.process + pattern: '{{.hostname}}(\:)' + - fieldName: severity + pattern: '{{.word}}' + - fieldName: log.processName + pattern: '{{.hostname}}' + - fieldName: log.pid + pattern: '\[{{.data}}\]' + - fieldName: log.eventInfo + pattern: '\[{{.data}}\]' + - fieldName: log.message + pattern: '{{.greedy}}' + + - grok: + patterns: + - fieldName: log.priority + pattern: '\<{{.data}}\>' + - fieldName: log.deviceTime + pattern: '{{.year}}(-){{.monthNumber}}(-){{.monthDay}}(T){{.time}}(Z)' + - fieldName: origin.hostname + pattern: '{{.hostname}}' + - fieldName: log.process + pattern: '{{.hostname}}' + - fieldName: log.pid + pattern: '\[{{.data}}\]:' + - fieldName: log.message + pattern: '{{.greedy}}' + + - grok: + patterns: + - fieldName: log.priority + pattern: '\<{{.data}}\>' + - fieldName: log.deviceTime + pattern: '{{.year}}-{{.monthNumber}}-{{.monthDay}}T{{.time}}Z' + - fieldName: origin.hostname + pattern: '{{.hostname}}' + - fieldName: log.process + pattern: '{{.hostname}}' + - fieldName: log.pid + pattern: '\[{{.data}}\]:' + - fieldName: log.originIdComponent + pattern: '\[{{.data}}\]' + - fieldName: log.message + pattern: '{{.greedy}}' + + - grok: + patterns: + - fieldName: log.moduleIdentifier + pattern: '\[{{.data}}\@' + - fieldName: log.irrelevant + pattern: '{{.data}}\=' + - fieldName: log.subModuleIdentifier + pattern: '{{.word}}\]' + source: log.originIdComponent + + - grok: + patterns: + - fieldName: log.irrelevant2 + pattern: '{{.data}}level{{.space}}=' + - fieldName: log.level + pattern: '{{.integer}}' + source: log.message + + # Removing unused caracters + - trim: + function: prefix + substring: '<' + fields: + - log.priority + - trim: + function: prefix + substring: '[' + fields: + - log.pid + - log.eventInfo + - log.moduleIdentifier + - trim: + function: prefix + substring: '-' + fields: + - log.message + - trim: + function: suffix + substring: '>' + fields: + - log.priority + - trim: + function: suffix + substring: ':' + fields: + - log.pid + - log.process + - trim: + function: suffix + substring: ']' + fields: + - log.pid + - log.eventInfo + - log.subModuleIdentifier + - trim: + function: suffix + substring: '-' + fields: + - log.message + - trim: + function: suffix + substring: '@' + fields: + - log.moduleIdentifier + + # Removing unused fields + - delete: + fields: + - log.processName + - log.irrelevant$$ + WHERE id = 1001; + ]]> + + + \ 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 f559743dd..8fd4434f5 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -431,6 +431,8 @@ + + From 4be733a1b4d691809251feebc7052b8e2f6a072e Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Tue, 17 Feb 2026 15:39:47 -0500 Subject: [PATCH 023/115] feat[shared]: create shared dependencies --- shared/archive/zip.go | 68 ++++++++++++++++++++++++ shared/exec/exec.go | 38 ++++++++++++++ shared/fs/fs.go | 108 ++++++++++++++++++++++++++++++++++++++ shared/fs/json.go | 33 ++++++++++++ shared/fs/yaml.go | 47 +++++++++++++++++ shared/go.mod | 39 ++++++++++++++ shared/go.sum | 84 +++++++++++++++++++++++++++++ shared/http/download.go | 91 ++++++++++++++++++++++++++++++++ shared/logger/logger.go | 59 +++++++++++++++++++++ shared/svc/svc.go | 9 ++++ shared/svc/svc_unix.go | 64 ++++++++++++++++++++++ shared/svc/svc_windows.go | 65 +++++++++++++++++++++++ 12 files changed, 705 insertions(+) create mode 100644 shared/archive/zip.go create mode 100644 shared/exec/exec.go create mode 100644 shared/fs/fs.go create mode 100644 shared/fs/json.go create mode 100644 shared/fs/yaml.go create mode 100644 shared/go.mod create mode 100644 shared/go.sum create mode 100644 shared/http/download.go create mode 100644 shared/logger/logger.go create mode 100644 shared/svc/svc.go create mode 100644 shared/svc/svc_unix.go create mode 100644 shared/svc/svc_windows.go diff --git a/shared/archive/zip.go b/shared/archive/zip.go new file mode 100644 index 000000000..84ce3ff12 --- /dev/null +++ b/shared/archive/zip.go @@ -0,0 +1,68 @@ +// Package archive provides utilities for working with archive files. +package archive + +import ( + "archive/zip" + "fmt" + "io" + "os" + "path/filepath" + "strings" +) + +// Unzip extracts a zip archive to the specified destination directory. +func Unzip(src, dest string) error { + r, err := zip.OpenReader(src) + if err != nil { + return fmt.Errorf("error opening zip file: %w", err) + } + defer r.Close() + + for _, f := range r.File { + if err := extractFile(f, dest); err != nil { + return err + } + } + return nil +} + +func extractFile(f *zip.File, dest string) error { + // Sanitize the file path to prevent zip slip attacks + filePath := filepath.Join(dest, f.Name) + if !strings.HasPrefix(filePath, filepath.Clean(dest)+string(os.PathSeparator)) { + return fmt.Errorf("invalid file path: %s", f.Name) + } + + if f.FileInfo().IsDir() { + if err := os.MkdirAll(filePath, os.ModePerm); err != nil { + return fmt.Errorf("error creating directory: %w", err) + } + return nil + } + + // Create parent directories if needed + if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { + return fmt.Errorf("error creating parent directory: %w", err) + } + + // Create the file + outFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return fmt.Errorf("error creating file: %w", err) + } + defer outFile.Close() + + // Open the file in the archive + rc, err := f.Open() + if err != nil { + return fmt.Errorf("error opening archived file: %w", err) + } + defer rc.Close() + + // Copy contents + if _, err = io.Copy(outFile, rc); err != nil { + return fmt.Errorf("error extracting file: %w", err) + } + + return nil +} diff --git a/shared/exec/exec.go b/shared/exec/exec.go new file mode 100644 index 000000000..22487ae65 --- /dev/null +++ b/shared/exec/exec.go @@ -0,0 +1,38 @@ +// Package exec provides utilities for executing external commands. +package exec + +import ( + "bytes" + "fmt" + "os/exec" +) + +// Run executes a command with the given arguments in the specified working directory. +// Returns an error if the command fails. +func Run(command, workDir string, args ...string) error { + cmd := exec.Command(command, args...) + cmd.Dir = workDir + + var stderr bytes.Buffer + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + if stderr.Len() > 0 { + return fmt.Errorf("command failed: %s: %w", stderr.String(), err) + } + return fmt.Errorf("command failed: %w", err) + } + return nil +} + +// RunWithOutput executes a command and returns its combined stdout and stderr output. +func RunWithOutput(command, workDir string, args ...string) (string, error) { + cmd := exec.Command(command, args...) + cmd.Dir = workDir + + output, err := cmd.CombinedOutput() + if err != nil { + return string(output), fmt.Errorf("command failed: %w", err) + } + return string(output), nil +} diff --git a/shared/fs/fs.go b/shared/fs/fs.go new file mode 100644 index 000000000..afd42eb80 --- /dev/null +++ b/shared/fs/fs.go @@ -0,0 +1,108 @@ +// Package fs provides filesystem utility functions. +package fs + +import ( + "fmt" + "io" + "os" + "path/filepath" +) + +// GetExecutablePath returns the directory path of the current executable. +func GetExecutablePath() string { + ex, err := os.Executable() + if err != nil { + return "" + } + return filepath.Dir(ex) +} + +// Exists checks if a path exists. +func Exists(path string) bool { + _, err := os.Stat(path) + return !os.IsNotExist(err) +} + +// CreateDirIfNotExist creates a directory and all parent directories if they don't exist. +func CreateDirIfNotExist(path string) error { + if _, err := os.Stat(path); os.IsNotExist(err) { + if err := os.MkdirAll(path, 0755); err != nil { + return fmt.Errorf("error creating directory: %w", err) + } + } else if err != nil { + return fmt.Errorf("error checking path: %w", err) + } + return nil +} + +// Copy copies a file from src to dst. +func Copy(src, dst string) error { + sourceFile, err := os.Open(src) + if err != nil { + return fmt.Errorf("error opening source file: %w", err) + } + defer sourceFile.Close() + + destFile, err := os.Create(dst) + if err != nil { + return fmt.Errorf("error creating destination file: %w", err) + } + defer destFile.Close() + + if _, err = io.Copy(destFile, sourceFile); err != nil { + return fmt.Errorf("error copying file: %w", err) + } + return nil +} + +// WriteString writes a string to a file, creating or truncating it. +func WriteString(path string, content string) error { + file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644) + if err != nil { + return fmt.Errorf("error opening file: %w", err) + } + defer file.Close() + + if _, err = file.WriteString(content); err != nil { + return fmt.Errorf("error writing to file: %w", err) + } + return nil +} + +// ReadLines reads a file and returns its lines as a slice. +func ReadLines(path string) ([]string, error) { + content, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var lines []string + var line []byte + for _, b := range content { + if b == '\n' { + lines = append(lines, string(line)) + line = nil + } else if b != '\r' { + line = append(line, b) + } + } + if len(line) > 0 { + lines = append(lines, string(line)) + } + return lines, nil +} + +// IsEmpty checks if a directory is empty. +func IsEmpty(path string) (bool, error) { + f, err := os.Open(path) + if err != nil { + return false, err + } + defer f.Close() + + _, err = f.Readdirnames(1) + if err == io.EOF { + return true, nil + } + return false, err +} diff --git a/shared/fs/json.go b/shared/fs/json.go new file mode 100644 index 000000000..7bc39a1ba --- /dev/null +++ b/shared/fs/json.go @@ -0,0 +1,33 @@ +package fs + +import ( + "encoding/json" + "fmt" + "os" +) + +// ReadJSON reads a JSON file and unmarshals it into the provided data structure. +func ReadJSON(path string, data interface{}) error { + content, err := os.ReadFile(path) + if err != nil { + return fmt.Errorf("error reading file: %w", err) + } + + if err = json.Unmarshal(content, data); err != nil { + return fmt.Errorf("error unmarshaling JSON: %w", err) + } + return nil +} + +// WriteJSON marshals the data and writes it to a JSON file with indentation. +func WriteJSON(path string, data interface{}) error { + jsonData, err := json.MarshalIndent(data, "", " ") + if err != nil { + return fmt.Errorf("error marshaling JSON: %w", err) + } + + if err = WriteString(path, string(jsonData)); err != nil { + return fmt.Errorf("error writing JSON file: %w", err) + } + return nil +} diff --git a/shared/fs/yaml.go b/shared/fs/yaml.go new file mode 100644 index 000000000..c54b3d36a --- /dev/null +++ b/shared/fs/yaml.go @@ -0,0 +1,47 @@ +package fs + +import ( + "fmt" + "os" + "reflect" + + "gopkg.in/yaml.v3" +) + +// ReadYAML reads a YAML file and unmarshals it into the provided data structure. +// The data parameter must be a non-nil pointer. +func ReadYAML(path string, data interface{}) error { + if data == nil { + return fmt.Errorf("data interface is nil") + } + + rv := reflect.ValueOf(data) + if rv.Kind() != reflect.Ptr || rv.IsNil() { + return fmt.Errorf("data must be a non-nil pointer") + } + + file, err := os.Open(path) + if err != nil { + return fmt.Errorf("error opening file: %w", err) + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + if err := decoder.Decode(data); err != nil { + return fmt.Errorf("error decoding YAML: %w", err) + } + return nil +} + +// WriteYAML marshals the data and writes it to a YAML file. +func WriteYAML(path string, data interface{}) error { + yamlData, err := yaml.Marshal(data) + if err != nil { + return fmt.Errorf("error marshaling YAML: %w", err) + } + + if err = WriteString(path, string(yamlData)); err != nil { + return fmt.Errorf("error writing YAML file: %w", err) + } + return nil +} diff --git a/shared/go.mod b/shared/go.mod new file mode 100644 index 000000000..51c63efb4 --- /dev/null +++ b/shared/go.mod @@ -0,0 +1,39 @@ +module github.com/utmstack/UTMStack/shared + +go 1.25.1 + +require ( + github.com/threatwinds/logger v1.2.3 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.14.1 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/gabriel-vasile/mimetype v1.4.10 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/gin-gonic/gin v1.10.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.27.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + golang.org/x/arch v0.21.0 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/net v0.44.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + google.golang.org/protobuf v1.36.9 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect +) diff --git a/shared/go.sum b/shared/go.sum new file mode 100644 index 000000000..5f0c38bc2 --- /dev/null +++ b/shared/go.sum @@ -0,0 +1,84 @@ +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w= +github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= +github.com/gabriel-vasile/mimetype v1.4.10/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.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +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= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +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/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= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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= +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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/threatwinds/logger v1.2.3 h1:V2SVAXzbq+/huCvIWOfqzMTH+WBHJxankyBgVG2hy1Y= +github.com/threatwinds/logger v1.2.3/go.mod h1:N+bJKvF4FQNJZLfQpVYWpr6D8iEAFnAQfHYqH5iR1TI= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= +github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw= +golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/shared/http/download.go b/shared/http/download.go new file mode 100644 index 000000000..f116e14b3 --- /dev/null +++ b/shared/http/download.go @@ -0,0 +1,91 @@ +// Package http provides HTTP utility functions. +package http + +import ( + "crypto/tls" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "time" +) + +// DownloadOptions configures a download request. +type DownloadOptions struct { + // Headers to include in the request + Headers map[string]string + // SkipTLSVerify skips TLS certificate validation + SkipTLSVerify bool + // Timeout for the download (default: 5 minutes) + Timeout time.Duration +} + +// DefaultOptions returns default download options. +func DefaultOptions() DownloadOptions { + return DownloadOptions{ + Headers: make(map[string]string), + SkipTLSVerify: false, + Timeout: 5 * time.Minute, + } +} + +// Download downloads a file from the given URL to the specified destination. +func Download(url, destDir, filename string, opts DownloadOptions) error { + if opts.Timeout == 0 { + opts.Timeout = 5 * time.Minute + } + + transport := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: opts.SkipTLSVerify, + }, + } + + client := &http.Client{ + Timeout: opts.Timeout, + Transport: transport, + } + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return fmt.Errorf("error creating request: %w", err) + } + + for key, value := range opts.Headers { + req.Header.Set(key, value) + } + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("error downloading file: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("download failed with status: %s", resp.Status) + } + + destPath := filepath.Join(destDir, filename) + out, err := os.Create(destPath) + if err != nil { + return fmt.Errorf("error creating file: %w", err) + } + defer out.Close() + + if _, err = io.Copy(out, resp.Body); err != nil { + return fmt.Errorf("error writing file: %w", err) + } + + return nil +} + +// DownloadFile is a convenience function that downloads a file with common options. +func DownloadFile(url string, headers map[string]string, filename, destDir string, skipTLSVerify bool) error { + opts := DownloadOptions{ + Headers: headers, + SkipTLSVerify: skipTLSVerify, + Timeout: 5 * time.Minute, + } + return Download(url, destDir, filename, opts) +} diff --git a/shared/logger/logger.go b/shared/logger/logger.go new file mode 100644 index 000000000..666708179 --- /dev/null +++ b/shared/logger/logger.go @@ -0,0 +1,59 @@ +// Package logger provides a simple logging wrapper. +package logger + +import ( + "github.com/threatwinds/logger" +) + +// Log level constants +const ( + LevelDebug = 100 + LevelInfo = 200 + LevelNotice = 300 + LevelWarning = 400 + LevelError = 500 + LevelCritical = 502 + LevelAlert = 509 +) + +// Logger is the shared logger instance. +var Logger *logger.Logger + +// Init initializes the logger with the specified log file path and level. +func Init(logFile string, level int) { + Logger = logger.NewLogger(&logger.Config{ + Format: "text", + Level: level, + Output: logFile, + Retries: 3, + Wait: 5, + }) +} + +// Info logs an informational message. +func Info(format string, args ...any) { + if Logger != nil { + Logger.Info(format, args...) + } +} + +// Error logs an error message. +func Error(format string, args ...any) { + if Logger != nil { + Logger.ErrorF(format, args...) + } +} + +// Fatal logs a fatal message and exits. +func Fatal(format string, args ...any) { + if Logger != nil { + Logger.Fatal(format, args...) + } +} + +// Debug logs a debug message with the specified level. +func Debug(level int, format string, args ...any) { + if Logger != nil { + Logger.LogF(level, format, args...) + } +} diff --git a/shared/svc/svc.go b/shared/svc/svc.go new file mode 100644 index 000000000..e389c0f34 --- /dev/null +++ b/shared/svc/svc.go @@ -0,0 +1,9 @@ +// Package svc provides utilities for managing system services. +package svc + +// Service status constants +const ( + StatusRunning = "running" + StatusStopped = "stopped" + StatusUnknown = "unknown" +) diff --git a/shared/svc/svc_unix.go b/shared/svc/svc_unix.go new file mode 100644 index 000000000..ba2419ef1 --- /dev/null +++ b/shared/svc/svc_unix.go @@ -0,0 +1,64 @@ +//go:build linux || darwin +// +build linux darwin + +package svc + +import ( + "fmt" + "os/exec" + "strings" +) + +// Start starts a system service by name. +func Start(serviceName string) error { + cmd := exec.Command("systemctl", "start", serviceName) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to start service %s: %w", serviceName, err) + } + return nil +} + +// Stop stops a system service by name. +func Stop(serviceName string) error { + cmd := exec.Command("systemctl", "stop", serviceName) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to stop service %s: %w", serviceName, err) + } + return nil +} + +// Restart restarts a system service by name. +func Restart(serviceName string) error { + cmd := exec.Command("systemctl", "restart", serviceName) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to restart service %s: %w", serviceName, err) + } + return nil +} + +// IsActive checks if a system service is running. +func IsActive(serviceName string) (bool, error) { + cmd := exec.Command("systemctl", "is-active", serviceName) + output, err := cmd.Output() + if err != nil { + // is-active returns non-zero for inactive services + return false, nil + } + return strings.TrimSpace(string(output)) == "active", nil +} + +// Status returns the status of a system service. +func Status(serviceName string) (string, error) { + cmd := exec.Command("systemctl", "is-active", serviceName) + output, _ := cmd.Output() + status := strings.TrimSpace(string(output)) + + switch status { + case "active": + return StatusRunning, nil + case "inactive", "failed": + return StatusStopped, nil + default: + return StatusUnknown, nil + } +} diff --git a/shared/svc/svc_windows.go b/shared/svc/svc_windows.go new file mode 100644 index 000000000..52d24b70a --- /dev/null +++ b/shared/svc/svc_windows.go @@ -0,0 +1,65 @@ +//go:build windows +// +build windows + +package svc + +import ( + "fmt" + "os/exec" + "strings" +) + +// Start starts a Windows service by name. +func Start(serviceName string) error { + cmd := exec.Command("sc", "start", serviceName) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to start service %s: %w", serviceName, err) + } + return nil +} + +// Stop stops a Windows service by name. +func Stop(serviceName string) error { + cmd := exec.Command("sc", "stop", serviceName) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to stop service %s: %w", serviceName, err) + } + return nil +} + +// Restart restarts a Windows service by stopping and starting it. +func Restart(serviceName string) error { + if err := Stop(serviceName); err != nil { + return err + } + return Start(serviceName) +} + +// IsActive checks if a Windows service is running. +func IsActive(serviceName string) (bool, error) { + cmd := exec.Command("sc", "query", serviceName) + output, err := cmd.Output() + if err != nil { + return false, fmt.Errorf("failed to query service %s: %w", serviceName, err) + } + return strings.Contains(string(output), "RUNNING"), nil +} + +// Status returns the status of a Windows service. +func Status(serviceName string) (string, error) { + cmd := exec.Command("sc", "query", serviceName) + output, err := cmd.Output() + if err != nil { + return StatusUnknown, fmt.Errorf("failed to query service %s: %w", serviceName, err) + } + + outputStr := string(output) + switch { + case strings.Contains(outputStr, "RUNNING"): + return StatusRunning, nil + case strings.Contains(outputStr, "STOPPED"): + return StatusStopped, nil + default: + return StatusUnknown, nil + } +} From ee8652a97b7de96ef41bfee1f62573f1cd85209f Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Tue, 17 Feb 2026 15:40:41 -0500 Subject: [PATCH 024/115] feat[installer]: remove mfa in dev environments --- installer/docker/compose.go | 39 +++++++++++++++++++++--------------- installer/install.go | 2 +- installer/setup/apply.go | 10 +++++---- installer/updater/service.go | 2 +- installer/utils/secret.go | 21 +++++++++---------- 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/installer/docker/compose.go b/installer/docker/compose.go index 81cc43247..0e8077150 100644 --- a/installer/docker/compose.go +++ b/installer/docker/compose.go @@ -199,6 +199,28 @@ func (c *Compose) Populate(conf *config.Config, stack *StackConfig) error { backendMem := stack.ServiceResources["backend"].AssignedMemory backendMin := stack.ServiceResources["backend"].MinMemory + backendEnv := []string{ + "SERVER_NAME=" + conf.ServerName, + "DB_USER=postgres", + "DB_PASS=" + conf.Password, + "DB_HOST=postgres", + "DB_PORT=5432", + "DB_NAME=utmstack", + "ELASTICSEARCH_HOST=node1", + "ELASTICSEARCH_PORT=9200", + "INTERNAL_KEY=" + conf.InternalKey, + "ENCRYPTION_KEY=" + conf.InternalKey, + "GRPC_AGENT_MANAGER_HOST=agentmanager", + "GRPC_AGENT_MANAGER_PORT=9000", + "EVENT_PROCESSOR_HOST=event-processor-manager", + "EVENT_PROCESSOR_PORT=9002", + } + + // Disable TFA in dev and rc environments + if conf.Branch == "dev" || conf.Branch == "rc" { + backendEnv = append(backendEnv, "APP_TFA_ENABLED=false") + } + c.Services["backend"] = Service{ Image: utils.PointerOf[string]("ghcr.io/utmstack/utmstack/backend:${UTMSTACK_TAG}"), DependsOn: []string{ @@ -206,22 +228,7 @@ func (c *Compose) Populate(conf *config.Config, stack *StackConfig) error { "node1", "agentmanager", }, - Environment: []string{ - "SERVER_NAME=" + conf.ServerName, - "DB_USER=postgres", - "DB_PASS=" + conf.Password, - "DB_HOST=postgres", - "DB_PORT=5432", - "DB_NAME=utmstack", - "ELASTICSEARCH_HOST=node1", - "ELASTICSEARCH_PORT=9200", - "INTERNAL_KEY=" + conf.InternalKey, - "ENCRYPTION_KEY=" + conf.InternalKey, - "GRPC_AGENT_MANAGER_HOST=agentmanager", - "GRPC_AGENT_MANAGER_PORT=9000", - "EVENT_PROCESSOR_HOST=event-processor-manager", - "EVENT_PROCESSOR_PORT=9002", - }, + Environment: backendEnv, Volumes: []string{ stack.DataSources + ":/etc/utmstack", conf.UpdatesFolder + ":/updates", diff --git a/installer/install.go b/installer/install.go index a70cdb02e..37a0ce2f4 100644 --- a/installer/install.go +++ b/installer/install.go @@ -34,7 +34,7 @@ func Install() error { return err } - pass, err := setup.Apply(version.Version) + pass, err := setup.Apply(version.Version, false) if err != nil { return fmt.Errorf("error applying setup: %v", err) } diff --git a/installer/setup/apply.go b/installer/setup/apply.go index 0e0cecef2..b23cff57b 100644 --- a/installer/setup/apply.go +++ b/installer/setup/apply.go @@ -12,7 +12,7 @@ import ( "github.com/utmstack/UTMStack/installer/utils" ) -func Apply(version string) (string, error) { +func Apply(version string, updating bool) (string, error) { cnf := config.GetConfig() fmt.Println("Generating Stack configuration...") @@ -256,10 +256,12 @@ func Apply(version string) (string, error) { fmt.Println(" [OK]") } - fmt.Print("Waiting for Backend to be ready. This may take a while.") + if !updating { + fmt.Print("Waiting for Backend to be ready. This may take a while.") - if err := services.Backend(); err != nil { - return "", err + if err := services.Backend(); err != nil { + return "", err + } } fmt.Println(" [OK]") diff --git a/installer/updater/service.go b/installer/updater/service.go index 926b04405..0195b2935 100644 --- a/installer/updater/service.go +++ b/installer/updater/service.go @@ -54,7 +54,7 @@ func (p *program) run() { config.Logger().Info("Applying pending update: %s-%s", pendingUpdate.Version, pendingUpdate.Edition) // Apply setup with the pending version - if _, err := setup.Apply(pendingUpdate.Version); err != nil { + if _, err := setup.Apply(pendingUpdate.Version, true); err != nil { config.Logger().ErrorF("error applying setup for version %s: %v", pendingUpdate.Version, err) } else { config.Logger().Info("Successfully applied update %s", pendingUpdate.Version) diff --git a/installer/utils/secret.go b/installer/utils/secret.go index 378c48513..03e24a7d6 100644 --- a/installer/utils/secret.go +++ b/installer/utils/secret.go @@ -1,23 +1,20 @@ package utils import ( - "crypto/rand" - "math/big" + "math/rand" ) func GenerateSecret(size int) string { var characters = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") - if size <= 0 { - return "" - } - result := make([]rune, size) - for i := range result { - num, err := rand.Int(rand.Reader, big.NewInt(int64(len(characters)))) - if err != nil { - panic(err) // Consider returning error if signature allows, but keeping panic for now as panic on CSPRNG fail is reasonable for secret gen + var s string + for { + if len(s) >= size { + break } - result[i] = characters[num.Int64()] + + s += string(characters[rand.Intn(len(characters))]) } - return string(result) + + return s } From aeef223ba104bc758eebabe50b0057a642e8daf7 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Tue, 17 Feb 2026 15:51:00 -0500 Subject: [PATCH 025/115] refactor[agent]: restructure collector, commands, and dependency management --- agent/agent/conn.go | 90 +++ agent/agent/delete.go | 3 +- agent/agent/grpc_errors.go | 78 +++ agent/agent/incident_response.go | 92 +-- .../processor.go => agent/logprocessor.go} | 104 +-- agent/agent/ping_imp.go | 55 +- agent/agent/register.go | 6 +- agent/agent/uninstall.go | 10 +- agent/agent/update.go | 47 +- agent/cmd/change_paths.go | 92 +++ agent/cmd/change_port.go | 40 ++ agent/cmd/change_retention.go | 36 + agent/cmd/clean_logs.go | 41 ++ agent/cmd/disable_integration.go | 73 ++ agent/cmd/enable_integration.go | 125 ++++ agent/cmd/helpers.go | 30 + agent/cmd/install.go | 63 ++ agent/cmd/load_tls_certs.go | 66 ++ agent/cmd/root.go | 48 ++ agent/cmd/run.go | 20 + agent/cmd/uninstall.go | 68 ++ agent/collector/collector.go | 118 ++++ agent/collector/config.go | 381 +++++++++++ agent/collector/file/file.go | 287 ++++++++ agent/collector/netflow/goflow.go | 263 ++++++++ agent/collector/netflow/metrics.go | 43 ++ agent/collector/netflow/netflow.go | 457 +++++++++++++ agent/collector/netflow/parser.go | 87 +++ agent/collector/netflow/proto.go | 158 +++++ agent/collector/netflow/tehmaze.go | 94 +++ agent/collector/platform/darwin.go | 168 +++++ agent/collector/platform/filebeat_amd64.go | 201 ++++++ agent/collector/platform/linux_amd64.go | 162 +++++ agent/collector/platform/platform.go | 20 + agent/collector/platform/windows_amd64.go | 415 ++++++++++++ .../platform}/windows_arm64.go | 81 ++- agent/collector/schema/schema.go | 67 ++ agent/collector/syslog/framing.go | 109 +++ agent/collector/syslog/handler.go | 157 +++++ agent/collector/syslog/listener.go | 301 +++++++++ agent/collector/syslog/syslog.go | 194 ++++++ agent/collectors/collectors.go | 54 -- agent/collectors/filebeat_amd64.go | 152 ----- agent/collectors/linux_amd64.go | 11 - agent/collectors/macos.go | 103 --- agent/collectors/windows_amd64.go | 115 ---- agent/config/config.go | 75 +-- agent/config/const.go | 49 +- agent/database/db.go | 52 +- agent/dependency/dependency.go | 267 ++++++++ agent/dependency/deps_darwin.go | 68 ++ agent/dependency/deps_linux_amd64.go | 98 +++ agent/dependency/deps_linux_arm64.go | 51 ++ agent/dependency/deps_windows_amd64.go | 86 +++ agent/dependency/deps_windows_arm64.go | 46 ++ agent/go.mod | 7 +- agent/go.sum | 10 +- agent/main.go | 298 +------- agent/models/data.go | 6 + agent/modules/configuration.go | 295 -------- agent/modules/modules.go | 153 ----- agent/modules/netflow.go | 352 ---------- agent/modules/syslog.go | 634 ------------------ agent/serv/service.go | 163 +++-- agent/serv/uninstall.go | 3 +- agent/updater/config/config.go | 22 +- agent/updater/config/const.go | 23 +- agent/updater/go.mod | 14 +- agent/updater/go.sum | 31 +- agent/updater/main.go | 10 +- agent/updater/service/install.go | 4 +- agent/updater/service/service.go | 8 +- agent/updater/updates/update.go | 135 ++-- agent/updates/dependencies.go | 63 -- agent/updates/update.go | 154 ----- agent/utils/cmd.go | 7 - agent/utils/crypt.go | 40 +- agent/utils/files.go | 98 --- agent/utils/int_tls.go | 14 +- agent/utils/logger.go | 5 +- agent/utils/req.go | 5 +- agent/utils/services.go | 45 +- agent/utils/watcher.go | 93 ++- 83 files changed, 5782 insertions(+), 3087 deletions(-) create mode 100644 agent/agent/conn.go create mode 100644 agent/agent/grpc_errors.go rename agent/{logservice/processor.go => agent/logprocessor.go} (66%) create mode 100644 agent/cmd/change_paths.go create mode 100644 agent/cmd/change_port.go create mode 100644 agent/cmd/change_retention.go create mode 100644 agent/cmd/clean_logs.go create mode 100644 agent/cmd/disable_integration.go create mode 100644 agent/cmd/enable_integration.go create mode 100644 agent/cmd/helpers.go create mode 100644 agent/cmd/install.go create mode 100644 agent/cmd/load_tls_certs.go create mode 100644 agent/cmd/root.go create mode 100644 agent/cmd/run.go create mode 100644 agent/cmd/uninstall.go create mode 100644 agent/collector/collector.go create mode 100644 agent/collector/config.go create mode 100644 agent/collector/file/file.go create mode 100644 agent/collector/netflow/goflow.go create mode 100644 agent/collector/netflow/metrics.go create mode 100644 agent/collector/netflow/netflow.go create mode 100644 agent/collector/netflow/parser.go create mode 100644 agent/collector/netflow/proto.go create mode 100644 agent/collector/netflow/tehmaze.go create mode 100644 agent/collector/platform/darwin.go create mode 100644 agent/collector/platform/filebeat_amd64.go create mode 100644 agent/collector/platform/linux_amd64.go create mode 100644 agent/collector/platform/platform.go create mode 100644 agent/collector/platform/windows_amd64.go rename agent/{collectors => collector/platform}/windows_arm64.go (86%) create mode 100644 agent/collector/schema/schema.go create mode 100644 agent/collector/syslog/framing.go create mode 100644 agent/collector/syslog/handler.go create mode 100644 agent/collector/syslog/listener.go create mode 100644 agent/collector/syslog/syslog.go delete mode 100644 agent/collectors/collectors.go delete mode 100644 agent/collectors/filebeat_amd64.go delete mode 100644 agent/collectors/linux_amd64.go delete mode 100644 agent/collectors/macos.go delete mode 100644 agent/collectors/windows_amd64.go create mode 100644 agent/dependency/dependency.go create mode 100644 agent/dependency/deps_darwin.go create mode 100644 agent/dependency/deps_linux_amd64.go create mode 100644 agent/dependency/deps_linux_arm64.go create mode 100644 agent/dependency/deps_windows_amd64.go create mode 100644 agent/dependency/deps_windows_arm64.go delete mode 100644 agent/modules/configuration.go delete mode 100644 agent/modules/modules.go delete mode 100644 agent/modules/netflow.go delete mode 100644 agent/modules/syslog.go delete mode 100644 agent/updates/dependencies.go delete mode 100644 agent/updates/update.go diff --git a/agent/agent/conn.go b/agent/agent/conn.go new file mode 100644 index 000000000..ded43381c --- /dev/null +++ b/agent/agent/conn.go @@ -0,0 +1,90 @@ +package agent + +import ( + "crypto/tls" + "fmt" + "sync" + "time" + + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials" +) + +const ( + maxMessageSize = 1024 * 1024 * 1024 + maxConnectionAttempts = 3 + initialReconnectDelay = 10 * time.Second + maxReconnectDelay = 60 * time.Second +) + +type connEntry struct { + mu sync.Mutex + conn *grpc.ClientConn +} + +func (e *connEntry) getOrCreate(cnf *config.Config, port string, name string) (*grpc.ClientConn, error) { + e.mu.Lock() + defer e.mu.Unlock() + + if e.conn != nil { + state := e.conn.GetState() + if state != connectivity.Shutdown && state != connectivity.TransientFailure { + return e.conn, nil + } + e.conn.Close() + } + + conn, err := connectToServer(cnf.Server, port, cnf.SkipCertValidation) + if err != nil { + return nil, fmt.Errorf("error connecting to %s: %v", name, err) + } + e.conn = conn + return e.conn, nil +} + +var ( + agentManagerEntry connEntry + correlationEntry connEntry +) + +func GetAgentManagerConnection(cnf *config.Config) (*grpc.ClientConn, error) { + return agentManagerEntry.getOrCreate(cnf, config.AgentManagerPort, "Agent Manager") +} + +func GetCorrelationConnection(cnf *config.Config) (*grpc.ClientConn, error) { + return correlationEntry.getOrCreate(cnf, config.LogAuthProxyPort, "Correlation") +} + +func connectToServer(addrs, port string, skip bool) (*grpc.ClientConn, error) { + connectionAttemps := 0 + reconnectDelay := initialReconnectDelay + + serverAddress := addrs + ":" + port + var conn *grpc.ClientConn + var err error + + for { + if connectionAttemps >= maxConnectionAttempts { + return nil, fmt.Errorf("failed to connect to Server: %v", err) + } + + conn, err = grpc.NewClient( + serverAddress, + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMessageSize)), + grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: skip}))) + if err != nil { + connectionAttemps++ + utils.Logger.ErrorF("error connecting to Server, trying again in %.0f seconds", reconnectDelay.Seconds()) + time.Sleep(reconnectDelay) + reconnectDelay = utils.IncrementReconnectDelay(reconnectDelay, maxReconnectDelay) + continue + } + + break + } + + return conn, nil +} diff --git a/agent/agent/delete.go b/agent/agent/delete.go index d700b30c0..c1c359aca 100644 --- a/agent/agent/delete.go +++ b/agent/agent/delete.go @@ -7,13 +7,12 @@ import ( "strconv" "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/conn" "github.com/utmstack/UTMStack/agent/utils" "google.golang.org/grpc/metadata" ) func DeleteAgent(cnf *config.Config) error { - connection, err := conn.GetAgentManagerConnection(cnf) + connection, err := GetAgentManagerConnection(cnf) if err != nil { return fmt.Errorf("error connecting to Agent Manager: %v", err) } diff --git a/agent/agent/grpc_errors.go b/agent/agent/grpc_errors.go new file mode 100644 index 000000000..5a71dbf03 --- /dev/null +++ b/agent/agent/grpc_errors.go @@ -0,0 +1,78 @@ +package agent + +import ( + "strings" + "time" + + "github.com/utmstack/UTMStack/agent/utils" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// StreamAction indicates what the caller should do after handling a gRPC error. +type StreamAction int + +const ( + // ActionContinue means retry the operation (non-fatal error). + ActionContinue StreamAction = iota + // ActionReconnect means break the inner loop and reconnect the stream. + ActionReconnect +) + +// HandleGRPCStreamError processes a gRPC stream error and returns the appropriate action. +// It handles EOF, Unavailable, and Canceled errors with deduplication of log messages. +// The errorLogged pointer tracks whether an error has already been logged to avoid spam. +func HandleGRPCStreamError(err error, msg string, errorLogged *bool) StreamAction { + // EOF means the stream was closed by the server - reconnect + if strings.Contains(err.Error(), "EOF") { + utils.Logger.LogF(100, "%s: %v", msg, err) + time.Sleep(timeToSleep) + return ActionReconnect + } + + st, ok := status.FromError(err) + isTransient := ok && (st.Code() == codes.Unavailable || st.Code() == codes.Canceled) + + // Log error only once to avoid spam + logError(err, msg, errorLogged) + time.Sleep(timeToSleep) + + if isTransient { + // Transient errors (Unavailable, Canceled) require reconnection + return ActionReconnect + } + + // Other errors - retry the operation + return ActionContinue +} + +// logError logs an error message with deduplication. +// After the first error, subsequent errors are logged at debug level. +func logError(err error, msg string, errorLogged *bool) { + if !*errorLogged { + utils.Logger.ErrorF("%s: %v", msg, err) + *errorLogged = true + } else { + utils.Logger.LogF(100, "%s: %v", msg, err) + } +} + +// LogConnectionError logs a connection error with deduplication. +func LogConnectionError(err error, target string, errorLogged *bool) { + if !*errorLogged { + utils.Logger.ErrorF("error connecting to %s: %v", target, err) + *errorLogged = true + } else { + utils.Logger.LogF(100, "error connecting to %s: %v", target, err) + } +} + +// LogStreamError logs a stream creation error with deduplication. +func LogStreamError(err error, streamName string, errorLogged *bool) { + if !*errorLogged { + utils.Logger.ErrorF("failed to start %s: %v", streamName, err) + *errorLogged = true + } else { + utils.Logger.LogF(100, "failed to start %s: %v", streamName, err) + } +} diff --git a/agent/agent/incident_response.go b/agent/agent/incident_response.go index 7a5d9e8f3..3c9927d45 100644 --- a/agent/agent/incident_response.go +++ b/agent/agent/incident_response.go @@ -2,30 +2,25 @@ package agent import ( "context" + "fmt" "runtime" "strconv" - "strings" "time" "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/conn" "github.com/utmstack/UTMStack/agent/utils" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" + "github.com/utmstack/UTMStack/shared/fs" "google.golang.org/protobuf/types/known/timestamppb" ) func IncidentResponseStream(cnf *config.Config, ctx context.Context) { - path := utils.GetMyPath() - var connErrMsgWritten, errorLogged bool + path := fs.GetExecutablePath() + var connErrLogged, streamErrLogged bool for { - connection, err := conn.GetAgentManagerConnection(cnf) + connection, err := GetAgentManagerConnection(cnf) if err != nil { - if !connErrMsgWritten { - utils.Logger.ErrorF("error connecting to Agent Manager: %v", err) - connErrMsgWritten = true - } + LogConnectionError(err, "Agent Manager", &connErrLogged) time.Sleep(timeToSleep) continue } @@ -33,80 +28,36 @@ func IncidentResponseStream(cnf *config.Config, ctx context.Context) { client := NewAgentServiceClient(connection) stream, err := client.AgentStream(ctx) if err != nil { - if !connErrMsgWritten { - utils.Logger.ErrorF("failed to start AgentStream: %v", err) - connErrMsgWritten = true - } else { - utils.Logger.LogF(100, "failed to start AgentStream: %v", err) - } + LogStreamError(err, "AgentStream", &connErrLogged) time.Sleep(timeToSleep) continue } - connErrMsgWritten = false + connErrLogged = false + recvLoop: for { in, err := stream.Recv() if err != nil { - if strings.Contains(err.Error(), "EOF") { - utils.Logger.LogF(100, "error receiving command from server: %v", err) - time.Sleep(timeToSleep) - break - } - st, ok := status.FromError(err) - if ok && (st.Code() == codes.Unavailable || st.Code() == codes.Canceled) { - if !errorLogged { - utils.Logger.ErrorF("error receiving command from server: %v", err) - errorLogged = true - } else { - utils.Logger.LogF(100, "error receiving command from server: %v", err) - } - time.Sleep(timeToSleep) - break - } else { - if !errorLogged { - utils.Logger.ErrorF("error receiving command from server: %v", err) - errorLogged = true - } else { - utils.Logger.LogF(100, "error receiving command from server: %v", err) - } - time.Sleep(timeToSleep) - continue + action := HandleGRPCStreamError(err, "error receiving command from server", &streamErrLogged) + if action == ActionReconnect { + break recvLoop } + continue } switch msg := in.StreamMessage.(type) { case *BidirectionalStream_Command: err = commandProcessor(path, stream, cnf, []string{msg.Command.Command, in.GetCommand().CmdId}) if err != nil { - if strings.Contains(err.Error(), "EOF") { - utils.Logger.LogF(100, "error sending result to server: %v", err) - time.Sleep(timeToSleep) - break - } - st, ok := status.FromError(err) - if ok && (st.Code() == codes.Unavailable || st.Code() == codes.Canceled) { - if !errorLogged { - utils.Logger.ErrorF("error sending result to server: %v", err) - errorLogged = true - } else { - utils.Logger.LogF(100, "error sending result to server: %v", err) - } - time.Sleep(timeToSleep) - break - } else { - if !errorLogged { - utils.Logger.ErrorF("error sending result to server: %v", err) - errorLogged = true - } else { - utils.Logger.LogF(100, "error sending result to server: %v", err) - } - time.Sleep(timeToSleep) - continue + action := HandleGRPCStreamError(err, "error sending result to server", &streamErrLogged) + if action == ActionReconnect { + break recvLoop } + continue } } - errorLogged = false + streamErrLogged = false } } } @@ -123,7 +74,8 @@ func commandProcessor(path string, stream AgentService_AgentStreamClient, cnf *c case "linux", "darwin": result, errB = utils.ExecuteWithResult("sh", path, "-c", commandPair[0]) default: - utils.Logger.Fatal("unsupported operating system: %s", runtime.GOOS) + utils.Logger.ErrorF("unsupported operating system: %s", runtime.GOOS) + return fmt.Errorf("unsupported operating system: %s", runtime.GOOS) } if errB { @@ -138,8 +90,8 @@ func commandProcessor(path string, stream AgentService_AgentStreamClient, cnf *c }, }); err != nil { return err - } else { - utils.Logger.LogF(100, "Result sent to server successfully!!!") } + + utils.Logger.LogF(100, "Result sent to server successfully") return nil } diff --git a/agent/logservice/processor.go b/agent/agent/logprocessor.go similarity index 66% rename from agent/logservice/processor.go rename to agent/agent/logprocessor.go index 537e8f922..fe59bf1aa 100644 --- a/agent/logservice/processor.go +++ b/agent/agent/logprocessor.go @@ -1,4 +1,4 @@ -package logservice +package agent import ( "context" @@ -12,14 +12,11 @@ import ( "github.com/google/uuid" "github.com/threatwinds/go-sdk/plugins" - "github.com/utmstack/UTMStack/agent/agent" "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/conn" "github.com/utmstack/UTMStack/agent/database" "github.com/utmstack/UTMStack/agent/models" "github.com/utmstack/UTMStack/agent/utils" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" + "github.com/utmstack/UTMStack/shared/fs" ) type LogProcessor struct { @@ -32,8 +29,7 @@ type LogProcessor struct { var ( processor LogProcessor processorOnce sync.Once - LogQueue = make(chan *plugins.Log) - timeToSleep = 10 * time.Second + LogQueue = make(chan *plugins.Log, 10000) timeCLeanLogs = 10 * time.Minute ) @@ -54,7 +50,7 @@ func (l *LogProcessor) ProcessLogs(cnf *config.Config, ctx context.Context) { for { ctxEof, cancelEof := context.WithCancel(context.Background()) - connection, err := conn.GetCorrelationConnection(cnf) + connection, err := GetCorrelationConnection(cnf) if err != nil { if !l.connErrWritten { utils.Logger.ErrorF("error connecting to Correlation: %v", err) @@ -81,38 +77,20 @@ func (l *LogProcessor) handleAcknowledgements(plClient plugins.Integration_Proce default: ack, err := plClient.Recv() if err != nil { - if strings.Contains(err.Error(), "EOF") { - time.Sleep(timeToSleep) + action := HandleGRPCStreamError(err, "failed to receive ack", &l.ackErrWritten) + if action == ActionReconnect { cancel() return } - st, ok := status.FromError(err) - if ok && (st.Code() == codes.Unavailable || st.Code() == codes.Canceled) { - if !l.ackErrWritten { - utils.Logger.ErrorF("failed to receive ack: %v", err) - l.ackErrWritten = true - } - time.Sleep(timeToSleep) - cancel() - return - } else { - if !l.ackErrWritten { - utils.Logger.ErrorF("failed to receive ack: %v", err) - l.ackErrWritten = true - } - time.Sleep(timeToSleep) - continue - } + continue } l.ackErrWritten = false - l.db.Lock() err = l.db.Update(&models.Log{}, "id", ack.LastId, "processed", true) if err != nil { utils.Logger.ErrorF("failed to update log: %v", err) } - l.db.Unlock() } } } @@ -124,44 +102,28 @@ func (l *LogProcessor) processLogs(plClient plugins.Integration_ProcessLogClient utils.Logger.Info("context done, exiting processLogs") return case newLog := <-LogQueue: - id, err := uuid.NewRandom() - if err != nil { - utils.Logger.ErrorF("failed to generate uuid: %v", err) - continue - } + if newLog.Id == "" { + id, err := uuid.NewRandom() + if err != nil { + utils.Logger.ErrorF("failed to generate uuid: %v", err) + continue + } - newLog.Id = id.String() - l.db.Lock() - err = l.db.Create(&models.Log{ID: newLog.Id, Log: newLog.Raw, Type: newLog.DataType, CreatedAt: time.Now(), DataSource: newLog.DataSource, Processed: false}) - if err != nil { - utils.Logger.ErrorF("failed to save log: %v :log: %s", err, newLog.Raw) + newLog.Id = id.String() + err = l.db.Create(&models.Log{ID: newLog.Id, Log: newLog.Raw, Type: newLog.DataType, CreatedAt: time.Now(), DataSource: newLog.DataSource, Processed: false}) + if err != nil { + utils.Logger.ErrorF("failed to save log: %v :log: %s", err, newLog.Raw) + } } - l.db.Unlock() - err = plClient.Send(newLog) + err := plClient.Send(newLog) if err != nil { - if strings.Contains(err.Error(), "EOF") { - time.Sleep(timeToSleep) + action := HandleGRPCStreamError(err, "failed to send log", &l.sendErrWritten) + if action == ActionReconnect { cancel() return } - st, ok := status.FromError(err) - if ok && (st.Code() == codes.Unavailable || st.Code() == codes.Canceled) { - if !l.sendErrWritten { - utils.Logger.ErrorF("failed to send log: %v :log: %s", err, newLog.Raw) - l.sendErrWritten = true - } - time.Sleep(timeToSleep) - cancel() - return - } else { - if !l.sendErrWritten { - utils.Logger.ErrorF("failed to send log: %v :log: %s", err, newLog.Raw) - l.sendErrWritten = true - } - time.Sleep(timeToSleep) - continue - } + continue } l.sendErrWritten = false } @@ -185,17 +147,13 @@ func (l *LogProcessor) CleanCountedLogs() { continue } } - l.db.Lock() _, err = l.db.DeleteOld(&models.Log{}, dataRetention) if err != nil { utils.Logger.ErrorF("error deleting old logs: %s", err) } - l.db.Unlock() unprocessed := make([]models.Log, 0, 10) - l.db.Lock() found, err := l.db.Find(&unprocessed, "processed", false) - l.db.Unlock() if err != nil { utils.Logger.ErrorF("error finding unprocessed logs: %s", err) continue @@ -218,18 +176,26 @@ func (l *LogProcessor) CleanCountedLogs() { func createClient(client plugins.IntegrationClient, ctx context.Context) plugins.Integration_ProcessLogClient { var connErrMsgWritten bool invalidKeyCounter := 0 + invalidKeyDelay := timeToSleep + maxInvalidKeyDelay := 5 * time.Minute + maxInvalidKeyAttempts := 100 // ~8+ hours with backoff before uninstall for { plClient, err := client.ProcessLog(ctx) if err != nil { if strings.Contains(err.Error(), "invalid agent key") { invalidKeyCounter++ - if invalidKeyCounter >= 20 { - utils.Logger.Info("Uninstalling agent: reason: agent has been removed from the panel...") - _ = agent.UninstallAll() + utils.Logger.ErrorF("invalid agent key (attempt %d/%d), retrying in %v", invalidKeyCounter, maxInvalidKeyAttempts, invalidKeyDelay) + if invalidKeyCounter >= maxInvalidKeyAttempts { + utils.Logger.ErrorF("uninstalling agent after %d consecutive invalid key errors", maxInvalidKeyAttempts) + _ = UninstallAll() os.Exit(1) } + time.Sleep(invalidKeyDelay) + invalidKeyDelay = utils.IncrementReconnectDelay(invalidKeyDelay, maxInvalidKeyDelay) + continue } else { invalidKeyCounter = 0 + invalidKeyDelay = timeToSleep } if !connErrMsgWritten { utils.Logger.ErrorF("failed to create input client: %v", err) @@ -256,12 +222,12 @@ func SetDataRetention(retention string) error { return errors.New("retention must be greater than 0") } - return utils.WriteJSON(config.RetentionConfigFile, models.DataRetention{Retention: retentionInt}) + return fs.WriteJSON(config.RetentionConfigFile, models.DataRetention{Retention: retentionInt}) } func GetDataRetention() (int, error) { retention := models.DataRetention{} - err := utils.ReadJson(config.RetentionConfigFile, &retention) + err := fs.ReadJSON(config.RetentionConfigFile, &retention) if err != nil { return 0, err } diff --git a/agent/agent/ping_imp.go b/agent/agent/ping_imp.go index c8660740a..c4cc93f6e 100644 --- a/agent/agent/ping_imp.go +++ b/agent/agent/ping_imp.go @@ -2,14 +2,10 @@ package agent import ( "context" - "strings" "time" "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/conn" "github.com/utmstack/UTMStack/agent/utils" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) var ( @@ -18,17 +14,12 @@ var ( ) func StartPing(cnf *config.Config, ctx context.Context) { - var connErrMsgWritten, errorLogged bool + var connErrLogged, streamErrLogged bool for { - connection, err := conn.GetAgentManagerConnection(cnf) + connection, err := GetAgentManagerConnection(cnf) if err != nil { - if !connErrMsgWritten { - utils.Logger.ErrorF("error connecting to Agent Manager: %v", err) - connErrMsgWritten = true - } else { - utils.Logger.LogF(100, "error connecting to Agent Manager: %v", err) - } + LogConnectionError(err, "Agent Manager", &connErrLogged) time.Sleep(timeToSleep) continue } @@ -36,52 +27,28 @@ func StartPing(cnf *config.Config, ctx context.Context) { client := NewPingServiceClient(connection) stream, err := client.Ping(ctx) if err != nil { - if !connErrMsgWritten { - utils.Logger.ErrorF("failed to start Ping Stream: %v", err) - connErrMsgWritten = true - } else { - utils.Logger.LogF(100, "failed to start Ping Stream: %v", err) - } + LogStreamError(err, "Ping Stream", &connErrLogged) time.Sleep(timeToSleep) continue } utils.Logger.LogF(100, "Ping Stream started") - connErrMsgWritten = false + connErrLogged = false ticker := time.NewTicker(pingInterval) + pingLoop: for range ticker.C { err := stream.Send(&PingRequest{Type: ConnectorType_AGENT}) if err != nil { - if strings.Contains(err.Error(), "EOF") { - utils.Logger.LogF(100, "error sending Ping request: %v", err) - time.Sleep(timeToSleep) - break - } - st, ok := status.FromError(err) - if ok && (st.Code() == codes.Unavailable || st.Code() == codes.Canceled) { - if !errorLogged { - utils.Logger.ErrorF("error sending Ping request: %v", err) - errorLogged = true - } else { - utils.Logger.LogF(100, "error sending Ping request: %v", err) - } - time.Sleep(timeToSleep) - break - } else { - if !errorLogged { - utils.Logger.ErrorF("error sending Ping request: %v", err) - errorLogged = true - } else { - utils.Logger.LogF(100, "error sending Ping request: %v", err) - } - time.Sleep(timeToSleep) - continue + action := HandleGRPCStreamError(err, "error sending Ping request", &streamErrLogged) + if action == ActionReconnect { + break pingLoop } + continue } - errorLogged = false + streamErrLogged = false utils.Logger.LogF(100, "Ping request sent") } diff --git a/agent/agent/register.go b/agent/agent/register.go index b90dd0489..59615ab98 100644 --- a/agent/agent/register.go +++ b/agent/agent/register.go @@ -5,14 +5,14 @@ import ( "fmt" "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/conn" "github.com/utmstack/UTMStack/agent/models" "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/fs" "google.golang.org/grpc/metadata" ) func RegisterAgent(cnf *config.Config, UTMKey string) error { - connection, err := conn.GetAgentManagerConnection(cnf) + connection, err := GetAgentManagerConnection(cnf) if err != nil { return fmt.Errorf("error connecting to Agent Manager: %v", err) } @@ -33,7 +33,7 @@ func RegisterAgent(cnf *config.Config, UTMKey string) error { } version := models.Version{} - err = utils.ReadJson(config.VersionPath, &version) + err = fs.ReadJSON(config.VersionPath, &version) if err != nil { return fmt.Errorf("error reading version file: %v", err) } diff --git a/agent/agent/uninstall.go b/agent/agent/uninstall.go index 3d0bbf291..fe931fccc 100644 --- a/agent/agent/uninstall.go +++ b/agent/agent/uninstall.go @@ -2,15 +2,15 @@ package agent import ( "fmt" - "path/filepath" + "os" - "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/exec" + "github.com/utmstack/UTMStack/shared/fs" ) func UninstallAll() error { - err := utils.Execute(filepath.Join(utils.GetMyPath(), fmt.Sprintf(config.ServiceFile, "")), utils.GetMyPath(), "uninstall") - if err != nil { + // Use the current executable path - the agent uninstalls itself + if err := exec.Run(os.Args[0], fs.GetExecutablePath(), "uninstall"); err != nil { return fmt.Errorf("%v", err) } return nil diff --git a/agent/agent/update.go b/agent/agent/update.go index 7fca7450e..a9616454f 100644 --- a/agent/agent/update.go +++ b/agent/agent/update.go @@ -2,31 +2,54 @@ package agent import ( context "context" - "fmt" + "time" "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/conn" "github.com/utmstack/UTMStack/agent/models" "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/fs" ) -func UpdateAgent(cnf *config.Config, ctx context.Context) error { - connection, err := conn.GetAgentManagerConnection(cnf) +const updateInterval = 5 * time.Minute + +func UpdateAgent(cnf *config.Config, ctx context.Context) { + var errLogged bool + + for { + err := updateAgentOnce(cnf, ctx) + if err != nil { + if !errLogged { + utils.Logger.ErrorF("error updating agent: %v", err) + errLogged = true + } + } else { + errLogged = false + } + + select { + case <-ctx.Done(): + return + case <-time.After(updateInterval): + } + } +} + +func updateAgentOnce(cnf *config.Config, ctx context.Context) error { + connection, err := GetAgentManagerConnection(cnf) if err != nil { - return fmt.Errorf("error connecting to Agent Manager: %v", err) + return err } client := NewAgentServiceClient(connection) osInfo, err := utils.GetOsInfo() if err != nil { - return fmt.Errorf("error getting os info: %v", err) + return err } version := models.Version{} - err = utils.ReadJson(config.VersionPath, &version) - if err != nil { - utils.Logger.Fatal("error reading version file: %v", err) + if err = fs.ReadJSON(config.VersionPath, &version); err != nil { + return err } request := &AgentRequest{ @@ -40,9 +63,5 @@ func UpdateAgent(cnf *config.Config, ctx context.Context) error { } _, err = client.UpdateAgent(ctx, request) - if err != nil { - return fmt.Errorf("error updating agent: %v", err) - } - - return nil + return err } diff --git a/agent/cmd/change_paths.go b/agent/cmd/change_paths.go new file mode 100644 index 000000000..639baa6e5 --- /dev/null +++ b/agent/cmd/change_paths.go @@ -0,0 +1,92 @@ +package cmd + +import ( + "fmt" + "os" + "strings" + "time" + + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/collector" + "github.com/utmstack/UTMStack/agent/config" +) + +var changePathsCmd = &cobra.Command{ + Use: "change-paths [path2] [path3] ...", + Short: "Change the file paths for a file-based integration", + Long: `Change the file paths for a file-based integration. + +Glob patterns are supported (e.g., /var/log/nginx/*.log). + +Examples: + utmstack_agent change-paths nginx /var/log/nginx/access.log /var/log/nginx/error.log + utmstack_agent change-paths postgresql "/var/log/postgresql/*.log"`, + Args: cobra.MinimumNArgs(2), + PreRunE: requireInstalled, + RunE: func(cmd *cobra.Command, args []string) error { + integration := args[0] + paths := args[1:] + + // Validate this is a file-type integration + if config.ValidateModuleType(integration) != "file" { + fmt.Printf("Error: %s is not a file-based integration\n", integration) + fmt.Println("Valid file integrations:", strings.Join(getFileIntegrations(), ", ")) + os.Exit(1) + } + + fmt.Printf("Changing paths for %s...\n", integration) + + result, err := collector.ChangeFilePaths(integration, paths) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + fmt.Println("Old paths:") + for _, p := range result.OldPaths { + fmt.Printf(" - %s\n", p) + } + fmt.Println("New paths:") + for _, p := range paths { + fmt.Printf(" - %s\n", p) + } + time.Sleep(5 * time.Second) + + return nil + }, +} + +var showPathsCmd = &cobra.Command{ + Use: "show-paths ", + Short: "Show the configured paths for a file-based integration", + Args: cobra.ExactArgs(1), + PreRunE: requireInstalled, + RunE: func(cmd *cobra.Command, args []string) error { + integration := args[0] + + // Validate this is a file-type integration + if config.ValidateModuleType(integration) != "file" { + fmt.Printf("Error: %s is not a file-based integration\n", integration) + fmt.Println("Valid file integrations:", strings.Join(getFileIntegrations(), ", ")) + os.Exit(1) + } + + paths, err := collector.GetFileIntegrationPaths(integration) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + fmt.Printf("Configured paths for %s:\n", integration) + for _, p := range paths { + fmt.Printf(" - %s\n", p) + } + + return nil + }, +} + +func init() { + rootCmd.AddCommand(changePathsCmd) + rootCmd.AddCommand(showPathsCmd) +} diff --git a/agent/cmd/change_port.go b/agent/cmd/change_port.go new file mode 100644 index 000000000..d50be7b20 --- /dev/null +++ b/agent/cmd/change_port.go @@ -0,0 +1,40 @@ +package cmd + +import ( + "fmt" + "os" + "time" + + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/collector" +) + +var changePortCmd = &cobra.Command{ + Use: "change-port ", + Short: "Change the port for a specific integration and protocol", + Args: cobra.ExactArgs(3), + PreRunE: requireInstalled, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Changing integration port ...") + integration := args[0] + proto := args[1] + port := args[2] + + result, err := collector.ChangePort(integration, proto, port) + if err != nil { + fmt.Println("Error trying to change integration port: ", err) + os.Exit(1) + } + fmt.Printf("Port changed correctly from %s to %s\n", result.OldPort, port) + if result.Warning != "" { + fmt.Printf("Warning: %s\n", result.Warning) + } + time.Sleep(5 * time.Second) + + return nil + }, +} + +func init() { + rootCmd.AddCommand(changePortCmd) +} diff --git a/agent/cmd/change_retention.go b/agent/cmd/change_retention.go new file mode 100644 index 000000000..55def4a4d --- /dev/null +++ b/agent/cmd/change_retention.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "fmt" + "os" + "time" + + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/agent" +) + +var changeRetentionCmd = &cobra.Command{ + Use: "change-retention ", + Short: "Change the log retention", + Long: "Change the log retention to . Retention must be a number of megabytes. Example: 20", + Args: cobra.ExactArgs(1), + PreRunE: requireInstalled, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Changing log retention ...") + retention := args[0] + + if err := agent.SetDataRetention(retention); err != nil { + fmt.Println("Error trying to change retention: ", err) + os.Exit(1) + } + + fmt.Printf("Retention changed correctly to %s\n", retention) + time.Sleep(5 * time.Second) + + return nil + }, +} + +func init() { + rootCmd.AddCommand(changeRetentionCmd) +} diff --git a/agent/cmd/clean_logs.go b/agent/cmd/clean_logs.go new file mode 100644 index 000000000..fcd422c19 --- /dev/null +++ b/agent/cmd/clean_logs.go @@ -0,0 +1,41 @@ +package cmd + +import ( + "fmt" + "os" + "time" + + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/agent" + "github.com/utmstack/UTMStack/agent/database" + "github.com/utmstack/UTMStack/agent/models" +) + +var cleanLogsCmd = &cobra.Command{ + Use: "clean-logs", + Short: "Clean old logs from the database", + Args: cobra.NoArgs, + PreRunE: requireInstalled, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Cleaning old logs ...") + db := database.GetDB() + datR, err := agent.GetDataRetention() + if err != nil { + fmt.Println("Error getting retention: ", err) + os.Exit(1) + } + _, err = db.DeleteOld(models.Log{}, datR) + if err != nil { + fmt.Println("Error cleaning logs: ", err) + os.Exit(1) + } + fmt.Println("Logs cleaned correctly") + time.Sleep(5 * time.Second) + + return nil + }, +} + +func init() { + rootCmd.AddCommand(cleanLogsCmd) +} diff --git a/agent/cmd/disable_integration.go b/agent/cmd/disable_integration.go new file mode 100644 index 000000000..f2bc3b63f --- /dev/null +++ b/agent/cmd/disable_integration.go @@ -0,0 +1,73 @@ +package cmd + +import ( + "fmt" + "os" + "time" + + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/collector" + "github.com/utmstack/UTMStack/agent/config" +) + +var disableIntegrationCmd = &cobra.Command{ + Use: "disable-integration [protocol]", + Short: "Disable integration for a specific integration and protocol", + Long: `Disable integration for a specific integration and protocol. + +For syslog integrations, protocol (tcp/udp) is required. +For file integrations (nginx, postgresql), protocol is not needed. + +Examples: + utmstack_agent disable-integration syslog tcp # Disable syslog TCP + utmstack_agent disable-integration nginx # Disable file-based nginx collector`, + Args: cobra.RangeArgs(1, 2), + PreRunE: requireInstalled, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Changing integration status ...") + integration := args[0] + + // Check integration type + intType := config.ValidateModuleType(integration) + + switch intType { + case "file": + // File-based integration - no protocol needed + _, err := collector.ChangeFileIntegrationStatus(integration, false) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + fmt.Printf("Integration %s disabled\n", integration) + time.Sleep(5 * time.Second) + return nil + + case "syslog", "netflow": + // Syslog/netflow integration - protocol required + if len(args) < 2 { + fmt.Println("Error: protocol (tcp/udp) is required for this integration type") + os.Exit(1) + } + proto := args[1] + + port, err := collector.ChangeIntegrationStatus(integration, proto, false) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + fmt.Printf("Integration %s %s disabled (port %s freed)\n", integration, proto, port) + time.Sleep(5 * time.Second) + return nil + + default: + fmt.Printf("Error: invalid integration: %s\n", integration) + os.Exit(1) + return nil + } + }, +} + +func init() { + rootCmd.AddCommand(disableIntegrationCmd) +} diff --git a/agent/cmd/enable_integration.go b/agent/cmd/enable_integration.go new file mode 100644 index 000000000..1e93f4d6c --- /dev/null +++ b/agent/cmd/enable_integration.go @@ -0,0 +1,125 @@ +package cmd + +import ( + "fmt" + "os" + "strings" + "time" + + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/collector" + "github.com/utmstack/UTMStack/agent/config" +) + +var enableTLS bool + +var enableIntegrationCmd = &cobra.Command{ + Use: "enable-integration [protocol]", + Short: "Enable integration for a specific integration and protocol", + Long: `Enable integration for a specific integration and protocol. + +For syslog integrations, protocol (tcp/udp) is required. +For file integrations (nginx, postgresql), protocol is not needed. + +Available flag: --tls (enable TLS for TCP only) + +Examples: + utmstack_agent enable-integration syslog tcp --tls # Enable syslog with TLS + utmstack_agent enable-integration syslog tcp # Enable syslog without TLS + utmstack_agent enable-integration nginx # Enable file-based nginx collector`, + Args: cobra.RangeArgs(1, 2), + PreRunE: requireInstalled, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Changing integration status ...") + integration := args[0] + + // Check integration type + intType := config.ValidateModuleType(integration) + + switch intType { + case "file": + // File-based integration - no protocol needed + paths, err := collector.ChangeFileIntegrationStatus(integration, true) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + fmt.Printf("Integration %s enabled. Watching paths:\n", integration) + for _, p := range paths { + fmt.Printf(" - %s\n", p) + } + time.Sleep(5 * time.Second) + return nil + + case "syslog": + // Syslog integration - protocol required + if len(args) < 2 { + fmt.Println("Error: protocol (tcp/udp) is required for syslog integrations") + os.Exit(1) + } + proto := args[1] + + var port string + var err error + + if enableTLS { + port, err = collector.ChangeIntegrationStatus(integration, proto, true, true) + } else { + port, err = collector.ChangeIntegrationStatus(integration, proto, true, false) + } + + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + if enableTLS { + fmt.Printf("Integration %s %s enabled with TLS on port %s\n", integration, proto, port) + } else { + fmt.Printf("Integration %s %s enabled on port %s\n", integration, proto, port) + } + time.Sleep(5 * time.Second) + return nil + + case "netflow": + // Netflow integration + if len(args) < 2 { + fmt.Println("Error: protocol (udp) is required for netflow integration") + os.Exit(1) + } + proto := args[1] + port, err := collector.ChangeIntegrationStatus(integration, proto, true) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + fmt.Printf("Integration %s %s enabled on port %s\n", integration, proto, port) + time.Sleep(5 * time.Second) + return nil + + default: + fmt.Printf("Error: invalid integration: %s\n", integration) + fmt.Println("Valid syslog integrations:", strings.Join(getSyslogIntegrations(), ", ")) + fmt.Println("Valid file integrations:", strings.Join(getFileIntegrations(), ", ")) + os.Exit(1) + return nil + } + }, +} + +func getSyslogIntegrations() []string { + return []string{"syslog", "vmware-esxi", "antivirus-esmc-eset", "antivirus-kaspersky", + "firewall-cisco-asa", "firewall-cisco-firepower", "cisco-switch", "firewall-meraki", + "firewall-fortigate-traffic", "firewall-paloalto", "firewall-mikrotik", "firewall-sophos-xg", + "firewall-sonicwall", "deceptive-bytes", "antivirus-sentinel-one", "ibm-aix", + "firewall-pfsense", "firewall-fortiweb", "suricata"} +} + +func getFileIntegrations() []string { + return []string{"nginx", "postgresql"} +} + +func init() { + enableIntegrationCmd.Flags().BoolVar(&enableTLS, "tls", false, "Enable TLS for TCP") + rootCmd.AddCommand(enableIntegrationCmd) +} diff --git a/agent/cmd/helpers.go b/agent/cmd/helpers.go new file mode 100644 index 000000000..6b1ccf9a3 --- /dev/null +++ b/agent/cmd/helpers.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/utils" +) + +func requireInstalled(cmd *cobra.Command, args []string) error { + isInstalled, err := utils.CheckIfServiceIsInstalled("UTMStackAgent") + if err != nil { + return fmt.Errorf("error checking if service is installed: %v", err) + } + if !isInstalled { + return fmt.Errorf("UTMStackAgent service is not installed") + } + return nil +} + +func requireNotInstalled(cmd *cobra.Command, args []string) error { + isInstalled, err := utils.CheckIfServiceIsInstalled("UTMStackAgent") + if err != nil { + return fmt.Errorf("error checking if service is installed: %v", err) + } + if isInstalled { + return fmt.Errorf("UTMStackAgent service is already installed") + } + return nil +} diff --git a/agent/cmd/install.go b/agent/cmd/install.go new file mode 100644 index 000000000..ad9f9b076 --- /dev/null +++ b/agent/cmd/install.go @@ -0,0 +1,63 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/agent" + pb "github.com/utmstack/UTMStack/agent/agent" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/serv" + "github.com/utmstack/UTMStack/agent/utils" +) + +var installCmd = &cobra.Command{ + Use: "install ", + Short: "Install the UTMStackAgent service", + Args: cobra.ExactArgs(3), + PreRunE: requireNotInstalled, + RunE: func(cmd *cobra.Command, args []string) error { + cnf := &config.Config{ + Server: args[0], + SkipCertValidation: args[2] == "yes", + } + utmKey := args[1] + + utils.PrintBanner() + fmt.Println("Installing UTMStackAgent service ...") + + fmt.Print("Checking server connection ... ") + if err := utils.ArePortsReachable(cnf.Server, config.AgentManagerPort, config.LogAuthProxyPort, config.DependenciesPort); err != nil { + fmt.Println("\nError trying to connect to server: ", err) + os.Exit(1) + } + fmt.Println("[OK]") + + fmt.Print("Configuring agent ... ") + if err := pb.RegisterAgent(cnf, utmKey); err != nil { + fmt.Println("\nError registering agent: ", err) + os.Exit(1) + } + if err := config.SaveConfig(cnf); err != nil { + fmt.Println("\nError saving config: ", err) + os.Exit(1) + } + if err := agent.SetDataRetention(""); err != nil { + fmt.Println("\nError setting retention: ", err) + os.Exit(1) + } + fmt.Println("[OK]") + + fmt.Print("Creating service ... ") + serv.InstallService() + fmt.Println("[OK]") + fmt.Println("UTMStackAgent service installed correctly") + + return nil + }, +} + +func init() { + rootCmd.AddCommand(installCmd) +} diff --git a/agent/cmd/load_tls_certs.go b/agent/cmd/load_tls_certs.go new file mode 100644 index 000000000..eaa6006a0 --- /dev/null +++ b/agent/cmd/load_tls_certs.go @@ -0,0 +1,66 @@ +package cmd + +import ( + "fmt" + "os" + "time" + + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" +) + +var loadTLSCertsCmd = &cobra.Command{ + Use: "load-tls-certs [ca_certificate_path]", + Short: "Load your own TLS certificates (RECOMMENDED for production)", + Long: `Load your own TLS certificates. + +Examples: + utmstack_agent load-tls-certs /path/to/server.crt /path/to/server.key /path/to/ca.crt + utmstack_agent load-tls-certs /path/to/server.crt /path/to/server.key # Without CA`, + Args: cobra.RangeArgs(2, 3), + PreRunE: requireInstalled, + RunE: func(cmd *cobra.Command, args []string) error { + userCertPath := args[0] + userKeyPath := args[1] + var userCAPath string + if len(args) > 2 { + userCAPath = args[2] + } + + fmt.Println("Loading user TLS certificates ...") + + fmt.Print("Validating certificate files ... ") + if err := utils.ValidateIntegrationCertificates(userCertPath, userKeyPath); err != nil { + fmt.Printf("\nError: Invalid certificate files: %v\n", err) + os.Exit(1) + } + fmt.Println("[OK]") + + fmt.Print("Installing certificates ... ") + src := utils.CertificateFiles{ + CertPath: userCertPath, + KeyPath: userKeyPath, + CAPath: userCAPath, + } + dest := utils.CertificateFiles{ + CertPath: config.IntegrationCertPath, + KeyPath: config.IntegrationKeyPath, + CAPath: config.IntegrationCAPath, + } + if err := utils.LoadUserCertificatesWithStruct(src, dest); err != nil { + fmt.Printf("\nError loading certificates: %v\n", err) + os.Exit(1) + } + fmt.Println("[OK]") + + fmt.Println("TLS certificates loaded successfully!") + time.Sleep(5 * time.Second) + + return nil + }, +} + +func init() { + rootCmd.AddCommand(loadTLSCertsCmd) +} diff --git a/agent/cmd/root.go b/agent/cmd/root.go new file mode 100644 index 000000000..e50d8fcde --- /dev/null +++ b/agent/cmd/root.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/serv" +) + +var rootCmd = &cobra.Command{ + Use: "utmstack_agent", + Short: "UTMStack Agent CLI", + Long: `UTMStack Agent CLI + +Usage Examples: + utmstack_agent install + utmstack_agent enable-integration [--tls] + utmstack_agent disable-integration + utmstack_agent change-port + utmstack_agent change-retention + utmstack_agent load-tls-certs [ca] + utmstack_agent clean-logs + utmstack_agent uninstall + +TLS Certificate Management: + # Load your own certificates (RECOMMENDED) + utmstack_agent load-tls-certs /path/to/server.crt /path/to/server.key /path/to/ca.crt + utmstack_agent load-tls-certs /path/to/server.crt /path/to/server.key # Without CA + +TLS Integration Examples: + utmstack_agent enable-integration syslog tcp --tls # Enable with TLS + utmstack_agent enable-integration syslog tcp # Enable without TLS (default) + utmstack_agent disable-integration syslog tcp # Disable (auto-disables TLS) + +Note: + - Make sure to run commands with appropriate permissions. + - All commands require administrative privileges. + - For detailed logs, check the service log file.`, + Run: func(cmd *cobra.Command, args []string) { + serv.RunService() + }, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/agent/cmd/run.go b/agent/cmd/run.go new file mode 100644 index 000000000..e875e392f --- /dev/null +++ b/agent/cmd/run.go @@ -0,0 +1,20 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "github.com/utmstack/UTMStack/agent/serv" +) + +var runCmd = &cobra.Command{ + Use: "run", + Short: "Run the UTMStackAgent service", + Args: cobra.NoArgs, + PreRunE: requireInstalled, + Run: func(cmd *cobra.Command, args []string) { + serv.RunService() + }, +} + +func init() { + rootCmd.AddCommand(runCmd) +} diff --git a/agent/cmd/uninstall.go b/agent/cmd/uninstall.go new file mode 100644 index 000000000..996ad0f49 --- /dev/null +++ b/agent/cmd/uninstall.go @@ -0,0 +1,68 @@ +package cmd + +import ( + "fmt" + "os" + "path/filepath" + "time" + + "github.com/spf13/cobra" + pb "github.com/utmstack/UTMStack/agent/agent" + "github.com/utmstack/UTMStack/agent/collector" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/dependency" + "github.com/utmstack/UTMStack/agent/serv" + "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/exec" + "github.com/utmstack/UTMStack/shared/fs" +) + +var uninstallCmd = &cobra.Command{ + Use: "uninstall", + Short: "Uninstall the UTMStackAgent service", + Args: cobra.NoArgs, + PreRunE: requireInstalled, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Uninstalling UTMStackAgent service ...") + + fmt.Print("Stopping UTMStackUpdater service... ") + updaterPath := filepath.Join(fs.GetExecutablePath(), dependency.UpdaterFile("")) + if fs.Exists(updaterPath) { + err := exec.Run(updaterPath, fs.GetExecutablePath(), "uninstall") + if err != nil { + fmt.Printf("Warning: %v\n", err) + } else { + fmt.Println("[OK]") + } + time.Sleep(2 * time.Second) + } else { + fmt.Println("[SKIPPED - not found]") + } + + cnf, err := config.GetCurrentConfig() + if err != nil { + fmt.Println("Error getting config: ", err) + os.Exit(1) + } + if err = pb.DeleteAgent(cnf); err != nil { + utils.Logger.ErrorF("error deleting agent: %v", err) + } + if err = collector.UninstallAll(); err != nil { + fmt.Printf("error uninstalling collectors: %v\n", err) + os.Exit(1) + } + os.Remove(config.ConfigurationFile) + + serv.UninstallService() + + fmt.Println("[OK]") + fmt.Println("UTMStackAgent service uninstalled correctly") + os.Exit(0) + + return nil + }, +} + +func init() { + rootCmd.AddCommand(uninstallCmd) +} diff --git a/agent/collector/collector.go b/agent/collector/collector.go new file mode 100644 index 000000000..34c413c74 --- /dev/null +++ b/agent/collector/collector.go @@ -0,0 +1,118 @@ +package collector + +import ( + "context" + "fmt" + "sync" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/agent" + "github.com/utmstack/UTMStack/agent/collector/file" + "github.com/utmstack/UTMStack/agent/collector/netflow" + "github.com/utmstack/UTMStack/agent/collector/platform" + "github.com/utmstack/UTMStack/agent/collector/syslog" + "github.com/utmstack/UTMStack/agent/utils" +) + +// Collector is the interface that every collector must implement. +type Collector interface { + Name() string + Start(ctx context.Context, queue chan *plugins.Log) + Stop() +} + +// Installable is an optional interface for collectors that install system services. +type Installable interface { + Install() error + Uninstall() error +} + +var ( + activeCollectors []Collector + collectorsMu sync.Mutex +) + +// StartAll starts all collectors: platform-specific, syslog and netflow. +// The context is used for graceful shutdown signaling. +func StartAll(ctx context.Context) { + collectorsMu.Lock() + defer collectorsMu.Unlock() + + // Clear previous collectors + activeCollectors = nil + + // Create syslog collector + syslogCollector := syslog.New() + activeCollectors = append(activeCollectors, syslogCollector) + go runCollector(ctx, syslogCollector, agent.LogQueue) + + // Create netflow collector + netflowCollector := netflow.New() + activeCollectors = append(activeCollectors, netflowCollector) + go runCollector(ctx, netflowCollector, agent.LogQueue) + + // Create file collector (nginx, postgresql, etc.) + fileCollector := file.New() + activeCollectors = append(activeCollectors, fileCollector) + go runCollector(ctx, fileCollector, agent.LogQueue) + + // Create platform collectors (filebeat, winlogbeat, macos, etc.) + platformCollectors := platform.GetCollectors() + for _, c := range platformCollectors { + activeCollectors = append(activeCollectors, c) + go runCollector(ctx, c, agent.LogQueue) + } + + utils.Logger.Info("All collectors started") +} + +// runCollector runs a collector with panic recovery. +func runCollector(ctx context.Context, c Collector, queue chan *plugins.Log) { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in collector %s: %v", c.Name(), r) + } + }() + c.Start(ctx, queue) +} + +// StopAll stops all active collectors. +func StopAll() { + collectorsMu.Lock() + defer collectorsMu.Unlock() + + for _, c := range activeCollectors { + utils.Logger.Info("Stopping collector: %s", c.Name()) + c.Stop() + } + activeCollectors = nil + utils.Logger.Info("All collectors stopped") +} + +// InstallAll installs all platform collectors that implement Installable. +func InstallAll() error { + platformCollectors := platform.GetCollectors() + for _, c := range platformCollectors { + if inst, ok := c.(Installable); ok { + if err := inst.Install(); err != nil { + return fmt.Errorf("%v", err) + } + } + } + utils.Logger.LogF(100, "collectors installed correctly") + return nil +} + +// UninstallAll uninstalls all platform collectors that implement Installable. +func UninstallAll() error { + platformCollectors := platform.GetCollectors() + for _, c := range platformCollectors { + if inst, ok := c.(Installable); ok { + if err := inst.Uninstall(); err != nil { + return fmt.Errorf("%v", err) + } + } + } + utils.Logger.LogF(100, "collectors uninstalled correctly") + return nil +} diff --git a/agent/collector/config.go b/agent/collector/config.go new file mode 100644 index 000000000..e35e05a7d --- /dev/null +++ b/agent/collector/config.go @@ -0,0 +1,381 @@ +package collector + +import ( + "fmt" + "net" + + "github.com/utmstack/UTMStack/agent/collector/schema" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/fs" +) + +// SyncCollectorConfig ensures the collector config file exists and is synchronized +// with the current ProtoPorts and FilePaths. It handles both initial creation and version upgrades: +// - If config doesn't exist → creates it with all integrations from ProtoPorts and FilePaths +// - If config exists → adds new integrations, removes obsolete ones +func SyncCollectorConfig() error { + cnf, err := schema.ReadCollectorConfig() + if err != nil { + // Config file doesn't exist, create it + integrations := make(map[string]schema.Integration) + for logTyp, ports := range config.ProtoPorts { + integrations[string(logTyp)] = schema.Integration{ + TCP: schema.Port{IsListen: false, Port: ports.TCP}, + UDP: schema.Port{IsListen: false, Port: ports.UDP}, + } + } + + fileIntegrations := make(map[string]schema.FileIntegration) + for logTyp, paths := range config.FilePaths { + fileIntegrations[string(logTyp)] = schema.FileIntegration{ + Enabled: false, + Paths: paths, + } + } + + newConfig := &schema.CollectorConfig{ + Integrations: integrations, + FileIntegrations: fileIntegrations, + } + return schema.WriteCollectorConfig(newConfig) + } + + modified := false + + // Add new integrations from ProtoPorts + for logTyp, ports := range config.ProtoPorts { + key := string(logTyp) + if _, exists := cnf.Integrations[key]; !exists { + cnf.Integrations[key] = schema.Integration{ + TCP: schema.Port{IsListen: false, Port: ports.TCP}, + UDP: schema.Port{IsListen: false, Port: ports.UDP}, + } + modified = true + utils.Logger.Info("Added new integration to config: %s", key) + } + } + + // Remove integrations that no longer exist in ProtoPorts + for key := range cnf.Integrations { + if _, exists := config.ProtoPorts[config.DataType(key)]; !exists { + delete(cnf.Integrations, key) + modified = true + utils.Logger.Info("Removed obsolete integration from config: %s", key) + } + } + + // Initialize FileIntegrations map if nil + if cnf.FileIntegrations == nil { + cnf.FileIntegrations = make(map[string]schema.FileIntegration) + } + + // Add new file integrations from FilePaths + for logTyp, paths := range config.FilePaths { + key := string(logTyp) + if _, exists := cnf.FileIntegrations[key]; !exists { + cnf.FileIntegrations[key] = schema.FileIntegration{ + Enabled: false, + Paths: paths, + } + modified = true + utils.Logger.Info("Added new file integration to config: %s", key) + } + } + + // Remove file integrations that no longer exist in FilePaths + for key := range cnf.FileIntegrations { + if _, exists := config.FilePaths[config.DataType(key)]; !exists { + delete(cnf.FileIntegrations, key) + modified = true + utils.Logger.Info("Removed obsolete file integration from config: %s", key) + } + } + + if modified { + return schema.WriteCollectorConfig(&cnf) + } + return nil +} + +// ConfigureFirstTime is an alias for SyncCollectorConfig for backward compatibility. +// Deprecated: Use SyncCollectorConfig instead. +func ConfigureFirstTime() error { + return SyncCollectorConfig() +} + +// ChangeIntegrationStatus enables or disables an integration. +func ChangeIntegrationStatus(logTyp string, proto string, isEnabled bool, tlsOptions ...bool) (string, error) { + var port string + cnf, err := schema.ReadCollectorConfig() + if err != nil { + return "", fmt.Errorf("error reading collector config: %v", err) + } + + if valid := config.ValidateModuleType(logTyp); valid == "nil" { + return "", fmt.Errorf("invalid integration: %s", logTyp) + } + + integration := cnf.Integrations[logTyp] + switch proto { + case "tcp": + port = integration.TCP.Port + case "udp": + port = integration.UDP.Port + default: + return "", fmt.Errorf("invalid protocol: %s", proto) + } + + // When enabling, validate port is available + if isEnabled { + if conflicting := CheckPortConflict(port, proto, &cnf, logTyp); conflicting != "" { + return "", fmt.Errorf("port %s is already in use by integration '%s'", port, conflicting) + } + if !IsPortBindable(port, proto) { + return "", fmt.Errorf("port %s is already in use by another process", port) + } + } + + switch proto { + case "tcp": + integration.TCP.IsListen = isEnabled + + if len(tlsOptions) > 0 && isEnabled { + if tlsOptions[0] { + if !fs.Exists(config.IntegrationCertPath) || !fs.Exists(config.IntegrationKeyPath) { + return "", fmt.Errorf("TLS certificates not found. Please load certificates first") + } + integration.TCP.TLSEnabled = true + } else { + integration.TCP.TLSEnabled = false + } + } + + if !isEnabled { + integration.TCP.TLSEnabled = false + } + + case "udp": + integration.UDP.IsListen = isEnabled + + if len(tlsOptions) > 0 && tlsOptions[0] { + return "", fmt.Errorf("TLS is not supported for UDP protocol. Use TCP for TLS connections") + } + } + + cnf.Integrations[logTyp] = integration + return port, schema.WriteCollectorConfig(&cnf) +} + +// ChangePortResult contains the result of a port change operation. +type ChangePortResult struct { + OldPort string + Warning string +} + +// ChangePort changes the port for an integration. +// Returns the old port and a warning if another integration has the same port configured. +func ChangePort(logTyp string, proto string, port string) (ChangePortResult, error) { + result := ChangePortResult{} + cnf, err := schema.ReadCollectorConfig() + if err != nil { + return result, fmt.Errorf("error reading collector config: %v", err) + } + + if valid := config.ValidateModuleType(logTyp); valid == "nil" { + return result, fmt.Errorf("invalid integration: %s", logTyp) + } + + if !schema.ValidatePortChange(port) { + return result, fmt.Errorf("port %s is out of valid range %s-%s", port, config.PortRangeMin, config.PortRangeMax) + } + + // Check if port is in use by an enabled integration (error) + if conflicting := CheckPortConflict(port, proto, &cnf, logTyp); conflicting != "" { + return result, fmt.Errorf("port %s is already in use by integration '%s'", port, conflicting) + } + + // Check if port is configured by another integration (warning) + if configured := CheckPortConfigured(port, proto, &cnf, logTyp); configured != "" { + result.Warning = fmt.Sprintf("port %s is also configured for integration '%s' (currently disabled)", port, configured) + } + + integration := cnf.Integrations[logTyp] + switch proto { + case "tcp": + result.OldPort = integration.TCP.Port + integration.TCP.Port = port + case "udp": + result.OldPort = integration.UDP.Port + integration.UDP.Port = port + default: + return result, fmt.Errorf("invalid protocol: %s", proto) + } + + cnf.Integrations[logTyp] = integration + return result, schema.WriteCollectorConfig(&cnf) +} + +// CheckPortConflict checks if a port is in use by another enabled integration. +// Returns the integration name if there's a conflict, empty string otherwise. +func CheckPortConflict(port string, proto string, cnf *schema.CollectorConfig, currentIntegration string) string { + for integration, integrationConfig := range cnf.Integrations { + if integration == currentIntegration { + continue + } + if proto == "tcp" && integrationConfig.TCP.Port == port && integrationConfig.TCP.IsListen { + return integration + } + if proto == "udp" && integrationConfig.UDP.Port == port && integrationConfig.UDP.IsListen { + return integration + } + } + return "" +} + +// CheckPortConfigured checks if a port is configured (but not necessarily enabled) by another integration. +// Returns the integration name if configured, empty string otherwise. +func CheckPortConfigured(port string, proto string, cnf *schema.CollectorConfig, currentIntegration string) string { + for integration, integrationConfig := range cnf.Integrations { + if integration == currentIntegration { + continue + } + if proto == "tcp" && integrationConfig.TCP.Port == port { + return integration + } + if proto == "udp" && integrationConfig.UDP.Port == port { + return integration + } + } + return "" +} + +// IsPortBindable checks if the system can bind to a port. +func IsPortBindable(port string, proto string) bool { + listener, err := net.Listen(proto, ":"+port) + if err != nil { + return false + } + listener.Close() + return true +} + +// EnableTLSForIntegration enables TLS for an integration. +func EnableTLSForIntegration(logTyp string, proto string) (string, error) { + cnf, err := schema.ReadCollectorConfig() + if err != nil { + return "", fmt.Errorf("error reading collector config: %v", err) + } + + if valid := config.ValidateModuleType(logTyp); valid == "nil" { + return "", fmt.Errorf("invalid integration: %s", logTyp) + } + + integration := cnf.Integrations[logTyp] + var port string + + switch proto { + case "tcp": + if integration.TCP.Port == "" { + return "", fmt.Errorf("TCP port not configured for %s", logTyp) + } + port = integration.TCP.Port + integration.TCP.TLSEnabled = true + case "udp": + return "", fmt.Errorf("TLS not supported for UDP protocol") + default: + return "", fmt.Errorf("invalid protocol: %s", proto) + } + + cnf.Integrations[logTyp] = integration + return port, schema.WriteCollectorConfig(&cnf) +} + +// DisableTLSForIntegration disables TLS for an integration. +func DisableTLSForIntegration(logTyp string, proto string) error { + cnf, err := schema.ReadCollectorConfig() + if err != nil { + return fmt.Errorf("error reading collector config: %v", err) + } + + integration := cnf.Integrations[logTyp] + switch proto { + case "tcp": + integration.TCP.TLSEnabled = false + case "udp": + return fmt.Errorf("TLS not supported for UDP protocol") + default: + return fmt.Errorf("invalid protocol: %s", proto) + } + + cnf.Integrations[logTyp] = integration + return schema.WriteCollectorConfig(&cnf) +} + +// ChangeFileIntegrationStatus enables or disables a file-based integration. +func ChangeFileIntegrationStatus(logTyp string, isEnabled bool) ([]string, error) { + cnf, err := schema.ReadCollectorConfig() + if err != nil { + return nil, fmt.Errorf("error reading collector config: %v", err) + } + + if valid := config.ValidateModuleType(logTyp); valid != "file" { + return nil, fmt.Errorf("invalid file integration: %s", logTyp) + } + + integration, exists := cnf.FileIntegrations[logTyp] + if !exists { + return nil, fmt.Errorf("file integration not found: %s", logTyp) + } + + integration.Enabled = isEnabled + cnf.FileIntegrations[logTyp] = integration + return integration.Paths, schema.WriteCollectorConfig(&cnf) +} + +// ChangeFilePathsResult contains the result of a path change operation. +type ChangeFilePathsResult struct { + OldPaths []string +} + +// ChangeFilePaths changes the paths for a file-based integration. +func ChangeFilePaths(logTyp string, paths []string) (ChangeFilePathsResult, error) { + result := ChangeFilePathsResult{} + cnf, err := schema.ReadCollectorConfig() + if err != nil { + return result, fmt.Errorf("error reading collector config: %v", err) + } + + if valid := config.ValidateModuleType(logTyp); valid != "file" { + return result, fmt.Errorf("invalid file integration: %s", logTyp) + } + + integration, exists := cnf.FileIntegrations[logTyp] + if !exists { + return result, fmt.Errorf("file integration not found: %s", logTyp) + } + + if len(paths) == 0 { + return result, fmt.Errorf("at least one path is required") + } + + result.OldPaths = integration.Paths + integration.Paths = paths + cnf.FileIntegrations[logTyp] = integration + return result, schema.WriteCollectorConfig(&cnf) +} + +// GetFileIntegrationPaths returns the configured paths for a file integration. +func GetFileIntegrationPaths(logTyp string) ([]string, error) { + cnf, err := schema.ReadCollectorConfig() + if err != nil { + return nil, fmt.Errorf("error reading collector config: %v", err) + } + + integration, exists := cnf.FileIntegrations[logTyp] + if !exists { + return nil, fmt.Errorf("file integration not found: %s", logTyp) + } + + return integration.Paths, nil +} diff --git a/agent/collector/file/file.go b/agent/collector/file/file.go new file mode 100644 index 000000000..4dccc1e2e --- /dev/null +++ b/agent/collector/file/file.go @@ -0,0 +1,287 @@ +package file + +import ( + "bufio" + "context" + "io" + "os" + "path/filepath" + "sync" + "time" + + "github.com/threatwinds/go-sdk/entities" + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/collector/schema" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" +) + +const ( + reconcileInterval = 10 * time.Second + pollInterval = 1 * time.Second +) + +// fileWatcher represents an active file being tailed. +type fileWatcher struct { + dataType string + path string + file *os.File + offset int64 + cancel context.CancelFunc +} + +// FileCollector manages file-based log collection. +type FileCollector struct { + watchers map[string]*fileWatcher // key: dataType+path + mu sync.RWMutex + queue chan *plugins.Log +} + +// New creates a new FileCollector. +func New() *FileCollector { + return &FileCollector{ + watchers: make(map[string]*fileWatcher), + } +} + +func (fc *FileCollector) Name() string { + return "file" +} + +func (fc *FileCollector) Stop() { + fc.mu.Lock() + defer fc.mu.Unlock() + for key, w := range fc.watchers { + if w.cancel != nil { + w.cancel() + } + if w.file != nil { + w.file.Close() + } + delete(fc.watchers, key) + } +} + +// Start begins the reconciliation loop. It reads the collector config every +// 10 seconds and adjusts file watchers as needed. +func (fc *FileCollector) Start(ctx context.Context, queue chan *plugins.Log) { + fc.queue = queue + for { + select { + case <-ctx.Done(): + utils.Logger.Info("file collector stopping due to context cancellation") + fc.Stop() + return + default: + fc.reconcile(ctx) + time.Sleep(reconcileInterval) + } + } +} + +func (fc *FileCollector) reconcile(ctx context.Context) { + cnf, err := schema.ReadCollectorConfig() + if err != nil { + utils.Logger.ErrorF("file collector: error reading config: %v", err) + return + } + + // Build set of desired watchers from enabled file integrations + desiredWatchers := make(map[string]struct{}) + + for intType, integration := range cnf.FileIntegrations { + // Only handle file-type integrations + if config.ValidateModuleType(intType) != "file" { + continue + } + + if !integration.Enabled { + // Stop watchers for disabled integrations + fc.stopWatchersForDataType(intType) + continue + } + + // Expand glob patterns and start watchers + for _, pathPattern := range integration.Paths { + matches, err := filepath.Glob(pathPattern) + if err != nil { + utils.Logger.ErrorF("file collector: invalid glob pattern %s: %v", pathPattern, err) + continue + } + + if len(matches) == 0 { + utils.Logger.LogF(100, "file collector: no files match pattern %s for %s", pathPattern, intType) + continue + } + + for _, filePath := range matches { + key := intType + ":" + filePath + desiredWatchers[key] = struct{}{} + fc.ensureWatcher(ctx, intType, filePath) + } + } + } + + // Stop watchers that are no longer needed + fc.mu.Lock() + for key, w := range fc.watchers { + if _, exists := desiredWatchers[key]; !exists { + utils.Logger.Info("file collector: stopping watcher for %s", key) + if w.cancel != nil { + w.cancel() + } + if w.file != nil { + w.file.Close() + } + delete(fc.watchers, key) + } + } + fc.mu.Unlock() +} + +func (fc *FileCollector) stopWatchersForDataType(dataType string) { + fc.mu.Lock() + defer fc.mu.Unlock() + for key, w := range fc.watchers { + if w.dataType == dataType { + utils.Logger.Info("file collector: stopping watcher for %s", key) + if w.cancel != nil { + w.cancel() + } + if w.file != nil { + w.file.Close() + } + delete(fc.watchers, key) + } + } +} + +func (fc *FileCollector) ensureWatcher(ctx context.Context, dataType, filePath string) { + key := dataType + ":" + filePath + + fc.mu.Lock() + if _, exists := fc.watchers[key]; exists { + fc.mu.Unlock() + return + } + + // Create a new watcher + watchCtx, cancel := context.WithCancel(ctx) + w := &fileWatcher{ + dataType: dataType, + path: filePath, + cancel: cancel, + } + fc.watchers[key] = w + fc.mu.Unlock() + + // Start tailing in a goroutine + go fc.tailFile(watchCtx, w) + utils.Logger.Info("file collector: started watching %s for %s", filePath, dataType) +} + +func (fc *FileCollector) tailFile(ctx context.Context, w *fileWatcher) { + hostname, err := os.Hostname() + if err != nil { + hostname = "unknown" + } + + for { + select { + case <-ctx.Done(): + return + default: + } + + // Open the file if not already open or if it was rotated + if w.file == nil { + file, err := os.Open(w.path) + if err != nil { + utils.Logger.LogF(100, "file collector: error opening %s: %v", w.path, err) + time.Sleep(pollInterval) + continue + } + w.file = file + + // Seek to end to only read new content + offset, err := file.Seek(0, io.SeekEnd) + if err != nil { + utils.Logger.ErrorF("file collector: error seeking %s: %v", w.path, err) + w.file.Close() + w.file = nil + time.Sleep(pollInterval) + continue + } + w.offset = offset + } + + // Check for file rotation (file was replaced) + stat, err := os.Stat(w.path) + if err != nil { + // File may have been deleted, close and retry + w.file.Close() + w.file = nil + time.Sleep(pollInterval) + continue + } + + fileStat, err := w.file.Stat() + if err != nil || !os.SameFile(stat, fileStat) { + // File was rotated, reopen + w.file.Close() + w.file = nil + w.offset = 0 + continue + } + + // Check if file was truncated + if stat.Size() < w.offset { + w.offset = 0 + w.file.Seek(0, io.SeekStart) + } + + // Read new lines + reader := bufio.NewReader(w.file) + for { + select { + case <-ctx.Done(): + return + default: + } + + line, err := reader.ReadString('\n') + if err != nil { + if err != io.EOF { + utils.Logger.ErrorF("file collector: error reading %s: %v", w.path, err) + } + break + } + + if len(line) == 0 { + continue + } + + // Update offset + w.offset += int64(len(line)) + + // Validate and send log + validatedLog, _, err := entities.ValidateString(line, false) + if err != nil { + utils.Logger.LogF(100, "file collector: validation error for %s: %v", w.path, err) + continue + } + + select { + case fc.queue <- &plugins.Log{ + DataType: w.dataType, + DataSource: hostname, + Raw: validatedLog, + }: + default: + utils.Logger.LogF(100, "file collector: queue full, discarding log from %s", w.path) + } + } + + time.Sleep(pollInterval) + } +} diff --git a/agent/collector/netflow/goflow.go b/agent/collector/netflow/goflow.go new file mode 100644 index 000000000..b105079d3 --- /dev/null +++ b/agent/collector/netflow/goflow.go @@ -0,0 +1,263 @@ +package netflow + +import ( + "encoding/binary" + "fmt" + "net" + "time" + + goflownetflow "github.com/netsampler/goflow2/decoders/netflow" + "github.com/netsampler/goflow2/decoders/netflowlegacy" +) + +// PrepareGoflowV5 converts goflow2 NetFlow v5 packet to metrics +func PrepareGoflowV5(addr string, p *netflowlegacy.PacketNetFlowV5) []Metric { + nfExporter, _, _ := net.SplitHostPort(addr) + var metrics []Metric + + for _, r := range p.Records { + met := Metric{OutBytes: "0", InBytes: "0", OutPacket: "0", InPacket: "0", NFSender: nfExporter} + met.FlowVersion = "Netflow-V5" + + // Convert timestamps (First and Last are relative to SysUptime in milliseconds) + met.First = time.Unix(int64(p.UnixSecs), int64(p.UnixNSecs)).Add(-time.Duration(p.SysUptime-r.First) * time.Millisecond).Format(time.RFC3339Nano) + met.Last = time.Unix(int64(p.UnixSecs), int64(p.UnixNSecs)).Add(-time.Duration(p.SysUptime-r.Last) * time.Millisecond).Format(time.RFC3339Nano) + + met.Protocol = ProtoToName(fmt.Sprintf("%v", r.Proto)) + met.Bytes = fmt.Sprintf("%v", r.DOctets) + met.Packets = fmt.Sprintf("%v", r.DPkts) + met.TCPFlags = fmt.Sprintf("%v", r.TCPFlags) + met.SrcAs = fmt.Sprintf("%v", r.SrcAS) + met.DstAs = fmt.Sprintf("%v", r.DstAS) + met.SrcMask = fmt.Sprintf("%v", r.SrcMask) + met.DstMask = fmt.Sprintf("%v", r.DstMask) + + // Convert uint32 IPs to string + srcIP := make(net.IP, 4) + binary.BigEndian.PutUint32(srcIP, r.SrcAddr) + met.SrcIP = srcIP.String() + + dstIP := make(net.IP, 4) + binary.BigEndian.PutUint32(dstIP, r.DstAddr) + met.DstIP = dstIP.String() + + nextHop := make(net.IP, 4) + binary.BigEndian.PutUint32(nextHop, r.NextHop) + met.NextHop = nextHop.String() + + met.SrcPort = fmt.Sprintf("%v", r.SrcPort) + met.DstPort = fmt.Sprintf("%v", r.DstPort) + + met.InEthernet = fmt.Sprintf("%v", r.Input) + met.OutEthernet = fmt.Sprintf("%v", r.Output) + + metrics = append(metrics, met) + } + + return metrics +} + +// PrepareGoflowV9 converts goflow2 NetFlow v9 packet to metrics +func PrepareGoflowV9(addr string, p *goflownetflow.NFv9Packet) []Metric { + nfExporter, _, _ := net.SplitHostPort(addr) + var metrics []Metric + + for _, fs := range p.FlowSets { + // FlowSets can be TemplateFlowSet, DataFlowSet, or OptionsDataFlowSet + switch ds := fs.(type) { + case goflownetflow.DataFlowSet: + for _, record := range ds.Records { + met := Metric{OutBytes: "0", InBytes: "0", OutPacket: "0", InPacket: "0", NFSender: nfExporter} + met.FlowVersion = "Netflow-V9" + + for _, field := range record.Values { + extractFieldValue(&met, field.Type, field.Value, p.UnixSeconds) + } + + metrics = append(metrics, met) + } + } + } + + return metrics +} + +// PrepareGoflowIPFIX converts goflow2 IPFIX packet to metrics +func PrepareGoflowIPFIX(addr string, p *goflownetflow.IPFIXPacket) []Metric { + nfExporter, _, _ := net.SplitHostPort(addr) + var metrics []Metric + + for _, fs := range p.FlowSets { + // FlowSets can be TemplateFlowSet, DataFlowSet, or OptionsDataFlowSet + switch ds := fs.(type) { + case goflownetflow.DataFlowSet: + for _, record := range ds.Records { + met := Metric{OutBytes: "0", InBytes: "0", OutPacket: "0", InPacket: "0", NFSender: nfExporter} + met.FlowVersion = "IPFIX" + + for _, field := range record.Values { + extractFieldValue(&met, field.Type, field.Value, p.ExportTime) + } + + metrics = append(metrics, met) + } + } + } + + return metrics +} + +// extractFieldValue extracts field values based on IPFIX/NetFlow v9 field type IDs +// Field type IDs are defined in RFC 5102 and RFC 3954 +func extractFieldValue(met *Metric, fieldType uint16, value interface{}, exportTime uint32) { + switch fieldType { + // Timestamps + case 21: // flowEndSysUpTime + if v, ok := toUint32(value); ok { + met.Last = time.Unix(int64(exportTime), 0).Add(-time.Duration(v) * time.Millisecond).Format(time.RFC3339Nano) + } + case 22: // flowStartSysUpTime + if v, ok := toUint32(value); ok { + met.First = time.Unix(int64(exportTime), 0).Add(-time.Duration(v) * time.Millisecond).Format(time.RFC3339Nano) + } + case 150: // flowStartSeconds + if v, ok := toUint32(value); ok { + met.First = time.Unix(int64(v), 0).Format(time.RFC3339Nano) + } + case 151: // flowEndSeconds + if v, ok := toUint32(value); ok { + met.Last = time.Unix(int64(v), 0).Format(time.RFC3339Nano) + } + + // Byte and packet counts + case 1: // octetDeltaCount + met.Bytes = fmt.Sprintf("%v", value) + case 2: // packetDeltaCount + met.Packets = fmt.Sprintf("%v", value) + case 85: // octetTotalCount + met.Bytes = fmt.Sprintf("%v", value) + case 86: // packetTotalCount + met.Packets = fmt.Sprintf("%v", value) + + // Interfaces + case 10: // ingressInterface + met.InEthernet = fmt.Sprintf("%v", value) + case 14: // egressInterface + met.OutEthernet = fmt.Sprintf("%v", value) + + // IPv4 addresses + case 8: // sourceIPv4Address + if ip, ok := toIP(value); ok { + met.SrcIP = ip.String() + } else { + met.SrcIP = fmt.Sprintf("%v", value) + } + case 12: // destinationIPv4Address + if ip, ok := toIP(value); ok { + met.DstIP = ip.String() + } else { + met.DstIP = fmt.Sprintf("%v", value) + } + case 15: // ipNextHopIPv4Address + if ip, ok := toIP(value); ok { + met.NextHop = ip.String() + } else { + met.NextHop = fmt.Sprintf("%v", value) + } + + // IPv6 addresses + case 27: // sourceIPv6Address + if ip, ok := toIP(value); ok { + met.SrcIP = ip.String() + } else { + met.SrcIP = fmt.Sprintf("%v", value) + } + case 28: // destinationIPv6Address + if ip, ok := toIP(value); ok { + met.DstIP = ip.String() + } else { + met.DstIP = fmt.Sprintf("%v", value) + } + case 62: // ipNextHopIPv6Address + if ip, ok := toIP(value); ok { + met.NextHop = ip.String() + } else { + met.NextHop = fmt.Sprintf("%v", value) + } + + // Protocol and ports + case 4: // protocolIdentifier + met.Protocol = ProtoToName(fmt.Sprintf("%v", value)) + case 7: // sourceTransportPort + met.SrcPort = fmt.Sprintf("%v", value) + case 11: // destinationTransportPort + met.DstPort = fmt.Sprintf("%v", value) + + // Masks + case 9: // sourceIPv4PrefixLength + met.SrcMask = fmt.Sprintf("%v", value) + case 13: // destinationIPv4PrefixLength + met.DstMask = fmt.Sprintf("%v", value) + case 29: // sourceIPv6PrefixLength + met.SrcMask = fmt.Sprintf("%v", value) + case 30: // destinationIPv6PrefixLength + met.DstMask = fmt.Sprintf("%v", value) + + // AS numbers + case 16: // bgpSourceAsNumber + met.SrcAs = fmt.Sprintf("%v", value) + case 17: // bgpDestinationAsNumber + met.DstAs = fmt.Sprintf("%v", value) + + // TCP flags + case 6: // tcpControlBits + met.TCPFlags = fmt.Sprintf("%v", value) + + // Flow direction + case 61: // flowDirection + switch fmt.Sprintf("%v", value) { + case "0": + met.Direction = "Ingress" + case "1": + met.Direction = "Egress" + default: + met.Direction = fmt.Sprintf("%v", value) + } + } +} + +// toUint32 attempts to convert value to uint32 +func toUint32(value interface{}) (uint32, bool) { + switch v := value.(type) { + case uint32: + return v, true + case uint64: + return uint32(v), true + case int64: + return uint32(v), true + case int: + return uint32(v), true + case []byte: + if len(v) == 4 { + return binary.BigEndian.Uint32(v), true + } + } + return 0, false +} + +// toIP attempts to convert value to net.IP +func toIP(value interface{}) (net.IP, bool) { + switch v := value.(type) { + case net.IP: + return v, true + case []byte: + if len(v) == 4 || len(v) == 16 { + return net.IP(v), true + } + case uint32: + ip := make(net.IP, 4) + binary.BigEndian.PutUint32(ip, v) + return ip, true + } + return nil, false +} diff --git a/agent/collector/netflow/metrics.go b/agent/collector/netflow/metrics.go new file mode 100644 index 000000000..413ef5908 --- /dev/null +++ b/agent/collector/netflow/metrics.go @@ -0,0 +1,43 @@ +package netflow + +type Metric struct { + FlowVersion string `header:"version"` + NFSender string `header:"exporter"` + Last string `header:"last"` + First string `header:"first"` + Bytes string `header:"bytes"` + Packets string `header:"packets"` + InBytes string `header:"bytesIn"` + InPacket string `header:"packetsIn"` + OutBytes string `header:"bytesOut"` + OutPacket string `header:"packetsOut"` + InEthernet string `header:"inEth"` + OutEthernet string `header:"outEth"` + SrcIP string `header:"srcIp"` + SrcIp2lCountryShort string `header:"sCountry_S"` + SrcIp2lCountryLong string `header:"sCountry_L"` + SrcIp2lState string `header:"sState"` + SrcIp2lCity string `header:"sCity"` + SrcIp2lLat string `header:"sLat"` + SrcIp2lLong string `header:"sLong"` + DstIP string `header:"dstIp"` + DstIp2lCountryShort string `header:"dCountry_S"` + DstIp2lCountryLong string `header:"dCountry_L"` + DstIp2lState string `header:"dState"` + DstIp2lCity string `header:"dCity"` + DstIp2lLat string `header:"dLat"` + DstIp2lLong string `header:"dLong"` + Protocol string `header:"proto"` + SrcToS string `header:"srcToS"` + SrcPort string `header:"srcPort"` + DstPort string `header:"dstPort"` + FlowSamplerId string `header:"flowId"` + VendorPROPRIETARY string `header:"vendor"` + NextHop string `header:"nextHop"` + DstMask string `header:"dstMask"` + SrcMask string `header:"srcMask"` + TCPFlags string `header:"tcpFlags"` + Direction string `header:"direction"` + DstAs string `header:"dstAs"` + SrcAs string `header:"srcAs"` +} diff --git a/agent/collector/netflow/netflow.go b/agent/collector/netflow/netflow.go new file mode 100644 index 000000000..d41386586 --- /dev/null +++ b/agent/collector/netflow/netflow.go @@ -0,0 +1,457 @@ +package netflow + +import ( + "bytes" + "context" + "encoding/binary" + "errors" + "fmt" + "net" + "strconv" + "strings" + "sync" + "time" + + "github.com/netsampler/goflow2/decoders/netflow" + "github.com/netsampler/goflow2/decoders/netflowlegacy" + tehmaze "github.com/tehmaze/netflow" + "github.com/tehmaze/netflow/session" + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/collector/schema" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" +) + +const ( + reconcileInterval = 10 * time.Second + cacheCleanupInterval = 5 * time.Minute + cacheTTL = 30 * time.Minute +) + +// templateSystem implements netflow.NetFlowTemplateSystem for goflow2 +type templateSystem struct { + templates map[uint16]map[uint32]map[uint16]interface{} + lastUsed time.Time + mu sync.RWMutex +} + +func newTemplateSystem() *templateSystem { + return &templateSystem{ + templates: make(map[uint16]map[uint32]map[uint16]interface{}), + lastUsed: time.Now(), + } +} + +// legacyDecoderEntry wraps a decoder with its last used timestamp +type legacyDecoderEntry struct { + decoder *tehmaze.Decoder + lastUsed time.Time +} + +func (t *templateSystem) GetTemplate(version uint16, obsDomainId uint32, templateId uint16) (interface{}, error) { + t.mu.RLock() + defer t.mu.RUnlock() + + if versionMap, ok := t.templates[version]; ok { + if domainMap, ok := versionMap[obsDomainId]; ok { + if template, ok := domainMap[templateId]; ok { + return template, nil + } + } + } + return nil, fmt.Errorf("template not found: version=%d, obsDomainId=%d, templateId=%d", version, obsDomainId, templateId) +} + +func (t *templateSystem) AddTemplate(version uint16, obsDomainId uint32, template interface{}) { + t.mu.Lock() + defer t.mu.Unlock() + + if _, ok := t.templates[version]; !ok { + t.templates[version] = make(map[uint32]map[uint16]interface{}) + } + if _, ok := t.templates[version][obsDomainId]; !ok { + t.templates[version][obsDomainId] = make(map[uint16]interface{}) + } + + var templateId uint16 + switch tmpl := template.(type) { + case netflow.TemplateRecord: + templateId = tmpl.TemplateId + case netflow.IPFIXOptionsTemplateRecord: + templateId = tmpl.TemplateId + case netflow.NFv9OptionsTemplateRecord: + templateId = tmpl.TemplateId + default: + return + } + + t.templates[version][obsDomainId][templateId] = template +} + +// NetflowCollector manages the single netflow UDP listener. +// It reads the config file periodically and reconciles port state internally. +type NetflowCollector struct { + dataType string + legacyDecoders map[string]*legacyDecoderEntry + templateSystem map[string]*templateSystem + listener *net.UDPConn + ctx context.Context + cancel context.CancelFunc + isEnabled bool + port string + mu sync.RWMutex + queue chan *plugins.Log +} + +// New creates a new NetflowCollector. +func New() *NetflowCollector { + return &NetflowCollector{ + dataType: "netflow", + legacyDecoders: make(map[string]*legacyDecoderEntry), + templateSystem: make(map[string]*templateSystem), + isEnabled: false, + port: config.ProtoPorts[config.DataTypeNetflow].UDP, + } +} + +func (nc *NetflowCollector) Name() string { + return "netflow" +} + +func (nc *NetflowCollector) Stop() { + nc.disablePort() +} + +// Start begins the reconciliation loop. +func (nc *NetflowCollector) Start(ctx context.Context, queue chan *plugins.Log) { + nc.queue = queue + + // Start cache cleanup goroutine + go func() { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in netflow cache cleanup: %v", r) + } + }() + + ticker := time.NewTicker(cacheCleanupInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + nc.cleanupStaleEntries() + } + } + }() + + for { + select { + case <-ctx.Done(): + utils.Logger.Info("netflow collector stopping due to context cancellation") + return + default: + nc.reconcile() + time.Sleep(reconcileInterval) + } + } +} + +func (nc *NetflowCollector) reconcile() { + cnf, err := schema.ReadCollectorConfig() + if err != nil { + utils.Logger.ErrorF("error reading collector config: %v", err) + return + } + + integration, ok := cnf.Integrations["netflow"] + if !ok { + return + } + + cfgEnabled := integration.UDP.IsListen + cfgPort := integration.UDP.Port + + nc.mu.RLock() + isListening := nc.isEnabled + currentPort := nc.port + nc.mu.RUnlock() + + needKill := false + needStart := false + + if isListening && !cfgEnabled { + needKill = true + } else if !isListening && cfgEnabled { + needStart = true + } else if isListening && cfgEnabled && currentPort != cfgPort { + needKill = true + needStart = true + } + + if needKill { + nc.disablePort() + if needStart { + time.Sleep(200 * time.Millisecond) + } + } + + if cfgPort != "" { + nc.mu.Lock() + nc.port = cfgPort + nc.mu.Unlock() + } + + if needStart { + nc.enablePort() + } +} + +func (nc *NetflowCollector) enablePort() { + nc.mu.Lock() + if nc.isEnabled { + nc.mu.Unlock() + return + } + + port, err := strconv.Atoi(nc.port) + if err != nil { + nc.mu.Unlock() + utils.Logger.ErrorF("error converting port to int: %v", err) + return + } + + listener, err := net.ListenUDP("udp", &net.UDPAddr{ + Port: port, + IP: net.ParseIP("0.0.0.0"), + }) + if err != nil { + nc.mu.Unlock() + utils.Logger.ErrorF("error listening netflow: %v", err) + return + } + + nc.isEnabled = true + nc.listener = listener + nc.ctx, nc.cancel = context.WithCancel(context.Background()) + nc.mu.Unlock() + + utils.Logger.Info("Server %s listening in port: %s protocol: UDP", nc.dataType, nc.port) + + buffer := make([]byte, 65535) + + go func() { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in netflow listener: %v", r) + } + }() + + for { + select { + case <-nc.ctx.Done(): + return + default: + nc.listener.SetDeadline(time.Now().Add(1 * time.Second)) + + length, addr, err := nc.listener.ReadFromUDP(buffer) + if err != nil { + if errors.Is(err, net.ErrClosed) { + return + } + + var netOpErr *net.OpError + ok := errors.As(err, &netOpErr) + if ok && netOpErr.Timeout() { + continue + } + + utils.Logger.ErrorF("error connecting with netflow listener: %v", err) + continue + } + + packetData := buffer[:length] + packetInfo, validationErr := validateNetflowPacket(packetData) + if validationErr != nil { + utils.Logger.ErrorF("invalid NetFlow packet from %s (length: %d bytes): %v", addr.String(), length, validationErr) + continue + } + + var message interface{} + + switch packetInfo.version { + case 5: + msg, err := netflowlegacy.DecodeMessage(bytes.NewBuffer(packetData)) + if err != nil { + utils.Logger.ErrorF("error decoding %s message from %s: %v", packetInfo.versionName, addr.String(), err) + continue + } + message = msg + + case 9, 10: + ts := nc.getOrCreateTemplateSystem(addr.String()) + msg, err := netflow.DecodeMessage(bytes.NewBuffer(packetData), ts) + if err != nil { + if !strings.Contains(err.Error(), "template not found") { + utils.Logger.ErrorF("error decoding %s message from %s: %v", packetInfo.versionName, addr.String(), err) + } + continue + } + message = msg + + case 1, 6, 7: + d := nc.getOrCreateLegacyDecoder(addr.String()) + msg, err := d.Read(bytes.NewBuffer(packetData)) + if err != nil { + utils.Logger.ErrorF("error decoding %s message from %s: %v", packetInfo.versionName, addr.String(), err) + nc.removeLegacyDecoder(addr.String()) + continue + } + message = msg + + default: + utils.Logger.ErrorF("unsupported NetFlow version %d from %s", packetInfo.version, addr.String()) + continue + } + + err = processMessage(addr.String(), message, nc.queue) + if err != nil { + utils.Logger.ErrorF("error parsing netflow: %v", err) + } + } + } + }() +} + +func (nc *NetflowCollector) disablePort() { + nc.mu.Lock() + defer nc.mu.Unlock() + + if nc.isEnabled { + utils.Logger.Info("Server %s closed in port: %s protocol: UDP", nc.dataType, nc.port) + nc.cancel() + nc.listener.Close() + nc.isEnabled = false + } +} + +func (nc *NetflowCollector) getOrCreateTemplateSystem(addr string) *templateSystem { + nc.mu.Lock() + defer nc.mu.Unlock() + + if ts, ok := nc.templateSystem[addr]; ok { + ts.lastUsed = time.Now() + return ts + } + ts := newTemplateSystem() + nc.templateSystem[addr] = ts + return ts +} + +func (nc *NetflowCollector) getOrCreateLegacyDecoder(addr string) *tehmaze.Decoder { + nc.mu.Lock() + defer nc.mu.Unlock() + + if entry, ok := nc.legacyDecoders[addr]; ok { + entry.lastUsed = time.Now() + return entry.decoder + } + s := session.New() + d := tehmaze.NewDecoder(s) + nc.legacyDecoders[addr] = &legacyDecoderEntry{ + decoder: d, + lastUsed: time.Now(), + } + return d +} + +func (nc *NetflowCollector) removeLegacyDecoder(addr string) { + nc.mu.Lock() + defer nc.mu.Unlock() + delete(nc.legacyDecoders, addr) +} + +// cleanupStaleEntries removes entries that haven't been used recently +func (nc *NetflowCollector) cleanupStaleEntries() { + nc.mu.Lock() + defer nc.mu.Unlock() + + now := time.Now() + + // Cleanup legacy decoders + for addr, entry := range nc.legacyDecoders { + if now.Sub(entry.lastUsed) > cacheTTL { + delete(nc.legacyDecoders, addr) + utils.Logger.Info("Removed stale legacy decoder for %s", addr) + } + } + + // Cleanup template systems + for addr, ts := range nc.templateSystem { + if now.Sub(ts.lastUsed) > cacheTTL { + delete(nc.templateSystem, addr) + utils.Logger.Info("Removed stale template system for %s", addr) + } + } +} + +// --- Packet validation --- + +type netflowPacketInfo struct { + version uint16 + count uint16 + minSize int + versionName string +} + +func validateNetflowPacket(data []byte) (*netflowPacketInfo, error) { + if len(data) < 4 { + return nil, fmt.Errorf("packet too small: %d bytes (minimum 4 bytes for version and count)", len(data)) + } + + version := binary.BigEndian.Uint16(data[0:2]) + count := binary.BigEndian.Uint16(data[2:4]) + + info := &netflowPacketInfo{ + version: version, + count: count, + } + + switch version { + case 1: + info.versionName = "NetFlow v1" + info.minSize = 24 + int(count)*48 + case 5: + info.versionName = "NetFlow v5" + info.minSize = 24 + int(count)*48 + case 6: + info.versionName = "NetFlow v6" + info.minSize = 24 + int(count)*52 + case 7: + info.versionName = "NetFlow v7" + info.minSize = 24 + int(count)*52 + case 9: + info.versionName = "NetFlow v9" + info.minSize = 20 + case 10: + info.versionName = "IPFIX" + info.minSize = 16 + ipfixLength := binary.BigEndian.Uint16(data[2:4]) + if int(ipfixLength) != len(data) { + return nil, fmt.Errorf("IPFIX length mismatch: header says %d bytes, received %d bytes", ipfixLength, len(data)) + } + return info, nil + default: + return nil, fmt.Errorf("unsupported NetFlow version: %d", version) + } + + if len(data) < info.minSize { + return nil, fmt.Errorf("%s packet too small: received %d bytes, minimum expected %d bytes (count=%d)", + info.versionName, len(data), info.minSize, count) + } + + return info, nil +} + diff --git a/agent/collector/netflow/parser.go b/agent/collector/netflow/parser.go new file mode 100644 index 000000000..2a88c50d8 --- /dev/null +++ b/agent/collector/netflow/parser.go @@ -0,0 +1,87 @@ +package netflow + +import ( + "fmt" + "reflect" + "strings" + + goflownetflow "github.com/netsampler/goflow2/decoders/netflow" + "github.com/netsampler/goflow2/decoders/netflowlegacy" + "github.com/tehmaze/netflow/netflow1" + "github.com/tehmaze/netflow/netflow6" + "github.com/tehmaze/netflow/netflow7" + "github.com/threatwinds/go-sdk/entities" + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" +) + +// processMessage converts a decoded netflow message to log entries and sends them to the queue. +func processMessage(remote string, message interface{}, queue chan *plugins.Log) error { + var metrics []Metric + + switch m := message.(type) { + // goflow2 types (primary for v5, v9, IPFIX) + case netflowlegacy.PacketNetFlowV5: + metrics = PrepareGoflowV5(remote, &m) + case *netflowlegacy.PacketNetFlowV5: + metrics = PrepareGoflowV5(remote, m) + case goflownetflow.NFv9Packet: + metrics = PrepareGoflowV9(remote, &m) + case *goflownetflow.NFv9Packet: + metrics = PrepareGoflowV9(remote, m) + case goflownetflow.IPFIXPacket: + metrics = PrepareGoflowIPFIX(remote, &m) + case *goflownetflow.IPFIXPacket: + metrics = PrepareGoflowIPFIX(remote, m) + + // tehmaze types (fallback for v1, v6, v7) + case *netflow1.Packet: + metrics = PrepareV1(remote, m) + case *netflow6.Packet: + metrics = PrepareV6(remote, m) + case *netflow7.Packet: + metrics = PrepareV7(remote, m) + + default: + return fmt.Errorf("unknown netflow message type: %T", m) + } + + messages := dumpMetrics(metrics) + + for _, msg := range messages { + message, _, err := entities.ValidateString(msg, false) + if err != nil { + utils.Logger.ErrorF("error validating string: %v: message: %s", err, message) + continue + } + queue <- &plugins.Log{ + DataType: string(config.DataTypeNetflow), + DataSource: remote, + Raw: msg, + } + } + + return nil +} + +// dumpMetrics converts metrics to key-value string format. +func dumpMetrics(metrics []Metric) []string { + var allKVPairs []string + for _, metric := range metrics { + t := reflect.TypeOf(metric) + v := reflect.ValueOf(metric) + var kvPairs []string + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + value := v.Field(i) + if value.String() == "" { + continue + } + header := field.Tag.Get("header") + kvPairs = append(kvPairs, fmt.Sprintf("%s=\"%v\"", header, value)) + } + allKVPairs = append(allKVPairs, strings.Join(kvPairs, " ")) + } + return allKVPairs +} diff --git a/agent/collector/netflow/proto.go b/agent/collector/netflow/proto.go new file mode 100644 index 000000000..322b731fe --- /dev/null +++ b/agent/collector/netflow/proto.go @@ -0,0 +1,158 @@ +package netflow + +import "fmt" + +var Proto = map[string]string{ + "0": "HOPOPT", // + "1": "ICMP", // 1 + "2": "IGMP", // 2 + "3": "GGP", // + "4": "IPv4", // 4 + "5": "ST", // + "6": "TCP", // 6 + "7": "CBT", // + "8": "EGP", // + "9": "IGP", // + "10": "BBN-RCC-MON", // + "11": "NVP-II", // + "12": "PUP", // + "13": "ARGUS", // + "14": "EMCON", // + "15": "XNET", // + "16": "CHAOS", // + "17": "UDP", // 17 + "18": "MUX", // + "19": "DCN-MEAS", // + "20": "HMP", // + "21": "PRM", // + "22": "XNS-IDP", // + "23": "TRUNK-1", // + "24": "TRUNK-2", // + "25": "LEAF-1", // + "26": "LEAF-2", // + "27": "RDP", // + "28": "IRTP", // + "29": "ISO-TP4", // + "30": "NETBLT", // + "31": "MFE-NSP", // + "32": "MERIT-INP", // + "33": "DCCP", // + "34": "3PC", // + "35": "IDPR", // + "36": "XTP", // + "37": "DDP", // + "38": "IDPR-CMTP", // + "39": "TP++", // + "40": "IL", // + "41": "IPv6", // + "42": "SDRP", // + "43": "IPv6-Route", // + "44": "IPv6-Frag", // + "45": "IDRP", // + "46": "RSVP", // + "47": "GRE", // + "48": "DSR", // + "49": "BNA", // + "50": "ESP", // + "51": "AH", // + "52": "I-NLSP", // + "53": "SWIPE", // + "54": "NARP", // + "55": "MOBILE", // + "56": "TLSP", // + "57": "SKIP", // + "58": "ICMP-V6", // 58 + "59": "IPv6-NoNxt", // + "60": "IPv6-Opts", // + //"61": "", // + "62": "CFTP", // + //"63": "", // + "64": "SAT-EXPAK", // + "65": "KRYPTOLAN", // + "66": "RVD", // + "67": "IPPC", // + //"68": "", // + "69": "SAT-MON", // + "70": "VISA", // + "71": "IPCV", // + "72": "CPNX", // + "73": "CPHB", // + "74": "WSN", // + "75": "PVP", // + "76": "BR-SAT-MON", // + "77": "SUN-ND", // + "78": "WB-MON", // + "79": "WB-EXPAK", // + "80": "ISO-IP", // + "81": "VMTP", // + "82": "SECURE-VMTP", // + "83": "VINES", // + //"84": "", // + "84": "TTP-IPTM", // + "85": "NSFNET-IGP", // + "86": "DGP", // + "87": "TCF", // + "88": "EIGRP", // + "89": "OSPFIGP", // + "90": "Sprite-RPC", // + "91": "LARP", // + "92": "MTP", // + "93": "AX.25", // + "94": "IPIP", // + "95": "MICP", // + "96": "SCC-SP", // + "97": "ETHERIP", // + "98": "ENCAP", // + //"99": "", // + "100": "GMTP", // + "101": "IFMP", // + "102": "PNNI", // + "103": "PIM", // + "104": "ARIS", // + "105": "SCPS", // + "106": "QNX", // + "107": "A/N", // + "108": "IPComp", // + "109": "SNP", // + "110": "Compaq-Peer", // + "111": "IPX-in-IP", // + "112": "VRRP", // + "113": "PGM", // + //"114": "", // + "115": "L2TP", // + "116": "DDX", // + "117": "IATP", // + "118": "STP", // + "119": "SRP", // + "120": "UTI", // + "121": "SMP", // + "122": "SM", // + "123": "PTP", // + "124": "ISIS over IPv4", // + "125": "FIRE", // + "126": "CRTP", // + "127": "CRUDP", // + "128": "SSCOPMCE", // + "129": "IPLT", // + "130": "SPS", // + "131": "PIPE", // + "132": "SCTP", // + "133": "FC", // + "134": "RSVP-E2E-IGNORE", // + "135": "Mobility-Header", // + "136": "UDPLite", // + "137": "MPLS-in-IP", // + "138": "MANET", // + "139": "HIP", // + "140": "Shim6", // + "141": "WESP", // + "142": "ROHC", // +} + +// ProtoToName - Get Protocol Id and return Protocol Name +func ProtoToName(p string) string { + if v, ok := Proto[p]; ok { + return v + } + return fmt.Sprintf("/%v", p) +} diff --git a/agent/collector/netflow/tehmaze.go b/agent/collector/netflow/tehmaze.go new file mode 100644 index 000000000..264332303 --- /dev/null +++ b/agent/collector/netflow/tehmaze.go @@ -0,0 +1,94 @@ +package netflow + +import ( + "fmt" + "net" + "time" + + "github.com/tehmaze/netflow/netflow1" + "github.com/tehmaze/netflow/netflow6" + "github.com/tehmaze/netflow/netflow7" +) + +// PrepareV1 converts tehmaze NetFlow v1 packet to metrics. +func PrepareV1(addr string, p *netflow1.Packet) []Metric { + nfExporter, _, _ := net.SplitHostPort(addr) + var metrics []Metric + + for _, r := range p.Records { + met := Metric{OutBytes: "0", InBytes: "0", OutPacket: "0", InPacket: "0", NFSender: nfExporter} + met.FlowVersion = "Netflow-V1" + met.First = time.Unix(int64(r.First), 0).Format(time.RFC3339Nano) + met.Last = time.Unix(int64(r.Last), 0).Format(time.RFC3339Nano) + met.Protocol = ProtoToName(fmt.Sprintf("%v", r.Protocol)) + met.Bytes = fmt.Sprintf("%v", r.Bytes) + met.Packets = fmt.Sprintf("%v", r.Packets) + met.TCPFlags = fmt.Sprintf("%v", r.Flags) + met.NextHop = fmt.Sprintf("%v", r.NextHop) + met.SrcIP = fmt.Sprintf("%v", r.SrcAddr) + met.DstIP = fmt.Sprintf("%v", r.DstAddr) + met.SrcPort = fmt.Sprintf("%v", r.SrcPort) + met.DstPort = fmt.Sprintf("%v", r.DstPort) + metrics = append(metrics, met) + } + + return metrics +} + +// PrepareV6 converts tehmaze NetFlow v6 packet to metrics. +func PrepareV6(addr string, p *netflow6.Packet) []Metric { + nfExporter, _, _ := net.SplitHostPort(addr) + var metrics []Metric + + for _, r := range p.Records { + met := Metric{OutBytes: "0", InBytes: "0", OutPacket: "0", InPacket: "0", NFSender: nfExporter} + met.FlowVersion = "Netflow-V6" + met.First = time.Unix(int64(r.First), 0).Format(time.RFC3339Nano) + met.Last = time.Unix(int64(r.Last), 0).Format(time.RFC3339Nano) + met.Protocol = ProtoToName(fmt.Sprintf("%v", r.Protocol)) + met.Bytes = fmt.Sprintf("%v", r.Bytes) + met.Packets = fmt.Sprintf("%v", r.Packets) + met.TCPFlags = fmt.Sprintf("%v", r.TCPFlags) + met.SrcAs = fmt.Sprintf("%v", r.SrcAS) + met.DstAs = fmt.Sprintf("%v", r.DstAS) + met.SrcMask = fmt.Sprintf("%v", r.SrcMask) + met.DstMask = fmt.Sprintf("%v", r.DstMask) + met.NextHop = fmt.Sprintf("%v", r.NextHop) + met.SrcIP = fmt.Sprintf("%v", r.SrcAddr) + met.DstIP = fmt.Sprintf("%v", r.DstAddr) + met.SrcPort = fmt.Sprintf("%v", r.SrcPort) + met.DstPort = fmt.Sprintf("%v", r.DstPort) + metrics = append(metrics, met) + } + + return metrics +} + +// PrepareV7 converts tehmaze NetFlow v7 packet to metrics. +func PrepareV7(addr string, p *netflow7.Packet) []Metric { + nfExporter, _, _ := net.SplitHostPort(addr) + var metrics []Metric + + for _, r := range p.Records { + met := Metric{OutBytes: "0", InBytes: "0", OutPacket: "0", InPacket: "0", NFSender: nfExporter} + met.FlowVersion = "Netflow-V7" + met.First = time.Unix(int64(r.First), 0).Format(time.RFC3339Nano) + met.Last = time.Unix(int64(r.Last), 0).Format(time.RFC3339Nano) + met.Protocol = ProtoToName(fmt.Sprintf("%v", r.Protocol)) + met.Bytes = fmt.Sprintf("%v", r.Bytes) + met.Packets = fmt.Sprintf("%v", r.Packets) + met.TCPFlags = fmt.Sprintf("%v", r.TCPFlags) + met.SrcAs = fmt.Sprintf("%v", r.SrcAS) + met.DstAs = fmt.Sprintf("%v", r.DstAS) + met.SrcMask = fmt.Sprintf("%v", r.SrcMask) + met.DstMask = fmt.Sprintf("%v", r.DstMask) + met.NextHop = fmt.Sprintf("%v", r.NextHop) + met.SrcIP = fmt.Sprintf("%v", r.SrcAddr) + met.DstIP = fmt.Sprintf("%v", r.DstAddr) + met.SrcPort = fmt.Sprintf("%v", r.SrcPort) + met.DstPort = fmt.Sprintf("%v", r.DstPort) + metrics = append(metrics, met) + } + + return metrics +} diff --git a/agent/collector/platform/darwin.go b/agent/collector/platform/darwin.go new file mode 100644 index 000000000..12ec935e8 --- /dev/null +++ b/agent/collector/platform/darwin.go @@ -0,0 +1,168 @@ +//go:build darwin +// +build darwin + +package platform + +import ( + "bufio" + "context" + "os" + "os/exec" + "path/filepath" + "time" + + "github.com/threatwinds/go-sdk/entities" + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/fs" +) + +const ( + maxRestartDelay = 5 * time.Minute + baseRestartDelay = 5 * time.Second +) + +type Darwin struct{} + +func GetCollectors() []Collector { + return []Collector{Darwin{}} +} + +func (d Darwin) Name() string { + return "darwin" +} + +func (d Darwin) Start(ctx context.Context, queue chan *plugins.Log) { + path := fs.GetExecutablePath() + collectorPath := filepath.Join(path, "utmstack-collector-mac") + + // Verify binary exists before attempting to run + if _, err := os.Stat(collectorPath); os.IsNotExist(err) { + utils.Logger.ErrorF("macOS collector binary not found at: %s", collectorPath) + return + } + + host, err := os.Hostname() + if err != nil { + utils.Logger.ErrorF("error getting hostname: %v", err) + host = "unknown" + } + + restartDelay := baseRestartDelay + + // Restart loop with exponential backoff + for { + select { + case <-ctx.Done(): + utils.Logger.Info("macOS collector stopping due to context cancellation") + return + default: + } + + exitCode := d.runCollector(collectorPath, host, queue) + + if exitCode == 0 { + utils.Logger.Info("macOS collector exited normally") + } else { + utils.Logger.ErrorF("macOS collector exited with code %d, restarting in %v", exitCode, restartDelay) + } + + time.Sleep(restartDelay) + + // Exponential backoff up to max delay + restartDelay *= 2 + if restartDelay > maxRestartDelay { + restartDelay = maxRestartDelay + } + } +} + +func (d Darwin) runCollector(collectorPath, host string, queue chan *plugins.Log) int { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in macOS collector: %v", r) + } + }() + + cmd := exec.Command(collectorPath) + + stdout, err := cmd.StdoutPipe() + if err != nil { + utils.Logger.ErrorF("error creating stdout pipe: %v", err) + return -1 + } + + stderr, err := cmd.StderrPipe() + if err != nil { + utils.Logger.ErrorF("error creating stderr pipe: %v", err) + return -1 + } + + if err := cmd.Start(); err != nil { + utils.Logger.ErrorF("error starting macOS collector: %v", err) + return -1 + } + + utils.Logger.Info("macOS collector started successfully") + + go func() { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in stdout reader: %v", r) + } + }() + + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + logLine := scanner.Text() + utils.Logger.LogF(100, "output: %s", logLine) + + validatedLog, _, err := entities.ValidateString(logLine, false) + if err != nil { + utils.Logger.ErrorF("error validating log: %s: %v", logLine, err) + continue + } + + queue <- &plugins.Log{ + DataType: string(config.DataTypeMacOs), + DataSource: host, + Raw: validatedLog, + } + } + + if err := scanner.Err(); err != nil { + utils.Logger.ErrorF("error reading stdout: %v", err) + } + }() + + go func() { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in stderr reader: %v", r) + } + }() + + scanner := bufio.NewScanner(stderr) + for scanner.Scan() { + errLine := scanner.Text() + utils.Logger.ErrorF("collector error: %s", errLine) + } + + if err := scanner.Err(); err != nil { + utils.Logger.ErrorF("error reading stderr: %v", err) + } + }() + + if err := cmd.Wait(); err != nil { + utils.Logger.ErrorF("macOS collector process ended with error: %v", err) + if exitErr, ok := err.(*exec.ExitError); ok { + return exitErr.ExitCode() + } + return -1 + } + + return 0 +} + +func (d Darwin) Stop() {} diff --git a/agent/collector/platform/filebeat_amd64.go b/agent/collector/platform/filebeat_amd64.go new file mode 100644 index 000000000..0b829ebe7 --- /dev/null +++ b/agent/collector/platform/filebeat_amd64.go @@ -0,0 +1,201 @@ +package platform + +import ( + "context" + "fmt" + "os" + "path/filepath" + "regexp" + "runtime" + + "github.com/threatwinds/go-sdk/entities" + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/exec" + "github.com/utmstack/UTMStack/shared/fs" +) + +// beatsRegexps maps data types to their identifying regex patterns. +var beatsRegexps = map[config.DataType]*regexp.Regexp{ + config.DataTypeApacheModule: regexp.MustCompile(`"type":"apache"|"module":"apache"`), + config.DataTypeLinuxAuditdModule: regexp.MustCompile(`"type":"auditd"|"module":"auditd"`), + config.DataTypeElasticsearchModule: regexp.MustCompile(`"type":"elasticsearch"|"module":"elasticsearch"`), + config.DataTypeKafkaModule: regexp.MustCompile(`"type":"kafka"|"module":"kafka"`), + config.DataTypeKibanaModule: regexp.MustCompile(`"type":"kibana"|"module":"kibana"`), + config.DataTypeLogstashModule: regexp.MustCompile(`"type":"logstash"|"module":"logstash"`), + config.DataTypeMongodbModule: regexp.MustCompile(`"type":"mongodb"|"module":"mongodb"`), + config.DataTypeMysqlModule: regexp.MustCompile(`"type":"mysql"|"module":"mysql"`), + config.DataTypeNginxModule: regexp.MustCompile(`"type":"nginx"|"module":"nginx"`), + config.DataTypeOsqueryModule: regexp.MustCompile(`"type":"osquery"|"module":"osquery"`), + config.DataTypePostgresqlModule: regexp.MustCompile(`"type":"postgresql"|"module":"postgresql"`), + config.DataTypeRedisModule: regexp.MustCompile(`"type":"redis"|"module":"redis"`), + config.DataTypeLinuxAgent: regexp.MustCompile(`"type":"system"|"module":"system"`), + config.DataTypeIisModule: regexp.MustCompile(`"type":"iis"|"module":"iis"`), + config.DataTypeTraefikModule: regexp.MustCompile(`"type":"traefik"|"module":"traefik"`), + config.DataTypeNatsModule: regexp.MustCompile(`"type":"nats"|"module":"nats"`), + config.DataTypeHaproxyModule: regexp.MustCompile(`"type":"haproxy"|"module":"haproxy"`), +} + +// identifyBeatsSource determines the data type of a log message. +func identifyBeatsSource(log string) config.DataType { + for dataType, re := range beatsRegexps { + if re.MatchString(log) { + return dataType + } + } + return config.DataTypeGeneric +} + +type Filebeat struct{} + +func (f Filebeat) Name() string { + return "filebeat" +} + +func (f Filebeat) Install() error { + path := fs.GetExecutablePath() + + filebLogPath := filepath.Join(path, "beats", "filebeat") + beatConfig := CollectorConfig{ + LogsPath: filepath.Join(filebLogPath, "logs"), + LogFileName: "modulescollector", + } + + if isInstalled, err := utils.CheckIfServiceIsInstalled(config.ModulesServName); err != nil { + return fmt.Errorf("error checking if %s service is installed: %v", config.ModulesServName, err) + } else if !isInstalled { + if err = fs.CreateDirIfNotExist(beatConfig.LogsPath); err != nil { + return fmt.Errorf("error creating %s folder", beatConfig.LogsPath) + } + + configFile := filepath.Join(filebLogPath, "filebeat.yml") + templateFile := filepath.Join(path, "templates", "filebeat.yml") + if err = utils.GenerateFromTemplate(beatConfig, templateFile, configFile); err != nil { + return fmt.Errorf("error configuration from %s: %v", templateFile, err) + } + switch runtime.GOOS { + case "windows": + err = exec.Run("sc", + filebLogPath, + "create", + config.ModulesServName, + "binPath=", + fmt.Sprintf("\"%s\\filebeat.exe\" --environment=windows_service -c \"%s\\filebeat.yml\" --path.home \"%s\" --path.data \"C:\\ProgramData\\filebeat\" --path.logs \"C:\\ProgramData\\filebeat\\logs\" -E logging.files.redirect_stderr=true", filebLogPath, filebLogPath, filebLogPath), + "DisplayName=", + config.ModulesServName, + "start=", + "auto") + if err != nil { + return fmt.Errorf("error installing %s service: %s", config.ModulesServName, err) + } + + err = exec.Run("sc", filebLogPath, "start", config.ModulesServName) + if err != nil { + return fmt.Errorf("error starting %s service: %s", config.ModulesServName, err) + } + case "linux": + if err = utils.CreateLinuxService(config.ModulesServName, fmt.Sprintf( + "%s -c %s -path.home %s -path.config %s -path.data /var/lib/filebeat -path.logs /var/log/filebeat", + filepath.Join(filebLogPath, "filebeat"), + filepath.Join(filebLogPath, "filebeat.yml"), + filebLogPath, + filebLogPath, + ), + ); err != nil { + return fmt.Errorf("error creating %s service: %v", config.ModulesServName, err) + } + + if err = exec.Run("chmod", filebLogPath, "-R", "755", "filebeat"); err != nil { + return fmt.Errorf("error executing chmod: %v", err) + } + + if err = exec.Run("systemctl", filebLogPath, "daemon-reload"); err != nil { + return fmt.Errorf("error reloading daemon: %v", err) + } + + err := exec.Run("systemctl", filebLogPath, "enable", config.ModulesServName) + if err != nil { + return fmt.Errorf("%s", err) + } + + err = exec.Run("systemctl", filebLogPath, "start", config.ModulesServName) + if err != nil { + return fmt.Errorf("%s", err) + } + + err = exec.Run("./filebeat", filebLogPath, "modules", "enable", "system") + if err != nil { + return fmt.Errorf("%s", err) + } + + err = exec.Run("sed", filepath.Join(filebLogPath, "modules.d"), "-i", "s/enabled: false/enabled: true/g", "system.yml") + if err != nil { + return fmt.Errorf("%s", err) + } + + err = exec.Run("systemctl", filebLogPath, "restart", config.ModulesServName) + if err != nil { + return fmt.Errorf("%s", err) + } + } + } + + return nil +} + +func (f Filebeat) Start(ctx context.Context, queue chan *plugins.Log) { + logLinesChan := make(chan string) + path := fs.GetExecutablePath() + filebLogPath := filepath.Join(path, "beats", "filebeat", "logs") + host, err := os.Hostname() + if err != nil { + utils.Logger.ErrorF("error getting hostname: %v", err) + host = "unknown" + } + + go utils.WatchFolder("modulescollector", filebLogPath, logLinesChan) + + for { + select { + case <-ctx.Done(): + utils.Logger.Info("filebeat collector stopping due to context cancellation") + return + case logLine, ok := <-logLinesChan: + if !ok { + return + } + message, _, err := entities.ValidateString(logLine, false) + if err != nil { + utils.Logger.ErrorF("error validating string: %v: message: %s", err, message) + continue + } + + dataType := identifyBeatsSource(logLine) + queue <- &plugins.Log{ + DataType: string(dataType), + DataSource: host, + Raw: logLine, + } + } + } +} + +func (f Filebeat) Uninstall() error { + if isInstalled, err := utils.CheckIfServiceIsInstalled(config.ModulesServName); err != nil { + return fmt.Errorf("error checking if %s is running: %v", config.ModulesServName, err) + } else if isInstalled { + err = utils.StopService(config.ModulesServName) + if err != nil { + return fmt.Errorf("error stopping %s: %v", config.ModulesServName, err) + } + + err = utils.UninstallService(config.ModulesServName) + if err != nil { + return fmt.Errorf("error uninstalling %s: %v", config.ModulesServName, err) + } + } + return nil +} + +func (f Filebeat) Stop() {} diff --git a/agent/collector/platform/linux_amd64.go b/agent/collector/platform/linux_amd64.go new file mode 100644 index 000000000..673319dce --- /dev/null +++ b/agent/collector/platform/linux_amd64.go @@ -0,0 +1,162 @@ +//go:build linux && amd64 +// +build linux,amd64 + +package platform + +import ( + "bufio" + "context" + "os" + "os/exec" + "sync" + "time" + + "github.com/threatwinds/go-sdk/entities" + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" +) + +const ( + journaldRestartDelay = 5 * time.Second + journaldMaxRestartDelay = 5 * time.Minute +) + +type LinuxSystem struct { + cmd *exec.Cmd + cancel context.CancelFunc + mu sync.Mutex +} + +func (l *LinuxSystem) Name() string { + return "linux-system" +} + +func (l *LinuxSystem) Start(ctx context.Context, queue chan *plugins.Log) { + host, err := os.Hostname() + if err != nil { + utils.Logger.ErrorF("error getting hostname: %v", err) + host = "unknown" + } + + restartDelay := journaldRestartDelay + + for { + select { + case <-ctx.Done(): + utils.Logger.Info("Linux system collector stopping due to context cancellation") + return + default: + } + + exitCode := l.runJournalctl(ctx, host, queue) + + if exitCode == 0 { + utils.Logger.Info("journalctl exited normally") + } else { + utils.Logger.ErrorF("journalctl exited with code %d, restarting in %v", exitCode, restartDelay) + } + + select { + case <-ctx.Done(): + return + case <-time.After(restartDelay): + } + + // Exponential backoff + restartDelay *= 2 + if restartDelay > journaldMaxRestartDelay { + restartDelay = journaldMaxRestartDelay + } + } +} + +func (l *LinuxSystem) runJournalctl(ctx context.Context, host string, queue chan *plugins.Log) int { + l.mu.Lock() + cmdCtx, cancel := context.WithCancel(ctx) + l.cancel = cancel + + // journalctl -f: follow, -o json: JSON output, --no-pager: don't use pager + l.cmd = exec.CommandContext(cmdCtx, "journalctl", "-f", "-o", "json", "--no-pager") + + stdout, err := l.cmd.StdoutPipe() + if err != nil { + l.mu.Unlock() + utils.Logger.ErrorF("error creating journalctl stdout pipe: %v", err) + return -1 + } + + stderr, err := l.cmd.StderrPipe() + if err != nil { + l.mu.Unlock() + utils.Logger.ErrorF("error creating journalctl stderr pipe: %v", err) + return -1 + } + + if err := l.cmd.Start(); err != nil { + l.mu.Unlock() + utils.Logger.ErrorF("error starting journalctl: %v", err) + return -1 + } + l.mu.Unlock() + + utils.Logger.Info("Linux system log collector started (journald)") + + // Read stderr in background + go func() { + scanner := bufio.NewScanner(stderr) + for scanner.Scan() { + utils.Logger.ErrorF("journalctl error: %s", scanner.Text()) + } + }() + + // Read stdout (log entries) + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + line := scanner.Text() + + validatedLog, _, err := entities.ValidateString(line, false) + if err != nil { + utils.Logger.ErrorF("error validating log: %v", err) + continue + } + + queue <- &plugins.Log{ + DataType: string(config.DataTypeLinuxAgent), + DataSource: host, + Raw: validatedLog, + } + } + + if err := scanner.Err(); err != nil { + utils.Logger.ErrorF("error reading journalctl output: %v", err) + } + + if err := l.cmd.Wait(); err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + return exitErr.ExitCode() + } + return -1 + } + + return 0 +} + +func (l *LinuxSystem) Stop() { + l.mu.Lock() + defer l.mu.Unlock() + + if l.cancel != nil { + l.cancel() + } + if l.cmd != nil && l.cmd.Process != nil { + l.cmd.Process.Kill() + } +} + +func GetCollectors() []Collector { + return []Collector{ + &LinuxSystem{}, + // Filebeat{}, // TODO: remove after testing native collector + } +} diff --git a/agent/collector/platform/platform.go b/agent/collector/platform/platform.go new file mode 100644 index 000000000..bb8a00383 --- /dev/null +++ b/agent/collector/platform/platform.go @@ -0,0 +1,20 @@ +package platform + +import ( + "context" + + "github.com/threatwinds/go-sdk/plugins" +) + +// CollectorConfig holds paths for log collection. +type CollectorConfig struct { + LogsPath string + LogFileName string +} + +// Collector is the interface that platform collectors must implement. +type Collector interface { + Name() string + Start(ctx context.Context, queue chan *plugins.Log) + Stop() +} diff --git a/agent/collector/platform/windows_amd64.go b/agent/collector/platform/windows_amd64.go new file mode 100644 index 000000000..5d3a9b61c --- /dev/null +++ b/agent/collector/platform/windows_amd64.go @@ -0,0 +1,415 @@ +//go:build windows && amd64 +// +build windows,amd64 + +package platform + +import ( + "context" + "encoding/json" + "encoding/xml" + "fmt" + "log" + "os" + "strconv" + "strings" + "sync" + "syscall" + "time" + "unsafe" + + "github.com/threatwinds/go-sdk/entities" + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" + "golang.org/x/sys/windows" +) + +type Event struct { + XMLName xml.Name `xml:"Event"` + System SystemData `xml:"System"` + EventData []*EventData `xml:"EventData>Data"` +} + +type EventData struct { + Key string `xml:"Name,attr"` + Value string `xml:",chardata"` +} + +type ProviderData struct { + ProviderName string `xml:"Name,attr"` + ProviderGUID string `xml:"Guid,attr"` +} + +type TimeCreatedData struct { + SystemTime string `xml:"SystemTime,attr"` +} + +type CorrelationData struct { + ActivityID string `xml:"ActivityID,attr"` +} + +type ExecutionData struct { + ProcessID int `xml:"ProcessID,attr"` + ThreadID int `xml:"ThreadID,attr"` +} + +type SecurityData struct{} + +type SystemData struct { + Provider ProviderData `xml:"Provider"` + EventID int `xml:"EventID"` + Version int `xml:"Version"` + Level int `xml:"Level"` + Task int `xml:"Task"` + Opcode int `xml:"Opcode"` + Keywords string `xml:"Keywords"` + TimeCreated TimeCreatedData `xml:"TimeCreated"` + EventRecordID int64 `xml:"EventRecordID"` + Correlation CorrelationData `xml:"Correlation"` + Execution ExecutionData `xml:"Execution"` + Channel string `xml:"Channel"` + Computer string `xml:"Computer"` + Security SecurityData `xml:"Security"` +} + +type EventSubscription struct { + Channel string + Query string + Errors chan error + winAPIHandle windows.Handle + + mu sync.Mutex + running bool +} + +const ( + EvtSubscribeToFutureEvents = 1 + evtSubscribeActionError = 0 + evtSubscribeActionDeliver = 1 + evtRenderEventXML = 1 +) + +var ( + modwevtapi = windows.NewLazySystemDLL("wevtapi.dll") + procEvtSubscribe = modwevtapi.NewProc("EvtSubscribe") + procEvtRender = modwevtapi.NewProc("EvtRender") + procEvtClose = modwevtapi.NewProc("EvtClose") + incomingEvents = make(chan string, 1024) +) + +func (evtSub *EventSubscription) Create() error { + evtSub.mu.Lock() + defer evtSub.mu.Unlock() + + if evtSub.winAPIHandle != 0 { + return fmt.Errorf("windows_events: subscription has already been created") + } + + winChannel, err := windows.UTF16PtrFromString(evtSub.Channel) + if err != nil { + return fmt.Errorf("windows_events: invalid channel name: %s", err) + } + + winQuery, err := windows.UTF16PtrFromString(evtSub.Query) + if err != nil { + return fmt.Errorf("windows_events: invalid query: %s", err) + } + + callback := syscall.NewCallback(evtSub.winAPICallback) + + log.Printf("Debug - Subscribing to channel: %s", evtSub.Channel) + + handle, _, err := procEvtSubscribe.Call( + 0, + 0, + uintptr(unsafe.Pointer(winChannel)), + uintptr(unsafe.Pointer(winQuery)), + 0, + 0, + callback, + uintptr(EvtSubscribeToFutureEvents), + ) + + if handle == 0 { + return fmt.Errorf("windows_events: failed to subscribe to events: %v", err) + } + + evtSub.winAPIHandle = windows.Handle(handle) + return nil +} + +func (evtSub *EventSubscription) Close() error { + evtSub.mu.Lock() + defer evtSub.mu.Unlock() + + if evtSub.winAPIHandle == 0 { + return fmt.Errorf("windows_events: no active subscription to close") + } + ret, _, err := procEvtClose.Call(uintptr(evtSub.winAPIHandle)) + if ret == 0 { + return fmt.Errorf("windows_events: error closing handle: %s", err) + } + evtSub.winAPIHandle = 0 + return nil +} + +func (evtSub *EventSubscription) winAPICallback(action, userContext, event uintptr) uintptr { + switch action { + case evtSubscribeActionError: + err := fmt.Errorf("windows_events: error in callback, code: %x", uint16(event)) + evtSub.Errors <- err + + go func(channel string) { + utils.Logger.LogF(100, "Attempting to resubscribe to channel: %s after error: %v", channel, err) + evtSub.mu.Lock() + defer evtSub.mu.Unlock() + + _ = evtSub.Close() + + for { + time.Sleep(5 * time.Second) + if err := evtSub.Create(); err != nil { + utils.Logger.ErrorF("Retry failed for channel %s: %s", channel, err) + } else { + utils.Logger.LogF(100, "Resubscribed to channel: %s", channel) + break + } + } + }(evtSub.Channel) + + case evtSubscribeActionDeliver: + utils.Logger.LogF(100, "Received event from channel: %s", evtSub.Channel) + xmlStr, err := quickRenderXML(event) + if err != nil { + evtSub.Errors <- fmt.Errorf("render in callback: %v", err) + break + } + select { + case incomingEvents <- xmlStr: + default: + utils.Logger.ErrorF("incomingEvents full: event discarded") + } + default: + evtSub.Errors <- fmt.Errorf("windows_events: unsupported action in callback: %x", uint16(action)) + } + return 0 +} + +func quickRenderXML(h uintptr) (string, error) { + bufSize := uint32(4096) + for { + space := make([]uint16, bufSize/2) + used := uint32(0) + prop := uint32(0) + + ret, _, err := procEvtRender.Call( + 0, h, evtRenderEventXML, + uintptr(bufSize), + uintptr(unsafe.Pointer(&space[0])), + uintptr(unsafe.Pointer(&used)), + uintptr(unsafe.Pointer(&prop)), + ) + if ret == 0 { + if err == windows.ERROR_INSUFFICIENT_BUFFER { + bufSize *= 2 + continue + } + return "", err + } + return cleanXML(windows.UTF16ToString(space)), nil + } +} + +func cleanXML(xmlStr string) string { + xmlStr = strings.TrimSpace(xmlStr) + if idx := strings.Index(xmlStr, " 0 { + xmlStr = xmlStr[idx:] + } + xmlStr = strings.Map(func(r rune) rune { + if r < 32 && r != '\n' && r != '\r' && r != '\t' { + return -1 + } + return r + }, xmlStr) + return xmlStr +} + +type Windows struct { + stopChan chan struct{} + subscriptions []*EventSubscription + mu sync.Mutex +} + +var windowsCollector = &Windows{ + stopChan: make(chan struct{}), +} + +func GetCollectors() []Collector { + return []Collector{ + windowsCollector, + // Filebeat{}, // TODO: remove after testing native collector + } +} + +func (w *Windows) Name() string { + return "windows-amd64" +} + +func (w *Windows) Start(ctx context.Context, queue chan *plugins.Log) { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in Windows AMD64 collector: %v", r) + } + }() + + errorsChan := make(chan error, 10) + go eventWorker(queue) + + channels := []string{ + "Security", "Application", "System", "Windows Powershell", "Microsoft-Windows-Powershell/Operational", "ForwardedEvents", + "Microsoft-Windows-WinLogon/Operational", "Microsoft-Windows-Windows Firewall With Advanced Security/Firewall", + "Microsoft-Windows-Windows Defender/Operational", + } + + w.mu.Lock() + w.subscriptions = nil + for _, channel := range channels { + sub := &EventSubscription{ + Channel: channel, + Query: "*", + Errors: errorsChan, + } + if err := sub.Create(); err != nil { + utils.Logger.ErrorF("Error subscribing to channel %s: %s", channel, err) + continue + } + w.subscriptions = append(w.subscriptions, sub) + utils.Logger.LogF(100, "Subscribed to channel: %s", channel) + } + w.mu.Unlock() + + go func() { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in error handler: %v", r) + } + }() + for err := range errorsChan { + utils.Logger.ErrorF("Subscription error: %s", err) + } + }() + + // Block until context is cancelled or Stop() is called + select { + case <-ctx.Done(): + case <-w.stopChan: + } + + utils.Logger.Info("Windows AMD64 collector stopping...") + w.mu.Lock() + for _, sub := range w.subscriptions { + if err := sub.Close(); err != nil { + utils.Logger.ErrorF("Error closing subscription for %s: %v", sub.Channel, err) + } + } + w.subscriptions = nil + w.mu.Unlock() + utils.Logger.Info("Windows AMD64 collector stopped.") +} + +func eventWorker(queue chan *plugins.Log) { + host, err := os.Hostname() + if err != nil { + utils.Logger.ErrorF("error getting hostname: %v", err) + host = "unknown" + } + + for xmlStr := range incomingEvents { + ev := new(Event) + if err := xml.Unmarshal([]byte(xmlStr), ev); err != nil { + utils.Logger.ErrorF("unmarshal error: %v", err) + continue + } + + eventJSON, err := convertEventToJSON(ev) + if err != nil { + utils.Logger.ErrorF("toJSON error: %v", err) + continue + } + + validatedLog, _, err := entities.ValidateString(eventJSON, false) + if err != nil { + utils.Logger.LogF(100, "validation error: %s: %v", eventJSON, err) + continue + } + + select { + case queue <- &plugins.Log{ + DataSource: host, + DataType: string(config.DataTypeWindowsAgent), + Raw: validatedLog, + }: + default: + utils.Logger.LogF(100, "LogQueue full: event discarded") + } + } +} + +func convertEventToJSON(event *Event) (string, error) { + eventMap := map[string]interface{}{ + "timestamp": event.System.TimeCreated.SystemTime, + "provider_name": event.System.Provider.ProviderName, + "provider_guid": event.System.Provider.ProviderGUID, + "eventCode": event.System.EventID, + "version": event.System.Version, + "level": event.System.Level, + "task": event.System.Task, + "opcode": event.System.Opcode, + "keywords": event.System.Keywords, + "timeCreated": event.System.TimeCreated.SystemTime, + "recordId": event.System.EventRecordID, + "correlation": event.System.Correlation, + "execution": event.System.Execution, + "channel": event.System.Channel, + "computer": event.System.Computer, + "data": make(map[string]interface{}), + } + + dataMap := eventMap["data"].(map[string]interface{}) + for _, data := range event.EventData { + if strings.HasPrefix(data.Value, "0x") { + if val, err := strconv.ParseInt(data.Value[2:], 16, 64); err == nil { + dataMap[data.Key] = val + continue + } + } + if data.Key != "" { + value := strings.TrimSpace(data.Value) + if value != "" { + dataMap[data.Key] = value + } + } + } + + jsonBytes, err := json.Marshal(eventMap) + if err != nil { + return "", err + } + return string(jsonBytes), nil +} + +func (w *Windows) Install() error { + return nil +} + +func (w *Windows) Uninstall() error { + return nil +} + +func (w *Windows) Stop() { + select { + case w.stopChan <- struct{}{}: + default: + // Already stopped or not started + } +} diff --git a/agent/collectors/windows_arm64.go b/agent/collector/platform/windows_arm64.go similarity index 86% rename from agent/collectors/windows_arm64.go rename to agent/collector/platform/windows_arm64.go index 5a844926e..8b31d70c3 100644 --- a/agent/collectors/windows_arm64.go +++ b/agent/collector/platform/windows_arm64.go @@ -1,15 +1,15 @@ //go:build windows && arm64 // +build windows,arm64 -package collectors +package platform import ( + "context" "encoding/json" "encoding/xml" "fmt" "log" "os" - "os/signal" "strconv" "strings" "sync" @@ -20,7 +20,6 @@ import ( "github.com/threatwinds/go-sdk/entities" "github.com/threatwinds/go-sdk/plugins" "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/logservice" "github.com/utmstack/UTMStack/agent/utils" "golang.org/x/sys/windows" ) @@ -235,17 +234,33 @@ func cleanXML(xmlStr string) string { return xmlStr } -type Windows struct{} +type Windows struct { + stopChan chan struct{} + subscriptions []*EventSubscription + mu sync.Mutex +} + +var windowsCollector = &Windows{ + stopChan: make(chan struct{}), +} + +func GetCollectors() []Collector { + return []Collector{windowsCollector} +} -func getCollectorsInstances() []Collector { - var collectors []Collector - collectors = append(collectors, Windows{}) - return collectors +func (w *Windows) Name() string { + return "windows-arm64" } -func (w Windows) SendLogs() { +func (w *Windows) Start(ctx context.Context, queue chan *plugins.Log) { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in Windows ARM64 collector: %v", r) + } + }() + errorsChan := make(chan error, 10) - go eventWorker() + go eventWorker(queue) channels := []string{ "Security", "Application", "System", "Windows Powershell", "Microsoft-Windows-Powershell/Operational", "ForwardedEvents", @@ -253,8 +268,8 @@ func (w Windows) SendLogs() { "Microsoft-Windows-Windows Defender/Operational", } - var subscriptions []*EventSubscription - + w.mu.Lock() + w.subscriptions = nil for _, channel := range channels { sub := &EventSubscription{ Channel: channel, @@ -265,29 +280,41 @@ func (w Windows) SendLogs() { utils.Logger.ErrorF("Error subscribing to channel %s: %s", channel, err) continue } - subscriptions = append(subscriptions, sub) + w.subscriptions = append(w.subscriptions, sub) utils.Logger.LogF(100, "Subscribed to channel: %s", channel) } + w.mu.Unlock() go func() { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in error handler: %v", r) + } + }() for err := range errorsChan { utils.Logger.ErrorF("Subscription error: %s", err) } }() - exitChan := make(chan os.Signal, 1) - signal.Notify(exitChan, os.Interrupt) - <-exitChan - utils.Logger.LogF(100, "Interrupt received, closing subscriptions...") - for _, sub := range subscriptions { + // Block until context is cancelled or Stop() is called + select { + case <-ctx.Done(): + case <-w.stopChan: + } + + utils.Logger.Info("Windows ARM64 collector stopping...") + w.mu.Lock() + for _, sub := range w.subscriptions { if err := sub.Close(); err != nil { utils.Logger.ErrorF("Error closing subscription for %s: %v", sub.Channel, err) } } - utils.Logger.LogF(100, "Agent finished successfully.") + w.subscriptions = nil + w.mu.Unlock() + utils.Logger.Info("Windows ARM64 collector stopped.") } -func eventWorker() { +func eventWorker(queue chan *plugins.Log) { host, err := os.Hostname() if err != nil { utils.Logger.ErrorF("error getting hostname: %v", err) @@ -314,7 +341,7 @@ func eventWorker() { } select { - case logservice.LogQueue <- &plugins.Log{ + case queue <- &plugins.Log{ DataSource: host, DataType: string(config.DataTypeWindowsAgent), Raw: validatedLog, @@ -368,10 +395,18 @@ func convertEventToJSON(event *Event) (string, error) { return string(jsonBytes), nil } -func (w Windows) Install() error { +func (w *Windows) Install() error { return nil } -func (w Windows) Uninstall() error { +func (w *Windows) Uninstall() error { return nil } + +func (w *Windows) Stop() { + select { + case w.stopChan <- struct{}{}: + default: + // Already stopped or not started + } +} diff --git a/agent/collector/schema/schema.go b/agent/collector/schema/schema.go new file mode 100644 index 000000000..8a9d87db3 --- /dev/null +++ b/agent/collector/schema/schema.go @@ -0,0 +1,67 @@ +// Package schema defines shared configuration types for collector packages. +// This package exists to avoid circular imports between collector, syslog, and netflow. +package schema + +import ( + "encoding/json" + "os" + "strconv" + + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/shared/fs" +) + +// Port represents a TCP or UDP port configuration. +type Port struct { + IsListen bool `json:"enabled"` + Port string `json:"value"` + TLSEnabled bool `json:"tls_enabled,omitempty"` +} + +// Integration represents a collector integration configuration (syslog/netflow). +type Integration struct { + TCP Port `json:"tcp_port,omitempty"` + UDP Port `json:"udp_port,omitempty"` +} + +// FileIntegration represents a file-based log collector configuration. +type FileIntegration struct { + Enabled bool `json:"enabled"` + Paths []string `json:"paths"` +} + +// CollectorConfig represents the full collector configuration file. +type CollectorConfig struct { + Integrations map[string]Integration `json:"integrations"` + FileIntegrations map[string]FileIntegration `json:"file_integrations,omitempty"` +} + +// ReadCollectorConfig reads the collector configuration from disk. +func ReadCollectorConfig() (CollectorConfig, error) { + cnf := CollectorConfig{} + err := fs.ReadJSON(config.CollectorFileName, &cnf) + if err != nil { + return cnf, err + } + return cnf, nil +} + +// WriteCollectorConfig writes the collector configuration to disk. +func WriteCollectorConfig(cnf *CollectorConfig) error { + data, err := json.MarshalIndent(cnf, "", " ") + if err != nil { + return err + } + return os.WriteFile(config.CollectorFileName, data, 0644) +} + +// ValidatePortChange returns true if the port is within the valid range. +func ValidatePortChange(newPort string) bool { + port, err := strconv.Atoi(newPort) + if err != nil { + return false + } + min, _ := strconv.Atoi(config.PortRangeMin) + max, _ := strconv.Atoi(config.PortRangeMax) + return port >= min && port <= max +} diff --git a/agent/collector/syslog/framing.go b/agent/collector/syslog/framing.go new file mode 100644 index 000000000..a10bdfd91 --- /dev/null +++ b/agent/collector/syslog/framing.go @@ -0,0 +1,109 @@ +package syslog + +import ( + "bufio" + "fmt" + "io" + "strconv" + "strings" + + "github.com/utmstack/UTMStack/agent/utils" +) + +// Buffer size constants for syslog message processing. +const ( + MinBufferSize = 480 + RecommendedBufferSize = 2048 + MaxBufferSize = 8192 + UDPBufferSize = 2048 +) + +// FramingMethod represents the syslog message framing method. +type FramingMethod int + +const ( + FramingNewline FramingMethod = iota + FramingOctetCounting +) + +// detectFramingMethod determines whether the message uses octet counting or newline framing. +func detectFramingMethod(reader *bufio.Reader) (FramingMethod, error) { + firstByte, err := reader.Peek(1) + if err != nil { + utils.Logger.ErrorF("failed to peek first byte for framing detection: %v", err) + return 0, fmt.Errorf("failed to peek first byte: %w", err) + } + + if firstByte[0] >= '0' && firstByte[0] <= '9' { + return FramingOctetCounting, nil + } + + if firstByte[0] == '<' { + return FramingNewline, nil + } + + utils.Logger.ErrorF("unknown framing method detected, first byte: 0x%02x", firstByte[0]) + return 0, fmt.Errorf("unknown framing method, first byte: 0x%02x", firstByte[0]) +} + +// readOctetCountingFrame reads a syslog message using octet counting framing (RFC 5425). +func readOctetCountingFrame(reader *bufio.Reader) (string, error) { + lengthStr, err := reader.ReadString(' ') + if err != nil { + utils.Logger.ErrorF("failed to read message length in octet counting frame: %v", err) + return "", fmt.Errorf("failed to read message length: %w", err) + } + + lengthStr = strings.TrimSuffix(lengthStr, " ") + msgLen, err := strconv.Atoi(lengthStr) + if err != nil { + utils.Logger.ErrorF("invalid message length '%s' in octet counting frame: %v", lengthStr, err) + return "", fmt.Errorf("invalid message length '%s': %w", lengthStr, err) + } + + if msgLen < 1 { + utils.Logger.ErrorF("message length %d is too small (minimum 1 byte)", msgLen) + return "", fmt.Errorf("message length %d is too small (minimum 1)", msgLen) + } + if msgLen > MaxBufferSize { + utils.Logger.ErrorF("message length %d exceeds maximum %d bytes", msgLen, MaxBufferSize) + return "", fmt.Errorf("message length %d exceeds maximum %d", msgLen, MaxBufferSize) + } + + msgBytes := make([]byte, msgLen) + _, err = io.ReadFull(reader, msgBytes) + if err != nil { + utils.Logger.ErrorF("failed to read %d byte message body: %v", msgLen, err) + return "", fmt.Errorf("failed to read %d byte message body: %w", msgLen, err) + } + + return string(msgBytes), nil +} + +// readNewlineFrame reads a syslog message using newline-delimited framing. +func readNewlineFrame(reader *bufio.Reader) (string, error) { + message, err := reader.ReadString('\n') + if err != nil { + utils.Logger.ErrorF("failed to read newline-delimited message: %v", err) + return "", fmt.Errorf("failed to read newline-delimited message: %w", err) + } + return message, nil +} + +// readSyslogMessage reads a complete syslog message, auto-detecting the framing method. +func readSyslogMessage(reader *bufio.Reader) (string, error) { + method, err := detectFramingMethod(reader) + if err != nil { + return "", err + } + + switch method { + case FramingOctetCounting: + return readOctetCountingFrame(reader) + case FramingNewline: + return readNewlineFrame(reader) + default: + utils.Logger.ErrorF("unsupported framing method: %d", method) + return "", fmt.Errorf("unsupported framing method: %d", method) + } +} diff --git a/agent/collector/syslog/handler.go b/agent/collector/syslog/handler.go new file mode 100644 index 000000000..69fcea32e --- /dev/null +++ b/agent/collector/syslog/handler.go @@ -0,0 +1,157 @@ +package syslog + +import ( + "bufio" + "context" + "crypto/tls" + "io" + "net" + "os" + "strings" + "time" + + "github.com/threatwinds/go-sdk/entities" + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/models" + "github.com/utmstack/UTMStack/agent/utils" +) + +// resolveRemoteAddr extracts the IP from addr and replaces localhost with hostname. +func resolveRemoteAddr(addr string) string { + host, _, err := net.SplitHostPort(addr) + if err != nil { + utils.Logger.ErrorF("error splitting host and port: %v", err) + return "unknown" + } + if host == "127.0.0.1" { + if hostname, err := os.Hostname(); err == nil { + return hostname + } + } + return host +} + +// readLoop reads syslog messages from reader and sends them to msgChannel. +// If conn is provided, it sets a deadline before each read (used for TLS). +func (inst *syslogInstance) readLoop(ctx context.Context, reader *bufio.Reader, remoteAddr string, msgChannel chan models.MSGDS, conn net.Conn) { + connType := "TCP" + if conn != nil { + connType = "TLS" + } + + for { + select { + case <-ctx.Done(): + return + default: + if conn != nil { + conn.SetDeadline(time.Now().Add(30 * time.Second)) + } + message, err := readSyslogMessage(reader) + if err != nil { + if err == io.EOF { + utils.Logger.Info("%s connection closed by %s", connType, remoteAddr) + return + } + if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + utils.Logger.Info("%s connection timeout from %s", connType, remoteAddr) + return + } + utils.Logger.ErrorF("error reading %s data from %s: %v", connType, remoteAddr, err) + return + } + msgChannel <- models.MSGDS{ + DataSource: remoteAddr, + Message: message, + } + } + } +} + +func (inst *syslogInstance) handleConnectionTCP(c net.Conn, queue chan *plugins.Log) { + defer c.Close() + reader := bufio.NewReader(c) + remoteAddr := resolveRemoteAddr(c.RemoteAddr().String()) + + // Detect and reject TLS connections when TLS is disabled + c.SetReadDeadline(time.Now().Add(5 * time.Second)) + firstBytes := make([]byte, 3) + n, err := reader.Read(firstBytes) + if err != nil { + utils.Logger.ErrorF("error reading initial bytes from %s: %v", remoteAddr, err) + return + } + + // TLS handshake starts with: 0x16 (22 decimal) for TLS 1.0-1.3 + if n >= 1 && firstBytes[0] == 0x16 { + utils.Logger.ErrorF("TLS connection rejected from %s: TLS is disabled, only plain text connections accepted", remoteAddr) + return + } + + // Reset deadline and create a new reader that includes the read bytes + c.SetReadDeadline(time.Time{}) + reader = bufio.NewReader(io.MultiReader(strings.NewReader(string(firstBytes[:n])), reader)) + + msgChannel := make(chan models.MSGDS) + defer close(msgChannel) + go inst.handleMessage(inst.TCPListener.CTX, msgChannel, queue) + + inst.readLoop(inst.TCPListener.CTX, reader, remoteAddr, msgChannel, nil) +} + +func (inst *syslogInstance) handleTLSConnection(conn net.Conn, queue chan *plugins.Log) { + defer conn.Close() + + remoteAddr := resolveRemoteAddr(conn.RemoteAddr().String()) + + tlsConfig, err := utils.LoadIntegrationTLSConfig( + config.IntegrationCertPath, + config.IntegrationKeyPath, + ) + if err != nil { + utils.Logger.ErrorF("error loading TLS config: %v", err) + return + } + + tlsConn := tls.Server(conn, tlsConfig) + + conn.SetDeadline(time.Now().Add(10 * time.Second)) + if err := tlsConn.Handshake(); err != nil { + utils.Logger.ErrorF("TLS handshake failed from %s: %v", remoteAddr, err) + return + } + + reader := bufio.NewReader(tlsConn) + msgChannel := make(chan models.MSGDS) + defer close(msgChannel) + go inst.handleMessage(inst.TCPListener.CTX, msgChannel, queue) + + inst.readLoop(inst.TCPListener.CTX, reader, remoteAddr, msgChannel, conn) +} + +// handleMessage processes messages from the channel and sends them to the queue. +func (inst *syslogInstance) handleMessage(ctx context.Context, logsChannel chan models.MSGDS, queue chan *plugins.Log) { + for { + select { + case <-ctx.Done(): + return + case msgDS, ok := <-logsChannel: + if !ok { + return // channel closed + } + message := strings.TrimSuffix(msgDS.Message, "\n") + message, _, err := entities.ValidateString(message, false) + if err != nil { + utils.Logger.ErrorF("error validating string: %v: message: %s", err, message) + continue + } + + queue <- &plugins.Log{ + DataType: inst.DataType, + DataSource: msgDS.DataSource, + Raw: message, + } + } + } +} diff --git a/agent/collector/syslog/listener.go b/agent/collector/syslog/listener.go new file mode 100644 index 000000000..84cb929d6 --- /dev/null +++ b/agent/collector/syslog/listener.go @@ -0,0 +1,301 @@ +package syslog + +import ( + "context" + "errors" + "fmt" + "net" + "sync" + "time" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/models" + "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/fs" +) + +// listenerState holds common state for TCP and UDP listeners. +type listenerState struct { + CTX context.Context + Cancel context.CancelFunc + IsEnabled bool + Port string +} + +// disable cancels the context and marks the listener as disabled. +// The caller must close the actual listener and hold the appropriate lock. +func (ls *listenerState) disable() { + if ls.Cancel != nil { + ls.Cancel() + } + ls.IsEnabled = false +} + +type listenerTCP struct { + listenerState + Listener net.Listener + TLSEnabled bool +} + +type listenerUDP struct { + listenerState + Listener net.PacketConn +} + +type syslogInstance struct { + DataType string + TCPListener listenerTCP + UDPListener listenerUDP + mu sync.RWMutex +} + +// --- Port query methods --- + +func (inst *syslogInstance) isPortListen(proto string) bool { + inst.mu.RLock() + defer inst.mu.RUnlock() + switch proto { + case "tcp": + return inst.TCPListener.IsEnabled + case "udp": + return inst.UDPListener.IsEnabled + default: + return false + } +} + +func (inst *syslogInstance) getPort(proto string) string { + inst.mu.RLock() + defer inst.mu.RUnlock() + switch proto { + case "tcp": + return inst.TCPListener.Port + case "udp": + return inst.UDPListener.Port + default: + return "" + } +} + +func (inst *syslogInstance) setNewPort(proto string, port string) { + inst.mu.Lock() + defer inst.mu.Unlock() + switch proto { + case "tcp": + inst.TCPListener.Port = port + case "udp": + inst.UDPListener.Port = port + } +} + +// --- Port enable/disable --- + +func (inst *syslogInstance) enablePort(proto string, enableTLS bool, queue chan *plugins.Log) error { + switch proto { + case "tcp": + if enableTLS { + if !fs.Exists(config.IntegrationCertPath) || !fs.Exists(config.IntegrationKeyPath) { + return fmt.Errorf("TLS certificates not found. Please load certificates first") + } + } + inst.TCPListener.TLSEnabled = enableTLS + go inst.enableTCP(queue) + return nil + case "udp": + if enableTLS { + return fmt.Errorf("TLS not supported for UDP protocol") + } + go inst.enableUDP(queue) + return nil + default: + return fmt.Errorf("unsupported protocol: %s", proto) + } +} + +func (inst *syslogInstance) disablePort(proto string) { + switch proto { + case "tcp": + inst.disableTCP() + case "udp": + inst.disableUDP() + } +} + +func (inst *syslogInstance) enableTCP(queue chan *plugins.Log) { + inst.mu.Lock() + if inst.TCPListener.IsEnabled || inst.TCPListener.Port == "" { + inst.mu.Unlock() + return + } + + listener, err := net.Listen("tcp", "0.0.0.0:"+inst.TCPListener.Port) + if err != nil { + inst.mu.Unlock() + utils.Logger.ErrorF("error listening TCP in port %s: %v", inst.TCPListener.Port, err) + return + } + + inst.TCPListener.IsEnabled = true + inst.TCPListener.Listener = listener + inst.TCPListener.CTX, inst.TCPListener.Cancel = context.WithCancel(context.Background()) + inst.mu.Unlock() + + utils.Logger.Info("Server %s listening in port: %s protocol: TCP", inst.DataType, inst.TCPListener.Port) + if inst.TCPListener.TLSEnabled { + utils.Logger.Info("Server %s TLS enabled in port: %s protocol: TCP", inst.DataType, inst.TCPListener.Port) + } + + go func() { + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in TCP listener for %s: %v", inst.DataType, r) + } + err = inst.TCPListener.Listener.Close() + if err != nil { + utils.Logger.ErrorF("error closing tcp listener: %v", err) + } + }() + for { + select { + case <-inst.TCPListener.CTX.Done(): + return + default: + conn, err := inst.TCPListener.Listener.Accept() + if err != nil { + if errors.Is(err, net.ErrClosed) { + return + } + + var netOpErr *net.OpError + ok := errors.As(err, &netOpErr) + if ok && netOpErr.Timeout() { + continue + } + + utils.Logger.ErrorF("error connecting with tcp listener: %v", err) + continue + } + + if inst.TCPListener.TLSEnabled { + go inst.handleTLSConnection(conn, queue) + } else { + go inst.handleConnectionTCP(conn, queue) + } + } + } + }() +} + +func (inst *syslogInstance) enableUDP(queue chan *plugins.Log) { + inst.mu.Lock() + if inst.UDPListener.IsEnabled || inst.UDPListener.Port == "" { + inst.mu.Unlock() + return + } + + listener, err := net.ListenPacket("udp", "0.0.0.0:"+inst.UDPListener.Port) + if err != nil { + inst.mu.Unlock() + utils.Logger.ErrorF("error listening UDP in port %s: %v", inst.UDPListener.Port, err) + return + } + + udpListener, ok := listener.(*net.UDPConn) + if !ok { + inst.mu.Unlock() + utils.Logger.ErrorF("could not assert to *net.UDPConn") + listener.Close() + return + } + + inst.UDPListener.IsEnabled = true + inst.UDPListener.Listener = listener + inst.UDPListener.CTX, inst.UDPListener.Cancel = context.WithCancel(context.Background()) + inst.mu.Unlock() + + utils.Logger.Info("Server %s listening in port: %s protocol: UDP", inst.DataType, inst.UDPListener.Port) + + buffer := make([]byte, UDPBufferSize) + msgChannel := make(chan models.MSGDS) + + go inst.handleMessage(inst.UDPListener.CTX, msgChannel, queue) + + go func() { + defer close(msgChannel) + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in UDP listener for %s: %v", inst.DataType, r) + } + if err := inst.UDPListener.Listener.Close(); err != nil { + utils.Logger.ErrorF("error closing udp listener: %v", err) + } + }() + for { + select { + case <-inst.UDPListener.CTX.Done(): + return + default: + udpListener.SetDeadline(time.Now().Add(time.Second * 1)) + + n, addr, err := listener.ReadFrom(buffer) + if err != nil { + if errors.Is(err, net.ErrClosed) { + return + } + + var netOpErr *net.OpError + ok := errors.As(err, &netOpErr) + if ok && netOpErr.Timeout() { + continue + } + + utils.Logger.ErrorF("error connecting with udp listener: %v", err) + continue + } + + remoteAddr := resolveRemoteAddr(addr.String()) + msgChannel <- models.MSGDS{ + DataSource: remoteAddr, + Message: string(buffer[:n]), + } + } + } + }() +} + +func (inst *syslogInstance) disableTCP() { + inst.mu.Lock() + defer inst.mu.Unlock() + + if !inst.TCPListener.IsEnabled { + return + } + + utils.Logger.Info("Server %s closed in port: %s protocol: TCP", inst.DataType, inst.TCPListener.Port) + + if inst.TCPListener.Listener != nil { + if err := inst.TCPListener.Listener.Close(); err != nil { + utils.Logger.ErrorF("error closing TCP listener: %v", err) + } + } + inst.TCPListener.disable() +} + +func (inst *syslogInstance) disableUDP() { + inst.mu.Lock() + defer inst.mu.Unlock() + + if !inst.UDPListener.IsEnabled { + return + } + + utils.Logger.Info("Server %s closed in port: %s protocol: UDP", inst.DataType, inst.UDPListener.Port) + + if inst.UDPListener.Listener != nil { + if err := inst.UDPListener.Listener.Close(); err != nil { + utils.Logger.ErrorF("error closing UDP listener: %v", err) + } + } + inst.UDPListener.disable() +} diff --git a/agent/collector/syslog/syslog.go b/agent/collector/syslog/syslog.go new file mode 100644 index 000000000..ba318febc --- /dev/null +++ b/agent/collector/syslog/syslog.go @@ -0,0 +1,194 @@ +package syslog + +import ( + "context" + "sync" + "time" + + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/collector/schema" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" +) + +const reconcileInterval = 10 * time.Second + +// SyslogCollector manages all syslog instances. It reads the config file +// periodically and reconciles port state internally. +type SyslogCollector struct { + instances map[string]*syslogInstance + mu sync.RWMutex + queue chan *plugins.Log +} + +// New creates a new SyslogCollector. +func New() *SyslogCollector { + return &SyslogCollector{ + instances: make(map[string]*syslogInstance), + } +} + +func (sc *SyslogCollector) Name() string { + return "syslog" +} + +func (sc *SyslogCollector) Stop() { + sc.mu.RLock() + defer sc.mu.RUnlock() + for _, inst := range sc.instances { + inst.disableTCP() + inst.disableUDP() + } +} + +// Start begins the reconciliation loop. It reads the collector config every +// 10 seconds and adjusts listening ports as needed. This is the only entry +// point for syslog port management — no external code touches ports. +func (sc *SyslogCollector) Start(ctx context.Context, queue chan *plugins.Log) { + sc.queue = queue + for { + select { + case <-ctx.Done(): + utils.Logger.Info("syslog collector stopping due to context cancellation") + return + default: + sc.reconcile() + time.Sleep(reconcileInterval) + } + } +} + +func (sc *SyslogCollector) reconcile() { + cnf, err := schema.ReadCollectorConfig() + if err != nil { + utils.Logger.ErrorF("error reading collector config: %v", err) + return + } + + for intType, integration := range cnf.Integrations { + // Only handle syslog-type integrations + if config.ValidateModuleType(intType) != "syslog" { + continue + } + + inst := sc.getOrCreateInstance(intType) + + sc.reconcileProto(inst, intType, "tcp", integration) + sc.reconcileProto(inst, intType, "udp", integration) + } +} + +func (sc *SyslogCollector) getOrCreateInstance(dataType string) *syslogInstance { + sc.mu.Lock() + defer sc.mu.Unlock() + + if inst, ok := sc.instances[dataType]; ok { + return inst + } + + inst := &syslogInstance{ + DataType: dataType, + TCPListener: listenerTCP{ + listenerState: listenerState{ + IsEnabled: false, + Port: config.ProtoPorts[config.DataType(dataType)].TCP, + }, + }, + UDPListener: listenerUDP{ + listenerState: listenerState{ + IsEnabled: false, + Port: config.ProtoPorts[config.DataType(dataType)].UDP, + }, + }, + } + sc.instances[dataType] = inst + return inst +} + +func (sc *SyslogCollector) reconcileProto(inst *syslogInstance, intType string, proto string, integration schema.Integration) { + var cfgEnabled bool + var cfgPort string + var cfgTLS bool + + switch proto { + case "tcp": + cfgEnabled = integration.TCP.IsListen + cfgPort = integration.TCP.Port + cfgTLS = integration.TCP.TLSEnabled + case "udp": + cfgEnabled = integration.UDP.IsListen + cfgPort = integration.UDP.Port + } + + isListening := inst.isPortListen(proto) + currentPort := inst.getPort(proto) + + needKill := false + needStart := false + + if isListening && !cfgEnabled { + needKill = true + } else if !isListening && cfgEnabled { + needStart = true + } else if isListening && cfgEnabled && currentPort != cfgPort { + needKill = true + needStart = true + } + + changeAllowed := true + if cfgPort != "" && currentPort != cfgPort { + changeAllowed = schema.ValidatePortChange(cfgPort) + } + + if needKill { + inst.disablePort(proto) + if needStart { + time.Sleep(200 * time.Millisecond) + } + } + + if changeAllowed { + inst.setNewPort(proto, cfgPort) + if needStart { + enableTLS := proto == "tcp" && cfgTLS + err := inst.enablePort(proto, enableTLS, sc.queue) + if err != nil { + utils.Logger.ErrorF("error enabling port for %s %s: %v", intType, proto, err) + } + } + } else { + utils.Logger.Info("port %s is out of valid range %s-%s", cfgPort, config.PortRangeMin, config.PortRangeMax) + sc.writeConfigFromInstances() + } +} + +// writeConfigFromInstances writes the current live state back to the config file (rollback). +func (sc *SyslogCollector) writeConfigFromInstances() { + sc.mu.RLock() + defer sc.mu.RUnlock() + + // Read current config to preserve FileIntegrations + cnf, err := schema.ReadCollectorConfig() + if err != nil { + cnf = schema.CollectorConfig{ + Integrations: make(map[string]schema.Integration), + } + } + + // Update only the syslog integrations + for _, inst := range sc.instances { + cnf.Integrations[inst.DataType] = schema.Integration{ + TCP: schema.Port{ + IsListen: inst.isPortListen("tcp"), + Port: inst.getPort("tcp"), + }, + UDP: schema.Port{ + IsListen: inst.isPortListen("udp"), + Port: inst.getPort("udp"), + }, + } + } + if err := schema.WriteCollectorConfig(&cnf); err != nil { + utils.Logger.ErrorF("error fixing collector config: %v", err) + } +} diff --git a/agent/collectors/collectors.go b/agent/collectors/collectors.go deleted file mode 100644 index 753a655ff..000000000 --- a/agent/collectors/collectors.go +++ /dev/null @@ -1,54 +0,0 @@ -package collectors - -import ( - "fmt" - - "github.com/utmstack/UTMStack/agent/utils" -) - -type CollectorConfig struct { - LogsPath string - LogFileName string -} - -type Collector interface { - Install() error - SendLogs() - Uninstall() error -} - -func InstallCollectors() error { - collectors := getCollectorsInstances() - - for _, collector := range collectors { - err := collector.Install() - if err != nil { - return fmt.Errorf("%v", err) - } - } - - utils.Logger.LogF(100, "collectors installed correctly") - - return nil -} - -func LogsReader() { - collectors := getCollectorsInstances() - for _, collector := range collectors { - go collector.SendLogs() - } -} - -func UninstallCollectors() error { - collectors := getCollectorsInstances() - - for _, collector := range collectors { - err := collector.Uninstall() - if err != nil { - return fmt.Errorf("%v", err) - } - } - - utils.Logger.LogF(100, "collectors uninstalled correctly") - return nil -} diff --git a/agent/collectors/filebeat_amd64.go b/agent/collectors/filebeat_amd64.go deleted file mode 100644 index 50f40065c..000000000 --- a/agent/collectors/filebeat_amd64.go +++ /dev/null @@ -1,152 +0,0 @@ -package collectors - -import ( - "fmt" - "os" - "path/filepath" - "runtime" - - "github.com/threatwinds/go-sdk/entities" - "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/logservice" - "github.com/utmstack/UTMStack/agent/parser" - "github.com/utmstack/UTMStack/agent/utils" -) - -type Filebeat struct{} - -func (f Filebeat) Install() error { - path := utils.GetMyPath() - - filebLogPath := filepath.Join(path, "beats", "filebeat") - beatConfig := CollectorConfig{ - LogsPath: filepath.Join(filebLogPath, "logs"), - LogFileName: "modulescollector", - } - - if isInstalled, err := utils.CheckIfServiceIsInstalled(config.ModulesServName); err != nil { - return fmt.Errorf("error checking if %s service is installed: %v", config.ModulesServName, err) - } else if !isInstalled { - if err = utils.CreatePathIfNotExist(beatConfig.LogsPath); err != nil { - return fmt.Errorf("error creating %s folder", beatConfig.LogsPath) - } - - configFile := filepath.Join(filebLogPath, "filebeat.yml") - templateFile := filepath.Join(path, "templates", "filebeat.yml") - if err = utils.GenerateFromTemplate(beatConfig, templateFile, configFile); err != nil { - return fmt.Errorf("error configuration from %s: %v", templateFile, err) - } - switch runtime.GOOS { - case "windows": - err = utils.Execute("sc", - filebLogPath, - "create", - config.ModulesServName, - "binPath=", - fmt.Sprintf("\"%s\\filebeat.exe\" --environment=windows_service -c \"%s\\filebeat.yml\" --path.home \"%s\" --path.data \"C:\\ProgramData\\filebeat\" --path.logs \"C:\\ProgramData\\filebeat\\logs\" -E logging.files.redirect_stderr=true", filebLogPath, filebLogPath, filebLogPath), - "DisplayName=", - config.ModulesServName, - "start=", - "auto") - if err != nil { - return fmt.Errorf("error installing %s service: %s", config.ModulesServName, err) - } - - err = utils.Execute("sc", filebLogPath, "start", config.ModulesServName) - if err != nil { - return fmt.Errorf("error starting %s service: %s", config.ModulesServName, err) - } - case "linux": - if err = utils.CreateLinuxService(config.ModulesServName, fmt.Sprintf( - "%s -c %s -path.home %s -path.config %s -path.data /var/lib/filebeat -path.logs /var/log/filebeat", - filepath.Join(filebLogPath, "filebeat"), - filepath.Join(filebLogPath, "filebeat.yml"), - filebLogPath, - filebLogPath, - ), - ); err != nil { - return fmt.Errorf("error creating %s service: %v", config.ModulesServName, err) - } - - if err = utils.Execute("chmod", filebLogPath, "-R", "755", "filebeat"); err != nil { - return fmt.Errorf("error executing chmod: %v", err) - } - - if err = utils.Execute("systemctl", filebLogPath, "daemon-reload"); err != nil { - return fmt.Errorf("error reloading daemon: %v", err) - } - - err := utils.Execute("systemctl", filebLogPath, "enable", config.ModulesServName) - if err != nil { - return fmt.Errorf("%s", err) - } - - err = utils.Execute("systemctl", filebLogPath, "start", config.ModulesServName) - if err != nil { - return fmt.Errorf("%s", err) - } - - err = utils.Execute("./filebeat", filebLogPath, "modules", "enable", "system") - if err != nil { - return fmt.Errorf("%s", err) - } - - err = utils.Execute("sed", filepath.Join(filebLogPath, "modules.d"), "-i", "s/enabled: false/enabled: true/g", "system.yml") - if err != nil { - return fmt.Errorf("%s", err) - } - - err = utils.Execute("systemctl", filebLogPath, "restart", config.ModulesServName) - if err != nil { - return fmt.Errorf("%s", err) - } - } - } - - return nil -} - -func (f Filebeat) SendLogs() { - logLinesChan := make(chan string) - path := utils.GetMyPath() - filebLogPath := filepath.Join(path, "beats", "filebeat", "logs") - host, err := os.Hostname() - if err != nil { - utils.Logger.ErrorF("error getting hostname: %v", err) - host = "unknown" - } - - parser := parser.GetParser("beats") - - go utils.WatchFolder("modulescollector", filebLogPath, logLinesChan) - - for logLine := range logLinesChan { - message, _, err := entities.ValidateString(logLine, false) - if err != nil { - utils.Logger.ErrorF("error validating string: %v: message: %s", err, message) - continue - } - err = parser.ProcessData(logLine, host, logservice.LogQueue) - if err != nil { - utils.Logger.ErrorF("error processing beats data: %v", err) - continue - } - } -} - -func (f Filebeat) Uninstall() error { - if isInstalled, err := utils.CheckIfServiceIsInstalled(config.ModulesServName); err != nil { - return fmt.Errorf("error checking if %s is running: %v", config.ModulesServName, err) - } else if isInstalled { - err = utils.StopService(config.ModulesServName) - if err != nil { - return fmt.Errorf("error stopping %s: %v", config.ModulesServName, err) - } - - err = utils.UninstallService(config.ModulesServName) - if err != nil { - return fmt.Errorf("error uninstalling %s: %v", config.ModulesServName, err) - } - } - return nil -} diff --git a/agent/collectors/linux_amd64.go b/agent/collectors/linux_amd64.go deleted file mode 100644 index fb50593f2..000000000 --- a/agent/collectors/linux_amd64.go +++ /dev/null @@ -1,11 +0,0 @@ -//go:build linux && amd64 -// +build linux,amd64 - -package collectors - -func getCollectorsInstances() []Collector { - var collectors []Collector - collectors = append(collectors, Filebeat{}) - - return collectors -} diff --git a/agent/collectors/macos.go b/agent/collectors/macos.go deleted file mode 100644 index 323fbf535..000000000 --- a/agent/collectors/macos.go +++ /dev/null @@ -1,103 +0,0 @@ -//go:build darwin -// +build darwin - -package collectors - -import ( - "bufio" - "os" - "os/exec" - "path/filepath" - - "github.com/threatwinds/go-sdk/entities" - "github.com/threatwinds/go-sdk/plugins" - "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/logservice" - "github.com/utmstack/UTMStack/agent/utils" -) - -type Darwin struct{} - -func (d Darwin) Install() error { - return nil -} - -func getCollectorsInstances() []Collector { - var collectors []Collector - collectors = append(collectors, Darwin{}) - return collectors -} - -func (d Darwin) SendLogs() { - path := utils.GetMyPath() - collectorPath := filepath.Join(path, "utmstack-collector-mac") - host, err := os.Hostname() - if err != nil { - utils.Logger.ErrorF("error getting hostname: %v", err) - host = "unknown" - } - - cmd := exec.Command(collectorPath) - - stdout, err := cmd.StdoutPipe() - if err != nil { - _ = utils.Logger.ErrorF("error creating stdout pipe: %v", err) - return - } - - stderr, err := cmd.StderrPipe() - if err != nil { - _ = utils.Logger.ErrorF("error creating stderr pipe: %v", err) - return - } - - if err := cmd.Start(); err != nil { - _ = utils.Logger.ErrorF("error starting macOS collector: %v", err) - return - } - - go func() { - scanner := bufio.NewScanner(stdout) - for scanner.Scan() { - logLine := scanner.Text() - - utils.Logger.LogF(100, "output: %s", logLine) - - validatedLog, _, err := entities.ValidateString(logLine, false) - if err != nil { - utils.Logger.ErrorF("error validating log: %s: %v", logLine, err) - continue - } - - logservice.LogQueue <- &plugins.Log{ - DataType: string(config.DataTypeMacOs), - DataSource: host, - Raw: validatedLog, - } - } - - if err := scanner.Err(); err != nil { - _ = utils.Logger.ErrorF("error reading stdout: %v", err) - } - }() - - go func() { - scanner := bufio.NewScanner(stderr) - for scanner.Scan() { - errLine := scanner.Text() - _ = utils.Logger.ErrorF("collector error: %s", errLine) - } - - if err := scanner.Err(); err != nil { - _ = utils.Logger.ErrorF("error reading stderr: %v", err) - } - }() - - if err := cmd.Wait(); err != nil { - _ = utils.Logger.ErrorF("macOS collector process ended with error: %v", err) - } -} - -func (d Darwin) Uninstall() error { - return nil -} diff --git a/agent/collectors/windows_amd64.go b/agent/collectors/windows_amd64.go deleted file mode 100644 index 4d4db012c..000000000 --- a/agent/collectors/windows_amd64.go +++ /dev/null @@ -1,115 +0,0 @@ -//go:build windows && amd64 -// +build windows,amd64 - -package collectors - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/threatwinds/go-sdk/entities" - "github.com/threatwinds/go-sdk/plugins" - "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/logservice" - "github.com/utmstack/UTMStack/agent/utils" -) - -type Windows struct{} - -func getCollectorsInstances() []Collector { - var collectors []Collector - collectors = append(collectors, Windows{}) - collectors = append(collectors, Filebeat{}) - - return collectors -} - -func (w Windows) Install() error { - path := utils.GetMyPath() - - winlogPath := filepath.Join(path, "beats", "winlogbeat") - beatConfig := CollectorConfig{ - LogsPath: filepath.Join(winlogPath, "logs"), - LogFileName: "windowscollector", - } - - if isInstalled, err := utils.CheckIfServiceIsInstalled(config.WinServName); err != nil { - return fmt.Errorf("error checking if %s service is installed: %v", config.WinServName, err) - } else if !isInstalled { - err = utils.CreatePathIfNotExist(beatConfig.LogsPath) - if err != nil { - return fmt.Errorf("error creating %s folder", beatConfig.LogsPath) - } - - configFile := filepath.Join(winlogPath, "winlogbeat.yml") - templateFile := filepath.Join(path, "templates", "winlogbeat.yml") - err = utils.GenerateFromTemplate(beatConfig, templateFile, configFile) - if err != nil { - return fmt.Errorf("error configuration from %s: %v", templateFile, err) - } - - err = utils.Execute("sc", - winlogPath, - "create", - config.WinServName, - "binPath=", - fmt.Sprintf("\"%s\\winlogbeat.exe\" --environment=windows_service -c \"%s\\winlogbeat.yml\" --path.home \"%s\" --path.data \"C:\\ProgramData\\winlogbeat\" --path.logs \"C:\\ProgramData\\winlogbeat\\logs\" -E logging.files.redirect_stderr=true", winlogPath, winlogPath, winlogPath), - "DisplayName=", - config.WinServName, - "start=", - "auto") - if err != nil { - return fmt.Errorf("error installing %s service: %s", config.WinServName, err) - } - - err = utils.Execute("sc", winlogPath, "start", config.WinServName) - if err != nil { - return fmt.Errorf("error starting %s service: %s", config.WinServName, err) - } - } - - return nil -} - -func (w Windows) SendLogs() { - logLinesChan := make(chan string) - path := utils.GetMyPath() - winbLogPath := filepath.Join(path, "beats", "winlogbeat", "logs") - host, err := os.Hostname() - if err != nil { - utils.Logger.ErrorF("error getting hostname: %v", err) - host = "unknown" - } - - go utils.WatchFolder("windowscollector", winbLogPath, logLinesChan) - for logLine := range logLinesChan { - validatedLog, _, err := entities.ValidateString(logLine, false) - if err != nil { - utils.Logger.ErrorF("error validating log: %s: %v", logLine, err) - continue - } - logservice.LogQueue <- &plugins.Log{ - DataType: string(config.DataTypeWindowsAgent), - DataSource: host, - Raw: validatedLog, - } - } -} - -func (w Windows) Uninstall() error { - if isInstalled, err := utils.CheckIfServiceIsInstalled(config.WinServName); err != nil { - return fmt.Errorf("error checking if %s is running: %v", config.WinServName, err) - } else if isInstalled { - err = utils.StopService(config.WinServName) - if err != nil { - return fmt.Errorf("error stopping %s: %v", config.WinServName, err) - } - err = utils.UninstallService(config.WinServName) - if err != nil { - return fmt.Errorf("error uninstalling %s: %v", config.WinServName, err) - } - } - - return nil -} diff --git a/agent/config/config.go b/agent/config/config.go index 1f4042e5c..97cb46d3d 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -2,19 +2,13 @@ package config import ( "fmt" - "os" "sync" - aesCrypt "github.com/AtlasInsideCorp/AtlasInsideAES" "github.com/google/uuid" "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/fs" ) -type MSGDS struct { - DataSource string - Message string -} - type InstallationUUID struct { UUID string `yaml:"uuid"` } @@ -26,19 +20,6 @@ type Config struct { SkipCertValidation bool `yaml:"insecure"` } -func GetInitialConfig() (*Config, string) { - cnf := Config{ - Server: os.Args[2], - } - skip := os.Args[4] - if skip == "yes" { - cnf.SkipCertValidation = true - } else { - cnf.SkipCertValidation = false - } - return &cnf, os.Args[3] -} - var ( cnf = Config{} confOnce sync.Once @@ -49,39 +30,21 @@ var ( func GetCurrentConfig() (*Config, error) { var errR error confOnce.Do(func() { - uuidExists := utils.CheckIfPathExist(UUIDFileName) - var encryptConfig Config - if err := utils.ReadYAML(ConfigurationFile, &encryptConfig); err != nil { + if err := fs.ReadYAML(ConfigurationFile, &encryptConfig); err != nil { errR = fmt.Errorf("error reading config file: %v", err) return } - var key []byte - var err error - if uuidExists { - id, err := GetUUID() - if err != nil { - errR = fmt.Errorf("failed to get uuid: %v", err) - return - } - - key, err = utils.GenerateKeyByUUID(REPLACE_KEY, id) - if err != nil { - errR = fmt.Errorf("error geneating key: %v", err) - return - } - } else { - key, err = utils.GenerateKey(REPLACE_KEY) - if err != nil { - errR = fmt.Errorf("error geneating key: %v", err) - return - } + id, err := GetUUID() + if err != nil { + errR = fmt.Errorf("failed to get uuid: %v", err) + return } - agentKey, err := aesCrypt.AESDecrypt(encryptConfig.AgentKey, key) + agentKey, err := utils.DecryptAES(encryptConfig.AgentKey, REPLACE_KEY, id) if err != nil { - errR = fmt.Errorf("error encoding agent key: %v", err) + errR = fmt.Errorf("error decrypting agent key: %v", err) return } @@ -89,13 +52,6 @@ func GetCurrentConfig() (*Config, error) { cnf.AgentID = encryptConfig.AgentID cnf.AgentKey = agentKey cnf.SkipCertValidation = encryptConfig.SkipCertValidation - - if !uuidExists { - if err := SaveConfig(&cnf); err != nil { - errR = fmt.Errorf("error writing config file: %v", err) - return - } - } }) if errR != nil { return nil, errR @@ -109,14 +65,9 @@ func SaveConfig(cnf *Config) error { return fmt.Errorf("failed to generate uuid: %v", err) } - key, err := utils.GenerateKeyByUUID(REPLACE_KEY, id) - if err != nil { - return fmt.Errorf("error geneating key: %v", err) - } - - agentKey, err := aesCrypt.AESEncrypt(cnf.AgentKey, key) + agentKey, err := utils.EncryptAES(cnf.AgentKey, REPLACE_KEY, id) if err != nil { - return fmt.Errorf("error encoding agent key: %v", err) + return fmt.Errorf("error encrypting agent key: %v", err) } encryptConf := &Config{ @@ -126,7 +77,7 @@ func SaveConfig(cnf *Config) error { SkipCertValidation: cnf.SkipCertValidation, } - if err := utils.WriteYAML(ConfigurationFile, encryptConf); err != nil { + if err := fs.WriteYAML(ConfigurationFile, encryptConf); err != nil { return err } return nil @@ -142,7 +93,7 @@ func GenerateNewUUID() (string, error) { UUID: id.String(), } - if err = utils.WriteYAML(UUIDFileName, InstallationUUID); err != nil { + if err = fs.WriteYAML(UUIDFileName, InstallationUUID); err != nil { return "", fmt.Errorf("error writing uuid file: %v", err) } @@ -153,7 +104,7 @@ func GetUUID() (string, error) { var errR error installationIdOnce.Do(func() { var id = InstallationUUID{} - if err := utils.ReadYAML(UUIDFileName, &id); err != nil { + if err := fs.ReadYAML(UUIDFileName, &id); err != nil { errR = fmt.Errorf("error reading uuid file: %v", err) return } diff --git a/agent/config/const.go b/agent/config/const.go index 910c9593b..7cd13ea87 100644 --- a/agent/config/const.go +++ b/agent/config/const.go @@ -3,7 +3,7 @@ package config import ( "path/filepath" - "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/fs" ) type DataType string @@ -25,24 +25,24 @@ var ( LogAuthProxyPort = "50051" DependenciesPort = "9001" - ServiceLogFile = filepath.Join(utils.GetMyPath(), "logs", "utmstack_agent.log") + ServiceLogFile = filepath.Join(fs.GetExecutablePath(), "logs", "utmstack_agent.log") ModulesServName = "UTMStackModulesLogsCollector" WinServName = "UTMStackWindowsLogsCollector" - CollectorFileName = filepath.Join(utils.GetMyPath(), "log-collector-config.json") - UUIDFileName = filepath.Join(utils.GetMyPath(), "uuid.yml") - ConfigurationFile = filepath.Join(utils.GetMyPath(), "config.yml") - PortRangeMin = "7000" - PortRangeMax = "9000" - RetentionConfigFile = filepath.Join(utils.GetMyPath(), "retention.json") - LogsDBFile = filepath.Join(utils.GetMyPath(), "logs_process", "logs.db") - CertPath = filepath.Join(utils.GetMyPath(), "certs", "utm.crt") - VersionPath = filepath.Join(utils.GetMyPath(), "version.json") + CollectorFileName = filepath.Join(fs.GetExecutablePath(), "log-collector-config.json") + UUIDFileName = filepath.Join(fs.GetExecutablePath(), "uuid.yml") + ConfigurationFile = filepath.Join(fs.GetExecutablePath(), "config.yml") + PortRangeMin = "1" + PortRangeMax = "65535" + RetentionConfigFile = filepath.Join(fs.GetExecutablePath(), "retention.json") + LogsDBFile = filepath.Join(fs.GetExecutablePath(), "logs_process", "logs.db") + CertPath = filepath.Join(fs.GetExecutablePath(), "certs", "utm.crt") + VersionPath = filepath.Join(fs.GetExecutablePath(), "version.json") UpdaterSelfLinux = "utmstack_updater_self" // TLS Configuration for Integrations - IntegrationCertPath = filepath.Join(utils.GetMyPath(), "certs", "integration.crt") - IntegrationKeyPath = filepath.Join(utils.GetMyPath(), "certs", "integration.key") - IntegrationCAPath = filepath.Join(utils.GetMyPath(), "certs", "integration-ca.crt") + IntegrationCertPath = filepath.Join(fs.GetExecutablePath(), "certs", "integration.crt") + IntegrationKeyPath = filepath.Join(fs.GetExecutablePath(), "certs", "integration.key") + IntegrationCAPath = filepath.Join(fs.GetExecutablePath(), "certs", "integration-ca.crt") DataTypeWindowsAgent DataType = "wineventlog" DataTypeSyslog DataType = "syslog" @@ -77,7 +77,6 @@ var ( DataTypeIisModule DataType = "iis" DataTypeApacheModule DataType = "apache" DataTypeSentinelOne DataType = "antivirus-sentinel-one" - DataTypeCiscoGeneric DataType = "cisco" DataTypeMacOs DataType = "macos" DataTypeGeneric DataType = "generic" DataTypeNetflow DataType = "netflow" @@ -91,7 +90,10 @@ var ( DataTypeVmware: {UDP: "7002", TCP: "7002"}, DataTypeEset: {UDP: "7003", TCP: "7003"}, DataTypeKaspersky: {UDP: "7004", TCP: "7004"}, - DataTypeCiscoGeneric: {UDP: "514", TCP: "1470"}, + DataTypeCiscoAsa: {UDP: "514", TCP: "1470"}, + DataTypeCiscoFirepower: {UDP: "514", TCP: "1470"}, + DataTypeCiscoSwitch: {UDP: "514", TCP: "1470"}, + DataTypeCiscoMeraki: {UDP: "514", TCP: "1470"}, DataTypeFortinet: {UDP: "7005", TCP: "7005"}, DataTypePaloalto: {UDP: "7006", TCP: "7006"}, DataTypeMikrotik: {UDP: "7007", TCP: "7007"}, @@ -106,20 +108,27 @@ var ( DataTypeNetflow: {UDP: "2055", TCP: ""}, } - ProhibitedPortsChange = []DataType{DataTypeCiscoGeneric, DataTypeNetflow} + // FilePaths defines default log file paths for file-based integrations + FilePaths = map[DataType][]string{ + DataTypeNginxModule: {"/var/log/nginx/access.log", "/var/log/nginx/error.log"}, + DataTypePostgresqlModule: {"/var/log/postgresql/postgresql-*-main.log"}, + } ) func ValidateModuleType(typ string) string { switch DataType(typ) { case DataTypeSyslog, DataTypeVmware, DataTypeEset, DataTypeKaspersky, DataTypeFortinet, DataTypePaloalto, - DataTypeMikrotik, DataTypeSophosXG, DataTypeSonicwall, DataTypeSentinelOne, DataTypeCiscoGeneric, + DataTypeMikrotik, DataTypeSophosXG, DataTypeSonicwall, DataTypeSentinelOne, + DataTypeCiscoAsa, DataTypeCiscoFirepower, DataTypeCiscoSwitch, DataTypeCiscoMeraki, DataTypeDeceptivebytes, DataTypeAix, DataTypePfsense, DataTypeFortiweb, DataTypeSuricata: return "syslog" case DataTypeNetflow: return "netflow" - case DataTypeWindowsAgent, DataTypeLinuxAgent, DataTypeTraefikModule, DataTypeMongodbModule, DataTypeMysqlModule, DataTypePostgresqlModule, + case DataTypeNginxModule, DataTypePostgresqlModule: + return "file" + case DataTypeWindowsAgent, DataTypeLinuxAgent, DataTypeTraefikModule, DataTypeMongodbModule, DataTypeMysqlModule, DataTypeRedisModule, DataTypeElasticsearchModule, DataTypeKafkaModule, DataTypeKibanaModule, DataTypeLogstashModule, DataTypeNatsModule, - DataTypeOsqueryModule, DataTypeLinuxAuditdModule, DataTypeHaproxyModule, DataTypeNginxModule, DataTypeIisModule, DataTypeApacheModule: + DataTypeOsqueryModule, DataTypeLinuxAuditdModule, DataTypeHaproxyModule, DataTypeIisModule, DataTypeApacheModule: return "beats" default: return "nil" diff --git a/agent/database/db.go b/agent/database/db.go index fa7ac083c..73ebca493 100644 --- a/agent/database/db.go +++ b/agent/database/db.go @@ -10,7 +10,7 @@ import ( "github.com/glebarez/sqlite" "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/fs" "gorm.io/gorm" "gorm.io/gorm/logger" ) @@ -26,14 +26,20 @@ type Database struct { } func (d *Database) Migrate(data interface{}) error { + d.locker.Lock() + defer d.locker.Unlock() return d.db.AutoMigrate(data) } func (d *Database) Create(data interface{}) error { + d.locker.Lock() + defer d.locker.Unlock() return d.db.Create(data).Error } func (d *Database) Find(data interface{}, field string, value interface{}) (bool, error) { + d.locker.RLock() + defer d.locker.RUnlock() err := d.db.Where(fmt.Sprintf("%v = ?", field), value).Find(data).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -45,6 +51,8 @@ func (d *Database) Find(data interface{}, field string, value interface{}) (bool } func (d *Database) GetAll(data interface{}) error { + d.locker.RLock() + defer d.locker.RUnlock() if err := d.db.Find(data).Error; err != nil { return err } @@ -52,14 +60,30 @@ func (d *Database) GetAll(data interface{}) error { } func (d *Database) Update(data interface{}, searchField string, searchValue string, modifyField string, newValue interface{}) error { + d.locker.Lock() + defer d.locker.Unlock() return d.db.Model(data).Where(fmt.Sprintf("%v = ?", searchField), searchValue).Update(modifyField, newValue).Error } func (d *Database) Delete(data interface{}, field string, value string) error { + d.locker.Lock() + defer d.locker.Unlock() return d.db.Where(fmt.Sprintf("%v = ?", field), value).Delete(data).Error } +func (d *Database) Close() error { + d.locker.Lock() + defer d.locker.Unlock() + sqlDB, err := d.db.DB() + if err != nil { + return err + } + return sqlDB.Close() +} + func (d *Database) DeleteOld(data interface{}, retentionMegabytes int) (int, error) { + d.locker.Lock() + defer d.locker.Unlock() currentSize, err := GetDatabaseSizeInMB() if err != nil { return 0, fmt.Errorf("error getting database size: %v", err) @@ -67,33 +91,31 @@ func (d *Database) DeleteOld(data interface{}, retentionMegabytes int) (int, err var rowsAffected int for currentSize > retentionMegabytes { - result := d.db.Where("1 = 1").Order("created_at ASC").Limit(10).Delete(data) + result := d.db.Where("1 = 1").Order("created_at ASC").Limit(500).Delete(data) if result.Error != nil { - return rowsAffected, result.Error + break } rowsAffected += int(result.RowsAffected) - d.db.Exec("VACUUM;") + if result.RowsAffected == 0 { + break + } currentSize, err = GetDatabaseSizeInMB() if err != nil { - return rowsAffected, fmt.Errorf("error getting database size: %v", err) + break } } - return rowsAffected, nil -} - -func (d *Database) Lock() { - d.locker.Lock() -} + if rowsAffected > 0 { + d.db.Exec("VACUUM;") + } -func (d *Database) Unlock() { - d.locker.Unlock() + return rowsAffected, nil } func GetDB() *Database { dbOnce.Do(func() { - path := filepath.Join(utils.GetMyPath(), "logs_process") - err := utils.CreatePathIfNotExist(path) + path := filepath.Join(fs.GetExecutablePath(), "logs_process") + err := fs.CreateDirIfNotExist(path) if err != nil { log.Fatalf("error creating database path: %v", err) } diff --git a/agent/dependency/dependency.go b/agent/dependency/dependency.go new file mode 100644 index 000000000..e6d5641e1 --- /dev/null +++ b/agent/dependency/dependency.go @@ -0,0 +1,267 @@ +package dependency + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "sync" + + "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/fs" + "github.com/utmstack/UTMStack/shared/http" +) + +const ( + updaterBaseName = "utmstack_updater_service" + + // Dependency versions - single source of truth + UpdaterVersion = "11.2.3" + BeatsVersion = "11.2.3" + MacosCollectorVersion = "11.2.3" +) + +// UpdaterFile returns the updater binary name with the appropriate extension for the current OS. +func UpdaterFile(suffix string) string { + if runtime.GOOS == "windows" { + return updaterBaseName + suffix + ".exe" + } + return updaterBaseName + suffix +} + +// Dependency represents a dependency that the agent needs. +type Dependency struct { + Name string // Unique identifier + Version string // Current version in this agent build + BinaryPath string // Path to check if already exists + DownloadURL func(server string) string // URL template to download from + DownloadName string // Filename to save as (if different from BinaryPath basename) + Critical bool // If true, failure blocks agent startup + PostDownload func() error // Run after download (e.g., unzip). Can be nil. + Configure func() error // Run on first install (can be nil) + Update func() error // Run on version change (can be nil, uses Configure) + Uninstall func() error // Run when dependency is removed (can be nil) +} + +// Exists checks if the dependency binary exists on disk. +func (d *Dependency) Exists() bool { + return fs.Exists(d.BinaryPath) +} + +// InstalledDep represents a dependency that has been installed and tracked. +type InstalledDep struct { + Name string `json:"name"` + Version string `json:"version"` +} + +// InstalledDeps is the list of installed dependencies (persisted to JSON). +type InstalledDeps struct { + Dependencies []InstalledDep `json:"dependencies"` +} + +func (i *InstalledDeps) Get(name string) *InstalledDep { + for idx := range i.Dependencies { + if i.Dependencies[idx].Name == name { + return &i.Dependencies[idx] + } + } + return nil +} + +func (i *InstalledDeps) Add(name, version string) { + i.Dependencies = append(i.Dependencies, InstalledDep{Name: name, Version: version}) +} + +func (i *InstalledDeps) Update(name, version string) { + for idx := range i.Dependencies { + if i.Dependencies[idx].Name == name { + i.Dependencies[idx].Version = version + return + } + } +} + +func (i *InstalledDeps) Remove(name string) { + for idx := range i.Dependencies { + if i.Dependencies[idx].Name == name { + i.Dependencies = append(i.Dependencies[:idx], i.Dependencies[idx+1:]...) + return + } + } +} + +func (i *InstalledDeps) Has(name string) bool { + return i.Get(name) != nil +} + +var ( + installedDepsFile = filepath.Join(fs.GetExecutablePath(), "dependencies.json") + reconcileMu sync.Mutex +) + +// readInstalledDeps reads the installed dependencies from disk. +func readInstalledDeps() (*InstalledDeps, error) { + installed := &InstalledDeps{Dependencies: []InstalledDep{}} + if !fs.Exists(installedDepsFile) { + return installed, nil + } + if err := fs.ReadJSON(installedDepsFile, installed); err != nil { + return nil, fmt.Errorf("error reading dependencies file: %v", err) + } + return installed, nil +} + +// writeInstalledDeps writes the installed dependencies to disk. +func writeInstalledDeps(installed *InstalledDeps) error { + return fs.WriteJSON(installedDepsFile, installed) +} + +// Reconcile ensures all dependencies are installed and up-to-date. +// This should be called at agent startup, before starting collectors. +func Reconcile(server string, skipCertValidation bool) error { + reconcileMu.Lock() + defer reconcileMu.Unlock() + + utils.Logger.Info("Starting dependency reconciliation...") + + installed, err := readInstalledDeps() + if err != nil { + return fmt.Errorf("failed to read installed dependencies: %v", err) + } + + desired := GetDependencies() + var criticalErrors []error + + // Process each desired dependency + for _, dep := range desired { + inst := installed.Get(dep.Name) + + if inst == nil { + // Not tracked yet + if dep.Exists() { + // MIGRATION: Already installed by previous version, just track it + utils.Logger.Info("Migrating existing dependency: %s (version %s)", dep.Name, dep.Version) + installed.Add(dep.Name, dep.Version) + } else { + // FRESH INSTALL: Download and configure + utils.Logger.Info("Installing new dependency: %s (version %s)", dep.Name, dep.Version) + if err := downloadDependency(dep, server, skipCertValidation); err != nil { + errMsg := fmt.Errorf("failed to download dependency %s: %v", dep.Name, err) + utils.Logger.ErrorF("%v", errMsg) + if dep.Critical { + criticalErrors = append(criticalErrors, errMsg) + } + continue + } + + if dep.Configure != nil { + if err := dep.Configure(); err != nil { + errMsg := fmt.Errorf("failed to configure dependency %s: %v", dep.Name, err) + utils.Logger.ErrorF("%v", errMsg) + if dep.Critical { + criticalErrors = append(criticalErrors, errMsg) + } + continue + } + } + installed.Add(dep.Name, dep.Version) + utils.Logger.Info("Dependency %s installed successfully", dep.Name) + } + } else if inst.Version != dep.Version { + // VERSION CHANGED: Download and update + utils.Logger.Info("Updating dependency: %s (%s -> %s)", dep.Name, inst.Version, dep.Version) + if err := downloadDependency(dep, server, skipCertValidation); err != nil { + errMsg := fmt.Errorf("failed to download dependency update %s: %v", dep.Name, err) + utils.Logger.ErrorF("%v", errMsg) + if dep.Critical { + criticalErrors = append(criticalErrors, errMsg) + } + continue + } + + updateFn := dep.Update + if updateFn == nil { + updateFn = dep.Configure + } + if updateFn != nil { + if err := updateFn(); err != nil { + errMsg := fmt.Errorf("failed to update dependency %s: %v", dep.Name, err) + utils.Logger.ErrorF("%v", errMsg) + if dep.Critical { + criticalErrors = append(criticalErrors, errMsg) + } + continue + } + } + installed.Update(dep.Name, dep.Version) + utils.Logger.Info("Dependency %s updated successfully", dep.Name) + } else { + // Same version, nothing to do + utils.Logger.Info("Dependency %s is up to date (version %s)", dep.Name, dep.Version) + } + } + + // CLEANUP: Remove dependencies that are no longer needed + for _, inst := range installed.Dependencies { + found := false + for _, dep := range desired { + if dep.Name == inst.Name { + found = true + break + } + } + if !found { + utils.Logger.Info("Removing obsolete dependency: %s", inst.Name) + // Find the uninstall function if we have it from old code + // For now, just remove from tracking + installed.Remove(inst.Name) + } + } + + // Save the updated installed dependencies + if err := writeInstalledDeps(installed); err != nil { + return fmt.Errorf("failed to write installed dependencies: %v", err) + } + + if len(criticalErrors) > 0 { + return fmt.Errorf("critical dependency errors: %v", criticalErrors) + } + + utils.Logger.Info("Dependency reconciliation completed") + return nil +} + +// downloadDependency downloads the dependency binary. +func downloadDependency(dep Dependency, server string, skipCertValidation bool) error { + if dep.DownloadURL == nil { + return fmt.Errorf("dependency %s has no download URL", dep.Name) + } + + url := dep.DownloadURL(server) + + // Use DownloadName if specified, otherwise use basename of BinaryPath + filename := dep.DownloadName + if filename == "" { + filename = filepath.Base(dep.BinaryPath) + } + + destDir := filepath.Dir(dep.BinaryPath) + + // Ensure destination directory exists + if err := os.MkdirAll(destDir, 0755); err != nil { + return fmt.Errorf("failed to create directory %s: %v", destDir, err) + } + + if err := http.DownloadFile(url, map[string]string{}, filename, destDir, skipCertValidation); err != nil { + return err + } + + // Run post-download hook if defined (e.g., unzip) + if dep.PostDownload != nil { + if err := dep.PostDownload(); err != nil { + return fmt.Errorf("post-download failed: %v", err) + } + } + + return nil +} diff --git a/agent/dependency/deps_darwin.go b/agent/dependency/deps_darwin.go new file mode 100644 index 000000000..02244bf9b --- /dev/null +++ b/agent/dependency/deps_darwin.go @@ -0,0 +1,68 @@ +//go:build darwin +// +build darwin + +package dependency + +import ( + "fmt" + "path/filepath" + + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/shared/exec" + "github.com/utmstack/UTMStack/shared/fs" +) + +const macosCollectorBinary = "utmstack-collector-mac" + +// GetDependencies returns the list of dependencies for macOS. +func GetDependencies() []Dependency { + basePath := fs.GetExecutablePath() + + return []Dependency{ + { + Name: "updater", + Version: UpdaterVersion, + BinaryPath: filepath.Join(basePath, UpdaterFile("")), + DownloadURL: func(server string) string { + return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, UpdaterFile("")) + }, + Critical: false, + Configure: configureUpdater, + Uninstall: uninstallUpdater, + }, + { + Name: "macos-collector", + Version: MacosCollectorVersion, + BinaryPath: filepath.Join(basePath, macosCollectorBinary), + DownloadURL: func(server string) string { + return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, macosCollectorBinary) + }, + Critical: true, + Configure: configureMacosCollector, + }, + } +} + +func configureMacosCollector() error { + collectorPath := filepath.Join(fs.GetExecutablePath(), macosCollectorBinary) + return exec.Run("chmod", fs.GetExecutablePath(), "755", collectorPath) +} + +func configureUpdater() error { + updaterPath := filepath.Join(fs.GetExecutablePath(), UpdaterFile("")) + + // Set executable permissions + if err := exec.Run("chmod", fs.GetExecutablePath(), "755", updaterPath); err != nil { + return fmt.Errorf("error setting permissions on updater: %v", err) + } + + return exec.Run(updaterPath, fs.GetExecutablePath(), "install") +} + +func uninstallUpdater() error { + updaterPath := filepath.Join(fs.GetExecutablePath(), UpdaterFile("")) + if !fs.Exists(updaterPath) { + return nil + } + return exec.Run(updaterPath, fs.GetExecutablePath(), "uninstall") +} diff --git a/agent/dependency/deps_linux_amd64.go b/agent/dependency/deps_linux_amd64.go new file mode 100644 index 000000000..3b552475b --- /dev/null +++ b/agent/dependency/deps_linux_amd64.go @@ -0,0 +1,98 @@ +//go:build linux && amd64 +// +build linux,amd64 + +package dependency + +import ( + "fmt" + "path/filepath" + + "github.com/utmstack/UTMStack/agent/collector" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/shared/exec" + "github.com/utmstack/UTMStack/shared/fs" +) + +// TODO: Remove after testing native collectors +// const beatsZip = "utmstack_agent_dependencies_linux.zip" + +// GetDependencies returns the list of dependencies for Linux amd64. +func GetDependencies() []Dependency { + basePath := fs.GetExecutablePath() + + return []Dependency{ + { + Name: "updater", + Version: UpdaterVersion, + BinaryPath: filepath.Join(basePath, UpdaterFile("")), + DownloadURL: func(server string) string { + return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, UpdaterFile("")) + }, + Critical: false, + Configure: configureUpdater, + Uninstall: uninstallUpdater, + }, + // TODO: Remove beats dependency after testing native collectors + // { + // Name: "beats", + // Version: BeatsVersion, + // BinaryPath: filepath.Join(basePath, "beats", "filebeat", "filebeat"), + // DownloadName: beatsZip, + // DownloadURL: func(server string) string { + // return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, beatsZip) + // }, + // Critical: false, + // PostDownload: func() error { + // zipPath := filepath.Join(basePath, beatsZip) + // if err := archive.Unzip(zipPath, basePath); err != nil { + // return fmt.Errorf("error unzipping beats: %v", err) + // } + // // Remove zip after extraction + // os.Remove(zipPath) + // // Set executable permissions + // beatsPath := filepath.Join(basePath, "beats") + // exec.Run("chmod", basePath, "-R", "755", beatsPath) + // return nil + // }, + // Configure: configureBeats, + // Uninstall: uninstallBeats, + // }, + + // New beats dependency - only for uninstalling existing filebeat/winlogbeat + // No download, no install - native collectors are used instead + { + Name: "beats", + Version: BeatsVersion, + Critical: false, + Uninstall: uninstallBeats, + }, + } +} + +func configureUpdater() error { + updaterPath := filepath.Join(fs.GetExecutablePath(), UpdaterFile("")) + + // Set executable permissions + if err := exec.Run("chmod", fs.GetExecutablePath(), "755", updaterPath); err != nil { + return fmt.Errorf("error setting permissions on updater: %v", err) + } + + return exec.Run(updaterPath, fs.GetExecutablePath(), "install") +} + +func uninstallUpdater() error { + updaterPath := filepath.Join(fs.GetExecutablePath(), UpdaterFile("")) + if !fs.Exists(updaterPath) { + return nil + } + return exec.Run(updaterPath, fs.GetExecutablePath(), "uninstall") +} + +// TODO: Remove after testing native collectors +// func configureBeats() error { +// return collector.InstallAll() +// } + +func uninstallBeats() error { + return collector.UninstallAll() +} diff --git a/agent/dependency/deps_linux_arm64.go b/agent/dependency/deps_linux_arm64.go new file mode 100644 index 000000000..66473f04e --- /dev/null +++ b/agent/dependency/deps_linux_arm64.go @@ -0,0 +1,51 @@ +//go:build linux && arm64 +// +build linux,arm64 + +package dependency + +import ( + "fmt" + "path/filepath" + + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/shared/exec" + "github.com/utmstack/UTMStack/shared/fs" +) + +// GetDependencies returns the list of dependencies for Linux arm64. +// Linux arm64 uses integrated collectors, no beats needed. +func GetDependencies() []Dependency { + basePath := fs.GetExecutablePath() + + return []Dependency{ + { + Name: "updater", + Version: UpdaterVersion, + BinaryPath: filepath.Join(basePath, UpdaterFile("")), + DownloadURL: func(server string) string { + return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, UpdaterFile("")) + }, + Critical: false, + Configure: configureUpdater, + Uninstall: uninstallUpdater, + }, + } +} + +func configureUpdater() error { + updaterPath := filepath.Join(fs.GetExecutablePath(), UpdaterFile("")) + + if err := exec.Run("chmod", fs.GetExecutablePath(), "755", updaterPath); err != nil { + return fmt.Errorf("error setting permissions on updater: %v", err) + } + + return exec.Run(updaterPath, fs.GetExecutablePath(), "install") +} + +func uninstallUpdater() error { + updaterPath := filepath.Join(fs.GetExecutablePath(), UpdaterFile("")) + if !fs.Exists(updaterPath) { + return nil + } + return exec.Run(updaterPath, fs.GetExecutablePath(), "uninstall") +} diff --git a/agent/dependency/deps_windows_amd64.go b/agent/dependency/deps_windows_amd64.go new file mode 100644 index 000000000..067e6e529 --- /dev/null +++ b/agent/dependency/deps_windows_amd64.go @@ -0,0 +1,86 @@ +//go:build windows && amd64 +// +build windows,amd64 + +package dependency + +import ( + "fmt" + "path/filepath" + + "github.com/utmstack/UTMStack/agent/collector" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/shared/exec" + "github.com/utmstack/UTMStack/shared/fs" +) + +// GetDependencies returns the list of dependencies for Windows amd64. +func GetDependencies() []Dependency { + basePath := fs.GetExecutablePath() + + return []Dependency{ + { + Name: "updater", + Version: UpdaterVersion, + BinaryPath: filepath.Join(basePath, UpdaterFile("")), + DownloadURL: func(server string) string { + return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, UpdaterFile("")) + }, + Critical: false, // Agent can run without updater + Configure: configureUpdater, + Uninstall: uninstallUpdater, + }, + // TODO: Remove beats dependency after testing native collectors + // { + // Name: "beats", + // Version: BeatsVersion, + // BinaryPath: filepath.Join(basePath, "beats", "filebeat", "filebeat.exe"), + // DownloadName: beatsZip, + // DownloadURL: func(server string) string { + // return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, beatsZip) + // }, + // Critical: false, + // PostDownload: func() error { + // zipPath := filepath.Join(basePath, beatsZip) + // if err := archive.Unzip(zipPath, basePath); err != nil { + // return fmt.Errorf("error unzipping beats: %v", err) + // } + // // Remove zip after extraction + // os.Remove(zipPath) + // return nil + // }, + // Configure: configureBeats, + // Uninstall: uninstallBeats, + // }, + + // New beats dependency - only for uninstalling existing filebeat/winlogbeat + // No download, no install - native collectors are used instead + { + Name: "beats", + Version: BeatsVersion, + Critical: false, + Uninstall: uninstallBeats, + }, + } +} + +func configureUpdater() error { + updaterPath := filepath.Join(fs.GetExecutablePath(), UpdaterFile("")) + return exec.Run(updaterPath, fs.GetExecutablePath(), "install") +} + +func uninstallUpdater() error { + updaterPath := filepath.Join(fs.GetExecutablePath(), UpdaterFile("")) + if !fs.Exists(updaterPath) { + return nil + } + return exec.Run(updaterPath, fs.GetExecutablePath(), "uninstall") +} + +// TODO: Remove after testing native collectors +// func configureBeats() error { +// return collector.InstallAll() +// } + +func uninstallBeats() error { + return collector.UninstallAll() +} diff --git a/agent/dependency/deps_windows_arm64.go b/agent/dependency/deps_windows_arm64.go new file mode 100644 index 000000000..a45d4e8f4 --- /dev/null +++ b/agent/dependency/deps_windows_arm64.go @@ -0,0 +1,46 @@ +//go:build windows && arm64 +// +build windows,arm64 + +package dependency + +import ( + "fmt" + "path/filepath" + + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/shared/exec" + "github.com/utmstack/UTMStack/shared/fs" +) + +// GetDependencies returns the list of dependencies for Windows arm64. +// Windows arm64 uses integrated Windows collector, no beats needed. +func GetDependencies() []Dependency { + basePath := fs.GetExecutablePath() + + return []Dependency{ + { + Name: "updater", + Version: UpdaterVersion, + BinaryPath: filepath.Join(basePath, UpdaterFile("")), + DownloadURL: func(server string) string { + return fmt.Sprintf(config.DependUrl, server, config.DependenciesPort, UpdaterFile("")) + }, + Critical: false, + Configure: configureUpdater, + Uninstall: uninstallUpdater, + }, + } +} + +func configureUpdater() error { + updaterPath := filepath.Join(fs.GetExecutablePath(), UpdaterFile("")) + return exec.Run(updaterPath, fs.GetExecutablePath(), "install") +} + +func uninstallUpdater() error { + updaterPath := filepath.Join(fs.GetExecutablePath(), UpdaterFile("")) + if !fs.Exists(updaterPath) { + return nil + } + return exec.Run(updaterPath, fs.GetExecutablePath(), "uninstall") +} diff --git a/agent/go.mod b/agent/go.mod index c43246ec4..6f9e6d396 100644 --- a/agent/go.mod +++ b/agent/go.mod @@ -9,16 +9,19 @@ require ( github.com/google/uuid v1.6.0 github.com/kardianos/service v1.2.4 github.com/netsampler/goflow2 v1.3.7 + github.com/spf13/cobra v1.10.2 github.com/tehmaze/netflow v0.0.0-20240303214733-8c13bb004068 github.com/threatwinds/go-sdk v1.1.7 github.com/threatwinds/logger v1.2.3 + github.com/utmstack/UTMStack/shared v0.0.0 golang.org/x/sys v0.40.0 google.golang.org/grpc v1.78.0 google.golang.org/protobuf v1.36.11 - gopkg.in/yaml.v2 v2.4.0 gorm.io/gorm v1.31.1 ) +replace github.com/utmstack/UTMStack/shared => ../shared + require ( cel.dev/expr v0.25.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect @@ -40,6 +43,7 @@ require ( github.com/google/cel-go v0.26.1 // indirect github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -55,6 +59,7 @@ require ( github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/spf13/pflag v1.0.9 // 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 diff --git a/agent/go.sum b/agent/go.sum index 99830d697..e0070d261 100644 --- a/agent/go.sum +++ b/agent/go.sum @@ -12,6 +12,7 @@ github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2N github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -61,6 +62,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= @@ -106,6 +109,11 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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= @@ -196,8 +204,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/agent/main.go b/agent/main.go index 395a831b5..81100ca63 100644 --- a/agent/main.go +++ b/agent/main.go @@ -1,306 +1,12 @@ package main import ( - "fmt" - "os" - "path/filepath" - "time" - - pb "github.com/utmstack/UTMStack/agent/agent" - "github.com/utmstack/UTMStack/agent/collectors" + "github.com/utmstack/UTMStack/agent/cmd" "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/database" - "github.com/utmstack/UTMStack/agent/logservice" - "github.com/utmstack/UTMStack/agent/models" - "github.com/utmstack/UTMStack/agent/modules" - "github.com/utmstack/UTMStack/agent/serv" - "github.com/utmstack/UTMStack/agent/updates" "github.com/utmstack/UTMStack/agent/utils" ) func main() { utils.InitLogger(config.ServiceLogFile) - - if len(os.Args) > 1 { - arg := os.Args[1] - - isInstalled, err := utils.CheckIfServiceIsInstalled("UTMStackAgent") - if err != nil { - fmt.Println("Error checking if service is installed: ", err) - os.Exit(1) - } - if arg != "install" && !isInstalled { - fmt.Println("UTMStackAgent service is not installed") - os.Exit(1) - } else if arg == "install" && isInstalled { - fmt.Println("UTMStackAgent service is already installed") - os.Exit(1) - } - - switch arg { - case "run": - serv.RunService() - case "install": - utils.PrintBanner() - fmt.Println("Installing UTMStackAgent service ...") - - cnf, utmKey := config.GetInitialConfig() - - fmt.Print("Checking server connection ... ") - if err := utils.ArePortsReachable(cnf.Server, config.AgentManagerPort, config.LogAuthProxyPort, config.DependenciesPort); err != nil { - fmt.Println("\nError trying to connect to server: ", err) - os.Exit(1) - } - fmt.Println("[OK]") - - fmt.Print("Downloading dependencies ... ") - if err := updates.DownloadFirstDependencies(cnf.Server, cnf.SkipCertValidation); err != nil { - fmt.Println("\nError downloading dependencies: ", err) - os.Exit(1) - } - fmt.Println("[OK]") - - fmt.Print("Configuring agent ... ") - err = pb.RegisterAgent(cnf, utmKey) - if err != nil { - fmt.Println("\nError registering agent: ", err) - os.Exit(1) - } - if err = config.SaveConfig(cnf); err != nil { - fmt.Println("\nError saving config: ", err) - os.Exit(1) - } - if err = modules.ConfigureCollectorFirstTime(); err != nil { - fmt.Println("\nError configuring collector: ", err) - os.Exit(1) - } - if err = collectors.InstallCollectors(); err != nil { - fmt.Println("\nError installing beats: ", err) - os.Exit(1) - } - if err := logservice.SetDataRetention(""); err != nil { - fmt.Println("\nError setting retention: ", err) - os.Exit(1) - } - fmt.Println("[OK]") - - fmt.Print(("Creating service ... ")) - serv.InstallService() - fmt.Println("[OK]") - fmt.Println("UTMStackAgent service installed correctly") - - case "enable-integration", "disable-integration": - fmt.Println("Changing integration status ...") - integration := os.Args[2] - proto := os.Args[3] - - tlsEnabled := false - for _, arg := range os.Args[4:] { - if arg == "--tls" { - tlsEnabled = true - break - } - } - - var port string - var err error - - if arg == "enable-integration" && tlsEnabled { - port, err = modules.ChangeIntegrationStatus(integration, proto, true, true) - } else if arg == "enable-integration" { - port, err = modules.ChangeIntegrationStatus(integration, proto, true, false) - } else { - port, err = modules.ChangeIntegrationStatus(integration, proto, false) - } - - if err != nil { - fmt.Println("Error:", err) - os.Exit(1) - } - - if arg == "enable-integration" && tlsEnabled { - fmt.Printf("Integration %s %s enabled with TLS on port %s\n", integration, proto, port) - } else if arg == "enable-integration" { - fmt.Printf("Integration %s %s enabled on port %s\n", integration, proto, port) - } else { - fmt.Printf("Integration %s %s disabled (port %s freed)\n", integration, proto, port) - } - time.Sleep(5 * time.Second) - - case "load-tls-certs": - if len(os.Args) < 4 { - fmt.Println("Usage: ./utmstack_agent load-tls-certs [ca_certificate_path]") - fmt.Println("Example: ./utmstack_agent load-tls-certs /path/to/server.crt /path/to/server.key /path/to/ca.crt") - os.Exit(1) - } - - userCertPath := os.Args[2] - userKeyPath := os.Args[3] - var userCAPath string - if len(os.Args) > 4 { - userCAPath = os.Args[4] - } - - fmt.Println("Loading user TLS certificates ...") - - fmt.Print("Validating certificate files ... ") - if err := utils.ValidateIntegrationCertificates(userCertPath, userKeyPath); err != nil { - fmt.Printf("\nError: Invalid certificate files: %v\n", err) - os.Exit(1) - } - fmt.Println("[OK]") - - fmt.Print("Installing certificates ... ") - src := utils.CertificateFiles{ - CertPath: userCertPath, - KeyPath: userKeyPath, - CAPath: userCAPath, - } - dest := utils.CertificateFiles{ - CertPath: config.IntegrationCertPath, - KeyPath: config.IntegrationKeyPath, - CAPath: config.IntegrationCAPath, - } - if err := utils.LoadUserCertificatesWithStruct(src, dest); err != nil { - fmt.Printf("\nError loading certificates: %v\n", err) - os.Exit(1) - } - fmt.Println("[OK]") - - fmt.Println("TLS certificates loaded successfully!") - time.Sleep(5 * time.Second) - - case "change-port": - fmt.Println("Changing integration port ...") - integration := os.Args[2] - proto := os.Args[3] - port := os.Args[4] - - old, err := modules.ChangePort(integration, proto, port) - if err != nil { - fmt.Println("Error trying to change integration port: ", err) - os.Exit(1) - } - fmt.Printf("Port changed correctly from %s to %s\n", old, port) - time.Sleep(5 * time.Second) - - case "change-retention": - fmt.Println("Changing log retention ...") - retention := os.Args[2] - - if err := logservice.SetDataRetention(retention); err != nil { - fmt.Println("Error trying to change retention: ", err) - os.Exit(1) - } - - fmt.Printf("Retention changed correctly to %s\n", retention) - time.Sleep(5 * time.Second) - - case "clean-logs": - fmt.Println("Cleaning old logs ...") - db := database.GetDB() - datR, err := logservice.GetDataRetention() - if err != nil { - fmt.Println("Error getting retention: ", err) - os.Exit(1) - } - _, err = db.DeleteOld(models.Log{}, datR) - if err != nil { - fmt.Println("Error cleaning logs: ", err) - os.Exit(1) - } - fmt.Println("Logs cleaned correctly") - time.Sleep(5 * time.Second) - - case "uninstall": - fmt.Println("Uninstalling UTMStackAgent service ...") - - fmt.Print("Stopping UTMStackUpdater service... ") - updaterPath := filepath.Join(utils.GetMyPath(), fmt.Sprintf(config.UpdaterFile, "")) - if utils.CheckIfPathExist(updaterPath) { - err := utils.Execute(updaterPath, utils.GetMyPath(), "uninstall") - if err != nil { - fmt.Printf("Warning: %v\n", err) - } else { - fmt.Println("[OK]") - } - time.Sleep(2 * time.Second) - } else { - fmt.Println("[SKIPPED - not found]") - } - - cnf, err := config.GetCurrentConfig() - if err != nil { - fmt.Println("Error getting config: ", err) - os.Exit(1) - } - if err = pb.DeleteAgent(cnf); err != nil { - utils.Logger.ErrorF("error deleting agent: %v", err) - } - if err = collectors.UninstallCollectors(); err != nil { - utils.Logger.Fatal("error uninstalling collectors: %v", err) - } - os.Remove(config.ConfigurationFile) - - serv.UninstallService() - - fmt.Println("[OK]") - fmt.Println("UTMStackAgent service uninstalled correctly") - os.Exit(1) - case "help": - Help() - default: - fmt.Println("unknown option") - } - } else { - serv.RunService() - } -} - -func Help() { - fmt.Println("### UTMStackAgent ###") - fmt.Println("Usage:") - fmt.Println(" To run the service: ./utmstack_agent run") - fmt.Println(" To install the service: ./utmstack_agent install") - fmt.Println(" To enable integration: ./utmstack_agent enable-integration [--tls]") - fmt.Println(" To disable integration: ./utmstack_agent disable-integration ") - fmt.Println(" To change integration port: ./utmstack_agent change-port ") - fmt.Println(" To change log retention: ./utmstack_agent change-retention ") - fmt.Println(" To clean old logs: ./utmstack_agent clean-logs") - fmt.Println(" To load user TLS certificates: ./utmstack_agent load-tls-certs [ca]") - fmt.Println(" To check TLS certificates: ./utmstack_agent check-tls-certs") - fmt.Println(" To uninstall the service: ./utmstack_agent uninstall") - fmt.Println(" For help (this message): ./utmstack_agent help") - fmt.Println() - fmt.Println("Options:") - fmt.Println(" run Run the UTMStackAgent service") - fmt.Println(" install Install the UTMStackAgent service") - fmt.Println(" enable-integration Enable integration for a specific and ") - fmt.Println(" Available flag: --tls (enable TLS for TCP only)") - fmt.Println(" disable-integration Disable integration for a specific and (auto-disables TLS)") - fmt.Println(" change-port Change the port for a specific and to ") - fmt.Println(" change-retention Change the log retention to . Retention must be a number of megabytes. Example: 20") - fmt.Println(" clean-logs Clean old logs from the database") - fmt.Println(" load-tls-certs Load your own TLS certificates (RECOMMENDED for production)") - fmt.Println(" check-tls-certs Check status and validity of TLS certificates") - fmt.Println(" uninstall Uninstall the UTMStackAgent service") - fmt.Println(" help Display this help message") - fmt.Println() - fmt.Println("TLS Certificate Management:") - fmt.Println(" # Load your own certificates (RECOMMENDED)") - fmt.Println(" ./utmstack_agent load-tls-certs /path/to/server.crt /path/to/server.key /path/to/ca.crt") - fmt.Println(" ./utmstack_agent load-tls-certs /path/to/server.crt /path/to/server.key # Without CA") - fmt.Println() - fmt.Println("TLS Integration Examples:") - fmt.Println(" ./utmstack_agent enable-integration syslog tcp --tls # Enable with TLS") - fmt.Println(" ./utmstack_agent enable-integration syslog tcp # Enable without TLS (default)") - fmt.Println(" ./utmstack_agent disable-integration syslog tcp # Disable (auto-disables TLS)") - fmt.Println(" ./utmstack_agent check-tls-certs # Check certificate status") - fmt.Println() - fmt.Println("Note:") - fmt.Println(" - Make sure to run commands with appropriate permissions.") - fmt.Println(" - All commands require administrative privileges.") - fmt.Println(" - For detailed logs, check the service log file.") - fmt.Println() - os.Exit(0) + cmd.Execute() } diff --git a/agent/models/data.go b/agent/models/data.go index 051820585..7c676a48a 100644 --- a/agent/models/data.go +++ b/agent/models/data.go @@ -3,3 +3,9 @@ package models type DataRetention struct { Retention int `json:"retention"` } + +// MSGDS represents a message with its data source, used by collectors. +type MSGDS struct { + DataSource string + Message string +} diff --git a/agent/modules/configuration.go b/agent/modules/configuration.go deleted file mode 100644 index 4088d080f..000000000 --- a/agent/modules/configuration.go +++ /dev/null @@ -1,295 +0,0 @@ -package modules - -import ( - "fmt" - "net" - "os" - "strings" - "time" - - "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/utils" -) - -type Port struct { - IsListen bool `json:"enabled"` - Port string `json:"value"` - TLSEnabled bool `json:"tls_enabled,omitempty"` -} - -type Integration struct { - TCP Port `json:"tcp_port,omitempty"` - UDP Port `json:"udp_port,omitempty"` -} - -type CollectorConfiguration struct { - Integrations map[string]Integration `json:"integrations"` -} - -func ReadCollectorConfig() (CollectorConfiguration, error) { - cnf := CollectorConfiguration{} - err := utils.ReadJson(config.CollectorFileName, &cnf) - if err != nil { - return cnf, err - } - - return cnf, nil -} - -func ConfigureCollectorFirstTime() error { - integrations := make(map[string]Integration) - for logTyp, ports := range config.ProtoPorts { - newIntegration := Integration{} - newIntegration.TCP.IsListen = false - newIntegration.TCP.Port = ports.TCP - newIntegration.UDP.IsListen = false - newIntegration.UDP.Port = ports.UDP - integrations[string(logTyp)] = newIntegration - } - return WriteCollectorConfig(integrations, config.CollectorFileName) -} - -func ChangeIntegrationStatus(logTyp string, proto string, isEnabled bool, tlsOptions ...bool) (string, error) { - var port string - cnf, err := ReadCollectorConfig() - if err != nil { - return "", fmt.Errorf("error reading collector config: %v", err) - } - - if valid := config.ValidateModuleType(logTyp); valid == "nil" { - return "", fmt.Errorf("invalid integration: %s", logTyp) - } - - integration := cnf.Integrations[logTyp] - switch proto { - case "tcp": - integration.TCP.IsListen = isEnabled - port = integration.TCP.Port - - // Handle TLS configuration if specified - if len(tlsOptions) > 0 && isEnabled { - if tlsOptions[0] { - if !utils.CheckIfPathExist(config.IntegrationCertPath) || !utils.CheckIfPathExist(config.IntegrationKeyPath) { - return "", fmt.Errorf("TLS certificates not found. Please load certificates first") - } - // Enable TLS - integration.TCP.TLSEnabled = true - mod := GetModule(logTyp) - if mod != nil && mod.IsPortListen(proto) { - mod.DisablePort(proto) - time.Sleep(200 * time.Millisecond) - err := mod.EnablePort(proto, true) - if err != nil { - return "", fmt.Errorf("error enabling TLS on running module: %v", err) - } - } - } else { - // Disable TLS - integration.TCP.TLSEnabled = false - mod := GetModule(logTyp) - if mod != nil && mod.IsPortListen(proto) { - mod.DisablePort(proto) - time.Sleep(200 * time.Millisecond) - err := mod.EnablePort(proto, false) - if err != nil { - return "", fmt.Errorf("error disabling TLS on running module: %v", err) - } - } - } - } - - // Auto-disable TLS when disabling integration - if !isEnabled { - integration.TCP.TLSEnabled = false - } - - case "udp": - integration.UDP.IsListen = isEnabled - port = integration.UDP.Port - - // TLS validation for UDP - if len(tlsOptions) > 0 && tlsOptions[0] { - return "", fmt.Errorf("TLS is not supported for UDP protocol. Use TCP for TLS connections") - } - - default: - return "", fmt.Errorf("invalid protocol: %s", proto) - } - - cnf.Integrations[logTyp] = integration - return port, WriteCollectorConfig(cnf.Integrations, config.CollectorFileName) -} - -func ChangePort(logTyp string, proto string, port string) (string, error) { - var old string - cnf, err := ReadCollectorConfig() - if err != nil { - return "", fmt.Errorf("error reading collector config: %v", err) - } - - if valid := config.ValidateModuleType(logTyp); valid == "nil" { - return "", fmt.Errorf("invalid integration: %s", logTyp) - } - - if changeValid := ValidateChangeInPort(port, logTyp); !changeValid { - return "", fmt.Errorf("change in port %s protocol %s not allowed for %s or out range %s-%s", port, proto, logTyp, config.PortRangeMin, config.PortRangeMax) - } - - if !IsPortAvailable(port, proto, &cnf, logTyp) { - return "", fmt.Errorf("port %s is already in use", port) - } - - integration := cnf.Integrations[logTyp] - switch proto { - case "tcp": - old = integration.TCP.Port - integration.TCP.Port = port - case "udp": - old = integration.UDP.Port - integration.UDP.Port = port - default: - return "", fmt.Errorf("invalid protocol: %s", proto) - } - - cnf.Integrations[logTyp] = integration - return old, WriteCollectorConfig(cnf.Integrations, config.CollectorFileName) -} - -func IsPortAvailable(port string, proto string, cnf *CollectorConfiguration, currentIntegration string) bool { - for integration, integrationConfig := range cnf.Integrations { - if integration != currentIntegration { - if integrationConfig.TCP.Port == port || integrationConfig.UDP.Port == port { - return false - } - } - } - - listener, err := net.Listen(proto, ":"+port) - if err != nil { - return false - } - - listener.Close() - - return true -} - -func WriteCollectorConfig(integrations map[string]Integration, filename string) error { - fileContent := "{\n \"integrations\": {\n" - for name, integration := range integrations { - fileContent += fmt.Sprintf(" \"%s\": {\n", name) - if integration.TCP.Port != "" { - fileContent += fmt.Sprintf(" \"tcp_port\": {\"enabled\": %t, \"value\": \"%s\"", integration.TCP.IsListen, integration.TCP.Port) - if integration.TCP.TLSEnabled { - fileContent += fmt.Sprintf(", \"tls_enabled\": %t", integration.TCP.TLSEnabled) - } - fileContent += "},\n" - } - if integration.UDP.Port != "" { - fileContent += fmt.Sprintf(" \"udp_port\": {\"enabled\": %t, \"value\": \"%s\"},\n", integration.UDP.IsListen, integration.UDP.Port) - } - if strings.HasSuffix(fileContent, ",\n") { - fileContent = fileContent[:len(fileContent)-2] + "\n" - } - fileContent += " },\n" - } - if strings.HasSuffix(fileContent, ",\n") { - fileContent = fileContent[:len(fileContent)-2] + "\n" - } - fileContent += " }\n}\n" - - err := os.WriteFile(filename, []byte(fileContent), 0644) - if err != nil { - return err - } - - return nil -} - -func WriteCollectorConfigFromModules(mod []Module, filename string) error { - integrations := make(map[string]Integration) - - for _, m := range mod { - integrations[m.GetDataType()] = Integration{ - TCP: Port{ - IsListen: m.IsPortListen("tcp"), - Port: m.GetPort("tcp"), - }, - UDP: Port{ - IsListen: m.IsPortListen("udp"), - Port: m.GetPort("udp"), - }, - } - } - return WriteCollectorConfig(integrations, filename) -} - -func EnableTLSForIntegration(logTyp string, proto string) (string, error) { - cnf, err := ReadCollectorConfig() - if err != nil { - return "", fmt.Errorf("error reading collector config: %v", err) - } - - if valid := config.ValidateModuleType(logTyp); valid == "nil" { - return "", fmt.Errorf("invalid integration: %s", logTyp) - } - - integration := cnf.Integrations[logTyp] - var port string - - switch proto { - case "tcp": - if integration.TCP.Port == "" { - return "", fmt.Errorf("TCP port not configured for %s", logTyp) - } - port = integration.TCP.Port - integration.TCP.TLSEnabled = true - - mod := GetModule(logTyp) - if mod != nil && mod.IsPortListen(proto) { - mod.DisablePort(proto) - time.Sleep(200 * time.Millisecond) - err := mod.EnablePort(proto, true) - if err != nil { - return port, fmt.Errorf("error enabling TLS on running module: %v", err) - } - } - case "udp": - return "", fmt.Errorf("TLS not supported for UDP protocol") - default: - return "", fmt.Errorf("invalid protocol: %s", proto) - } - - cnf.Integrations[logTyp] = integration - return port, WriteCollectorConfig(cnf.Integrations, config.CollectorFileName) -} - -func DisableTLSForIntegration(logTyp string, proto string) error { - cnf, err := ReadCollectorConfig() - if err != nil { - return fmt.Errorf("error reading collector config: %v", err) - } - - integration := cnf.Integrations[logTyp] - switch proto { - case "tcp": - integration.TCP.TLSEnabled = false - - mod := GetModule(logTyp) - if mod != nil && mod.IsPortListen(proto) { - mod.DisablePort(proto) - time.Sleep(200 * time.Millisecond) - err := mod.EnablePort(proto, false) - if err != nil { - return fmt.Errorf("error disabling TLS on running module: %v", err) - } - } - case "udp": - return fmt.Errorf("TLS not supported for UDP protocol") - default: - return fmt.Errorf("invalid protocol: %s", proto) - } - - cnf.Integrations[logTyp] = integration - return WriteCollectorConfig(cnf.Integrations, config.CollectorFileName) -} diff --git a/agent/modules/modules.go b/agent/modules/modules.go deleted file mode 100644 index b5e247cba..000000000 --- a/agent/modules/modules.go +++ /dev/null @@ -1,153 +0,0 @@ -package modules - -import ( - "time" - - "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/utils" -) - -const ( - delayCheckSyslogCnfig = 10 * time.Second -) - -var ( - moCache = make([]Module, 0, 10) -) - -type Module interface { - GetDataType() string - IsPortListen(proto string) bool - SetNewPort(proto string, port string) - GetPort(proto string) string - EnablePort(proto string, enableTLS bool) error - DisablePort(proto string) -} - -func GetModule(typ string) Module { - switch config.ValidateModuleType(typ) { - case "syslog": - return GetSyslogModule(typ, config.ProtoPorts[config.DataType(typ)]) - case "netflow": - return GetNetflowModule() - default: - return nil - } -} - -func StartModules() { - for { - time.Sleep(delayCheckSyslogCnfig) - logCollectorConfig, err := ReadCollectorConfig() - if err != nil { - utils.Logger.Fatal("error reading collector config: %v", err) - } - - for intType, cnf := range logCollectorConfig.Integrations { - index := -1 - for i, mod := range moCache { - if mod.GetDataType() == intType { - index = i - break - } - } - - if index == -1 { - newModule := GetModule(intType) - if newModule == nil { - utils.Logger.LogF(100, "error getting module %s", intType) - continue - } - moCache = append(moCache, newModule) - index = len(moCache) - 1 - } - - configs, err := processConfigs(moCache[index], cnf) - if err != nil { - utils.Logger.ErrorF("error processing configs: %v", err) - continue - } - - for proto, conf := range configs { - changeAllowed := true - port := "" - - switch proto { - case "tcp": - port = cnf.TCP.Port - case "udp": - port = cnf.UDP.Port - } - - if port != "" && moCache[index].GetPort(proto) != port { - changeAllowed = ValidateChangeInPort(port, intType) - } - if conf[0] { - moCache[index].DisablePort(proto) - if conf[1] { - time.Sleep(200 * time.Millisecond) - } - } - if changeAllowed { - moCache[index].SetNewPort(proto, port) - if conf[1] { - enableTLS := proto == "tcp" && cnf.TCP.TLSEnabled - - err := moCache[index].EnablePort(proto, enableTLS) - if err != nil { - utils.Logger.ErrorF("error enabling port for %s %s: %v", intType, proto, err) - } - } - } else { - utils.Logger.Info("change in port %s protocol %s not allowed for %s or out range %s-%s", port, proto, intType, config.PortRangeMin, config.PortRangeMax) - err := WriteCollectorConfigFromModules(moCache, config.CollectorFileName) - if err != nil { - utils.Logger.ErrorF("error fixing collector config: %v", err) - continue - } - } - } - } - } -} - -func processConfigs(mod Module, cnf Integration) (map[string][]bool, error) { - configs := make(map[string][]bool) // first bool is if is necessary kill the port, second bool is if is necessary start the port - - protocols := []string{"tcp", "udp"} - for _, protocol := range protocols { - var isEnabled bool - var port string - - switch protocol { - case "tcp": - isEnabled = cnf.TCP.IsListen - port = cnf.TCP.Port - case "udp": - isEnabled = cnf.UDP.IsListen - port = cnf.UDP.Port - } - - if mod.IsPortListen(protocol) && !isEnabled { - configs[protocol] = []bool{true, false} - } else if !mod.IsPortListen(protocol) && isEnabled { - configs[protocol] = []bool{false, true} - } else if mod.IsPortListen(protocol) && isEnabled && mod.GetPort(protocol) != port { - configs[protocol] = []bool{true, true} - } else { - configs[protocol] = []bool{false, false} - } - } - - return configs, nil -} - -// ValidateChangeInPort returns true if the port change is allowed -func ValidateChangeInPort(newPort string, dataType string) bool { - for _, logType := range config.ProhibitedPortsChange { - if string(logType) == dataType { - return false - } - } - return config.PortRangeMin <= newPort && newPort <= config.PortRangeMax -} diff --git a/agent/modules/netflow.go b/agent/modules/netflow.go deleted file mode 100644 index e3ff518b2..000000000 --- a/agent/modules/netflow.go +++ /dev/null @@ -1,352 +0,0 @@ -package modules - -import ( - "bytes" - "context" - "encoding/binary" - "errors" - "fmt" - "net" - "strconv" - "strings" - "sync" - "time" - - "github.com/netsampler/goflow2/decoders/netflow" - "github.com/netsampler/goflow2/decoders/netflowlegacy" - tehmaze "github.com/tehmaze/netflow" - "github.com/tehmaze/netflow/session" - "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/logservice" - "github.com/utmstack/UTMStack/agent/parser" - "github.com/utmstack/UTMStack/agent/utils" -) - -var ( - netflowModule *NetflowModule - netflowOnce sync.Once -) - -// templateSystem implements netflow.NetFlowTemplateSystem for goflow2 -type templateSystem struct { - templates map[uint16]map[uint32]map[uint16]interface{} - mu sync.RWMutex -} - -func newTemplateSystem() *templateSystem { - return &templateSystem{ - templates: make(map[uint16]map[uint32]map[uint16]interface{}), - } -} - -func (t *templateSystem) GetTemplate(version uint16, obsDomainId uint32, templateId uint16) (interface{}, error) { - t.mu.RLock() - defer t.mu.RUnlock() - - if versionMap, ok := t.templates[version]; ok { - if domainMap, ok := versionMap[obsDomainId]; ok { - if template, ok := domainMap[templateId]; ok { - return template, nil - } - } - } - return nil, fmt.Errorf("template not found: version=%d, obsDomainId=%d, templateId=%d", version, obsDomainId, templateId) -} - -func (t *templateSystem) AddTemplate(version uint16, obsDomainId uint32, template interface{}) { - t.mu.Lock() - defer t.mu.Unlock() - - if _, ok := t.templates[version]; !ok { - t.templates[version] = make(map[uint32]map[uint16]interface{}) - } - if _, ok := t.templates[version][obsDomainId]; !ok { - t.templates[version][obsDomainId] = make(map[uint16]interface{}) - } - - // Extract template ID based on type - var templateId uint16 - switch tmpl := template.(type) { - case netflow.TemplateRecord: - templateId = tmpl.TemplateId - case netflow.IPFIXOptionsTemplateRecord: - templateId = tmpl.TemplateId - case netflow.NFv9OptionsTemplateRecord: - templateId = tmpl.TemplateId - default: - return - } - - t.templates[version][obsDomainId][templateId] = template -} - -type NetflowModule struct { - DataType string - Parser parser.Parser - LegacyDecoders map[string]*tehmaze.Decoder // For v1, v6, v7 (tehmaze/netflow) - TemplateSystem map[string]*templateSystem // For v5, v9, IPFIX (goflow2) - Listener *net.UDPConn - CTX context.Context - Cancel context.CancelFunc - IsEnabled bool - mu sync.RWMutex -} - -func GetNetflowModule() *NetflowModule { - netflowOnce.Do(func() { - netflowModule = &NetflowModule{ - Parser: parser.GetParser("netflow"), - DataType: "netflow", - IsEnabled: false, - LegacyDecoders: make(map[string]*tehmaze.Decoder), - TemplateSystem: make(map[string]*templateSystem), - } - }) - return netflowModule -} - -func (m *NetflowModule) getOrCreateTemplateSystem(addr string) *templateSystem { - m.mu.Lock() - defer m.mu.Unlock() - - if ts, ok := m.TemplateSystem[addr]; ok { - return ts - } - ts := newTemplateSystem() - m.TemplateSystem[addr] = ts - return ts -} - -func (m *NetflowModule) getOrCreateLegacyDecoder(addr string) *tehmaze.Decoder { - m.mu.Lock() - defer m.mu.Unlock() - - if d, ok := m.LegacyDecoders[addr]; ok { - return d - } - s := session.New() - d := tehmaze.NewDecoder(s) - m.LegacyDecoders[addr] = d - return d -} - -func (m *NetflowModule) removeLegacyDecoder(addr string) { - m.mu.Lock() - defer m.mu.Unlock() - delete(m.LegacyDecoders, addr) -} - -func (m *NetflowModule) EnablePort(proto string, enableTLS bool) error { - if enableTLS { - return fmt.Errorf("TLS not supported for NetFlow protocol") - } - - if proto == "udp" && !m.IsEnabled { - port, err := strconv.Atoi(config.ProtoPorts[config.DataTypeNetflow].UDP) - if err != nil { - utils.Logger.ErrorF("error converting port to int: %v", err) - return err - } - - listener, err := net.ListenUDP("udp", &net.UDPAddr{ - Port: port, - IP: net.ParseIP("0.0.0.0"), - }) - if err != nil { - utils.Logger.ErrorF("error listening netflow: %v", err) - return err - } - - m.IsEnabled = true - m.Listener = listener - m.CTX, m.Cancel = context.WithCancel(context.Background()) - - utils.Logger.Info("Server %s listening in port: %s protocol: UDP", m.DataType, config.ProtoPorts[config.DataTypeNetflow].UDP) - - buffer := make([]byte, 65535) - - go func() { - for { - select { - case <-m.CTX.Done(): - return - default: - m.Listener.SetDeadline(time.Now().Add(1 * time.Second)) - - length, addr, err := m.Listener.ReadFromUDP(buffer) - if err != nil { - if errors.Is(err, net.ErrClosed) { - return - } - - var netOpErr *net.OpError - ok := errors.As(err, &netOpErr) - if ok && netOpErr.Timeout() { - continue - } - - utils.Logger.ErrorF("error connecting with netflow listener: %v", err) - continue - } - - // Validate packet structure before attempting to decode - packetData := buffer[:length] - packetInfo, validationErr := validateNetflowPacket(packetData) - if validationErr != nil { - utils.Logger.ErrorF("invalid NetFlow packet from %s (length: %d bytes): %v", addr.String(), length, validationErr) - continue - } - - var message interface{} - - // Use hybrid approach: goflow2 for v5/v9/IPFIX, tehmaze for v1/v6/v7 - switch packetInfo.version { - case 5: - // Use goflow2 for NetFlow v5 - msg, err := netflowlegacy.DecodeMessage(bytes.NewBuffer(packetData)) - if err != nil { - utils.Logger.ErrorF("error decoding %s message from %s: %v", packetInfo.versionName, addr.String(), err) - continue - } - message = msg - - case 9, 10: - // Use goflow2 for NetFlow v9 and IPFIX - ts := m.getOrCreateTemplateSystem(addr.String()) - msg, err := netflow.DecodeMessage(bytes.NewBuffer(packetData), ts) - if err != nil { - // Template not found is expected when data arrives before template - // This is normal NetFlow v9/IPFIX behavior, don't log as error - if !strings.Contains(err.Error(), "template not found") { - utils.Logger.ErrorF("error decoding %s message from %s: %v", packetInfo.versionName, addr.String(), err) - } - continue - } - message = msg - - case 1, 6, 7: - // Use tehmaze/netflow for legacy versions (v1, v6, v7) - d := m.getOrCreateLegacyDecoder(addr.String()) - msg, err := d.Read(bytes.NewBuffer(packetData)) - if err != nil { - utils.Logger.ErrorF("error decoding %s message from %s: %v", packetInfo.versionName, addr.String(), err) - m.removeLegacyDecoder(addr.String()) - continue - } - message = msg - - default: - utils.Logger.ErrorF("unsupported NetFlow version %d from %s", packetInfo.version, addr.String()) - continue - } - - err = m.Parser.ProcessData(parser.NetflowObject{ - Remote: addr.String(), - Message: message, - }, "", logservice.LogQueue) - if err != nil { - utils.Logger.ErrorF("error parsing netflow: %v", err) - } - } - } - }() - return nil - } - return fmt.Errorf("NetFlow only supports UDP protocol") -} - -func (m *NetflowModule) DisablePort(proto string) { - if proto == "udp" && m.IsEnabled { - utils.Logger.Info("Server %s closed in port: %s protocol: UDP", m.DataType, config.ProtoPorts[config.DataTypeNetflow].UDP) - m.Cancel() - m.Listener.Close() - m.IsEnabled = false - } -} - -func (m *NetflowModule) GetDataType() string { - return m.DataType -} - -func (m *NetflowModule) IsPortListen(proto string) bool { - switch proto { - case "udp": - return m.IsEnabled - default: - return false - } -} - -func (m *NetflowModule) SetNewPort(_ string, _ string) { - //TODO: implement this function -} - -func (m *NetflowModule) GetPort(proto string) string { - switch proto { - case "udp": - return config.ProtoPorts[config.DataTypeNetflow].UDP - default: - return "" - } -} - -// netflowPacketInfo contains basic information about a NetFlow packet for validation -type netflowPacketInfo struct { - version uint16 - count uint16 - minSize int - versionName string -} - -// validateNetflowPacket checks if a NetFlow packet has valid structure before decoding -// Returns packet info if valid, error otherwise -func validateNetflowPacket(data []byte) (*netflowPacketInfo, error) { - if len(data) < 4 { - return nil, fmt.Errorf("packet too small: %d bytes (minimum 4 bytes for version and count)", len(data)) - } - - version := binary.BigEndian.Uint16(data[0:2]) - count := binary.BigEndian.Uint16(data[2:4]) - - info := &netflowPacketInfo{ - version: version, - count: count, - } - - switch version { - case 1: - info.versionName = "NetFlow v1" - info.minSize = 24 + int(count)*48 // header (24) + records (48 each) - case 5: - info.versionName = "NetFlow v5" - info.minSize = 24 + int(count)*48 // header (24) + records (48 each) - case 6: - info.versionName = "NetFlow v6" - info.minSize = 24 + int(count)*52 // header (24) + records (52 each) - case 7: - info.versionName = "NetFlow v7" - info.minSize = 24 + int(count)*52 // header (24) + records (52 each) - case 9: - info.versionName = "NetFlow v9" - // NetFlow v9 header is 20 bytes, minimum packet size is just the header - info.minSize = 20 - case 10: - info.versionName = "IPFIX" - // IPFIX header is 16 bytes, field at offset 2-4 is the total message length - info.minSize = 16 - ipfixLength := binary.BigEndian.Uint16(data[2:4]) - if int(ipfixLength) != len(data) { - return nil, fmt.Errorf("IPFIX length mismatch: header says %d bytes, received %d bytes", ipfixLength, len(data)) - } - return info, nil - default: - return nil, fmt.Errorf("unsupported NetFlow version: %d", version) - } - - if len(data) < info.minSize { - return nil, fmt.Errorf("%s packet too small: received %d bytes, minimum expected %d bytes (count=%d)", - info.versionName, len(data), info.minSize, count) - } - - return info, nil -} diff --git a/agent/modules/syslog.go b/agent/modules/syslog.go deleted file mode 100644 index 5d0987a11..000000000 --- a/agent/modules/syslog.go +++ /dev/null @@ -1,634 +0,0 @@ -package modules - -import ( - "bufio" - "context" - "crypto/tls" - "errors" - "fmt" - "io" - "net" - "os" - "strconv" - "strings" - "sync" - "time" - - "github.com/threatwinds/go-sdk/entities" - "github.com/threatwinds/go-sdk/plugins" - "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/logservice" - "github.com/utmstack/UTMStack/agent/parser" - "github.com/utmstack/UTMStack/agent/utils" -) - -var ( - syslogModules = make(map[string]*SyslogModule) - syslogMutex sync.RWMutex -) - -const ( - MinBufferSize = 480 - RecommendedBufferSize = 2048 - MaxBufferSize = 8192 - UDPBufferSize = 2048 -) - -type FramingMethod int - -const ( - FramingNewline FramingMethod = iota - FramingOctetCounting -) - -type SyslogModule struct { - DataType string - TCPListener listenerTCP - UDPListener listenerUDP - Parser parser.Parser - mu sync.RWMutex -} - -type listenerTCP struct { - Listener net.Listener - CTX context.Context - Cancel context.CancelFunc - IsEnabled bool - Port string - TLSEnabled bool -} - -type listenerUDP struct { - Listener net.PacketConn - CTX context.Context - Cancel context.CancelFunc - IsEnabled bool - Port string -} - -func GetSyslogModule(dataType string, protoPorts config.ProtoPort) *SyslogModule { - syslogMutex.Lock() - defer syslogMutex.Unlock() - - if mod, exists := syslogModules[dataType]; exists { - return mod - } - - newModule := &SyslogModule{ - DataType: dataType, - TCPListener: listenerTCP{ - IsEnabled: false, - Port: protoPorts.TCP, - }, - UDPListener: listenerUDP{ - IsEnabled: false, - Port: protoPorts.UDP, - }, - Parser: parser.GetParser(dataType), - } - - syslogModules[dataType] = newModule - return newModule -} - -func (m *SyslogModule) GetDataType() string { - return m.DataType -} - -func (m *SyslogModule) IsPortListen(proto string) bool { - m.mu.RLock() - defer m.mu.RUnlock() - - switch proto { - case "tcp": - return m.TCPListener.IsEnabled - case "udp": - return m.UDPListener.IsEnabled - default: - return false - } -} - -func (m *SyslogModule) SetNewPort(proto string, port string) { - m.mu.Lock() - defer m.mu.Unlock() - - switch proto { - case "tcp": - m.TCPListener.Port = port - case "udp": - m.UDPListener.Port = port - } -} - -func (m *SyslogModule) GetPort(proto string) string { - m.mu.RLock() - defer m.mu.RUnlock() - - switch proto { - case "tcp": - return m.TCPListener.Port - case "udp": - return m.UDPListener.Port - default: - return "" - } -} - -func (m *SyslogModule) EnablePort(proto string, enableTLS bool) error { - switch proto { - case "tcp": - if enableTLS { - if !utils.CheckIfPathExist(config.IntegrationCertPath) || !utils.CheckIfPathExist(config.IntegrationKeyPath) { - return fmt.Errorf("TLS certificates not found. Please load certificates first") - } - } - - m.TCPListener.TLSEnabled = enableTLS - go m.enableTCP() - return nil - case "udp": - if enableTLS { - return fmt.Errorf("TLS not supported for UDP protocol") - } - go m.enableUDP() - return nil - default: - return fmt.Errorf("unsupported protocol: %s", proto) - } -} - -func (m *SyslogModule) DisablePort(proto string) { - switch proto { - case "tcp": - m.disableTCP() - case "udp": - m.disableUDP() - } -} - -func (m *SyslogModule) enableTCP() { - m.mu.Lock() - if m.TCPListener.IsEnabled || m.TCPListener.Port == "" { - m.mu.Unlock() - return - } - - listener, err := net.Listen("tcp", "0.0.0.0:"+m.TCPListener.Port) - if err != nil { - m.mu.Unlock() - utils.Logger.ErrorF("error listening TCP in port %s: %v", m.TCPListener.Port, err) - return - } - - // Solo setear IsEnabled DESPUÉS de confirmar que el listener está activo - m.TCPListener.IsEnabled = true - m.TCPListener.Listener = listener - m.TCPListener.CTX, m.TCPListener.Cancel = context.WithCancel(context.Background()) - m.mu.Unlock() - - utils.Logger.Info("Server %s listening in port: %s protocol: TCP", m.DataType, m.TCPListener.Port) - if m.TCPListener.TLSEnabled { - utils.Logger.Info("Server %s TLS enabled in port: %s protocol: TCP", m.DataType, m.TCPListener.Port) - } - - go func() { - defer func() { - err = m.TCPListener.Listener.Close() - if err != nil { - utils.Logger.ErrorF("error closing tcp listener: %v", err) - } - }() - for { - select { - case <-m.TCPListener.CTX.Done(): - return - default: - conn, err := m.TCPListener.Listener.Accept() - if err != nil { - if errors.Is(err, net.ErrClosed) { - return - } - - var netOpErr *net.OpError - ok := errors.As(err, &netOpErr) - if ok && netOpErr.Timeout() { - continue - } - - utils.Logger.ErrorF("error connecting with tcp listener: %v", err) - continue - } - - // Connection handling based on TLS configuration - if m.TCPListener.TLSEnabled { - go m.handleTLSConnection(conn) - } else { - go m.handleConnectionTCP(conn) - } - } - } - }() -} - -func (m *SyslogModule) enableUDP() { - m.mu.Lock() - if m.UDPListener.IsEnabled || m.UDPListener.Port == "" { - m.mu.Unlock() - return - } - - listener, err := net.ListenPacket("udp", "0.0.0.0:"+m.UDPListener.Port) - if err != nil { - m.mu.Unlock() - utils.Logger.ErrorF("error listening UDP in port %s: %v", m.UDPListener.Port, err) - return - } - - udpListener, ok := listener.(*net.UDPConn) - if !ok { - m.mu.Unlock() - utils.Logger.ErrorF("could not assert to *net.UDPConn") - listener.Close() - return - } - - // Solo setear IsEnabled DESPUÉS de confirmar que el listener está activo - m.UDPListener.IsEnabled = true - m.UDPListener.Listener = listener - m.UDPListener.CTX, m.UDPListener.Cancel = context.WithCancel(context.Background()) - m.mu.Unlock() - - utils.Logger.Info("Server %s listening in port: %s protocol: UDP", m.DataType, m.UDPListener.Port) - - buffer := make([]byte, UDPBufferSize) - msgChannel := make(chan config.MSGDS) - - go m.handleConnectionUDP(msgChannel) - - go func() { - defer func() { - err = m.UDPListener.Listener.Close() - if err != nil { - utils.Logger.ErrorF("error closing udp listener: %v", err) - } - }() - for { - select { - case <-m.UDPListener.CTX.Done(): - return - default: - udpListener.SetDeadline(time.Now().Add(time.Second * 1)) - - n, add, err := listener.ReadFrom(buffer) - if err != nil { - if errors.Is(err, net.ErrClosed) { - return - } - - var netOpErr *net.OpError - ok := errors.As(err, &netOpErr) - if ok && netOpErr.Timeout() { - continue - } - - utils.Logger.ErrorF("error connecting with udp listener: %v", err) - continue - } - remoteAddr := add.String() - remoteAddr, _, err = net.SplitHostPort(remoteAddr) - if err != nil { - utils.Logger.ErrorF("error getting remote addr: %v", err) - continue - } - if remoteAddr == "127.0.0.1" { - remoteAddr, err = os.Hostname() - if err != nil { - utils.Logger.ErrorF("error getting hostname: %v\n", err) - continue - } - } - msgChannel <- config.MSGDS{ - DataSource: remoteAddr, - Message: string(buffer[:n]), - } - } - } - }() -} - -func (m *SyslogModule) disableTCP() { - m.mu.Lock() - defer m.mu.Unlock() - - if m.TCPListener.IsEnabled && m.TCPListener.Port != "" { - utils.Logger.Info("Server %s closed in port: %s protocol: TCP", m.DataType, m.TCPListener.Port) - - if m.TCPListener.Listener != nil { - if err := m.TCPListener.Listener.Close(); err != nil { - utils.Logger.ErrorF("error closing TCP listener: %v", err) - } - } - - m.TCPListener.Cancel() - m.TCPListener.IsEnabled = false - } -} - -func (m *SyslogModule) disableUDP() { - m.mu.Lock() - defer m.mu.Unlock() - - if m.UDPListener.IsEnabled && m.UDPListener.Port != "" { - utils.Logger.Info("Server %s closed in port: %s protocol: UDP", m.DataType, m.UDPListener.Port) - - if m.UDPListener.Listener != nil { - if err := m.UDPListener.Listener.Close(); err != nil { - utils.Logger.ErrorF("error closing UDP listener: %v", err) - } - } - - m.UDPListener.Cancel() - m.UDPListener.IsEnabled = false - } -} - -// detectFramingMethod detects the syslog framing method by peeking at the first byte -func detectFramingMethod(reader *bufio.Reader) (FramingMethod, error) { - firstByte, err := reader.Peek(1) - if err != nil { - utils.Logger.ErrorF("failed to peek first byte for framing detection: %v", err) - return 0, fmt.Errorf("failed to peek first byte: %w", err) - } - - if firstByte[0] >= '0' && firstByte[0] <= '9' { - return FramingOctetCounting, nil - } - - if firstByte[0] == '<' { - return FramingNewline, nil - } - - utils.Logger.ErrorF("unknown framing method detected, first byte: 0x%02x", firstByte[0]) - return 0, fmt.Errorf("unknown framing method, first byte: 0x%02x", firstByte[0]) -} - -// readOctetCountingFrame reads a syslog message using octet counting framing method -func readOctetCountingFrame(reader *bufio.Reader) (string, error) { - lengthStr, err := reader.ReadString(' ') - if err != nil { - utils.Logger.ErrorF("failed to read message length in octet counting frame: %v", err) - return "", fmt.Errorf("failed to read message length: %w", err) - } - - lengthStr = strings.TrimSuffix(lengthStr, " ") - msgLen, err := strconv.Atoi(lengthStr) - if err != nil { - utils.Logger.ErrorF("invalid message length '%s' in octet counting frame: %v", lengthStr, err) - return "", fmt.Errorf("invalid message length '%s': %w", lengthStr, err) - } - - if msgLen < 1 { - utils.Logger.ErrorF("message length %d is too small (minimum 1 byte)", msgLen) - return "", fmt.Errorf("message length %d is too small (minimum 1)", msgLen) - } - if msgLen > MaxBufferSize { - utils.Logger.ErrorF("message length %d exceeds maximum %d bytes", msgLen, MaxBufferSize) - return "", fmt.Errorf("message length %d exceeds maximum %d", msgLen, MaxBufferSize) - } - - msgBytes := make([]byte, msgLen) - _, err = io.ReadFull(reader, msgBytes) - if err != nil { - utils.Logger.ErrorF("failed to read %d byte message body: %v", msgLen, err) - return "", fmt.Errorf("failed to read %d byte message body: %w", msgLen, err) - } - - return string(msgBytes), nil -} - -// readNewlineFrame reads a syslog message using newline-delimited framing method -func readNewlineFrame(reader *bufio.Reader) (string, error) { - message, err := reader.ReadString('\n') - if err != nil { - utils.Logger.ErrorF("failed to read newline-delimited message: %v", err) - return "", fmt.Errorf("failed to read newline-delimited message: %w", err) - } - return message, nil -} - -// readSyslogMessage reads a syslog message with automatic framing detection -func readSyslogMessage(reader *bufio.Reader) (string, error) { - method, err := detectFramingMethod(reader) - if err != nil { - return "", err - } - - switch method { - case FramingOctetCounting: - return readOctetCountingFrame(reader) - case FramingNewline: - return readNewlineFrame(reader) - default: - utils.Logger.ErrorF("unsupported framing method: %d", method) - return "", fmt.Errorf("unsupported framing method: %d", method) - } -} - -func (m *SyslogModule) handleConnectionTCP(c net.Conn) { - defer c.Close() - reader := bufio.NewReader(c) - remoteAddr := c.RemoteAddr().String() - - var err error - remoteAddr, _, err = net.SplitHostPort(remoteAddr) - if err != nil { - utils.Logger.ErrorF("error spliting host and port: %v", err) - } - - if remoteAddr == "127.0.0.1" { - remoteAddr, err = os.Hostname() - if err != nil { - utils.Logger.ErrorF("error getting hostname: %v\n", err) - } - } - - // Detect and reject TLS connections when TLS is disabled - c.SetReadDeadline(time.Now().Add(5 * time.Second)) - firstBytes := make([]byte, 3) - n, err := reader.Read(firstBytes) - if err != nil { - utils.Logger.ErrorF("error reading initial bytes from %s: %v", remoteAddr, err) - return - } - - // TLS handshake starts with: 0x16 (22 decimal) for TLS 1.0-1.3 - if n >= 1 && firstBytes[0] == 0x16 { - utils.Logger.ErrorF("TLS connection rejected from %s: TLS is disabled, only plain text connections accepted", remoteAddr) - return - } - - // Reset deadline and create a new reader that includes the read bytes - c.SetReadDeadline(time.Time{}) - reader = bufio.NewReader(io.MultiReader(strings.NewReader(string(firstBytes[:n])), reader)) - - msgChannel := make(chan config.MSGDS) - go m.handleMessageTCP(msgChannel) - - for { - select { - case <-m.TCPListener.CTX.Done(): - return - default: - message, err := readSyslogMessage(reader) - if err != nil { - if err == io.EOF { - utils.Logger.Info("TCP connection closed by %s", remoteAddr) - return - } - if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - utils.Logger.Info("TCP connection timeout from %s", remoteAddr) - return - } - utils.Logger.ErrorF("error reading syslog message from %s: %v", remoteAddr, err) - return - } - msgChannel <- config.MSGDS{ - DataSource: remoteAddr, - Message: message, - } - } - } -} - -func (m *SyslogModule) handleTLSConnection(conn net.Conn) { - defer conn.Close() - - remoteAddr := conn.RemoteAddr().String() - remoteAddr, _, err := net.SplitHostPort(remoteAddr) - if err != nil { - utils.Logger.ErrorF("error splitting host and port: %v", err) - remoteAddr = "unknown" - } - - if remoteAddr == "127.0.0.1" { - if hostname, err := os.Hostname(); err == nil { - remoteAddr = hostname - } - } - - tlsConfig, err := utils.LoadIntegrationTLSConfig( - config.IntegrationCertPath, - config.IntegrationKeyPath, - ) - if err != nil { - utils.Logger.ErrorF("error loading TLS config: %v", err) - return - } - - tlsConn := tls.Server(conn, tlsConfig) - - conn.SetDeadline(time.Now().Add(10 * time.Second)) - if err := tlsConn.Handshake(); err != nil { - utils.Logger.ErrorF("TLS handshake failed from %s: %v", remoteAddr, err) - return - } - // Keep a reasonable read timeout instead of removing it entirely - conn.SetDeadline(time.Now().Add(30 * time.Second)) - - reader := bufio.NewReader(tlsConn) - msgChannel := make(chan config.MSGDS) - go m.handleMessageTCP(msgChannel) - - for { - select { - case <-m.TCPListener.CTX.Done(): - return - default: - // Set read timeout for each message - conn.SetDeadline(time.Now().Add(30 * time.Second)) - message, err := readSyslogMessage(reader) - if err != nil { - if err == io.EOF { - utils.Logger.Info("TLS connection closed by %s", remoteAddr) - return - } - if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - utils.Logger.Info("TLS connection timeout from %s", remoteAddr) - return - } - utils.Logger.ErrorF("error reading TLS data from %s: %v", remoteAddr, err) - return - } - msgChannel <- config.MSGDS{ - DataSource: remoteAddr, - Message: message, - } - } - } -} - -func (m *SyslogModule) handleMessageTCP(logsChannel chan config.MSGDS) { - for { - select { - case <-m.TCPListener.CTX.Done(): - return - - case msgDS := <-logsChannel: - message := msgDS.Message - message = strings.TrimSuffix(message, "\n") - message, _, err := entities.ValidateString(message, false) - if err != nil { - utils.Logger.ErrorF("error validating string: %v: message: %s", err, message) - continue - } - - if m.Parser != nil { - err := m.Parser.ProcessData(message, msgDS.DataSource, logservice.LogQueue) - if err != nil { - utils.Logger.ErrorF("error parsing data: %v", err) - continue - } - } else { - logservice.LogQueue <- &plugins.Log{ - DataType: m.DataType, - DataSource: msgDS.DataSource, - Raw: message, - } - } - - } - } -} - -func (m *SyslogModule) handleConnectionUDP(logsChannel chan config.MSGDS) { - for { - select { - case <-m.UDPListener.CTX.Done(): - return - - case msgDS := <-logsChannel: - message := msgDS.Message - message = strings.TrimSuffix(message, "\n") - message, _, err := entities.ValidateString(message, false) - if err != nil { - utils.Logger.ErrorF("error validating string: %v: message: %s", err, message) - continue - } - - if m.Parser != nil { - err := m.Parser.ProcessData(message, msgDS.DataSource, logservice.LogQueue) - if err != nil { - utils.Logger.ErrorF("error parsing data: %v", err) - continue - } - } else { - logservice.LogQueue <- &plugins.Log{ - DataType: m.DataType, - DataSource: msgDS.DataSource, - Raw: message, - } - } - } - } -} diff --git a/agent/serv/service.go b/agent/serv/service.go index ec0eeb0e8..c90849e16 100644 --- a/agent/serv/service.go +++ b/agent/serv/service.go @@ -2,29 +2,32 @@ package serv import ( "context" - "fmt" "os" "os/signal" - "path/filepath" - "runtime" "strconv" + "sync" "syscall" + "time" "github.com/kardianos/service" pb "github.com/utmstack/UTMStack/agent/agent" - "github.com/utmstack/UTMStack/agent/collectors" + "github.com/utmstack/UTMStack/agent/collector" "github.com/utmstack/UTMStack/agent/config" "github.com/utmstack/UTMStack/agent/database" - "github.com/utmstack/UTMStack/agent/logservice" + "github.com/utmstack/UTMStack/agent/dependency" "github.com/utmstack/UTMStack/agent/models" - "github.com/utmstack/UTMStack/agent/modules" - "github.com/utmstack/UTMStack/agent/updates" "github.com/utmstack/UTMStack/agent/utils" "google.golang.org/grpc/metadata" ) -type program struct{} +const shutdownTimeout = 30 * time.Second + +type program struct { + cancel context.CancelFunc + wg sync.WaitGroup + mu sync.Mutex +} func (p *program) Start(_ service.Service) error { go p.run() @@ -32,10 +35,58 @@ func (p *program) Start(_ service.Service) error { } func (p *program) Stop(_ service.Service) error { - // TODO: implement this function + utils.Logger.Info("Stopping UTMStack Agent...") + + p.mu.Lock() + cancel := p.cancel + p.mu.Unlock() + + if cancel != nil { + cancel() + } + + // Stop all collectors + collector.StopAll() + + // Wait for goroutines with timeout + done := make(chan struct{}) + go func() { + p.wg.Wait() + close(done) + }() + + select { + case <-done: + utils.Logger.Info("All goroutines stopped gracefully") + case <-time.After(shutdownTimeout): + utils.Logger.ErrorF("Shutdown timeout after %v, some goroutines may not have stopped", shutdownTimeout) + } + + // Close database + if db := database.GetDB(); db != nil { + if err := db.Close(); err != nil { + utils.Logger.ErrorF("error closing database: %v", err) + } + } + + utils.Logger.Info("UTMStack Agent stopped") return nil } +// goSafe launches a goroutine with panic recovery and WaitGroup tracking. +func (p *program) goSafe(name string, fn func()) { + p.wg.Add(1) + go func() { + defer p.wg.Done() + defer func() { + if r := recover(); r != nil { + utils.Logger.ErrorF("panic in %s: %v", name, r) + } + }() + fn() + }() +} + func (p *program) run() { utils.InitLogger(config.ServiceLogFile) cnf, err := config.GetCurrentConfig() @@ -49,76 +100,56 @@ func (p *program) run() { utils.Logger.ErrorF("error migrating logs table: %v", err) } - ensureUpdaterServiceInstalled() + // Reconcile dependencies (updater, beats, etc.) before starting collectors + if err := dependency.Reconcile(cnf.Server, cnf.SkipCertValidation); err != nil { + utils.Logger.ErrorF("error reconciling dependencies: %v", err) + // Continue anyway - agent should try to run with what it has + } ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + p.mu.Lock() + p.cancel = cancel + p.mu.Unlock() + ctx = metadata.AppendToOutgoingContext(ctx, "key", cnf.AgentKey) ctx = metadata.AppendToOutgoingContext(ctx, "id", strconv.Itoa(int(cnf.AgentID))) ctx = metadata.AppendToOutgoingContext(ctx, "type", "agent") - go pb.IncidentResponseStream(cnf, ctx) - go pb.StartPing(cnf, ctx) - - logProcessor := logservice.GetLogProcessor() - go logProcessor.ProcessLogs(cnf, ctx) + // Start all goroutines with panic recovery + p.goSafe("IncidentResponseStream", func() { + pb.IncidentResponseStream(cnf, ctx) + }) - go pb.UpdateAgent(cnf, ctx) - go modules.StartModules() - collectors.LogsReader() + p.goSafe("StartPing", func() { + pb.StartPing(cnf, ctx) + }) - go updates.UpdateDependencies(cnf) - - signals := make(chan os.Signal, 1) - signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) - <-signals -} + p.goSafe("ProcessLogs", func() { + logProcessor := pb.GetLogProcessor() + logProcessor.ProcessLogs(cnf, ctx) + }) -func ensureUpdaterServiceInstalled() { - isInstalled, err := utils.CheckIfServiceIsInstalled("UTMStackUpdater") - if err != nil { - utils.Logger.ErrorF("error checking if updater service is installed: %v", err) - return - } + p.goSafe("UpdateAgent", func() { + pb.UpdateAgent(cnf, ctx) + }) - if isInstalled { - utils.Logger.Info("updater service is already installed") - return + // Sync collector config with current version's ProtoPorts + if err := collector.SyncCollectorConfig(); err != nil { + utils.Logger.ErrorF("error syncing collector config: %v", err) } - utils.Logger.Info("updater service not found, installing...") - - updaterPath := filepath.Join(utils.GetMyPath(), fmt.Sprintf(config.UpdaterFile, "")) - - if !utils.CheckIfPathExist(updaterPath) { - - cnf, err := config.GetCurrentConfig() - if err != nil { - utils.Logger.ErrorF("error getting config to download updater: %v", err) - return - } - - updaterBinary := fmt.Sprintf(config.UpdaterFile, "") - if err := utils.DownloadFile(fmt.Sprintf(config.DependUrl, cnf.Server, config.DependenciesPort, updaterBinary), map[string]string{}, updaterBinary, utils.GetMyPath(), cnf.SkipCertValidation); err != nil { - utils.Logger.ErrorF("error downloading updater binary: %v", err) - return - } + // Start collectors (they manage their own goroutines with context) + collector.StartAll(ctx) - if runtime.GOOS == "linux" || runtime.GOOS == "darwin" { - if err := utils.Execute("chmod", utils.GetMyPath(), "755", updaterBinary); err != nil { - utils.Logger.ErrorF("error setting permissions on updater: %v", err) - return - } - } - - utils.Logger.Info("updater binary downloaded successfully") - } + // Wait for shutdown signal + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) - err = utils.Execute(updaterPath, utils.GetMyPath(), "install") - if err != nil { - utils.Logger.ErrorF("error installing updater service: %v", err) - return + select { + case sig := <-signals: + utils.Logger.Info("Received signal: %v", sig) + case <-ctx.Done(): + utils.Logger.Info("Context cancelled") } - - utils.Logger.Info("updater service installed successfully") } + diff --git a/agent/serv/uninstall.go b/agent/serv/uninstall.go index 644bc713a..f2bba7dff 100644 --- a/agent/serv/uninstall.go +++ b/agent/serv/uninstall.go @@ -2,10 +2,11 @@ package serv import ( "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/svc" ) func UninstallService() { - err := utils.StopService("UTMStackAgent") + err := svc.Stop("UTMStackAgent") if err != nil { utils.Logger.Fatal("error stopping UTMStackAgent: %v", err) } diff --git a/agent/updater/config/config.go b/agent/updater/config/config.go index 8daefeb23..6527a45fb 100644 --- a/agent/updater/config/config.go +++ b/agent/updater/config/config.go @@ -2,11 +2,10 @@ package config import ( "fmt" - "os" "path/filepath" "sync" - "gopkg.in/yaml.v3" + "github.com/utmstack/UTMStack/shared/fs" ) type Config struct { @@ -22,24 +21,11 @@ var ( func GetCurrentConfig() (*Config, error) { var errR error confOnce.Do(func() { - ex, err := os.Executable() - if err != nil { - errR = fmt.Errorf("error getting executable path: %v", err) - return - } - exPath := filepath.Dir(ex) - - configPath := filepath.Join(exPath, "config.yml") - content, err := os.ReadFile(configPath) - if err != nil { - errR = fmt.Errorf("error reading config file: %v", err) - return - } + configPath := filepath.Join(fs.GetExecutablePath(), "config.yml") var loadedConfig Config - err = yaml.Unmarshal(content, &loadedConfig) - if err != nil { - errR = fmt.Errorf("error parsing config file: %v", err) + if err := fs.ReadYAML(configPath, &loadedConfig); err != nil { + errR = fmt.Errorf("error reading config file: %v", err) return } diff --git a/agent/updater/config/const.go b/agent/updater/config/const.go index 5032035e0..3b254f4a1 100644 --- a/agent/updater/config/const.go +++ b/agent/updater/config/const.go @@ -2,13 +2,16 @@ package config import ( "path/filepath" + "runtime" - "github.com/utmstack/UTMStack/agent/updater/utils" + "github.com/utmstack/UTMStack/shared/fs" ) const ( SERV_LOG = "utmstack_updater.log" SERV_AGENT_NAME = "UTMStackAgent" + + agentBaseName = "utmstack_agent_service" ) var ( @@ -17,5 +20,21 @@ var ( LogAuthProxyPort = "50051" DependenciesPort = "9001" - VersionPath = filepath.Join(utils.GetMyPath(), "version.json") + VersionPath = filepath.Join(fs.GetExecutablePath(), "version.json") ) + +// ServiceFile returns the agent binary name with the appropriate suffix and extension. +func ServiceFile(suffix string) string { + // Check if this is arm64 + isArm64 := runtime.GOARCH == "arm64" + + base := agentBaseName + if isArm64 { + base = agentBaseName + "_arm64" + } + + if runtime.GOOS == "windows" { + return base + suffix + ".exe" + } + return base + suffix +} diff --git a/agent/updater/go.mod b/agent/updater/go.mod index 8b7e97c5f..79d47b014 100644 --- a/agent/updater/go.mod +++ b/agent/updater/go.mod @@ -4,11 +4,11 @@ go 1.25.5 require ( github.com/kardianos/service v1.2.4 - github.com/threatwinds/go-sdk v1.1.7 - github.com/threatwinds/logger v1.2.3 - gopkg.in/yaml.v3 v3.0.1 + github.com/utmstack/UTMStack/shared v0.0.0 ) +replace github.com/utmstack/UTMStack/shared => ../../shared + require ( github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic v1.14.2 // indirect @@ -25,6 +25,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -32,12 +33,9 @@ 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/tidwall/gjson v1.18.0 // indirect - github.com/tidwall/match v1.2.0 // indirect - github.com/tidwall/pretty v1.2.1 // indirect + github.com/threatwinds/logger v1.2.3 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/arch v0.23.0 // indirect golang.org/x/crypto v0.47.0 // indirect golang.org/x/net v0.49.0 // indirect @@ -45,5 +43,5 @@ require ( golang.org/x/text v0.33.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/agent/updater/go.sum b/agent/updater/go.sum index c710c1a04..9e557f96d 100644 --- a/agent/updater/go.sum +++ b/agent/updater/go.sum @@ -6,10 +6,10 @@ github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2N github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= @@ -54,15 +54,14 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= 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/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 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= @@ -74,28 +73,14 @@ 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.7 h1:2IJAWTCxZU4BDFiavPjH8MqpA/mam1QyIsjySbZLlRo= -github.com/threatwinds/go-sdk v1.1.7/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= github.com/threatwinds/logger v1.2.3 h1:V2SVAXzbq+/huCvIWOfqzMTH+WBHJxankyBgVG2hy1Y= github.com/threatwinds/logger v1.2.3/go.mod h1:N+bJKvF4FQNJZLfQpVYWpr6D8iEAFnAQfHYqH5iR1TI= -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= -github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= -github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= -go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= @@ -117,5 +102,3 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= -sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/agent/updater/main.go b/agent/updater/main.go index 9aae73b21..5b9d278cd 100644 --- a/agent/updater/main.go +++ b/agent/updater/main.go @@ -7,19 +7,20 @@ import ( "github.com/utmstack/UTMStack/agent/updater/config" "github.com/utmstack/UTMStack/agent/updater/service" - "github.com/utmstack/UTMStack/agent/updater/utils" + "github.com/utmstack/UTMStack/shared/fs" + "github.com/utmstack/UTMStack/shared/logger" ) func main() { - path := utils.GetMyPath() - utils.InitLogger(filepath.Join(path, "logs", config.SERV_LOG)) + basePath := fs.GetExecutablePath() + logger.Init(filepath.Join(basePath, "logs", config.SERV_LOG), logger.LevelInfo) if len(os.Args) > 1 { switch os.Args[1] { case "install": fmt.Println("Installing UTMStack Updater service...") - fmt.Print(("Creating service ... ")) + fmt.Print("Creating service ... ") service.InstallService() fmt.Println("[OK]") @@ -35,7 +36,6 @@ func main() { return case "stop": fmt.Println("Stopping UTMStack Updater service...") - // Will be handled by systemd return } } diff --git a/agent/updater/service/install.go b/agent/updater/service/install.go index 62b09f261..95db779c9 100644 --- a/agent/updater/service/install.go +++ b/agent/updater/service/install.go @@ -5,7 +5,7 @@ import ( "os" "github.com/kardianos/service" - "github.com/utmstack/UTMStack/agent/updater/utils" + "github.com/utmstack/UTMStack/shared/logger" ) func InstallService() { @@ -27,7 +27,7 @@ func InstallService() { fmt.Println("\nError starting new service: ", err) os.Exit(1) } - utils.UpdaterLogger.Info("updater service installed successfully") + logger.Info("updater service installed successfully") } func UninstallService() { diff --git a/agent/updater/service/service.go b/agent/updater/service/service.go index 837037b09..40429b7e7 100644 --- a/agent/updater/service/service.go +++ b/agent/updater/service/service.go @@ -4,7 +4,7 @@ import ( "github.com/kardianos/service" "github.com/utmstack/UTMStack/agent/updater/config" "github.com/utmstack/UTMStack/agent/updater/updates" - "github.com/utmstack/UTMStack/agent/updater/utils" + "github.com/utmstack/UTMStack/shared/logger" ) type program struct{} @@ -21,7 +21,7 @@ func (p *program) Stop(s service.Service) error { func (p *program) run() { cnf, err := config.GetCurrentConfig() if err != nil { - utils.UpdaterLogger.ErrorF("error getting config: %v", err) + logger.Error("error getting config: %v", err) return } @@ -33,11 +33,11 @@ func RunService() { prg := new(program) newService, err := service.New(prg, svcConfig) if err != nil { - utils.UpdaterLogger.Fatal("error creating service: %v", err) + logger.Fatal("error creating service: %v", err) } err = newService.Run() if err != nil { - utils.UpdaterLogger.Fatal("error running service: %v", err) + logger.Fatal("error running service: %v", err) } } diff --git a/agent/updater/updates/update.go b/agent/updater/updates/update.go index 80f8779db..0ac08ef37 100644 --- a/agent/updater/updates/update.go +++ b/agent/updater/updates/update.go @@ -8,147 +8,154 @@ import ( "time" "github.com/utmstack/UTMStack/agent/updater/config" - "github.com/utmstack/UTMStack/agent/updater/models" - "github.com/utmstack/UTMStack/agent/updater/utils" + "github.com/utmstack/UTMStack/shared/exec" + "github.com/utmstack/UTMStack/shared/fs" + "github.com/utmstack/UTMStack/shared/http" + "github.com/utmstack/UTMStack/shared/logger" + "github.com/utmstack/UTMStack/shared/svc" ) const ( checkEvery = 5 * time.Minute ) -var currentVersion = models.Version{} +// Version represents the version info from version.json +type Version struct { + Version string `json:"version"` +} + +var currentVersion = Version{} func UpdateDependencies(cnf *config.Config) { - if utils.CheckIfPathExist(config.VersionPath) { - err := utils.ReadJson(config.VersionPath, ¤tVersion) - if err != nil { - utils.UpdaterLogger.ErrorF("error reading version file: %v", err) + basePath := fs.GetExecutablePath() + + if fs.Exists(config.VersionPath) { + if err := fs.ReadJSON(config.VersionPath, ¤tVersion); err != nil { + logger.Error("error reading version file: %v", err) } } for { time.Sleep(checkEvery) - if err := utils.DownloadFile(fmt.Sprintf(config.DependUrl, cnf.Server, config.DependenciesPort, "version.json"), map[string]string{}, "version_new.json", utils.GetMyPath(), cnf.SkipCertValidation); err != nil { - utils.UpdaterLogger.ErrorF("error downloading version.json: %v", err) + if err := http.DownloadFile(fmt.Sprintf(config.DependUrl, cnf.Server, config.DependenciesPort, "version.json"), nil, "version_new.json", basePath, cnf.SkipCertValidation); err != nil { + logger.Error("error downloading version.json: %v", err) continue } - newVersion := models.Version{} - err := utils.ReadJson(filepath.Join(utils.GetMyPath(), "version_new.json"), &newVersion) - if err != nil { - utils.UpdaterLogger.ErrorF("error reading version file: %v", err) + + newVersion := Version{} + if err := fs.ReadJSON(filepath.Join(basePath, "version_new.json"), &newVersion); err != nil { + logger.Error("error reading version file: %v", err) continue } if newVersion.Version != currentVersion.Version { - utils.UpdaterLogger.Info("New version of agent found: %s", newVersion.Version) - if err := utils.DownloadFile(fmt.Sprintf(config.DependUrl, cnf.Server, config.DependenciesPort, fmt.Sprintf(config.ServiceFile, "")), map[string]string{}, fmt.Sprintf(config.ServiceFile, "_new"), utils.GetMyPath(), cnf.SkipCertValidation); err != nil { - utils.UpdaterLogger.ErrorF("error downloading agent: %v", err) + logger.Info("New version of agent found: %s", newVersion.Version) + + agentBinary := config.ServiceFile("") + if err := http.DownloadFile(fmt.Sprintf(config.DependUrl, cnf.Server, config.DependenciesPort, agentBinary), nil, config.ServiceFile("_new"), basePath, cnf.SkipCertValidation); err != nil { + logger.Error("error downloading agent: %v", err) continue } if runtime.GOOS == "linux" || runtime.GOOS == "darwin" { - if err = utils.Execute("chmod", utils.GetMyPath(), "-R", "755", filepath.Join(utils.GetMyPath(), fmt.Sprintf(config.ServiceFile, "_new"))); err != nil { - utils.UpdaterLogger.ErrorF("error executing chmod: %v", err) + if err := exec.Run("chmod", basePath, "-R", "755", filepath.Join(basePath, config.ServiceFile("_new"))); err != nil { + logger.Error("error executing chmod: %v", err) } } - utils.UpdaterLogger.Info("Starting update process...") - err = runUpdateProcess() - if err != nil { - utils.UpdaterLogger.ErrorF("error updating service: %v", err) - os.Remove(filepath.Join(utils.GetMyPath(), "version_new.json")) - os.Remove(filepath.Join(utils.GetMyPath(), fmt.Sprintf(config.ServiceFile, "_new"))) + logger.Info("Starting update process...") + if err := runUpdateProcess(basePath); err != nil { + logger.Error("error updating service: %v", err) + os.Remove(filepath.Join(basePath, "version_new.json")) + os.Remove(filepath.Join(basePath, config.ServiceFile("_new"))) } else { - utils.UpdaterLogger.Info("Update completed successfully") - if utils.CheckIfPathExist(config.VersionPath) { - err := utils.ReadJson(config.VersionPath, ¤tVersion) - if err != nil { - utils.UpdaterLogger.ErrorF("error reading updated version file: %v", err) + logger.Info("Update completed successfully") + if fs.Exists(config.VersionPath) { + if err := fs.ReadJSON(config.VersionPath, ¤tVersion); err != nil { + logger.Error("error reading updated version file: %v", err) } } } } else { - os.Remove(filepath.Join(utils.GetMyPath(), "version_new.json")) + os.Remove(filepath.Join(basePath, "version_new.json")) } } } -func runUpdateProcess() error { - path := utils.GetMyPath() - - newBin := fmt.Sprintf(config.ServiceFile, "_new") - oldBin := fmt.Sprintf(config.ServiceFile, "") - backupBin := fmt.Sprintf(config.ServiceFile, ".old") +func runUpdateProcess(basePath string) error { + newBin := config.ServiceFile("_new") + oldBin := config.ServiceFile("") + backupBin := config.ServiceFile(".old") - agentNew := filepath.Join(path, newBin) + agentNew := filepath.Join(basePath, newBin) if _, err := os.Stat(agentNew); err != nil { return fmt.Errorf("no _new binary found to update") } - if err := utils.StopService(config.SERV_AGENT_NAME); err != nil { + if err := svc.Stop(config.SERV_AGENT_NAME); err != nil { return fmt.Errorf("error stopping agent: %v", err) } time.Sleep(10 * time.Second) - backupPath := filepath.Join(path, backupBin) - if utils.CheckIfPathExist(backupPath) { - utils.UpdaterLogger.Info("Removing previous backup: %s", backupPath) + backupPath := filepath.Join(basePath, backupBin) + if fs.Exists(backupPath) { + logger.Info("Removing previous backup: %s", backupPath) if err := os.Remove(backupPath); err != nil { - utils.UpdaterLogger.ErrorF("could not remove old backup: %v", err) + logger.Error("could not remove old backup: %v", err) } } - if err := os.Rename(filepath.Join(path, oldBin), backupPath); err != nil { + if err := os.Rename(filepath.Join(basePath, oldBin), backupPath); err != nil { return fmt.Errorf("error backing up old binary: %v", err) } - if err := os.Rename(filepath.Join(path, newBin), filepath.Join(path, oldBin)); err != nil { - os.Rename(backupPath, filepath.Join(path, oldBin)) + if err := os.Rename(filepath.Join(basePath, newBin), filepath.Join(basePath, oldBin)); err != nil { + os.Rename(backupPath, filepath.Join(basePath, oldBin)) return fmt.Errorf("error renaming new binary: %v", err) } - if err := utils.StartService(config.SERV_AGENT_NAME); err != nil { - rollbackAgent(oldBin, backupBin, path) + if err := svc.Start(config.SERV_AGENT_NAME); err != nil { + rollbackAgent(oldBin, backupBin, basePath) return fmt.Errorf("error starting agent: %v", err) } time.Sleep(30 * time.Second) - isHealthy, err := utils.CheckIfServiceIsActive(config.SERV_AGENT_NAME) + isHealthy, err := svc.IsActive(config.SERV_AGENT_NAME) if err != nil || !isHealthy { - utils.UpdaterLogger.Info("New version failed health check, rolling back...") - rollbackAgent(oldBin, backupBin, path) + logger.Info("New version failed health check, rolling back...") + rollbackAgent(oldBin, backupBin, basePath) return fmt.Errorf("rollback completed: new version failed health check") } - utils.UpdaterLogger.Info("Health check passed for agent") + logger.Info("Health check passed for agent") - versionNewPath := filepath.Join(path, "version_new.json") - versionPath := filepath.Join(path, "version.json") - if utils.CheckIfPathExist(versionNewPath) { + versionNewPath := filepath.Join(basePath, "version_new.json") + versionPath := filepath.Join(basePath, "version.json") + if fs.Exists(versionNewPath) { if err := os.Rename(versionNewPath, versionPath); err != nil { - utils.UpdaterLogger.ErrorF("error updating version file: %v", err) + logger.Error("error updating version file: %v", err) } else { - utils.UpdaterLogger.Info("Version file updated successfully") + logger.Info("Version file updated successfully") } } return nil } -func rollbackAgent(currentBin, backupBin, path string) { - utils.UpdaterLogger.Info("Rolling back agent to previous version...") +func rollbackAgent(currentBin, backupBin, basePath string) { + logger.Info("Rolling back agent to previous version...") - utils.StopService(config.SERV_AGENT_NAME) + svc.Stop(config.SERV_AGENT_NAME) time.Sleep(5 * time.Second) - os.Remove(filepath.Join(path, currentBin)) - os.Rename(filepath.Join(path, backupBin), filepath.Join(path, currentBin)) + os.Remove(filepath.Join(basePath, currentBin)) + os.Rename(filepath.Join(basePath, backupBin), filepath.Join(basePath, currentBin)) - utils.StartService(config.SERV_AGENT_NAME) - os.Remove(filepath.Join(path, "version_new.json")) + svc.Start(config.SERV_AGENT_NAME) + os.Remove(filepath.Join(basePath, "version_new.json")) - utils.UpdaterLogger.Info("Rollback completed for agent") + logger.Info("Rollback completed for agent") } diff --git a/agent/updates/dependencies.go b/agent/updates/dependencies.go deleted file mode 100644 index 1b7f2aae7..000000000 --- a/agent/updates/dependencies.go +++ /dev/null @@ -1,63 +0,0 @@ -package updates - -import ( - "fmt" - "os" - "path/filepath" - "runtime" - "strings" - - "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/utils" -) - -func DownloadFirstDependencies(address string, insecure bool) error { - if err := utils.DownloadFile(fmt.Sprintf(config.DependUrl, address, config.DependenciesPort, "version.json"), map[string]string{}, "version.json", utils.GetMyPath(), insecure); err != nil { - return fmt.Errorf("error downloading version.json : %v", err) - } - - updaterBinary := fmt.Sprintf(config.UpdaterFile, "") - if err := utils.DownloadFile(fmt.Sprintf(config.DependUrl, address, config.DependenciesPort, updaterBinary), map[string]string{}, updaterBinary, utils.GetMyPath(), insecure); err != nil { - return fmt.Errorf("error downloading updater binary %s: %v", updaterBinary, err) - } - - if runtime.GOOS == "linux" || runtime.GOOS == "darwin" { - if err := utils.Execute("chmod", utils.GetMyPath(), "755", updaterBinary); err != nil { - return fmt.Errorf("error setting permissions on updater: %v", err) - } - } - - dependFiles := config.DependFiles - for _, file := range dependFiles { - if err := utils.DownloadFile(fmt.Sprintf(config.DependUrl, address, config.DependenciesPort, file), map[string]string{}, file, utils.GetMyPath(), insecure); err != nil { - return fmt.Errorf("error downloading file %s: %v", file, err) - } - } - - if err := handleDependenciesPostDownload(dependFiles); err != nil { - return err - } - - return nil - -} - -func handleDependenciesPostDownload(dependencies []string) error { - for _, file := range dependencies { - if strings.HasSuffix(file, ".zip") { - if err := utils.Unzip(filepath.Join(utils.GetMyPath(), file), utils.GetMyPath()); err != nil { - return fmt.Errorf("error unzipping dependencies: %v", err) - } - - if err := os.Remove(filepath.Join(utils.GetMyPath(), file)); err != nil { - return fmt.Errorf("error removing file %s: %v", file, err) - } - } else if runtime.GOOS == "linux" || runtime.GOOS == "darwin" { - if err := utils.Execute("chmod", utils.GetMyPath(), "-R", "755", file); err != nil { - return fmt.Errorf("error executing chmod on %s: %v", file, err) - } - } - } - - return nil -} diff --git a/agent/updates/update.go b/agent/updates/update.go deleted file mode 100644 index 0d743734d..000000000 --- a/agent/updates/update.go +++ /dev/null @@ -1,154 +0,0 @@ -package updates - -import ( - "fmt" - "os" - "path/filepath" - "runtime" - "time" - - "github.com/utmstack/UTMStack/agent/config" - "github.com/utmstack/UTMStack/agent/models" - "github.com/utmstack/UTMStack/agent/utils" -) - -const ( - checkEvery = 5 * time.Minute -) - -var currentVersion = models.Version{} - -func UpdateDependencies(cnf *config.Config) { - if utils.CheckIfPathExist(config.VersionPath) { - err := utils.ReadJson(config.VersionPath, ¤tVersion) - if err != nil { - utils.Logger.Fatal("error reading version file: %v", err) - } - } - - for { - time.Sleep(checkEvery) - - if err := utils.DownloadFile(fmt.Sprintf(config.DependUrl, cnf.Server, config.DependenciesPort, "version.json"), map[string]string{}, "version_new.json", utils.GetMyPath(), cnf.SkipCertValidation); err != nil { - utils.Logger.ErrorF("error downloading version.json: %v", err) - continue - } - newVersion := models.Version{} - err := utils.ReadJson(filepath.Join(utils.GetMyPath(), "version_new.json"), &newVersion) - if err != nil { - utils.Logger.ErrorF("error reading version file: %v", err) - continue - } - - if newVersion.UpdaterVersion != currentVersion.UpdaterVersion { - utils.Logger.Info("New version of updater found: %s", newVersion.UpdaterVersion) - if err := utils.DownloadFile(fmt.Sprintf(config.DependUrl, cnf.Server, config.DependenciesPort, fmt.Sprintf(config.UpdaterFile, "")), map[string]string{}, fmt.Sprintf(config.UpdaterFile, "_new"), utils.GetMyPath(), cnf.SkipCertValidation); err != nil { - utils.Logger.ErrorF("error downloading updater: %v", err) - continue - } - - if runtime.GOOS == "linux" || runtime.GOOS == "darwin" { - if err = utils.Execute("chmod", utils.GetMyPath(), "-R", "755", filepath.Join(utils.GetMyPath(), fmt.Sprintf(config.UpdaterFile, "_new"))); err != nil { - utils.Logger.ErrorF("error executing chmod: %v", err) - } - } - - utils.Logger.Info("Starting updater update process...") - err = runUpdateProcess() - if err != nil { - utils.Logger.ErrorF("error updating updater: %v", err) - os.Remove(filepath.Join(utils.GetMyPath(), "version_new.json")) - os.Remove(filepath.Join(utils.GetMyPath(), fmt.Sprintf(config.UpdaterFile, "_new"))) - } else { - utils.Logger.Info("Updater update completed successfully") - if utils.CheckIfPathExist(config.VersionPath) { - err := utils.ReadJson(config.VersionPath, ¤tVersion) - if err != nil { - utils.Logger.ErrorF("error reading updated version file: %v", err) - } - } - } - } else { - os.Remove(filepath.Join(utils.GetMyPath(), "version_new.json")) - } - } -} - -func runUpdateProcess() error { - path := utils.GetMyPath() - - newBin := fmt.Sprintf(config.UpdaterFile, "_new") - oldBin := fmt.Sprintf(config.UpdaterFile, "") - backupBin := fmt.Sprintf(config.UpdaterFile, ".old") - - updaterNew := filepath.Join(path, newBin) - if _, err := os.Stat(updaterNew); err != nil { - return fmt.Errorf("no _new binary found to update") - } - - if err := utils.StopService(config.SERVICE_UPDATER_NAME); err != nil { - return fmt.Errorf("error stopping updater: %v", err) - } - - time.Sleep(10 * time.Second) - - backupPath := filepath.Join(path, backupBin) - if utils.CheckIfPathExist(backupPath) { - utils.Logger.Info("Removing previous backup: %s", backupPath) - if err := os.Remove(backupPath); err != nil { - utils.Logger.ErrorF("could not remove old backup: %v", err) - } - } - - if err := os.Rename(filepath.Join(path, oldBin), backupPath); err != nil { - return fmt.Errorf("error backing up old binary: %v", err) - } - - if err := os.Rename(filepath.Join(path, newBin), filepath.Join(path, oldBin)); err != nil { - os.Rename(backupPath, filepath.Join(path, oldBin)) - return fmt.Errorf("error renaming new binary: %v", err) - } - - if err := utils.StartService(config.SERVICE_UPDATER_NAME); err != nil { - rollbackUpdater(oldBin, backupBin, path) - return fmt.Errorf("error starting updater: %v", err) - } - - time.Sleep(30 * time.Second) - - isHealthy, err := utils.CheckIfServiceIsActive(config.SERVICE_UPDATER_NAME) - if err != nil || !isHealthy { - utils.Logger.Info("New version failed health check, rolling back...") - rollbackUpdater(oldBin, backupBin, path) - return fmt.Errorf("rollback completed: new version failed health check") - } - - utils.Logger.Info("Health check passed for updater") - - versionNewPath := filepath.Join(path, "version_new.json") - versionPath := filepath.Join(path, "version.json") - if utils.CheckIfPathExist(versionNewPath) { - if err := os.Rename(versionNewPath, versionPath); err != nil { - utils.Logger.ErrorF("error updating version file: %v", err) - } else { - utils.Logger.Info("Version file updated successfully") - } - } - - return nil -} - -func rollbackUpdater(currentBin, backupBin, path string) { - utils.Logger.Info("Rolling back updater to previous version...") - - utils.StopService(config.SERVICE_UPDATER_NAME) - time.Sleep(5 * time.Second) - - os.Remove(filepath.Join(path, currentBin)) - os.Rename(filepath.Join(path, backupBin), filepath.Join(path, currentBin)) - - utils.StartService(config.SERVICE_UPDATER_NAME) - os.Remove(filepath.Join(path, "version_new.json")) - - utils.Logger.Info("Rollback completed for updater") -} diff --git a/agent/utils/cmd.go b/agent/utils/cmd.go index bc8caa535..bdfd3f43e 100644 --- a/agent/utils/cmd.go +++ b/agent/utils/cmd.go @@ -30,10 +30,3 @@ func ExecuteWithResult(c string, dir string, arg ...string) (string, bool) { return validUtf8Out, false } - -func Execute(c string, dir string, arg ...string) error { - cmd := exec.Command(c, arg...) - cmd.Dir = dir - - return cmd.Run() -} diff --git a/agent/utils/crypt.go b/agent/utils/crypt.go index 740a2a5b6..d784dd962 100644 --- a/agent/utils/crypt.go +++ b/agent/utils/crypt.go @@ -3,21 +3,39 @@ package utils import ( "encoding/base64" "fmt" -) -func GenerateKey(baseKey string) ([]byte, error) { - info, err := GetOsInfo() - if err != nil { - return nil, fmt.Errorf("error getting os info: %v", err) - } - - data := []byte(info.Hostname + info.Mac + info.OsType) - base64Key := base64.StdEncoding.EncodeToString(data) - return []byte(baseKey + base64Key), nil -} + aesCrypt "github.com/AtlasInsideCorp/AtlasInsideAES" +) +// GenerateKeyByUUID generates an AES key from baseKey and uuid. func GenerateKeyByUUID(baseKey string, uuid string) ([]byte, error) { data := []byte(baseKey + uuid) base64Key := base64.StdEncoding.EncodeToString(data) return []byte(base64Key), nil } + +// EncryptAES encrypts plaintext using AES with a key derived from baseKey and uuid. +func EncryptAES(plaintext, baseKey, uuid string) (string, error) { + key, err := GenerateKeyByUUID(baseKey, uuid) + if err != nil { + return "", fmt.Errorf("error generating key: %v", err) + } + encrypted, err := aesCrypt.AESEncrypt(plaintext, key) + if err != nil { + return "", fmt.Errorf("error encrypting: %v", err) + } + return encrypted, nil +} + +// DecryptAES decrypts ciphertext using AES with a key derived from baseKey and uuid. +func DecryptAES(ciphertext, baseKey, uuid string) (string, error) { + key, err := GenerateKeyByUUID(baseKey, uuid) + if err != nil { + return "", fmt.Errorf("error generating key: %v", err) + } + decrypted, err := aesCrypt.AESDecrypt(ciphertext, key) + if err != nil { + return "", fmt.Errorf("error decrypting: %v", err) + } + return decrypted, nil +} diff --git a/agent/utils/files.go b/agent/utils/files.go index 699910dce..d6903ddc5 100644 --- a/agent/utils/files.go +++ b/agent/utils/files.go @@ -2,50 +2,12 @@ package utils import ( "bufio" - "encoding/json" - "fmt" "html/template" "io" "os" "path/filepath" - "reflect" - - "gopkg.in/yaml.v2" ) -func GetMyPath() string { - ex, err := os.Executable() - if err != nil { - return "" - } - exPath := filepath.Dir(ex) - return exPath -} - -func ReadYAML(path string, result interface{}) error { - if result == nil { - return fmt.Errorf("result interface is nil") - } - - rv := reflect.ValueOf(result) - if rv.Kind() != reflect.Ptr || rv.IsNil() { - return fmt.Errorf("result must be a non-nil pointer") - } - - file, err := os.Open(path) - if err != nil { - return err - } - defer func() { _ = file.Close() }() - - d := yaml.NewDecoder(file) - if err := d.Decode(result); err != nil { - return err - } - - return nil -} - func WriteStringToFile(fileName string, body string) error { file, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.ModePerm) if err != nil { @@ -57,48 +19,6 @@ func WriteStringToFile(fileName string, body string) error { return err } -func WriteYAML(url string, data interface{}) error { - config, err := yaml.Marshal(data) - if err != nil { - return err - } - - err = WriteStringToFile(url, string(config)) - if err != nil { - return err - } - - return nil -} - -func WriteJSON(path string, data interface{}) error { - jsonData, err := json.MarshalIndent(data, "", " ") - if err != nil { - return err - } - - err = WriteStringToFile(path, string(jsonData)) - if err != nil { - return err - } - - return nil -} - -func ReadJson(fileName string, data interface{}) error { - content, err := os.ReadFile(fileName) - if err != nil { - return err - } - - err = json.Unmarshal(content, data) - if err != nil { - return err - } - - return nil -} - func GenerateFromTemplate(data interface{}, templateFile string, configFile string) error { _, fileName := filepath.Split(templateFile) ut, err := template.New(fileName).ParseFiles(templateFile) @@ -121,24 +41,6 @@ func GenerateFromTemplate(data interface{}, templateFile string, configFile stri return nil } -func CreatePathIfNotExist(path string) error { - if _, err := os.Stat(path); os.IsNotExist(err) { - if err := os.MkdirAll(path, 0755); err != nil { - return fmt.Errorf("error creating path: %v", err) - } - } else if err != nil { - return fmt.Errorf("error checking path: %v", err) - } - return nil -} - -func CheckIfPathExist(path string) bool { - if _, err := os.Stat(path); os.IsNotExist(err) { - return false - } - return true -} - func ReadFileLines(path string) ([]string, error) { file, err := os.Open(path) if err != nil { diff --git a/agent/utils/int_tls.go b/agent/utils/int_tls.go index 5ca5928f3..1c74ec7c1 100644 --- a/agent/utils/int_tls.go +++ b/agent/utils/int_tls.go @@ -8,6 +8,8 @@ import ( "os" "path/filepath" "time" + + "github.com/utmstack/UTMStack/shared/fs" ) const ( @@ -63,11 +65,11 @@ func LoadIntegrationTLSConfig(certPath, keyPath string) (*tls.Config, error) { } func ValidateIntegrationCertificates(certPath, keyPath string) error { - if !CheckIfPathExist(certPath) { + if !fs.Exists(certPath) { return fmt.Errorf("certificate file not found: %s", certPath) } - if !CheckIfPathExist(keyPath) { + if !fs.Exists(keyPath) { return fmt.Errorf("private key file not found: %s", keyPath) } @@ -121,10 +123,10 @@ func ValidateIntegrationCertificates(certPath, keyPath string) error { func LoadUserCertificatesWithStruct(src, dest CertificateFiles) error { // Validate source certificates - if !CheckIfPathExist(src.CertPath) { + if !fs.Exists(src.CertPath) { return fmt.Errorf("user certificate file not found: %s", src.CertPath) } - if !CheckIfPathExist(src.KeyPath) { + if !fs.Exists(src.KeyPath) { return fmt.Errorf("user private key file not found: %s", src.KeyPath) } if err := ValidateIntegrationCertificates(src.CertPath, src.KeyPath); err != nil { @@ -133,7 +135,7 @@ func LoadUserCertificatesWithStruct(src, dest CertificateFiles) error { // Prepare destination directory certsDir := filepath.Dir(dest.CertPath) - if err := CreatePathIfNotExist(certsDir); err != nil { + if err := fs.CreateDirIfNotExist(certsDir); err != nil { return fmt.Errorf("error creating certificates directory: %w", err) } @@ -147,7 +149,7 @@ func LoadUserCertificatesWithStruct(src, dest CertificateFiles) error { // Copy CA certificate (use source CA if exists, otherwise use cert as CA) caSource := src.CAPath - if caSource == "" || !CheckIfPathExist(caSource) { + if caSource == "" || !fs.Exists(caSource) { caSource = src.CertPath } if err := copyFile(caSource, dest.CAPath); err != nil { diff --git a/agent/utils/logger.go b/agent/utils/logger.go index a93ac7455..81e6abc3a 100644 --- a/agent/utils/logger.go +++ b/agent/utils/logger.go @@ -5,12 +5,13 @@ import ( "sync" "github.com/threatwinds/logger" + "github.com/utmstack/UTMStack/shared/fs" ) var ( Logger *logger.Logger loggerOnceInstance sync.Once - logLevelConfigFile = filepath.Join(GetMyPath(), "log_level.yml") + logLevelConfigFile = filepath.Join(fs.GetExecutablePath(), "log_level.yml") LogLevelMap = map[string]int{ "debug": 100, "info": 200, @@ -28,7 +29,7 @@ type LogLevels struct { func InitLogger(filename string) { logLevel := LogLevels{} - err := ReadYAML(logLevelConfigFile, &logLevel) + err := fs.ReadYAML(logLevelConfigFile, &logLevel) if err != nil { logLevel.Level = "info" } diff --git a/agent/utils/req.go b/agent/utils/req.go index a7d0dfa5d..d0ed278dd 100644 --- a/agent/utils/req.go +++ b/agent/utils/req.go @@ -21,11 +21,10 @@ func DoReq[response any](url string, data []byte, method string, headers map[str req.Header.Add(k, v) } - tr := &http.Transport{ + client := &http.Client{} + client.Transport = &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: skipTlsVerification}, } - client := &http.Client{Transport: tr} - defer tr.CloseIdleConnections() resp, err := client.Do(req) if err != nil { diff --git a/agent/utils/services.go b/agent/utils/services.go index a0a828d3f..30f0cf4ee 100644 --- a/agent/utils/services.go +++ b/agent/utils/services.go @@ -5,12 +5,15 @@ import ( "os" "runtime" "strings" + + "github.com/utmstack/UTMStack/shared/exec" + "github.com/utmstack/UTMStack/shared/fs" ) func CheckIfServiceIsActive(serv string) (bool, error) { var errB bool var output string - path := GetMyPath() + path := fs.GetExecutablePath() switch runtime.GOOS { case "windows": @@ -42,21 +45,21 @@ func CheckIfServiceIsActive(serv string) (bool, error) { } func StartService(name string) error { - path := GetMyPath() + path := fs.GetExecutablePath() switch runtime.GOOS { case "windows": - err := Execute("sc", path, "start", name) + err := exec.Run("sc", path, "start", name) if err != nil { return fmt.Errorf("error starting service: %v", err) } case "linux": - err := Execute("systemctl", path, "start", name) + err := exec.Run("systemctl", path, "start", name) if err != nil { return fmt.Errorf("error starting service: %v", err) } case "darwin": plistPath := fmt.Sprintf("/Library/LaunchDaemons/%s.plist", name) - err := Execute("launchctl", path, "load", plistPath) + err := exec.Run("launchctl", path, "load", plistPath) if err != nil { return fmt.Errorf("error starting macOS service: %v", err) } @@ -65,20 +68,20 @@ func StartService(name string) error { } func StopService(name string) error { - path := GetMyPath() + path := fs.GetExecutablePath() switch runtime.GOOS { case "windows": - err := Execute("sc", path, "stop", name) + err := exec.Run("sc", path, "stop", name) if err != nil { return fmt.Errorf("error stoping service: %v", err) } case "linux": - err := Execute("systemctl", path, "stop", name) + err := exec.Run("systemctl", path, "stop", name) if err != nil { return fmt.Errorf("error stoping service: %v", err) } case "darwin": - err := Execute("launchctl", path, "remove", name) + err := exec.Run("launchctl", path, "remove", name) if err != nil { return fmt.Errorf("error stopping macOS service: %v", err) } @@ -87,41 +90,41 @@ func StopService(name string) error { } func UninstallService(name string) error { - path := GetMyPath() + path := fs.GetExecutablePath() switch runtime.GOOS { case "windows": - err := Execute("sc", path, "delete", name) + err := exec.Run("sc", path, "delete", name) if err != nil { return fmt.Errorf("error uninstalling service: %v", err) } case "linux": - err := Execute("systemctl", path, "disable", name) + err := exec.Run("systemctl", path, "disable", name) if err != nil { return fmt.Errorf("error uninstalling service: %v", err) } - err = Execute("rm", "/etc/systemd/system/", "/etc/systemd/system/"+name+".service") + err = exec.Run("rm", "/etc/systemd/system/", "/etc/systemd/system/"+name+".service") if err != nil { return fmt.Errorf("error uninstalling service: %v", err) } case "darwin": - Execute("launchctl", path, "remove", name) - Execute("rm", "/Library/LaunchDaemons/"+name+".plist") - Execute("rm", "/Users/"+os.Getenv("USER")+"/Library/LaunchAgents/"+name+".plist") + exec.Run("launchctl", path, "remove", name) + exec.Run("rm", "/Library/LaunchDaemons/"+name+".plist") + exec.Run("rm", "/Users/"+os.Getenv("USER")+"/Library/LaunchAgents/"+name+".plist") } return nil } func CheckIfServiceIsInstalled(serv string) (bool, error) { - path := GetMyPath() + path := fs.GetExecutablePath() var err error switch runtime.GOOS { case "windows": - err = Execute("sc", path, "query", serv) + err = exec.Run("sc", path, "query", serv) case "linux": - err = Execute("systemctl", path, "status", serv) + err = exec.Run("systemctl", path, "status", serv) case "darwin": - err = Execute("launchctl", path, "list", serv) + err = exec.Run("launchctl", path, "list", serv) default: return false, fmt.Errorf("operative system unknown") } @@ -131,7 +134,7 @@ func CheckIfServiceIsInstalled(serv string) (bool, error) { func CreateLinuxService(serviceName string, execStart string) error { servicePath := "/etc/systemd/system/" + serviceName + ".service" - if !CheckIfPathExist(servicePath) { + if !fs.Exists(servicePath) { file, err := os.Create(servicePath) if err != nil { return fmt.Errorf("error creating %s file: %v", servicePath, err) diff --git a/agent/utils/watcher.go b/agent/utils/watcher.go index 4ad83f882..109b623ce 100644 --- a/agent/utils/watcher.go +++ b/agent/utils/watcher.go @@ -1,53 +1,79 @@ package utils import ( + "bufio" "fmt" + "io" + "os" "regexp" "time" ) -func TailLogFile(filePath string, logLinesChan chan string, stopChan chan bool) { - latestLine := "null" +func TailLogFile(filePath string, logLinesChan chan string, stopChan chan struct{}) { + var offset int64 + + // Start from end of file to avoid re-sending old lines + if info, err := os.Stat(filePath); err == nil { + offset = info.Size() + } -loop: for { select { case <-stopChan: - break loop - + return default: - lines, err := ReadFileLines(filePath) + newOffset, err := readNewLines(filePath, offset, logLinesChan) if err != nil { - Logger.Info("error reading file %s: %v\n", filePath, err) - continue - } - if len(lines) == 1 && latestLine != lines[0] { - logLinesChan <- lines[0] - latestLine = lines[0] - } else if len(lines) > 1 && latestLine != lines[len(lines)-1] { - var startIndex = 0 - if latestLine != "null" { - for i, v := range lines { - if v == latestLine { - startIndex = i + 1 - break - } - } - } - for _, line := range lines[startIndex:] { - logLinesChan <- line - } - if len(lines) > 0 { - latestLine = lines[len(lines)-1] - } + Logger.Info("error reading file %s: %v", filePath, err) + } else { + offset = newOffset } time.Sleep(time.Second) } } } +func readNewLines(filePath string, offset int64, logLinesChan chan string) (int64, error) { + file, err := os.Open(filePath) + if err != nil { + return offset, err + } + defer file.Close() + + // Check if file was truncated or rotated + info, err := file.Stat() + if err != nil { + return offset, err + } + if info.Size() < offset { + offset = 0 + } + + if info.Size() == offset { + return offset, nil + } + + if _, err := file.Seek(offset, io.SeekStart); err != nil { + return offset, err + } + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if line != "" { + logLinesChan <- line + } + } + + newOffset, err := file.Seek(0, io.SeekCurrent) + if err != nil { + return offset, err + } + return newOffset, scanner.Err() +} + func WatchFolder(logType string, logsPath string, logLinesChan chan string) { - stopChan := make(chan bool) + var currentStopChan chan struct{} latestLog := "" pattern := regexp.MustCompile(fmt.Sprintf(`%s-(\d+)(?:-(\d+))?\.ndjson`, logType)) @@ -56,7 +82,7 @@ func WatchFolder(logType string, logsPath string, logLinesChan chan string) { for range ticker.C { isEmpty, err := IsDirEmpty(logsPath) if err != nil { - Logger.Info("error checking if %s is empty: %v\n", logsPath, err) + Logger.Info("error checking if %s is empty: %v", logsPath, err) continue } if !isEmpty { @@ -66,11 +92,12 @@ func WatchFolder(logType string, logsPath string, logLinesChan chan string) { continue } if newLatestLog != latestLog && newLatestLog != "" { - if latestLog != "" { - stopChan <- true + if currentStopChan != nil { + close(currentStopChan) } latestLog = newLatestLog - go TailLogFile(latestLog, logLinesChan, stopChan) + currentStopChan = make(chan struct{}) + go TailLogFile(latestLog, logLinesChan, currentStopChan) } } } From 15856f61b08e14e2765c52730fa8901ded766b6a Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Tue, 17 Feb 2026 15:57:32 -0500 Subject: [PATCH 026/115] chore[go-deps]: update golang dependencies --- agent-manager/go.mod | 8 ++--- agent-manager/go.sum | 38 +++++++++++---------- agent/go.mod | 19 +++++------ agent/go.sum | 61 ++++++++++++++++----------------- installer/go.mod | 6 ++-- installer/go.sum | 12 +++---- plugins/alerts/go.mod | 2 +- plugins/alerts/go.sum | 4 +-- plugins/aws/go.mod | 19 +++++------ plugins/aws/go.sum | 61 ++++++++++++++++----------------- plugins/azure/go.mod | 15 ++++---- plugins/azure/go.sum | 49 +++++++++++++-------------- plugins/bitdefender/go.mod | 13 ++++--- plugins/bitdefender/go.sum | 49 +++++++++++++-------------- plugins/config/go.mod | 8 ++--- plugins/config/go.sum | 16 ++++----- plugins/crowdstrike/go.mod | 13 ++++--- plugins/crowdstrike/go.sum | 35 +++++++++---------- plugins/events/go.mod | 2 +- plugins/events/go.sum | 4 +-- plugins/feeds/go.mod | 11 +++--- plugins/feeds/go.sum | 23 ++++++------- plugins/gcp/go.mod | 19 +++++------ plugins/gcp/go.sum | 52 ++++++++++++++-------------- plugins/geolocation/go.mod | 6 ++-- plugins/geolocation/go.sum | 12 +++---- plugins/inputs/go.mod | 13 ++++--- plugins/inputs/go.sum | 49 +++++++++++++-------------- plugins/modules-config/go.mod | 25 +++++++------- plugins/modules-config/go.sum | 64 +++++++++++++++++------------------ plugins/o365/go.mod | 13 ++++--- plugins/o365/go.sum | 49 +++++++++++++-------------- plugins/soc-ai/go.mod | 11 +++--- plugins/soc-ai/go.sum | 23 ++++++------- plugins/sophos/go.mod | 13 ++++--- plugins/sophos/go.sum | 49 +++++++++++++-------------- plugins/stats/go.mod | 6 ++-- plugins/stats/go.sum | 12 +++---- utmstack-collector/go.mod | 17 +++++----- utmstack-collector/go.sum | 35 +++++++++---------- 40 files changed, 451 insertions(+), 485 deletions(-) diff --git a/agent-manager/go.mod b/agent-manager/go.mod index 08a1bc007..78140e2d6 100644 --- a/agent-manager/go.mod +++ b/agent-manager/go.mod @@ -7,7 +7,7 @@ require ( github.com/gin-gonic/gin v1.11.0 github.com/google/uuid v1.6.0 github.com/utmstack/config-client-go v1.2.7 - google.golang.org/grpc v1.78.0 + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 gorm.io/driver/postgres v1.6.0 gorm.io/gorm v1.31.1 @@ -18,7 +18,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/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect @@ -40,7 +40,7 @@ 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/threatwinds/go-sdk v1.1.8 + github.com/threatwinds/go-sdk v1.1.14 github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect golang.org/x/arch v0.23.0 // indirect @@ -49,5 +49,5 @@ require ( golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect ) diff --git a/agent-manager/go.sum b/agent-manager/go.sum index 189507d1b..bb5bde9b0 100644 --- a/agent-manager/go.sum +++ b/agent-manager/go.sum @@ -6,14 +6,16 @@ github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uS github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -87,8 +89,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.8 h1:jDd6HH4GZVRswv9ToaVU+xcyzNlKnA7f1lf/e1Xyt3A= -github.com/threatwinds/go-sdk v1.1.8/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= @@ -97,16 +99,16 @@ github.com/utmstack/config-client-go v1.2.7 h1:JeRdI5JjH1liNzMW3LmyevjuPd67J/yt9 github.com/utmstack/config-client-go v1.2.7/go.mod h1:kM0KoUizM9ZlcQp0qKviGTWn/+anT5Rfjx3zfZk79nM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= @@ -124,10 +126,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/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/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/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/agent/go.mod b/agent/go.mod index 6f9e6d396..aec39b5d0 100644 --- a/agent/go.mod +++ b/agent/go.mod @@ -11,11 +11,11 @@ require ( github.com/netsampler/goflow2 v1.3.7 github.com/spf13/cobra v1.10.2 github.com/tehmaze/netflow v0.0.0-20240303214733-8c13bb004068 - github.com/threatwinds/go-sdk v1.1.7 + github.com/threatwinds/go-sdk v1.1.14 github.com/threatwinds/logger v1.2.3 github.com/utmstack/UTMStack/shared v0.0.0 - golang.org/x/sys v0.40.0 - google.golang.org/grpc v1.78.0 + golang.org/x/sys v0.41.0 + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 gorm.io/gorm v1.31.1 ) @@ -26,12 +26,12 @@ require ( cel.dev/expr v0.25.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.14.2 // indirect - github.com/bytedance/sonic/loader v0.4.0 // indirect + 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/dustin/go-humanize v1.0.1 // indirect github.com/elastic/go-windows v1.0.2 // 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/glebarez/go-sqlite v1.22.0 // indirect @@ -40,7 +40,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/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -60,7 +60,6 @@ require ( github.com/quic-go/quic-go v0.59.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/spf13/pflag v1.0.9 // 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 @@ -72,8 +71,8 @@ require ( golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/text v0.33.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.1 // indirect diff --git a/agent/go.sum b/agent/go.sum index e0070d261..9f727d900 100644 --- a/agent/go.sum +++ b/agent/go.sum @@ -6,10 +6,12 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= -github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= -github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= -github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -23,8 +25,8 @@ github.com/elastic/go-sysinfo v1.15.4 h1:A3zQcunCxik14MgXu39cXFXcIw2sFXZ0zL886ey github.com/elastic/go-sysinfo v1.15.4/go.mod h1:ZBVXmqS368dOn/jvijV/zHLfakWTYHBZPk3G244lHrU= github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI= github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8= -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= @@ -51,8 +53,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= @@ -114,8 +116,6 @@ github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -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= @@ -124,15 +124,14 @@ 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/tehmaze/netflow v0.0.0-20240303214733-8c13bb004068 h1:1B+EAUqxEyPByCfk55tB21DtR7WF7Q2w71g7+uVkvIg= github.com/tehmaze/netflow v0.0.0-20240303214733-8c13bb004068/go.mod h1:QRP5wJOf7gGMGL2fCAfmh/5CMZQspRxT5DqghaPRrjM= -github.com/threatwinds/go-sdk v1.1.7 h1:2IJAWTCxZU4BDFiavPjH8MqpA/mam1QyIsjySbZLlRo= -github.com/threatwinds/go-sdk v1.1.7/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= github.com/threatwinds/logger v1.2.3 h1:V2SVAXzbq+/huCvIWOfqzMTH+WBHJxankyBgVG2hy1Y= github.com/threatwinds/logger v1.2.3/go.mod h1:N+bJKvF4FQNJZLfQpVYWpr6D8iEAFnAQfHYqH5iR1TI= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -153,16 +152,16 @@ github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ= github.com/wI2L/jsondiff v0.7.0/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -182,20 +181,20 @@ golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= 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-20260120221211-b8f7ae30c516 h1:vmC/ws+pLzWjj/gzApyoZuSVrDtF1aod4u/+bbj8hgM= -google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:p3MLuOwURrGBRoEyFHBT3GjUwaCQVKeNqqWxlcISGdw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/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/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/installer/go.mod b/installer/go.mod index 0a6f803e3..bfb952ff7 100644 --- a/installer/go.mod +++ b/installer/go.mod @@ -3,11 +3,11 @@ module github.com/utmstack/UTMStack/installer go 1.25.1 require ( - github.com/cloudfoundry/gosigar v1.3.112 + github.com/cloudfoundry/gosigar v1.3.116 github.com/docker/docker v28.5.2+incompatible github.com/kardianos/service v1.2.4 github.com/levigross/grequests v0.0.0-20250606031859-3f3c12e4e704 - github.com/lib/pq v1.10.9 + github.com/lib/pq v1.11.2 github.com/shirou/gopsutil/v3 v3.24.5 github.com/threatwinds/logger v1.2.3 github.com/utmstack/license-manager-sdk v0.1.0 @@ -77,7 +77,7 @@ require ( golang.org/x/arch v0.23.0 // indirect golang.org/x/crypto v0.47.0 // indirect golang.org/x/net v0.49.0 // indirect - golang.org/x/sys v0.40.0 // indirect + golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.41.0 // indirect diff --git a/installer/go.sum b/installer/go.sum index 5c0d8e6a2..1215bbd73 100644 --- a/installer/go.sum +++ b/installer/go.sum @@ -16,8 +16,8 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1x github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudfoundry/gosigar v1.3.112 h1:cGGZ2sj1GKyiwSxzouIR7ATNbgAkC4zqwWDxYQ2ObPc= -github.com/cloudfoundry/gosigar v1.3.112/go.mod h1:Ldc+tVw3dfqPwasZ9om1LT2aRwpjC1eFfbWKfv2WbDI= +github.com/cloudfoundry/gosigar v1.3.116 h1:CCl5yCoHaIC6K7TgERlPql7BerCpT5DPgdYYA5Hh09c= +github.com/cloudfoundry/gosigar v1.3.116/go.mod h1:qDRZlMlpxT/tmEjp0ote3QApxQ102Xn3qTImzxSjXyc= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -93,8 +93,8 @@ 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/levigross/grequests v0.0.0-20250606031859-3f3c12e4e704 h1:iH9CoV+Eoc3eeaCTt8qCU8/Qod7HdRKHlytTmOCIutw= github.com/levigross/grequests v0.0.0-20250606031859-3f3c12e4e704/go.mod h1:qnB03x2PLQFGb5PZv0m87zJrVuvrHUuvZMlHTUuZVnM= -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.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs= +github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -207,8 +207,8 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= diff --git a/plugins/alerts/go.mod b/plugins/alerts/go.mod index 3ec1e0b45..ec1fd100d 100644 --- a/plugins/alerts/go.mod +++ b/plugins/alerts/go.mod @@ -3,7 +3,7 @@ module github.com/utmstack/UTMStack/plugins/alerts go 1.25.5 require ( - github.com/threatwinds/go-sdk v1.1.13 + github.com/threatwinds/go-sdk v1.1.14 github.com/tidwall/gjson v1.18.0 google.golang.org/protobuf v1.36.11 ) diff --git a/plugins/alerts/go.sum b/plugins/alerts/go.sum index 123356719..1abc5a7c4 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.13 h1:uOKaET5Pvxso+tYSevBiF8n6HP04oY5uf8K9uv+bYGk= -github.com/threatwinds/go-sdk v1.1.13/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/aws/go.mod b/plugins/aws/go.mod index d85c1dfa4..3e66b6e26 100644 --- a/plugins/aws/go.mod +++ b/plugins/aws/go.mod @@ -4,10 +4,10 @@ go 1.25.5 require ( github.com/aws/aws-sdk-go-v2 v1.41.1 - github.com/aws/aws-sdk-go-v2/config v1.32.7 - github.com/aws/aws-sdk-go-v2/credentials v1.19.7 + github.com/aws/aws-sdk-go-v2/config v1.32.8 + github.com/aws/aws-sdk-go-v2/credentials v1.19.8 github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.9 + github.com/threatwinds/go-sdk v1.1.14 ) require ( @@ -33,20 +33,20 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 // indirect github.com/aws/smithy-go v1.24.0 // indirect 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 github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/goccy/go-json v0.10.5 // indirect - github.com/google/cel-go v0.26.1 // indirect + github.com/google/cel-go v0.27.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect @@ -54,7 +54,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // 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 @@ -66,9 +65,9 @@ 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/grpc v1.78.0 + google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect diff --git a/plugins/aws/go.sum b/plugins/aws/go.sum index b0cc86430..f967d7975 100644 --- a/plugins/aws/go.sum +++ b/plugins/aws/go.sum @@ -6,10 +6,10 @@ github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6ce github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4= -github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY= -github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY= -github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8= -github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw= +github.com/aws/aws-sdk-go-v2/config v1.32.8 h1:iu+64gwDKEoKnyTQskSku72dAwggKI5sV6rNvgSMpMs= +github.com/aws/aws-sdk-go-v2/config v1.32.8/go.mod h1:MI2XvA+qDi3i9AJxX1E2fu730syEBzp/jnXrjxuHwgI= +github.com/aws/aws-sdk-go-v2/credentials v1.19.8 h1:Jp2JYH1lRT3KhX4mshHPvVYsR5qqRec3hGvEarNYoR0= +github.com/aws/aws-sdk-go-v2/credentials v1.19.8/go.mod h1:fZG9tuvyVfxknv1rKibIz3DobRaFw1Poe8IKtXB3XYY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U= @@ -28,8 +28,8 @@ github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4 github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M= github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk= github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 h1:gd84Omyu9JLriJVCbGApcLzVR3XtmC4ZDPcAI6Ftvds= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 h1:0jbJeuEHlwKJ9PfXtpSFc4MF+WIWORdhN1n30ITZGFM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo= github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ= github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ= github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= @@ -40,14 +40,16 @@ github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uS github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -70,8 +72,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= @@ -109,8 +111,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= @@ -118,13 +118,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -143,16 +142,16 @@ github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ= github.com/wI2L/jsondiff v0.7.0/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -174,12 +173,12 @@ 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/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugins/azure/go.mod b/plugins/azure/go.mod index a2bf8c395..55ed03781 100644 --- a/plugins/azure/go.mod +++ b/plugins/azure/go.mod @@ -3,17 +3,17 @@ module github.com/utmstack/UTMStack/plugins/azure go 1.25.5 require ( + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs/v2 v2.0.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.9 - google.golang.org/grpc v1.78.0 + github.com/threatwinds/go-sdk v1.1.14 + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) require ( cel.dev/expr v0.25.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/go-amqp v1.5.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect @@ -21,7 +21,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 @@ -29,7 +29,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 @@ -41,7 +41,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 @@ -54,8 +53,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-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/plugins/azure/go.sum b/plugins/azure/go.sum index f07ebba16..88b1689df 100644 --- a/plugins/azure/go.sum +++ b/plugins/azure/go.sum @@ -26,6 +26,8 @@ github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uS github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= @@ -36,8 +38,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -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= @@ -64,8 +66,8 @@ github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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= @@ -109,8 +111,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= @@ -118,13 +118,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -143,16 +142,16 @@ github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ= github.com/wI2L/jsondiff v0.7.0/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -174,12 +173,12 @@ 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/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugins/bitdefender/go.mod b/plugins/bitdefender/go.mod index 7dc65e835..d251061a0 100644 --- a/plugins/bitdefender/go.mod +++ b/plugins/bitdefender/go.mod @@ -5,8 +5,8 @@ go 1.25.5 require ( github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 - github.com/threatwinds/go-sdk v1.1.9 - google.golang.org/grpc v1.78.0 + github.com/threatwinds/go-sdk v1.1.14 + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) @@ -17,7 +17,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 @@ -25,7 +25,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 @@ -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-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/plugins/bitdefender/go.sum b/plugins/bitdefender/go.sum index c26cade06..2eb91827c 100644 --- a/plugins/bitdefender/go.sum +++ b/plugins/bitdefender/go.sum @@ -8,14 +8,16 @@ github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uS github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 +40,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= @@ -79,8 +81,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 +88,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -113,16 +112,16 @@ github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ= github.com/wI2L/jsondiff v0.7.0/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -144,12 +143,12 @@ 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/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugins/config/go.mod b/plugins/config/go.mod index db172777d..959755c23 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.11.1 - github.com/threatwinds/go-sdk v1.1.10 + github.com/lib/pq v1.11.2 + github.com/threatwinds/go-sdk v1.1.14 gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/yaml v1.6.0 ) @@ -49,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-20260202165425-ce8ad4cf556b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // 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 98dd2873b..6f922c27f 100644 --- a/plugins/config/go.sum +++ b/plugins/config/go.sum @@ -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.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI= -github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs= +github.com/lib/pq v1.11.2/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= @@ -90,8 +90,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -141,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-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/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/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/crowdstrike/go.mod b/plugins/crowdstrike/go.mod index 2d7c073e3..3dbaf59a6 100644 --- a/plugins/crowdstrike/go.mod +++ b/plugins/crowdstrike/go.mod @@ -5,8 +5,8 @@ go 1.25.5 require ( github.com/crowdstrike/gofalcon v0.19.0 github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.9 - google.golang.org/grpc v1.78.0 + github.com/threatwinds/go-sdk v1.1.14 + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) @@ -19,7 +19,7 @@ require ( github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cespare/xxhash/v2 v2.3.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-logr/logr v1.4.3 // indirect @@ -51,7 +51,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.5.0 // 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 @@ -65,7 +65,6 @@ require ( github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect github.com/sirupsen/logrus v1.9.4 // 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 @@ -86,8 +85,8 @@ require ( golang.org/x/sync v0.19.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-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/plugins/crowdstrike/go.sum b/plugins/crowdstrike/go.sum index a5109a5ba..494a0d183 100644 --- a/plugins/crowdstrike/go.sum +++ b/plugins/crowdstrike/go.sum @@ -20,8 +20,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= @@ -95,8 +95,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= @@ -138,8 +138,6 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= -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= @@ -147,13 +145,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -178,10 +175,10 @@ go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= @@ -209,12 +206,12 @@ 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/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugins/events/go.mod b/plugins/events/go.mod index 9f5635523..01104171c 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.13 + github.com/threatwinds/go-sdk v1.1.14 github.com/tidwall/gjson v1.18.0 ) diff --git a/plugins/events/go.sum b/plugins/events/go.sum index 123356719..1abc5a7c4 100644 --- a/plugins/events/go.sum +++ b/plugins/events/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.13 h1:uOKaET5Pvxso+tYSevBiF8n6HP04oY5uf8K9uv+bYGk= -github.com/threatwinds/go-sdk v1.1.13/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/feeds/go.mod b/plugins/feeds/go.mod index 68b1d5bd7..f8fa84d58 100644 --- a/plugins/feeds/go.mod +++ b/plugins/feeds/go.mod @@ -5,7 +5,7 @@ go 1.25.5 require ( github.com/AtlasInsideCorp/AtlasInsideAES v1.0.0 github.com/opensearch-project/opensearch-go/v2 v2.3.0 - github.com/threatwinds/go-sdk v1.1.9 + github.com/threatwinds/go-sdk v1.1.14 golang.org/x/sync v0.19.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -17,7 +17,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 @@ -25,7 +25,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 @@ -38,7 +38,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 @@ -51,8 +50,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-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // 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/feeds/go.sum b/plugins/feeds/go.sum index 7154bbee2..f8fdd174d 100644 --- a/plugins/feeds/go.sum +++ b/plugins/feeds/go.sum @@ -29,8 +29,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= @@ -53,8 +53,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.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -98,8 +98,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= @@ -107,14 +105,13 @@ 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.2/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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -197,10 +194,10 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/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/gcp/go.mod b/plugins/gcp/go.mod index b08ac0228..ca74803b1 100644 --- a/plugins/gcp/go.mod +++ b/plugins/gcp/go.mod @@ -5,9 +5,9 @@ go 1.25.5 require ( cloud.google.com/go/pubsub v1.50.1 github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.9 - google.golang.org/api v0.263.0 - google.golang.org/grpc v1.78.0 + github.com/threatwinds/go-sdk v1.1.14 + google.golang.org/api v0.267.0 + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) @@ -26,7 +26,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/felixge/httpsnoop v1.0.4 // 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-logr/logr v1.4.3 // indirect @@ -37,10 +37,10 @@ require ( github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/google/cel-go v0.26.1 // indirect + github.com/google/cel-go v0.27.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect - github.com/googleapis/gax-go/v2 v2.16.0 // indirect + github.com/googleapis/gax-go/v2 v2.17.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 @@ -52,7 +52,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 @@ -70,14 +69,14 @@ require ( golang.org/x/crypto v0.47.0 // indirect golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/net v0.49.0 // indirect - golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/oauth2 v0.35.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.14.0 // indirect google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // 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-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/plugins/gcp/go.sum b/plugins/gcp/go.sum index 68097e872..71153b223 100644 --- a/plugins/gcp/go.sum +++ b/plugins/gcp/go.sum @@ -35,8 +35,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= -github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -44,16 +44,16 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= -github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= -github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= -github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -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= @@ -91,8 +91,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -109,8 +109,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao= github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8= -github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= -github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= +github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc= +github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -146,8 +146,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= @@ -160,8 +158,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.9 h1:4i8UZczXyGbRJsUEHRgaS2oQ03VTRjh/DYyTtGdSfRA= -github.com/threatwinds/go-sdk v1.1.9/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -225,8 +223,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= +golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -253,8 +251,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/api v0.263.0 h1:UFs7qn8gInIdtk1ZA6eXRXp5JDAnS4x9VRsRVCeKdbk= -google.golang.org/api v0.263.0/go.mod h1:fAU1xtNNisHgOF5JooAs8rRaTkl2rT3uaoNGo9NS3R8= +google.golang.org/api v0.267.0 h1:w+vfWPMPYeRs8qH1aYYsFX68jMls5acWl/jocfLomwE= +google.golang.org/api v0.267.0/go.mod h1:Jzc0+ZfLnyvXma3UtaTl023TdhZu6OMBP9tJ+0EmFD0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -262,17 +260,17 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM= google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM= -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-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -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/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/plugins/geolocation/go.mod b/plugins/geolocation/go.mod index 0ac2ba241..2b49180db 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.10 + github.com/threatwinds/go-sdk v1.1.14 github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 ) @@ -47,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-20260202165425-ce8ad4cf556b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // 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 f69c3240f..a37652182 100644 --- a/plugins/geolocation/go.sum +++ b/plugins/geolocation/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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -140,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-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/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/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/inputs/go.mod b/plugins/inputs/go.mod index 479bb46bf..906942c47 100644 --- a/plugins/inputs/go.mod +++ b/plugins/inputs/go.mod @@ -5,8 +5,8 @@ go 1.25.5 require ( github.com/gin-gonic/gin v1.11.0 github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.9 - google.golang.org/grpc v1.78.0 + github.com/threatwinds/go-sdk v1.1.14 + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) @@ -17,14 +17,14 @@ 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/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect 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/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect @@ -49,8 +48,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-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/plugins/inputs/go.sum b/plugins/inputs/go.sum index 9978feb4f..7402b79de 100644 --- a/plugins/inputs/go.sum +++ b/plugins/inputs/go.sum @@ -8,14 +8,16 @@ github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uS github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 +40,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 +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= @@ -86,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -111,16 +110,16 @@ github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ= github.com/wI2L/jsondiff v0.7.0/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -142,12 +141,12 @@ 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/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugins/modules-config/go.mod b/plugins/modules-config/go.mod index fdc0111f5..5f06af9bb 100644 --- a/plugins/modules-config/go.mod +++ b/plugins/modules-config/go.mod @@ -6,15 +6,15 @@ require ( cloud.google.com/go/pubsub v1.50.1 github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs/v2 v2.0.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 - github.com/aws/aws-sdk-go-v2/config v1.32.7 - github.com/aws/aws-sdk-go-v2/credentials v1.19.7 + github.com/aws/aws-sdk-go-v2/config v1.32.8 + github.com/aws/aws-sdk-go-v2/credentials v1.19.8 github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.63.1 github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 github.com/crowdstrike/gofalcon v0.19.0 github.com/gin-gonic/gin v1.11.0 - github.com/threatwinds/go-sdk v1.1.9 - google.golang.org/api v0.263.0 - google.golang.org/grpc v1.78.0 + github.com/threatwinds/go-sdk v1.1.14 + google.golang.org/api v0.267.0 + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) @@ -40,7 +40,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 // indirect github.com/aws/smithy-go v1.24.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/bytedance/gopkg v0.1.3 // indirect @@ -49,7 +49,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/felixge/httpsnoop v1.0.4 // 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/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -80,11 +80,11 @@ require ( github.com/go-viper/mapstructure/v2 v2.5.0 // 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/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect - github.com/googleapis/gax-go/v2 v2.16.0 // indirect + github.com/googleapis/gax-go/v2 v2.17.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 @@ -98,7 +98,6 @@ require ( github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect github.com/sirupsen/logrus v1.9.4 // 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 @@ -118,14 +117,14 @@ require ( golang.org/x/crypto v0.47.0 // indirect golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/net v0.49.0 // indirect - golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/oauth2 v0.35.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.14.0 // indirect google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // 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-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/plugins/modules-config/go.sum b/plugins/modules-config/go.sum index 0324da313..48bf6b99f 100644 --- a/plugins/modules-config/go.sum +++ b/plugins/modules-config/go.sum @@ -44,10 +44,10 @@ github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6ce github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4= -github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY= -github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY= -github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8= -github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw= +github.com/aws/aws-sdk-go-v2/config v1.32.8 h1:iu+64gwDKEoKnyTQskSku72dAwggKI5sV6rNvgSMpMs= +github.com/aws/aws-sdk-go-v2/config v1.32.8/go.mod h1:MI2XvA+qDi3i9AJxX1E2fu730syEBzp/jnXrjxuHwgI= +github.com/aws/aws-sdk-go-v2/credentials v1.19.8 h1:Jp2JYH1lRT3KhX4mshHPvVYsR5qqRec3hGvEarNYoR0= +github.com/aws/aws-sdk-go-v2/credentials v1.19.8/go.mod h1:fZG9tuvyVfxknv1rKibIz3DobRaFw1Poe8IKtXB3XYY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U= @@ -66,8 +66,8 @@ github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4 github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M= github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk= github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 h1:gd84Omyu9JLriJVCbGApcLzVR3XtmC4ZDPcAI6Ftvds= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 h1:0jbJeuEHlwKJ9PfXtpSFc4MF+WIWORdhN1n30ITZGFM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo= github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ= github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ= github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= @@ -87,8 +87,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= -github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/crowdstrike/gofalcon v0.19.0 h1:pKvA8Az85wD6OR7aq/tvc+tORtR5CSyKp3+LDQXc4pc= @@ -100,18 +100,18 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= -github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= -github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= -github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -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= @@ -203,8 +203,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -221,8 +221,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao= github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8= -github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= -github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= +github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc= +github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -268,8 +268,6 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= -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= @@ -282,8 +280,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.9 h1:4i8UZczXyGbRJsUEHRgaS2oQ03VTRjh/DYyTtGdSfRA= -github.com/threatwinds/go-sdk v1.1.9/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -349,8 +347,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= +golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -377,8 +375,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/api v0.263.0 h1:UFs7qn8gInIdtk1ZA6eXRXp5JDAnS4x9VRsRVCeKdbk= -google.golang.org/api v0.263.0/go.mod h1:fAU1xtNNisHgOF5JooAs8rRaTkl2rT3uaoNGo9NS3R8= +google.golang.org/api v0.267.0 h1:w+vfWPMPYeRs8qH1aYYsFX68jMls5acWl/jocfLomwE= +google.golang.org/api v0.267.0/go.mod h1:Jzc0+ZfLnyvXma3UtaTl023TdhZu6OMBP9tJ+0EmFD0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -386,17 +384,17 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM= google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM= -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-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -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/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/plugins/o365/go.mod b/plugins/o365/go.mod index c755c130b..9fc336715 100644 --- a/plugins/o365/go.mod +++ b/plugins/o365/go.mod @@ -4,8 +4,8 @@ go 1.25.5 require ( github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.9 - google.golang.org/grpc v1.78.0 + github.com/threatwinds/go-sdk v1.1.14 + google.golang.org/grpc v1.79.1 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/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect @@ -49,8 +48,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-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/plugins/o365/go.sum b/plugins/o365/go.sum index 9978feb4f..7402b79de 100644 --- a/plugins/o365/go.sum +++ b/plugins/o365/go.sum @@ -8,14 +8,16 @@ github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uS github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 +40,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 +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= @@ -86,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -111,16 +110,16 @@ github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ= github.com/wI2L/jsondiff v0.7.0/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -142,12 +141,12 @@ 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/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugins/soc-ai/go.mod b/plugins/soc-ai/go.mod index 44b6744e3..2bbc877d7 100644 --- a/plugins/soc-ai/go.mod +++ b/plugins/soc-ai/go.mod @@ -20,14 +20,14 @@ 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 github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/goccy/go-json v0.10.5 // 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/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect @@ -36,8 +36,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect - github.com/threatwinds/go-sdk v1.1.9 + github.com/threatwinds/go-sdk v1.1.14 github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect @@ -49,8 +48,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-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect diff --git a/plugins/soc-ai/go.sum b/plugins/soc-ai/go.sum index 9978feb4f..1abc5a7c4 100644 --- a/plugins/soc-ai/go.sum +++ b/plugins/soc-ai/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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/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/sophos/go.mod b/plugins/sophos/go.mod index fbd307c8c..6bd956d58 100644 --- a/plugins/sophos/go.mod +++ b/plugins/sophos/go.mod @@ -4,8 +4,8 @@ go 1.25.5 require ( github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.9 - google.golang.org/grpc v1.78.0 + github.com/threatwinds/go-sdk v1.1.14 + google.golang.org/grpc v1.79.1 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/gjson v1.18.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect @@ -49,8 +48,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-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/plugins/sophos/go.sum b/plugins/sophos/go.sum index 9978feb4f..7402b79de 100644 --- a/plugins/sophos/go.sum +++ b/plugins/sophos/go.sum @@ -8,14 +8,16 @@ github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uS github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 +40,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 +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= @@ -86,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -111,16 +110,16 @@ github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ= github.com/wI2L/jsondiff v0.7.0/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -142,12 +141,12 @@ 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/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugins/stats/go.mod b/plugins/stats/go.mod index acbcafcbf..8f5733dc0 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.10 + github.com/threatwinds/go-sdk v1.1.14 google.golang.org/protobuf v1.36.11 ) @@ -47,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-20260202165425-ce8ad4cf556b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // 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 0c6ce469a..1abc5a7c4 100644 --- a/plugins/stats/go.sum +++ b/plugins/stats/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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -139,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-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/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/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/utmstack-collector/go.mod b/utmstack-collector/go.mod index 6757cf795..166d9165b 100644 --- a/utmstack-collector/go.mod +++ b/utmstack-collector/go.mod @@ -9,9 +9,9 @@ require ( github.com/glebarez/sqlite v1.11.0 github.com/google/uuid v1.6.0 github.com/kardianos/service v1.2.4 - github.com/threatwinds/go-sdk v1.1.7 + github.com/threatwinds/go-sdk v1.1.14 github.com/threatwinds/logger v1.2.3 - google.golang.org/grpc v1.78.0 + google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v2 v2.4.0 gorm.io/gorm v1.31.1 @@ -22,8 +22,8 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.14.2 // indirect - github.com/bytedance/sonic/loader v0.4.0 // indirect + github.com/bytedance/sonic v1.15.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/containerd/errdefs v1.0.0 // indirect @@ -35,7 +35,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/elastic/go-windows v1.0.2 // indirect github.com/felixge/httpsnoop v1.0.4 // 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/glebarez/go-sqlite v1.22.0 // indirect @@ -46,7 +46,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/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect @@ -70,7 +70,6 @@ require ( github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // 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 @@ -90,8 +89,8 @@ require ( golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.14.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.2 // indirect diff --git a/utmstack-collector/go.sum b/utmstack-collector/go.sum index e817de2a8..ba290d189 100644 --- a/utmstack-collector/go.sum +++ b/utmstack-collector/go.sum @@ -10,10 +10,10 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= -github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= -github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= -github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -46,8 +46,8 @@ github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -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= @@ -75,8 +75,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= @@ -149,8 +149,6 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -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= @@ -159,13 +157,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.7 h1:2IJAWTCxZU4BDFiavPjH8MqpA/mam1QyIsjySbZLlRo= -github.com/threatwinds/go-sdk v1.1.7/go.mod h1:N19iqJPaNAoWwZTCuFvV0hIvT0D1jOR1KkKYgAoPLmw= +github.com/threatwinds/go-sdk v1.1.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= github.com/threatwinds/logger v1.2.3 h1:V2SVAXzbq+/huCvIWOfqzMTH+WBHJxankyBgVG2hy1Y= github.com/threatwinds/logger v1.2.3/go.mod h1:N+bJKvF4FQNJZLfQpVYWpr6D8iEAFnAQfHYqH5iR1TI= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -233,12 +230,12 @@ golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= 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-20260120221211-b8f7ae30c516 h1:vmC/ws+pLzWjj/gzApyoZuSVrDtF1aod4u/+bbj8hgM= -google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:p3MLuOwURrGBRoEyFHBT3GjUwaCQVKeNqqWxlcISGdw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/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/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From e7a7410a92a34be5dc1851f6272429291d721575 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 01:58:24 -0500 Subject: [PATCH 027/115] feat[agent]: add macOS build pipeline and standardize binary naming --- .github/workflows/v11-deployment-pipeline.yml | 196 +++++++++++++++--- agent/dependency/dependency.go | 12 +- 2 files changed, 177 insertions(+), 31 deletions(-) diff --git a/.github/workflows/v11-deployment-pipeline.yml b/.github/workflows/v11-deployment-pipeline.yml index 65d09e245..04cfac660 100644 --- a/.github/workflows/v11-deployment-pipeline.yml +++ b/.github/workflows/v11-deployment-pipeline.yml @@ -130,16 +130,27 @@ jobs: - name: Check out code into the right branch uses: actions/checkout@v4 - - name: Build Linux Binaries + - name: Build Linux Binaries (amd64) env: GOOS: linux GOARCH: amd64 run: | cd ${{ github.workspace }}/agent - go build -o utmstack_agent_service -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . + go build -o utmstack_agent_service_linux_amd64 -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . cd ${{ github.workspace }}/agent/updater - go build -o utmstack_updater_service . + go build -o utmstack_updater_service_linux_amd64 . + + - name: Build Linux Binaries (arm64) + env: + GOOS: linux + GOARCH: arm64 + run: | + cd ${{ github.workspace }}/agent + go build -o utmstack_agent_service_linux_arm64 -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . + + cd ${{ github.workspace }}/agent/updater + go build -o utmstack_updater_service_linux_arm64 . - name: Build Windows Binaries (amd64) env: @@ -147,10 +158,10 @@ jobs: GOARCH: amd64 run: | cd ${{ github.workspace }}/agent - go build -o utmstack_agent_service.exe -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . + go build -o utmstack_agent_service_windows_amd64.exe -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . cd ${{ github.workspace }}/agent/updater - go build -o utmstack_updater_service.exe . + go build -o utmstack_updater_service_windows_amd64.exe . - name: Build Windows Binaries (arm64) env: @@ -158,34 +169,138 @@ jobs: GOARCH: arm64 run: | cd ${{ github.workspace }}/agent - go build -o utmstack_agent_service_arm64.exe -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . + go build -o utmstack_agent_service_windows_arm64.exe -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . cd ${{ github.workspace }}/agent/updater - go build -o utmstack_updater_service_arm64.exe . + go build -o utmstack_updater_service_windows_arm64.exe . - name: Sign Windows Agents run: | cd ${{ github.workspace }}/agent - signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /f "${{ vars.SIGN_CERT }}" /csp "eToken Base Cryptographic Provider" /k "[{{${{ secrets.SIGN_KEY }}}}]=${{ secrets.SIGN_CONTAINER }}" "utmstack_agent_service.exe" - signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /f "${{ vars.SIGN_CERT }}" /csp "eToken Base Cryptographic Provider" /k "[{{${{ secrets.SIGN_KEY }}}}]=${{ secrets.SIGN_CONTAINER }}" "utmstack_agent_service_arm64.exe" + signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /f "${{ vars.SIGN_CERT }}" /csp "eToken Base Cryptographic Provider" /k "[{{${{ secrets.SIGN_KEY }}}}]=${{ secrets.SIGN_CONTAINER }}" "utmstack_agent_service_windows_amd64.exe" + signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /f "${{ vars.SIGN_CERT }}" /csp "eToken Base Cryptographic Provider" /k "[{{${{ secrets.SIGN_KEY }}}}]=${{ secrets.SIGN_CONTAINER }}" "utmstack_agent_service_windows_arm64.exe" cd ${{ github.workspace }}/agent/updater - signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /f "${{ vars.SIGN_CERT }}" /csp "eToken Base Cryptographic Provider" /k "[{{${{ secrets.SIGN_KEY }}}}]=${{ secrets.SIGN_CONTAINER }}" "utmstack_updater_service.exe" - signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /f "${{ vars.SIGN_CERT }}" /csp "eToken Base Cryptographic Provider" /k "[{{${{ secrets.SIGN_KEY }}}}]=${{ secrets.SIGN_CONTAINER }}" "utmstack_updater_service_arm64.exe" + signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /f "${{ vars.SIGN_CERT }}" /csp "eToken Base Cryptographic Provider" /k "[{{${{ secrets.SIGN_KEY }}}}]=${{ secrets.SIGN_CONTAINER }}" "utmstack_updater_service_windows_amd64.exe" + signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /f "${{ vars.SIGN_CERT }}" /csp "eToken Base Cryptographic Provider" /k "[{{${{ secrets.SIGN_KEY }}}}]=${{ secrets.SIGN_CONTAINER }}" "utmstack_updater_service_windows_arm64.exe" - name: Upload signed binaries as artifacts uses: actions/upload-artifact@v4 with: name: signed-agents path: | - ${{ github.workspace }}/agent/utmstack_agent_service - ${{ github.workspace }}/agent/utmstack_agent_service.exe - ${{ github.workspace }}/agent/utmstack_agent_service_arm64.exe - ${{ github.workspace }}/agent/updater/utmstack_updater_service - ${{ github.workspace }}/agent/updater/utmstack_updater_service.exe - ${{ github.workspace }}/agent/updater/utmstack_updater_service_arm64.exe + ${{ github.workspace }}/agent/utmstack_agent_service_linux_amd64 + ${{ github.workspace }}/agent/utmstack_agent_service_linux_arm64 + ${{ github.workspace }}/agent/utmstack_agent_service_windows_amd64.exe + ${{ github.workspace }}/agent/utmstack_agent_service_windows_arm64.exe + ${{ github.workspace }}/agent/updater/utmstack_updater_service_linux_amd64 + ${{ github.workspace }}/agent/updater/utmstack_updater_service_linux_arm64 + ${{ github.workspace }}/agent/updater/utmstack_updater_service_windows_amd64.exe + ${{ github.workspace }}/agent/updater/utmstack_updater_service_windows_arm64.exe retention-days: 1 - + + build_agent_darwin: + name: Build and Sign Agent (macOS) + needs: [setup_deployment] + if: ${{ needs.setup_deployment.outputs.tag != '' }} + runs-on: macos-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.23' + + - name: Install Apple Certificate + env: + CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} + CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + run: | + # Create temporary keychain + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + KEYCHAIN_PASSWORD=$(openssl rand -base64 32) + + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # Import certificate + echo "$CERTIFICATE_BASE64" | base64 --decode > $RUNNER_TEMP/certificate.p12 + security import $RUNNER_TEMP/certificate.p12 -P "$CERTIFICATE_PASSWORD" \ + -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + # Allow codesign without prompt + security set-key-partition-list -S apple-tool:,apple:,codesign: \ + -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + - name: Build macOS Agent (arm64) + env: + GOOS: darwin + GOARCH: arm64 + CGO_ENABLED: 0 + run: | + cd ${{ github.workspace }}/agent + go build -o utmstack_agent_service_darwin_arm64 -v \ + -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . + + cd ${{ github.workspace }}/agent/updater + go build -o utmstack_updater_service_darwin_arm64 . + + - name: Sign macOS Binaries + env: + SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + run: | + # Sign agent + codesign --force --options runtime \ + --sign "$SIGNING_IDENTITY" \ + --timestamp \ + ${{ github.workspace }}/agent/utmstack_agent_service_darwin_arm64 + + # Sign updater + codesign --force --options runtime \ + --sign "$SIGNING_IDENTITY" \ + --timestamp \ + ${{ github.workspace }}/agent/updater/utmstack_updater_service_darwin_arm64 + + - name: Notarize macOS Binaries + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + run: | + # Create zip for notarization (notarytool requires zip/pkg/dmg) + cd ${{ github.workspace }}/agent + zip utmstack_agent_darwin_arm64.zip utmstack_agent_service_darwin_arm64 + + cd ${{ github.workspace }}/agent/updater + zip utmstack_updater_darwin_arm64.zip utmstack_updater_service_darwin_arm64 + + # Notarize agent + xcrun notarytool submit ${{ github.workspace }}/agent/utmstack_agent_darwin_arm64.zip \ + --apple-id "$APPLE_ID" \ + --password "$APPLE_APP_PASSWORD" \ + --team-id "$APPLE_TEAM_ID" \ + --wait + + # Notarize updater + xcrun notarytool submit ${{ github.workspace }}/agent/updater/utmstack_updater_darwin_arm64.zip \ + --apple-id "$APPLE_ID" \ + --password "$APPLE_APP_PASSWORD" \ + --team-id "$APPLE_TEAM_ID" \ + --wait + + - name: Upload macOS binaries as artifacts + uses: actions/upload-artifact@v4 + with: + name: signed-agents-darwin + path: | + ${{ github.workspace }}/agent/utmstack_agent_service_darwin_arm64 + ${{ github.workspace }}/agent/updater/utmstack_updater_service_darwin_arm64 + retention-days: 1 + build_utmstack_collector: name: Build UTMStack Collector needs: [setup_deployment] @@ -211,8 +326,8 @@ jobs: build_agent_manager: name: Build Agent Manager Microservice - needs: [build_agent, build_utmstack_collector, setup_deployment] - if: ${{ always() && needs.build_agent.result == 'success' && needs.build_utmstack_collector.result == 'success' && needs.setup_deployment.outputs.tag != '' }} + needs: [build_agent, build_agent_darwin, build_utmstack_collector, setup_deployment] + if: ${{ always() && needs.build_agent.result == 'success' && needs.build_agent_darwin.result == 'success' && needs.build_utmstack_collector.result == 'success' && needs.setup_deployment.outputs.tag != '' }} runs-on: ubuntu-24.04 steps: - name: Check out code into the right branch @@ -230,6 +345,12 @@ jobs: name: utmstack-collector path: ${{ github.workspace }}/utmstack-collector + - name: Download signed macOS agents from artifacts + uses: actions/download-artifact@v4 + with: + name: signed-agents-darwin + path: ${{ github.workspace }}/agent-darwin + - name: Prepare dependencies for Agent Manager Image run: | cd ${{ github.workspace }}/agent-manager @@ -246,14 +367,33 @@ jobs: curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/agent/utmstack_agent_dependencies_linux.zip" -o ./dependencies/agent/utmstack_agent_dependencies_linux.zip curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/agent/utmstack_agent_dependencies_windows.zip" -o ./dependencies/agent/utmstack_agent_dependencies_windows.zip curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/agent/utmstack_agent_dependencies_windows_arm64.zip" -o ./dependencies/agent/utmstack_agent_dependencies_windows_arm64.zip - curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/agent/utmstack-macos-agent-v10.pkg" -o ./dependencies/agent/utmstack-macos-agent.pkg - - cp "${{ github.workspace }}/agent/utmstack_agent_service" ./dependencies/agent/ - cp "${{ github.workspace }}/agent/utmstack_agent_service.exe" ./dependencies/agent/ - cp "${{ github.workspace }}/agent/utmstack_agent_service_arm64.exe" ./dependencies/agent/ - cp "${{ github.workspace }}/agent/updater/utmstack_updater_service" ./dependencies/agent/ - cp "${{ github.workspace }}/agent/updater/utmstack_updater_service.exe" ./dependencies/agent/ - cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_arm64.exe" ./dependencies/agent/ + + # Linux agents + cp "${{ github.workspace }}/agent/utmstack_agent_service_linux_amd64" ./dependencies/agent/ + cp "${{ github.workspace }}/agent/utmstack_agent_service_linux_arm64" ./dependencies/agent/ + cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_linux_amd64" ./dependencies/agent/ + cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_linux_arm64" ./dependencies/agent/ + + # Windows agents + cp "${{ github.workspace }}/agent/utmstack_agent_service_windows_amd64.exe" ./dependencies/agent/ + cp "${{ github.workspace }}/agent/utmstack_agent_service_windows_arm64.exe" ./dependencies/agent/ + cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_windows_amd64.exe" ./dependencies/agent/ + cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_windows_arm64.exe" ./dependencies/agent/ + + # macOS agents (signed and notarized) + cp "${{ github.workspace }}/agent-darwin/utmstack_agent_service_darwin_arm64" ./dependencies/agent/ + cp "${{ github.workspace }}/agent-darwin/utmstack_updater_service_darwin_arm64" ./dependencies/agent/ + curl -sSL "https://storage.googleapis.com/utmstack-updates/agent_updates/release/macos-agent/latest/utmstack-collector-mac" -o ./dependencies/agent/utmstack-collector-mac + + # TODO: Remove legacy binary names after all agents have migrated to new naming convention + # Legacy names for backwards compatibility with existing agents + cp "${{ github.workspace }}/agent/utmstack_agent_service_linux_amd64" ./dependencies/agent/utmstack_agent_service + cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_linux_amd64" ./dependencies/agent/utmstack_updater_service + cp "${{ github.workspace }}/agent/utmstack_agent_service_windows_amd64.exe" ./dependencies/agent/utmstack_agent_service.exe + cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_windows_amd64.exe" ./dependencies/agent/utmstack_updater_service.exe + cp "${{ github.workspace }}/agent/utmstack_agent_service_windows_arm64.exe" ./dependencies/agent/utmstack_agent_service_arm64.exe + cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_windows_arm64.exe" ./dependencies/agent/utmstack_updater_service_arm64.exe + cp "${{ github.workspace }}/agent/version.json" ./dependencies/agent/ - name: Login to GitHub Container Registry diff --git a/agent/dependency/dependency.go b/agent/dependency/dependency.go index e6d5641e1..47d908f3d 100644 --- a/agent/dependency/dependency.go +++ b/agent/dependency/dependency.go @@ -21,12 +21,18 @@ const ( MacosCollectorVersion = "11.2.3" ) -// UpdaterFile returns the updater binary name with the appropriate extension for the current OS. +// UpdaterFile returns the updater binary name with OS and architecture suffix. +// Format: utmstack_updater_service__[.exe] +// Examples: +// - utmstack_updater_service_linux_amd64 +// - utmstack_updater_service_windows_amd64.exe +// - utmstack_updater_service_darwin_arm64 func UpdaterFile(suffix string) string { + name := fmt.Sprintf("%s_%s_%s%s", updaterBaseName, runtime.GOOS, runtime.GOARCH, suffix) if runtime.GOOS == "windows" { - return updaterBaseName + suffix + ".exe" + return name + ".exe" } - return updaterBaseName + suffix + return name } // Dependency represents a dependency that the agent needs. From 7c21d6dbee7a82cc1b9b095ba8d40695922578d8 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 02:10:24 -0500 Subject: [PATCH 028/115] refactor[agent]: sync updater with release/v11.2.3, remove legacy files --- agent/collector/platform/linux_arm64.go | 161 ++++++++++++++++++++++++ agent/updater/config/const.go | 21 ++-- agent/updater/config/linux_amd64.go | 9 -- agent/updater/config/linux_arm64.go | 9 -- agent/updater/config/macos.go | 9 -- agent/updater/config/windows_amd64.go | 9 -- agent/updater/config/windows_arm64.go | 9 -- agent/updater/go.mod | 15 ++- agent/updater/go.sum | 43 +++++-- agent/updater/models/version.go | 5 - agent/updater/utils/cmd.go | 39 ------ agent/updater/utils/download.go | 50 -------- agent/updater/utils/files.go | 92 -------------- agent/updater/utils/logger.go | 20 --- agent/updater/utils/services.go | 135 -------------------- agent/updater/utils/zip.go | 52 -------- 16 files changed, 211 insertions(+), 467 deletions(-) create mode 100644 agent/collector/platform/linux_arm64.go delete mode 100644 agent/updater/config/linux_amd64.go delete mode 100644 agent/updater/config/linux_arm64.go delete mode 100644 agent/updater/config/macos.go delete mode 100644 agent/updater/config/windows_amd64.go delete mode 100644 agent/updater/config/windows_arm64.go delete mode 100644 agent/updater/models/version.go delete mode 100644 agent/updater/utils/cmd.go delete mode 100644 agent/updater/utils/download.go delete mode 100644 agent/updater/utils/files.go delete mode 100644 agent/updater/utils/logger.go delete mode 100644 agent/updater/utils/services.go delete mode 100644 agent/updater/utils/zip.go diff --git a/agent/collector/platform/linux_arm64.go b/agent/collector/platform/linux_arm64.go new file mode 100644 index 000000000..1d8e43926 --- /dev/null +++ b/agent/collector/platform/linux_arm64.go @@ -0,0 +1,161 @@ +//go:build linux && arm64 +// +build linux,arm64 + +package platform + +import ( + "bufio" + "context" + "os" + "os/exec" + "sync" + "time" + + "github.com/threatwinds/go-sdk/entities" + "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" +) + +const ( + journaldRestartDelayArm64 = 5 * time.Second + journaldMaxRestartDelayArm64 = 5 * time.Minute +) + +type LinuxSystemArm64 struct { + cmd *exec.Cmd + cancel context.CancelFunc + mu sync.Mutex +} + +func (l *LinuxSystemArm64) Name() string { + return "linux-system" +} + +func (l *LinuxSystemArm64) Start(ctx context.Context, queue chan *plugins.Log) { + host, err := os.Hostname() + if err != nil { + utils.Logger.ErrorF("error getting hostname: %v", err) + host = "unknown" + } + + restartDelay := journaldRestartDelayArm64 + + for { + select { + case <-ctx.Done(): + utils.Logger.Info("Linux system collector stopping due to context cancellation") + return + default: + } + + exitCode := l.runJournalctl(ctx, host, queue) + + if exitCode == 0 { + utils.Logger.Info("journalctl exited normally") + } else { + utils.Logger.ErrorF("journalctl exited with code %d, restarting in %v", exitCode, restartDelay) + } + + select { + case <-ctx.Done(): + return + case <-time.After(restartDelay): + } + + // Exponential backoff + restartDelay *= 2 + if restartDelay > journaldMaxRestartDelayArm64 { + restartDelay = journaldMaxRestartDelayArm64 + } + } +} + +func (l *LinuxSystemArm64) runJournalctl(ctx context.Context, host string, queue chan *plugins.Log) int { + l.mu.Lock() + cmdCtx, cancel := context.WithCancel(ctx) + l.cancel = cancel + + // journalctl -f: follow, -o json: JSON output, --no-pager: don't use pager + l.cmd = exec.CommandContext(cmdCtx, "journalctl", "-f", "-o", "json", "--no-pager") + + stdout, err := l.cmd.StdoutPipe() + if err != nil { + l.mu.Unlock() + utils.Logger.ErrorF("error creating journalctl stdout pipe: %v", err) + return -1 + } + + stderr, err := l.cmd.StderrPipe() + if err != nil { + l.mu.Unlock() + utils.Logger.ErrorF("error creating journalctl stderr pipe: %v", err) + return -1 + } + + if err := l.cmd.Start(); err != nil { + l.mu.Unlock() + utils.Logger.ErrorF("error starting journalctl: %v", err) + return -1 + } + l.mu.Unlock() + + utils.Logger.Info("Linux system log collector started (journald)") + + // Read stderr in background + go func() { + scanner := bufio.NewScanner(stderr) + for scanner.Scan() { + utils.Logger.ErrorF("journalctl error: %s", scanner.Text()) + } + }() + + // Read stdout (log entries) + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + line := scanner.Text() + + validatedLog, _, err := entities.ValidateString(line, false) + if err != nil { + utils.Logger.ErrorF("error validating log: %v", err) + continue + } + + queue <- &plugins.Log{ + DataType: string(config.DataTypeLinuxAgent), + DataSource: host, + Raw: validatedLog, + } + } + + if err := scanner.Err(); err != nil { + utils.Logger.ErrorF("error reading journalctl output: %v", err) + } + + if err := l.cmd.Wait(); err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + return exitErr.ExitCode() + } + return -1 + } + + return 0 +} + +func (l *LinuxSystemArm64) Stop() { + l.mu.Lock() + defer l.mu.Unlock() + + if l.cancel != nil { + l.cancel() + } + if l.cmd != nil && l.cmd.Process != nil { + l.cmd.Process.Kill() + } +} + +func GetCollectors() []Collector { + return []Collector{ + &LinuxSystemArm64{}, + } +} diff --git a/agent/updater/config/const.go b/agent/updater/config/const.go index 3b254f4a1..e57a491db 100644 --- a/agent/updater/config/const.go +++ b/agent/updater/config/const.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "path/filepath" "runtime" @@ -23,18 +24,16 @@ var ( VersionPath = filepath.Join(fs.GetExecutablePath(), "version.json") ) -// ServiceFile returns the agent binary name with the appropriate suffix and extension. +// ServiceFile returns the agent binary name with OS and architecture suffix. +// Format: utmstack_agent_service__[.exe] +// Examples: +// - utmstack_agent_service_linux_amd64 +// - utmstack_agent_service_windows_amd64.exe +// - utmstack_agent_service_darwin_arm64 func ServiceFile(suffix string) string { - // Check if this is arm64 - isArm64 := runtime.GOARCH == "arm64" - - base := agentBaseName - if isArm64 { - base = agentBaseName + "_arm64" - } - + name := fmt.Sprintf("%s_%s_%s%s", agentBaseName, runtime.GOOS, runtime.GOARCH, suffix) if runtime.GOOS == "windows" { - return base + suffix + ".exe" + return name + ".exe" } - return base + suffix + return name } diff --git a/agent/updater/config/linux_amd64.go b/agent/updater/config/linux_amd64.go deleted file mode 100644 index 026f6c3f9..000000000 --- a/agent/updater/config/linux_amd64.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build linux && amd64 -// +build linux,amd64 - -package config - -var ( - ServiceFile = "utmstack_agent_service%s" - DependFiles = []string{"utmstack_agent_dependencies_linux.zip"} -) diff --git a/agent/updater/config/linux_arm64.go b/agent/updater/config/linux_arm64.go deleted file mode 100644 index d77ff9f09..000000000 --- a/agent/updater/config/linux_arm64.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build linux && arm64 -// +build linux,arm64 - -package config - -var ( - ServiceFile = "utmstack_agent_service_arm64%s" - DependFiles = []string{"utmstack_agent_dependencies_linux_arm64.zip"} -) diff --git a/agent/updater/config/macos.go b/agent/updater/config/macos.go deleted file mode 100644 index ecb80457a..000000000 --- a/agent/updater/config/macos.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build darwin -// +build darwin - -package config - -var ( - ServiceFile = "utmstack_agent_service%s" - DependFiles = []string{} -) diff --git a/agent/updater/config/windows_amd64.go b/agent/updater/config/windows_amd64.go deleted file mode 100644 index adad86d9f..000000000 --- a/agent/updater/config/windows_amd64.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build windows && amd64 -// +build windows,amd64 - -package config - -var ( - ServiceFile = "utmstack_agent_service%s.exe" - DependFiles = []string{"utmstack_agent_dependencies_windows.zip"} -) diff --git a/agent/updater/config/windows_arm64.go b/agent/updater/config/windows_arm64.go deleted file mode 100644 index 4705118c1..000000000 --- a/agent/updater/config/windows_arm64.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build windows && arm64 -// +build windows,arm64 - -package config - -var ( - ServiceFile = "utmstack_agent_service_arm64%s.exe" - DependFiles = []string{"utmstack_agent_dependencies_windows_arm64.zip"} -) diff --git a/agent/updater/go.mod b/agent/updater/go.mod index 79d47b014..d9b407d6a 100644 --- a/agent/updater/go.mod +++ b/agent/updater/go.mod @@ -4,6 +4,8 @@ go 1.25.5 require ( github.com/kardianos/service v1.2.4 + github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/logger v1.2.3 github.com/utmstack/UTMStack/shared v0.0.0 ) @@ -11,10 +13,10 @@ replace github.com/utmstack/UTMStack/shared => ../../shared require ( github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.14.2 // indirect - github.com/bytedance/sonic/loader v0.4.0 // indirect + 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 @@ -25,7 +27,6 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -33,9 +34,12 @@ 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/threatwinds/logger v1.2.3 // 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 github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/arch v0.23.0 // indirect golang.org/x/crypto v0.47.0 // indirect golang.org/x/net v0.49.0 // indirect @@ -44,4 +48,5 @@ require ( google.golang.org/protobuf v1.36.11 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/agent/updater/go.sum b/agent/updater/go.sum index 9e557f96d..455acc4b0 100644 --- a/agent/updater/go.sum +++ b/agent/updater/go.sum @@ -1,17 +1,17 @@ github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= -github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= -github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= -github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/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/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.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= @@ -54,14 +54,15 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +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/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= @@ -73,14 +74,28 @@ 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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= +github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= github.com/threatwinds/logger v1.2.3 h1:V2SVAXzbq+/huCvIWOfqzMTH+WBHJxankyBgVG2hy1Y= github.com/threatwinds/logger v1.2.3/go.mod h1:N+bJKvF4FQNJZLfQpVYWpr6D8iEAFnAQfHYqH5iR1TI= +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= +github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= +github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= -go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= @@ -102,3 +117,5 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/agent/updater/models/version.go b/agent/updater/models/version.go deleted file mode 100644 index ed01f06e4..000000000 --- a/agent/updater/models/version.go +++ /dev/null @@ -1,5 +0,0 @@ -package models - -type Version struct { - Version string `json:"version"` -} diff --git a/agent/updater/utils/cmd.go b/agent/updater/utils/cmd.go deleted file mode 100644 index eae4140d9..000000000 --- a/agent/updater/utils/cmd.go +++ /dev/null @@ -1,39 +0,0 @@ -package utils - -import ( - "errors" - "os/exec" - - twsdk "github.com/threatwinds/go-sdk/entities" -) - -func ExecuteWithResult(c string, dir string, arg ...string) (string, bool) { - cmd := exec.Command(c, arg...) - - cmd.Dir = dir - if errors.Is(cmd.Err, exec.ErrDot) { - cmd.Err = nil - } - - out, err := cmd.Output() - if err != nil { - return string(out[:]) + err.Error(), true - } - - if string(out[:]) == "" { - return "Command executed successfully but no output", false - } - validUtf8Out, _, err := twsdk.ValidateString(string(out[:]), false) - if err != nil { - return string(out[:]) + err.Error(), true - } - - return validUtf8Out, false -} - -func Execute(c string, dir string, arg ...string) error { - cmd := exec.Command(c, arg...) - cmd.Dir = dir - - return cmd.Run() -} diff --git a/agent/updater/utils/download.go b/agent/updater/utils/download.go deleted file mode 100644 index b8c223447..000000000 --- a/agent/updater/utils/download.go +++ /dev/null @@ -1,50 +0,0 @@ -package utils - -import ( - "crypto/tls" - "fmt" - "io" - "net/http" - "os" - "path/filepath" -) - -func DownloadFile(url string, headers map[string]string, fileName string, path string, skipTlsVerification bool) error { - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return fmt.Errorf("error creating new request: %v", err) - } - for key, value := range headers { - req.Header.Add(key, value) - } - - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: skipTlsVerification}, - DisableCompression: true, - } - client := &http.Client{Transport: tr} - defer tr.CloseIdleConnections() - - resp, err := client.Do(req) - if err != nil { - return fmt.Errorf("error sending request: %v", err) - } - defer func() { _ = resp.Body.Close() }() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("expected status %d; got %d", http.StatusOK, resp.StatusCode) - } - - out, err := os.Create(filepath.Join(path, fileName)) - if err != nil { - return fmt.Errorf("error creating file: %v", err) - } - defer func() { _ = out.Close() }() - - _, err = io.Copy(out, resp.Body) - if err != nil { - return fmt.Errorf("error copying file: %v", err) - } - - return nil -} diff --git a/agent/updater/utils/files.go b/agent/updater/utils/files.go deleted file mode 100644 index 6b0180721..000000000 --- a/agent/updater/utils/files.go +++ /dev/null @@ -1,92 +0,0 @@ -package utils - -import ( - "encoding/json" - "fmt" - "io" - "os" - "path/filepath" -) - -func GetMyPath() string { - ex, err := os.Executable() - if err != nil { - return "" - } - exPath := filepath.Dir(ex) - return exPath -} - -func CreatePathIfNotExist(path string) error { - if _, err := os.Stat(path); os.IsNotExist(err) { - if err := os.MkdirAll(path, 0755); err != nil { - return fmt.Errorf("error creating path: %v", err) - } - } else if err != nil { - return fmt.Errorf("error checking path: %v", err) - } - return nil -} - -func CheckIfPathExist(path string) bool { - if _, err := os.Stat(path); os.IsNotExist(err) { - return false - } - return true -} - -func ReadJson(fileName string, data interface{}) error { - content, err := os.ReadFile(fileName) - if err != nil { - return err - } - - err = json.Unmarshal(content, data) - if err != nil { - return err - } - - return nil -} - -func WriteStringToFile(fileName string, body string) error { - file, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.ModePerm) - if err != nil { - return err - } - defer func() { _ = file.Close() }() - - _, err = file.WriteString(body) - return err -} - -func WriteJSON(path string, data interface{}) error { - jsonData, err := json.MarshalIndent(data, "", " ") - if err != nil { - return err - } - - err = WriteStringToFile(path, string(jsonData)) - if err != nil { - return err - } - - return nil -} - -func copyFile(src, dst string) error { - sourceFile, err := os.Open(src) - if err != nil { - return err - } - defer sourceFile.Close() - - destFile, err := os.Create(dst) - if err != nil { - return err - } - defer destFile.Close() - - _, err = io.Copy(destFile, sourceFile) - return err -} diff --git a/agent/updater/utils/logger.go b/agent/updater/utils/logger.go deleted file mode 100644 index fc0cbb757..000000000 --- a/agent/updater/utils/logger.go +++ /dev/null @@ -1,20 +0,0 @@ -package utils - -import ( - "sync" - - "github.com/threatwinds/logger" -) - -var ( - UpdaterLogger *logger.Logger - loggerOnceInstance sync.Once -) - -func InitLogger(filename string) { - loggerOnceInstance.Do(func() { - UpdaterLogger = logger.NewLogger( - &logger.Config{Format: "text", Level: 100, Output: filename, Retries: 3, Wait: 5}, - ) - }) -} diff --git a/agent/updater/utils/services.go b/agent/updater/utils/services.go deleted file mode 100644 index 183980245..000000000 --- a/agent/updater/utils/services.go +++ /dev/null @@ -1,135 +0,0 @@ -package utils - -import ( - "fmt" - "runtime" - "strings" -) - -func CheckIfServiceIsActive(serv string) (bool, error) { - var errB bool - var output string - path := GetMyPath() - - switch runtime.GOOS { - case "windows": - output, errB = ExecuteWithResult("sc", path, "query", serv) - case "linux": - output, errB = ExecuteWithResult("systemctl", path, "is-active", serv) - case "darwin": - output, errB = ExecuteWithResult("launchctl", path, "list", serv) - default: - return false, fmt.Errorf("unknown operating system") - } - - if errB { - return false, nil - } - - serviceStatus := strings.ToLower(strings.TrimSpace(output)) - - switch runtime.GOOS { - case "windows": - return strings.Contains(serviceStatus, "running"), nil - case "linux": - return serviceStatus == "active", nil - case "darwin": - // launchctl list returns a JSON-ish block or error.If the service is listed, it's running - return true, nil - default: - return false, fmt.Errorf("unsupported operating system") - } -} - -func RestartService(serv string) error { - path := GetMyPath() - isRunning, err := CheckIfServiceIsActive(serv) - if err != nil { - return fmt.Errorf("error checking if %s service is active: %v", serv, err) - } - - switch runtime.GOOS { - case "windows": - if isRunning { - err := Execute("sc", path, "stop", serv) - if err != nil { - return fmt.Errorf("error stopping service: %v", err) - } - } - err := Execute("sc", path, "start", serv) - if err != nil { - return fmt.Errorf("error starting service: %v", err) - } - - case "linux": - if isRunning { - err := Execute("systemctl", path, "restart", serv) - if err != nil { - return fmt.Errorf("error restarting service: %v", err) - } - } else { - err := Execute("systemctl", path, "start", serv) - if err != nil { - return fmt.Errorf("error starting service: %v", err) - } - } - case "darwin": - plistPath := fmt.Sprintf("/Library/LaunchDaemons/%s.plist", serv) - - if isRunning { - if err := Execute("launchctl", path, "remove", serv); err != nil { - return fmt.Errorf("error stopping macOS service: %v", err) - } - } - - if err := Execute("launchctl", path, "load", plistPath); err != nil { - return fmt.Errorf("error starting macOS service: %v", err) - } - } - return nil -} - -func StopService(name string) error { - path := GetMyPath() - switch runtime.GOOS { - case "windows": - err := Execute("sc", path, "stop", name) - if err != nil { - return fmt.Errorf("error stoping service: %v", err) - } - case "linux": - err := Execute("systemctl", path, "stop", name) - if err != nil { - return fmt.Errorf("error stoping service: %v", err) - } - case "darwin": - err := Execute("launchctl", path, "remove", name) - if err != nil { - return fmt.Errorf("error stopping macOS service: %v", err) - } - } - return nil -} - -func StartService(name string) error { - path := GetMyPath() - switch runtime.GOOS { - case "windows": - err := Execute("sc", path, "start", name) - if err != nil { - return fmt.Errorf("error starting service: %v", err) - } - case "linux": - err := Execute("systemctl", path, "start", name) - if err != nil { - return fmt.Errorf("error starting service: %v", err) - } - case "darwin": - plistPath := fmt.Sprintf("/Library/LaunchDaemons/%s.plist", name) - err := Execute("launchctl", path, "load", plistPath) - if err != nil { - return fmt.Errorf("error starting macOS service: %v", err) - } - } - return nil -} diff --git a/agent/updater/utils/zip.go b/agent/updater/utils/zip.go deleted file mode 100644 index ecb690a38..000000000 --- a/agent/updater/utils/zip.go +++ /dev/null @@ -1,52 +0,0 @@ -package utils - -import ( - "archive/zip" - "io" - "os" - "path" - "path/filepath" -) - -func Unzip(zipFile, destPath string) error { - archive, err := zip.OpenReader(zipFile) - if err != nil { - return err - } - defer archive.Close() - - for _, f := range archive.File { - err := func() error { - filePath := path.Join(destPath, f.Name) - if f.FileInfo().IsDir() { - os.MkdirAll(filePath, os.ModePerm) - return nil - } - if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { - return err - } - - dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) - if err != nil { - return err - } - defer dstFile.Close() - - fileInArchive, err := f.Open() - if err != nil { - return err - } - defer fileInArchive.Close() - - if _, err := io.Copy(dstFile, fileInArchive); err != nil { - return err - } - - return nil - }() - if err != nil { - return err - } - } - return nil -} From bb9b934d0d408239abfc5160e8bf412367cb6084 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 02:29:27 -0500 Subject: [PATCH 029/115] fix[plugins]: add missing skipTlsVerification param to DoReq calls --- plugins/feeds/internal/client/cm_client.go | 1 + plugins/modules-config/config/config.go | 2 +- plugins/modules-config/validations/socai.go | 2 +- plugins/o365/main.go | 8 ++++---- plugins/sophos/main.go | 6 +++--- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/feeds/internal/client/cm_client.go b/plugins/feeds/internal/client/cm_client.go index ff41591a4..60b929487 100644 --- a/plugins/feeds/internal/client/cm_client.go +++ b/plugins/feeds/internal/client/cm_client.go @@ -66,6 +66,7 @@ func (c *CustomersManagerClient) RegisterUserReporter() (*RegistrationResponse, nil, http.MethodPost, headers, + false, ) if err != nil { diff --git a/plugins/modules-config/config/config.go b/plugins/modules-config/config/config.go index 580326cc4..042a2994e 100644 --- a/plugins/modules-config/config/config.go +++ b/plugins/modules-config/config/config.go @@ -174,7 +174,7 @@ func (s *ConfigServer) SyncConfigs(backend string, internalKey string) { url := fmt.Sprintf("%s/api/utm-modules/module-details-decrypted?nameShort=%s&serverId=1", backend, name) for { - response, status, err := utils.DoReq[ConfigurationSection](url, nil, "GET", map[string]string{"Utm-Internal-Key": internalKey}) + response, status, err := utils.DoReq[ConfigurationSection](url, nil, "GET", map[string]string{"Utm-Internal-Key": internalKey}, true) if err == nil && status == http.StatusOK { s.mu.Lock() s.cache[t] = &response diff --git a/plugins/modules-config/validations/socai.go b/plugins/modules-config/validations/socai.go index 352640dcc..7367600fd 100644 --- a/plugins/modules-config/validations/socai.go +++ b/plugins/modules-config/validations/socai.go @@ -39,7 +39,7 @@ func ValidateSOCAIConfig(config *config.ModuleGroup) error { "Content-Type": "application/json", } - response, status, err := utils.DoReq[map[string]any](url, nil, "GET", headers) + response, status, err := utils.DoReq[map[string]any](url, nil, "GET", headers, false) if err != nil || status != http.StatusOK { if status == http.StatusRequestTimeout { return fmt.Errorf("SOC_AI connection timed out") diff --git a/plugins/o365/main.go b/plugins/o365/main.go index 706e4c30f..5a2a00145 100644 --- a/plugins/o365/main.go +++ b/plugins/o365/main.go @@ -261,7 +261,7 @@ func (o *OfficeProcessor) GetAuth() error { var err error for retry := 0; retry < maxRetries; retry++ { - result, _, err = utils.DoReq[MicrosoftLoginResponse](requestUrl, dataBytes, http.MethodPost, headers) + result, _, err = utils.DoReq[MicrosoftLoginResponse](requestUrl, dataBytes, http.MethodPost, headers, false) if err == nil { o.Credentials = result return nil @@ -303,7 +303,7 @@ func (o *OfficeProcessor) StartSubscriptions() error { var err error for retry := 0; retry < maxRetries; retry++ { - _, _, err = utils.DoReq[StartSubscriptionResponse](link, []byte("{}"), http.MethodPost, headers) + _, _, err = utils.DoReq[StartSubscriptionResponse](link, []byte("{}"), http.MethodPost, headers, false) if err == nil { break } @@ -362,7 +362,7 @@ func (o *OfficeProcessor) GetContentList(subscription string, startTime time.Tim var err error for retry := 0; retry < maxRetries; retry++ { - respBody, status, err = utils.DoReq[[]ContentList](link, nil, http.MethodGet, headers) + respBody, status, err = utils.DoReq[[]ContentList](link, nil, http.MethodGet, headers, false) if err == nil && status == http.StatusOK { return respBody, nil } @@ -404,7 +404,7 @@ func (o *OfficeProcessor) GetContentDetails(url string) (ContentDetailsResponse, var err error for retry := 0; retry < maxRetries; retry++ { - respBody, status, err = utils.DoReq[ContentDetailsResponse](url, nil, http.MethodGet, headers) + respBody, status, err = utils.DoReq[ContentDetailsResponse](url, nil, http.MethodGet, headers, false) if err == nil { return respBody, nil } diff --git a/plugins/sophos/main.go b/plugins/sophos/main.go index a5c8dbdd5..e0a03be46 100644 --- a/plugins/sophos/main.go +++ b/plugins/sophos/main.go @@ -155,7 +155,7 @@ func (p *SophosCentralProcessor) getAccessToken() (string, error) { var err error for retry := 0; retry < maxRetries; retry++ { - response, _, err = utils.DoReq[map[string]any](authURL, []byte(data.Encode()), http.MethodPost, headers) + response, _, err = utils.DoReq[map[string]any](authURL, []byte(data.Encode()), http.MethodPost, headers, false) if err == nil { accessToken, ok := response["access_token"].(string) if ok && accessToken != "" { @@ -230,7 +230,7 @@ func (p *SophosCentralProcessor) getTenantInfo(accessToken string) error { var err error for retry := 0; retry < maxRetries; retry++ { - response, _, err = utils.DoReq[WhoamiResponse](whoamiURL, nil, http.MethodGet, headers) + response, _, err = utils.DoReq[WhoamiResponse](whoamiURL, nil, http.MethodGet, headers, false) if err == nil { if response.ID != "" && response.ApiHosts.DataRegion != "" { p.TenantID = response.ID @@ -368,7 +368,7 @@ func (p *SophosCentralProcessor) getLogs(fromTime int64, nextKey string) ([]stri // Retry logic for getting logs var response EventAggregate for retry := 0; retry < maxRetries; retry++ { - response, _, err = utils.DoReq[EventAggregate](u.String(), nil, http.MethodGet, headers) + response, _, err = utils.DoReq[EventAggregate](u.String(), nil, http.MethodGet, headers, false) if err == nil { break } From a5e7e234273446a9787a93e6d6710a95e60788ec Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 02:39:32 -0500 Subject: [PATCH 030/115] fix[ci]: correct macOS artifact paths in agent-manager build --- .github/workflows/v11-deployment-pipeline.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/v11-deployment-pipeline.yml b/.github/workflows/v11-deployment-pipeline.yml index 04cfac660..538de44a3 100644 --- a/.github/workflows/v11-deployment-pipeline.yml +++ b/.github/workflows/v11-deployment-pipeline.yml @@ -364,9 +364,6 @@ jobs: cp "${{ github.workspace }}/utmstack-collector/version.json" ./dependencies/collector/ mkdir -p ./dependencies/agent/ - curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/agent/utmstack_agent_dependencies_linux.zip" -o ./dependencies/agent/utmstack_agent_dependencies_linux.zip - curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/agent/utmstack_agent_dependencies_windows.zip" -o ./dependencies/agent/utmstack_agent_dependencies_windows.zip - curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/agent/utmstack_agent_dependencies_windows_arm64.zip" -o ./dependencies/agent/utmstack_agent_dependencies_windows_arm64.zip # Linux agents cp "${{ github.workspace }}/agent/utmstack_agent_service_linux_amd64" ./dependencies/agent/ @@ -382,7 +379,7 @@ jobs: # macOS agents (signed and notarized) cp "${{ github.workspace }}/agent-darwin/utmstack_agent_service_darwin_arm64" ./dependencies/agent/ - cp "${{ github.workspace }}/agent-darwin/utmstack_updater_service_darwin_arm64" ./dependencies/agent/ + cp "${{ github.workspace }}/agent-darwin/updater/utmstack_updater_service_darwin_arm64" ./dependencies/agent/ curl -sSL "https://storage.googleapis.com/utmstack-updates/agent_updates/release/macos-agent/latest/utmstack-collector-mac" -o ./dependencies/agent/utmstack-collector-mac # TODO: Remove legacy binary names after all agents have migrated to new naming convention From a4ec86654d0c671c53f542a95aa06c395c2cba79 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 03:30:34 -0500 Subject: [PATCH 031/115] fix[frontend](guides): update agent guides --- .../guide-linux-agent.component.html | 14 ++---- .../guide-linux-agent.component.ts | 40 +++++++++------- .../guide-macos-agent.component.html | 25 +++++----- .../guide-macos-agent.component.ts | 46 ++++++++++--------- .../guide-winlogbeat.component.ts | 12 ++--- .../agent-install-selector.component.ts | 2 +- 6 files changed, 72 insertions(+), 67 deletions(-) diff --git a/frontend/src/app/app-module/guides/guide-linux-agent/guide-linux-agent.component.html b/frontend/src/app/app-module/guides/guide-linux-agent/guide-linux-agent.component.html index 6ccd7488b..e28193977 100644 --- a/frontend/src/app/app-module/guides/guide-linux-agent/guide-linux-agent.component.html +++ b/frontend/src/app/app-module/guides/guide-linux-agent/guide-linux-agent.component.html @@ -5,20 +5,14 @@

-

- The Linux agent captures system and application logs and sends them to a UTMStack master server or probe/proxy, - monitors system activity, and executes incident response commands. The UTMStack agents communicate over ports 9000, 9001 and 50051. - Please make sure these ports are open. -

- -
    +
    1. 1 Check pre-installation requirements

        -
      • The UTMStack Linux Agent relies on the local rsyslog service to collect and forward system logs.
      • +
      • The UTMStack agents communicate over ports 9000, 9001 and 50051. Please make sure these ports are open.
    2. @@ -30,7 +24,9 @@

      The following commands contains sensitive information, don't share it.
      - + +

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 a143e9ed1..949b26fd3 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 @@ -13,7 +13,7 @@ export class GuideLinuxAgentComponent implements OnInit { @Input() serverId: number; @Input() version: string; token: string; - architectures = []; + platforms = []; constructor(private federationConnectionService: FederationConnectionService) { } @@ -21,7 +21,6 @@ export class GuideLinuxAgentComponent implements OnInit { this.getToken(); } - getToken() { this.federationConnectionService.getToken().subscribe(response => { if (response.body !== null && response.body !== '') { @@ -29,7 +28,7 @@ export class GuideLinuxAgentComponent implements OnInit { } else { this.token = ''; } - this.loadArchitectures(); + this.loadPlatforms(); }); } @@ -75,26 +74,35 @@ export class GuideLinuxAgentComponent implements OnInit { echo 'UTMStack Agent dependencies removed successfully.'"`; } - private loadArchitectures() { - this.architectures = [ + private loadPlatforms() { + const amd64 = 'utmstack_agent_service_linux_amd64'; + const arm64 = 'utmstack_agent_service_linux_arm64'; + + this.platforms = [ { - id: 1, name: 'Ubuntu / Debian', - install: this.getCommandUbuntu('utmstack_agent_service'), - uninstall: this.getUninstallCommand('utmstack_agent_service'), + id: 1, name: 'Ubuntu / Debian (AMD64)', + install: this.getCommandUbuntu(amd64), + uninstall: this.getUninstallCommand(amd64), shell: '' }, { - id: 2, name: 'Fedora / RedHat', - install: this.getCommandCentos7RedHat('utmstack_agent_service'), - uninstall: this.getUninstallCommand('utmstack_agent_service'), + id: 2, name: 'Ubuntu / Debian (ARM64)', + install: this.getCommandUbuntu(arm64), + uninstall: this.getUninstallCommand(arm64), shell: '' - }/*, + }, { - id: 3, name: 'Centos 8/AlmaLinux', - install: this.getCommandCentos8Almalinux('utmstack_agent_service'), - uninstall: this.getUninstallCommand('utmstack_agent_service'), + id: 3, name: 'Fedora / RedHat (AMD64)', + install: this.getCommandCentos7RedHat(amd64), + uninstall: this.getUninstallCommand(amd64), shell: '' - }*/ + }, + { + id: 4, name: 'Fedora / RedHat (ARM64)', + install: this.getCommandCentos7RedHat(arm64), + uninstall: this.getUninstallCommand(arm64), + shell: '' + } ]; } } diff --git a/frontend/src/app/app-module/guides/guide-macos-agent/guide-macos-agent.component.html b/frontend/src/app/app-module/guides/guide-macos-agent/guide-macos-agent.component.html index 6114adab4..6e1874cf8 100644 --- a/frontend/src/app/app-module/guides/guide-macos-agent/guide-macos-agent.component.html +++ b/frontend/src/app/app-module/guides/guide-macos-agent/guide-macos-agent.component.html @@ -1,33 +1,30 @@

- Installing MacOS agent + Installing macOS agent

-

- The MacOS agent captures system and application logs and sends them to a UTMStack master server or probe/proxy, monitors system activity, and executes incident response commands. - The UTMStack agents communicate over ports 9000, 9001 and 50051. Please make sure these ports are open. -

  1. 1 - Download the utmstack-macos-agent.pkg file. + Check pre-installation requirements

    +
      +
    • Compatible with macOS 11 (Big Sur) or higher on Apple Silicon (M1/M2/M3)
    • +
    • The UTMStack agents communicate over ports 9000, 9001 and 50051. Please make sure these ports are open.
    • +
  2. 2 - Run the utmstack-macos-agent.pkg file. This will download and install the required components for the UTMStack agent. + To install or uninstall the UTMStack agent, open a Terminal and run the following command:

    -
  3. -
  4. -

    - 3 - Use the following command according to the action you wish to perform (install or uninstall): -

    - +
    + The following command contains sensitive information, don't share it. +
    +
diff --git a/frontend/src/app/app-module/guides/guide-macos-agent/guide-macos-agent.component.ts b/frontend/src/app/app-module/guides/guide-macos-agent/guide-macos-agent.component.ts index 01ef208fb..64841d6ee 100644 --- a/frontend/src/app/app-module/guides/guide-macos-agent/guide-macos-agent.component.ts +++ b/frontend/src/app/app-module/guides/guide-macos-agent/guide-macos-agent.component.ts @@ -11,20 +11,18 @@ import { }) export class GuideMacosAgentComponent implements OnInit { @Input() integrationId: number; - @Input() filebeatModule: UtmModulesEnum; - @Input() filebeatModuleName: string; module = UtmModulesEnum; @Input() serverId: number; - architectures = []; + platforms = []; token: string; - constructor(private federationConnectionService: FederationConnectionService,) { } + + constructor(private federationConnectionService: FederationConnectionService) { } ngOnInit() { this.getToken(); } - getToken() { this.federationConnectionService.getToken().subscribe(response => { if (response.body !== null && response.body !== '') { @@ -32,35 +30,41 @@ export class GuideMacosAgentComponent implements OnInit { } else { this.token = ''; } - this.loadArchitectures(); + this.loadPlatforms(); }); } - getCommandARM(installerName: string): string { + getInstallCommand(installerName: string): string { const ip = window.location.host.includes(':') ? window.location.host.split(':')[0] : window.location.host; - return `sudo bash -c "/opt/utmstack/${installerName} ${ip} ${this.token} yes"`; + return `sudo bash -c "mkdir -p /opt/utmstack-macos-agent && \\ +curl -k -o /opt/utmstack-macos-agent/${installerName} \\ +https://${ip}:9001/private/dependencies/agent/${installerName} && \\ +chmod +x /opt/utmstack-macos-agent/${installerName} && \\ +/opt/utmstack-macos-agent/${installerName} install ${ip} ${this.token} yes"`; } - getUninstallCommand(installerName: string): string { - // tslint:disable-next-line:max-line-length - return `sudo bash -c "/opt/utmstack/${installerName}; launchctl bootout system /Library/LaunchDaemons/UTMStackAgent.plist 2>/dev/null; rm /Library/LaunchDaemons/UTMStackAgent.plist; rm -rf /opt/utmstack"`; + return `sudo bash -c "/opt/utmstack-macos-agent/${installerName} uninstall || true; \\ +launchctl bootout system /Library/LaunchDaemons/UTMStackAgent.plist 2>/dev/null || true; \\ +launchctl bootout system /Library/LaunchDaemons/UTMStackUpdater.plist 2>/dev/null || true; \\ +rm -f /Library/LaunchDaemons/UTMStackAgent.plist 2>/dev/null || true; \\ +rm -f /Library/LaunchDaemons/UTMStackUpdater.plist 2>/dev/null || true; \\ +echo 'Removing UTMStack Agent dependencies...' && sleep 10 && \\ +rm -rf /opt/utmstack-macos-agent 2>/dev/null || true; \\ +echo 'UTMStack Agent removed successfully.'"`; } - getDownloadUrl(): string { - const ip = window.location.host.includes(':') ? window.location.host.split(':')[0] : window.location.host; - return `https://${ip}:9001/private/dependencies/agent/utmstack-macos-agent.pkg`; - } + private loadPlatforms() { + const arm64 = 'utmstack_agent_service_darwin_arm64'; - private loadArchitectures() { - this.architectures = [ + this.platforms = [ { - id: 1, name: 'ARM64', - install: this.getCommandARM('utmstack_agent_service install'), - uninstall: this.getUninstallCommand('utmstack_agent_service uninstall'), + id: 1, name: 'ARM64 (Apple Silicon)', + install: this.getInstallCommand(arm64), + uninstall: this.getUninstallCommand(arm64), shell: '' - }, + } ]; } } diff --git a/frontend/src/app/app-module/guides/guide-winlogbeat/guide-winlogbeat.component.ts b/frontend/src/app/app-module/guides/guide-winlogbeat/guide-winlogbeat.component.ts index bca0c2878..114a38ff7 100644 --- a/frontend/src/app/app-module/guides/guide-winlogbeat/guide-winlogbeat.component.ts +++ b/frontend/src/app/app-module/guides/guide-winlogbeat/guide-winlogbeat.component.ts @@ -39,15 +39,15 @@ export class GuideWinlogbeatComponent implements OnInit { this.architectures = [ { id: 1, name: 'AMD64', - install: this.getCommand('utmstack_agent_service.exe'), - uninstall: this.getUninstallCommand('utmstack_agent_service.exe'), - shell: 'Windows Powershell terminal as “ADMINISTRATOR”' + install: this.getCommand('utmstack_agent_service_windows_amd64.exe'), + uninstall: this.getUninstallCommand('utmstack_agent_service_windows_amd64.exe'), + shell: 'Windows Powershell terminal as "ADMINISTRATOR"' }, { id: 2, name: 'ARM64', - install: this.getCommand('utmstack_agent_service_arm64.exe'), - uninstall: this.getUninstallCommand('utmstack_agent_service_arm64.exe'), - shell: 'Windows Powershell terminal as “ADMINISTRATOR”' + install: this.getCommand('utmstack_agent_service_windows_arm64.exe'), + uninstall: this.getUninstallCommand('utmstack_agent_service_windows_arm64.exe'), + shell: 'Windows Powershell terminal as "ADMINISTRATOR"' } ]; } diff --git a/frontend/src/app/app-module/guides/shared/components/agent-install-selector.component.ts b/frontend/src/app/app-module/guides/shared/components/agent-install-selector.component.ts index 6954ce64c..f1e6c50a7 100644 --- a/frontend/src/app/app-module/guides/shared/components/agent-install-selector.component.ts +++ b/frontend/src/app/app-module/guides/shared/components/agent-install-selector.component.ts @@ -11,7 +11,7 @@ import {UtmModulesEnum} from '../../../shared/enum/utm-module.enum';
From e220685c4ba39589b063f1c4142405667eca98d8 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 03:40:45 -0500 Subject: [PATCH 032/115] fix(agent-manager): correct FilterScope to properly chain WHERE clauses and fix LIKE syntax --- agent-manager/utils/filter.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/agent-manager/utils/filter.go b/agent-manager/utils/filter.go index c0b403fb0..dbb7f960a 100644 --- a/agent-manager/utils/filter.go +++ b/agent-manager/utils/filter.go @@ -75,17 +75,17 @@ func FilterScope(filters []Filter) func(db *gorm.DB) *gorm.DB { for _, filter := range filters { switch filter.Op { case Is: - db.Where(filter.Field+" = ?", filter.Value) + db = db.Where(filter.Field+" = ?", filter.Value) case IsNot: - db.Where(filter.Field+" <> ?", filter.Value) + db = db.Where(filter.Field+" <> ?", filter.Value) case Contain: - db.Where(filter.Field+" like %?%", filter.Value) + db = db.Where(filter.Field+" LIKE ?", "%"+fmt.Sprintf("%v", filter.Value)+"%") case NotContain: - db.Where(filter.Field+" not like %?%", filter.Value) + db = db.Where(filter.Field+" NOT LIKE ?", "%"+fmt.Sprintf("%v", filter.Value)+"%") case In: - db.Where(filter.Field+" in (?)", filter.Value) + db = db.Where(filter.Field+" IN (?)", filter.Value) case NotIn: - db.Where(filter.Field+" not in (?)", filter.Value) + db = db.Where(filter.Field+" NOT IN (?)", filter.Value) } } return db From 9e56840e5a7f3ab79b817fe99cfede4f9e81fc96 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 03:53:52 -0500 Subject: [PATCH 033/115] feat(agent): add shell selection for command execution and fix agent registration --- agent-manager/agent/agent.pb.go | 604 ++++++++--------------- agent-manager/agent/agent_grpc.pb.go | 2 +- agent-manager/agent/agent_imp.go | 17 +- agent-manager/agent/collector.pb.go | 6 +- agent-manager/agent/collector_grpc.pb.go | 2 +- agent-manager/agent/common.pb.go | 162 +++--- agent-manager/agent/ping.pb.go | 103 ++-- agent-manager/agent/ping_grpc.pb.go | 2 +- agent-manager/protos/agent.proto | 1 + agent/agent/agent.pb.go | 604 ++++++++--------------- agent/agent/agent_grpc.pb.go | 2 +- agent/agent/common.pb.go | 162 +++--- agent/agent/incident_response.go | 26 +- agent/agent/ping.pb.go | 103 ++-- agent/agent/ping_grpc.pb.go | 2 +- agent/protos/agent.proto | 2 + agent/version.json | 4 +- 17 files changed, 638 insertions(+), 1166 deletions(-) diff --git a/agent-manager/agent/agent.pb.go b/agent-manager/agent/agent.pb.go index 7c73da984..d5b52edb9 100644 --- a/agent-manager/agent/agent.pb.go +++ b/agent-manager/agent/agent.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 +// protoc-gen-go v1.36.10 +// protoc v5.29.3 // source: agent.proto package agent @@ -12,6 +12,7 @@ import ( timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -77,30 +78,27 @@ func (AgentCommandStatus) EnumDescriptor() ([]byte, []int) { } type AgentRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` - Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` - Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"` - Platform string `protobuf:"bytes,4,opt,name=platform,proto3" json:"platform,omitempty"` - Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` - RegisterBy string `protobuf:"bytes,6,opt,name=register_by,json=registerBy,proto3" json:"register_by,omitempty"` - Mac string `protobuf:"bytes,7,opt,name=mac,proto3" json:"mac,omitempty"` - OsMajorVersion string `protobuf:"bytes,8,opt,name=os_major_version,json=osMajorVersion,proto3" json:"os_major_version,omitempty"` - OsMinorVersion string `protobuf:"bytes,9,opt,name=os_minor_version,json=osMinorVersion,proto3" json:"os_minor_version,omitempty"` - Aliases string `protobuf:"bytes,10,opt,name=aliases,proto3" json:"aliases,omitempty"` - Addresses string `protobuf:"bytes,11,opt,name=addresses,proto3" json:"addresses,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` + Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` + Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"` + Platform string `protobuf:"bytes,4,opt,name=platform,proto3" json:"platform,omitempty"` + Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` + RegisterBy string `protobuf:"bytes,6,opt,name=register_by,json=registerBy,proto3" json:"register_by,omitempty"` + Mac string `protobuf:"bytes,7,opt,name=mac,proto3" json:"mac,omitempty"` + OsMajorVersion string `protobuf:"bytes,8,opt,name=os_major_version,json=osMajorVersion,proto3" json:"os_major_version,omitempty"` + OsMinorVersion string `protobuf:"bytes,9,opt,name=os_minor_version,json=osMinorVersion,proto3" json:"os_minor_version,omitempty"` + Aliases string `protobuf:"bytes,10,opt,name=aliases,proto3" json:"aliases,omitempty"` + Addresses string `protobuf:"bytes,11,opt,name=addresses,proto3" json:"addresses,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AgentRequest) Reset() { *x = AgentRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AgentRequest) String() string { @@ -111,7 +109,7 @@ func (*AgentRequest) ProtoMessage() {} func (x *AgentRequest) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -204,21 +202,18 @@ func (x *AgentRequest) GetAddresses() string { } type ListAgentsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Rows []*Agent `protobuf:"bytes,1,rep,name=rows,proto3" json:"rows,omitempty"` + Total int32 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` unknownFields protoimpl.UnknownFields - - Rows []*Agent `protobuf:"bytes,1,rep,name=rows,proto3" json:"rows,omitempty"` - Total int32 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ListAgentsResponse) Reset() { *x = ListAgentsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListAgentsResponse) String() string { @@ -229,7 +224,7 @@ func (*ListAgentsResponse) ProtoMessage() {} func (x *ListAgentsResponse) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -259,33 +254,30 @@ func (x *ListAgentsResponse) GetTotal() int32 { } type Agent struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` - Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` - Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"` - Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=agent.Status" json:"status,omitempty"` - Platform string `protobuf:"bytes,5,opt,name=platform,proto3" json:"platform,omitempty"` - Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` - AgentKey string `protobuf:"bytes,7,opt,name=agent_key,json=agentKey,proto3" json:"agent_key,omitempty"` - Id uint32 `protobuf:"varint,8,opt,name=id,proto3" json:"id,omitempty"` - LastSeen string `protobuf:"bytes,9,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` - Mac string `protobuf:"bytes,10,opt,name=mac,proto3" json:"mac,omitempty"` - OsMajorVersion string `protobuf:"bytes,11,opt,name=os_major_version,json=osMajorVersion,proto3" json:"os_major_version,omitempty"` - OsMinorVersion string `protobuf:"bytes,12,opt,name=os_minor_version,json=osMinorVersion,proto3" json:"os_minor_version,omitempty"` - Aliases string `protobuf:"bytes,13,opt,name=aliases,proto3" json:"aliases,omitempty"` - Addresses string `protobuf:"bytes,14,opt,name=addresses,proto3" json:"addresses,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` + Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` + Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"` + Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=agent.Status" json:"status,omitempty"` + Platform string `protobuf:"bytes,5,opt,name=platform,proto3" json:"platform,omitempty"` + Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` + AgentKey string `protobuf:"bytes,7,opt,name=agent_key,json=agentKey,proto3" json:"agent_key,omitempty"` + Id uint32 `protobuf:"varint,8,opt,name=id,proto3" json:"id,omitempty"` + LastSeen string `protobuf:"bytes,9,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + Mac string `protobuf:"bytes,10,opt,name=mac,proto3" json:"mac,omitempty"` + OsMajorVersion string `protobuf:"bytes,11,opt,name=os_major_version,json=osMajorVersion,proto3" json:"os_major_version,omitempty"` + OsMinorVersion string `protobuf:"bytes,12,opt,name=os_minor_version,json=osMinorVersion,proto3" json:"os_minor_version,omitempty"` + Aliases string `protobuf:"bytes,13,opt,name=aliases,proto3" json:"aliases,omitempty"` + Addresses string `protobuf:"bytes,14,opt,name=addresses,proto3" json:"addresses,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Agent) Reset() { *x = Agent{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Agent) String() string { @@ -296,7 +288,7 @@ func (*Agent) ProtoMessage() {} func (x *Agent) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -410,24 +402,21 @@ func (x *Agent) GetAddresses() string { } type BidirectionalStream struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to StreamMessage: + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to StreamMessage: // // *BidirectionalStream_Command // *BidirectionalStream_Result StreamMessage isBidirectionalStream_StreamMessage `protobuf_oneof:"stream_message"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *BidirectionalStream) Reset() { *x = BidirectionalStream{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BidirectionalStream) String() string { @@ -438,7 +427,7 @@ func (*BidirectionalStream) ProtoMessage() {} func (x *BidirectionalStream) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -453,23 +442,27 @@ func (*BidirectionalStream) Descriptor() ([]byte, []int) { return file_agent_proto_rawDescGZIP(), []int{3} } -func (m *BidirectionalStream) GetStreamMessage() isBidirectionalStream_StreamMessage { - if m != nil { - return m.StreamMessage +func (x *BidirectionalStream) GetStreamMessage() isBidirectionalStream_StreamMessage { + if x != nil { + return x.StreamMessage } return nil } func (x *BidirectionalStream) GetCommand() *UtmCommand { - if x, ok := x.GetStreamMessage().(*BidirectionalStream_Command); ok { - return x.Command + if x != nil { + if x, ok := x.StreamMessage.(*BidirectionalStream_Command); ok { + return x.Command + } } return nil } func (x *BidirectionalStream) GetResult() *CommandResult { - if x, ok := x.GetStreamMessage().(*BidirectionalStream_Result); ok { - return x.Result + if x != nil { + if x, ok := x.StreamMessage.(*BidirectionalStream_Result); ok { + return x.Result + } } return nil } @@ -491,26 +484,24 @@ func (*BidirectionalStream_Command) isBidirectionalStream_StreamMessage() {} func (*BidirectionalStream_Result) isBidirectionalStream_StreamMessage() {} type UtmCommand struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` + Command string `protobuf:"bytes,2,opt,name=command,proto3" json:"command,omitempty"` + ExecutedBy string `protobuf:"bytes,3,opt,name=executed_by,json=executedBy,proto3" json:"executed_by,omitempty"` + CmdId string `protobuf:"bytes,4,opt,name=cmd_id,json=cmdId,proto3" json:"cmd_id,omitempty"` + OriginType string `protobuf:"bytes,5,opt,name=origin_type,json=originType,proto3" json:"origin_type,omitempty"` + OriginId string `protobuf:"bytes,6,opt,name=origin_id,json=originId,proto3" json:"origin_id,omitempty"` + Reason string `protobuf:"bytes,7,opt,name=reason,proto3" json:"reason,omitempty"` + Shell string `protobuf:"bytes,8,opt,name=shell,proto3" json:"shell,omitempty"` // Shell to execute command: "cmd", "powershell" (Windows), "sh", "bash" (Linux/macOS). Empty = default unknownFields protoimpl.UnknownFields - - AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` - Command string `protobuf:"bytes,2,opt,name=command,proto3" json:"command,omitempty"` - ExecutedBy string `protobuf:"bytes,3,opt,name=executed_by,json=executedBy,proto3" json:"executed_by,omitempty"` - CmdId string `protobuf:"bytes,4,opt,name=cmd_id,json=cmdId,proto3" json:"cmd_id,omitempty"` - OriginType string `protobuf:"bytes,5,opt,name=origin_type,json=originType,proto3" json:"origin_type,omitempty"` - OriginId string `protobuf:"bytes,6,opt,name=origin_id,json=originId,proto3" json:"origin_id,omitempty"` - Reason string `protobuf:"bytes,7,opt,name=reason,proto3" json:"reason,omitempty"` + sizeCache protoimpl.SizeCache } func (x *UtmCommand) Reset() { *x = UtmCommand{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *UtmCommand) String() string { @@ -521,7 +512,7 @@ func (*UtmCommand) ProtoMessage() {} func (x *UtmCommand) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -585,24 +576,28 @@ func (x *UtmCommand) GetReason() string { return "" } +func (x *UtmCommand) GetShell() string { + if x != nil { + return x.Shell + } + return "" +} + type CommandResult struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` + Result string `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` + ExecutedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=executed_at,json=executedAt,proto3" json:"executed_at,omitempty"` + CmdId string `protobuf:"bytes,4,opt,name=cmd_id,json=cmdId,proto3" json:"cmd_id,omitempty"` unknownFields protoimpl.UnknownFields - - AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` - Result string `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` - ExecutedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=executed_at,json=executedAt,proto3" json:"executed_at,omitempty"` - CmdId string `protobuf:"bytes,4,opt,name=cmd_id,json=cmdId,proto3" json:"cmd_id,omitempty"` + sizeCache protoimpl.SizeCache } func (x *CommandResult) Reset() { *x = CommandResult{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CommandResult) String() string { @@ -613,7 +608,7 @@ func (*CommandResult) ProtoMessage() {} func (x *CommandResult) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -657,21 +652,18 @@ func (x *CommandResult) GetCmdId() string { } type ListAgentsCommandsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Rows []*AgentCommand `protobuf:"bytes,1,rep,name=rows,proto3" json:"rows,omitempty"` + Total int32 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` unknownFields protoimpl.UnknownFields - - Rows []*AgentCommand `protobuf:"bytes,1,rep,name=rows,proto3" json:"rows,omitempty"` - Total int32 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ListAgentsCommandsResponse) Reset() { *x = ListAgentsCommandsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListAgentsCommandsResponse) String() string { @@ -682,7 +674,7 @@ func (*ListAgentsCommandsResponse) ProtoMessage() {} func (x *ListAgentsCommandsResponse) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -712,10 +704,7 @@ func (x *ListAgentsCommandsResponse) GetTotal() int32 { } type AgentCommand struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` CreatedAt *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` AgentId uint32 `protobuf:"varint,3,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` @@ -727,15 +716,15 @@ type AgentCommand struct { Reason string `protobuf:"bytes,9,opt,name=reason,proto3" json:"reason,omitempty"` OriginType string `protobuf:"bytes,10,opt,name=origin_type,json=originType,proto3" json:"origin_type,omitempty"` OriginId string `protobuf:"bytes,11,opt,name=origin_id,json=originId,proto3" json:"origin_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AgentCommand) Reset() { *x = AgentCommand{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AgentCommand) String() string { @@ -746,7 +735,7 @@ func (*AgentCommand) ProtoMessage() {} func (x *AgentCommand) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -840,182 +829,116 @@ func (x *AgentCommand) GetOriginId() string { var File_agent_proto protoreflect.FileDescriptor -var file_agent_proto_rawDesc = []byte{ - 0x0a, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x02, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x73, 0x5f, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x73, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x73, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, - 0x73, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, - 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x4c, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x72, - 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x22, 0x88, 0x03, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x1a, 0x0a, - 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, - 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, - 0x61, 0x63, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x73, 0x5f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x73, - 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, - 0x6f, 0x73, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x73, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, - 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, - 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x0e, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x86, - 0x01, 0x0a, 0x13, 0x42, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x2d, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, - 0x55, 0x74, 0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x42, 0x10, 0x0a, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xcf, 0x01, 0x0a, 0x0a, 0x55, 0x74, 0x6d, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, - 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x15, 0x0a, 0x06, - 0x63, 0x6d, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6d, - 0x64, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, - 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x96, 0x01, 0x0a, 0x0d, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3b, - 0x0a, 0x0b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x63, - 0x6d, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6d, 0x64, - 0x49, 0x64, 0x22, 0x5b, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x27, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, - 0xa1, 0x03, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, - 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x40, 0x0a, 0x0e, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, - 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0d, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, - 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, - 0x64, 0x5f, 0x62, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x63, 0x6d, 0x64, 0x5f, 0x69, 0x64, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6d, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, - 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x72, 0x69, 0x67, - 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x49, 0x64, 0x2a, 0x57, 0x0a, 0x12, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x4f, 0x54, - 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x51, - 0x55, 0x45, 0x55, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, - 0x47, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x45, 0x44, 0x10, - 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x32, 0x9c, 0x03, 0x0a, - 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3b, 0x0a, - 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x13, - 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0b, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, - 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x67, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x3d, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, - 0x12, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x4b, 0x0a, 0x0b, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, - 0x1a, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x1a, 0x1a, 0x2e, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4c, 0x0a, - 0x11, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x73, 0x12, 0x12, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0x4f, 0x0a, 0x0c, 0x50, - 0x61, 0x6e, 0x65, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3f, 0x0a, 0x0e, 0x50, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x11, 0x2e, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x74, 0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x1a, 0x14, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x32, 0x5a, 0x30, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x74, 0x6d, 0x73, 0x74, - 0x61, 0x63, 0x6b, 0x2f, 0x55, 0x54, 0x4d, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +const file_agent_proto_rawDesc = "" + + "\n" + + "\vagent.proto\x12\x05agent\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\fcommon.proto\"\xbf\x02\n" + + "\fAgentRequest\x12\x0e\n" + + "\x02ip\x18\x01 \x01(\tR\x02ip\x12\x1a\n" + + "\bhostname\x18\x02 \x01(\tR\bhostname\x12\x0e\n" + + "\x02os\x18\x03 \x01(\tR\x02os\x12\x1a\n" + + "\bplatform\x18\x04 \x01(\tR\bplatform\x12\x18\n" + + "\aversion\x18\x05 \x01(\tR\aversion\x12\x1f\n" + + "\vregister_by\x18\x06 \x01(\tR\n" + + "registerBy\x12\x10\n" + + "\x03mac\x18\a \x01(\tR\x03mac\x12(\n" + + "\x10os_major_version\x18\b \x01(\tR\x0eosMajorVersion\x12(\n" + + "\x10os_minor_version\x18\t \x01(\tR\x0eosMinorVersion\x12\x18\n" + + "\aaliases\x18\n" + + " \x01(\tR\aaliases\x12\x1c\n" + + "\taddresses\x18\v \x01(\tR\taddresses\"L\n" + + "\x12ListAgentsResponse\x12 \n" + + "\x04rows\x18\x01 \x03(\v2\f.agent.AgentR\x04rows\x12\x14\n" + + "\x05total\x18\x02 \x01(\x05R\x05total\"\x88\x03\n" + + "\x05Agent\x12\x0e\n" + + "\x02ip\x18\x01 \x01(\tR\x02ip\x12\x1a\n" + + "\bhostname\x18\x02 \x01(\tR\bhostname\x12\x0e\n" + + "\x02os\x18\x03 \x01(\tR\x02os\x12%\n" + + "\x06status\x18\x04 \x01(\x0e2\r.agent.StatusR\x06status\x12\x1a\n" + + "\bplatform\x18\x05 \x01(\tR\bplatform\x12\x18\n" + + "\aversion\x18\x06 \x01(\tR\aversion\x12\x1b\n" + + "\tagent_key\x18\a \x01(\tR\bagentKey\x12\x0e\n" + + "\x02id\x18\b \x01(\rR\x02id\x12\x1b\n" + + "\tlast_seen\x18\t \x01(\tR\blastSeen\x12\x10\n" + + "\x03mac\x18\n" + + " \x01(\tR\x03mac\x12(\n" + + "\x10os_major_version\x18\v \x01(\tR\x0eosMajorVersion\x12(\n" + + "\x10os_minor_version\x18\f \x01(\tR\x0eosMinorVersion\x12\x18\n" + + "\aaliases\x18\r \x01(\tR\aaliases\x12\x1c\n" + + "\taddresses\x18\x0e \x01(\tR\taddresses\"\x86\x01\n" + + "\x13BidirectionalStream\x12-\n" + + "\acommand\x18\x01 \x01(\v2\x11.agent.UtmCommandH\x00R\acommand\x12.\n" + + "\x06result\x18\x02 \x01(\v2\x14.agent.CommandResultH\x00R\x06resultB\x10\n" + + "\x0estream_message\"\xe5\x01\n" + + "\n" + + "UtmCommand\x12\x19\n" + + "\bagent_id\x18\x01 \x01(\tR\aagentId\x12\x18\n" + + "\acommand\x18\x02 \x01(\tR\acommand\x12\x1f\n" + + "\vexecuted_by\x18\x03 \x01(\tR\n" + + "executedBy\x12\x15\n" + + "\x06cmd_id\x18\x04 \x01(\tR\x05cmdId\x12\x1f\n" + + "\vorigin_type\x18\x05 \x01(\tR\n" + + "originType\x12\x1b\n" + + "\torigin_id\x18\x06 \x01(\tR\boriginId\x12\x16\n" + + "\x06reason\x18\a \x01(\tR\x06reason\x12\x14\n" + + "\x05shell\x18\b \x01(\tR\x05shell\"\x96\x01\n" + + "\rCommandResult\x12\x19\n" + + "\bagent_id\x18\x01 \x01(\tR\aagentId\x12\x16\n" + + "\x06result\x18\x02 \x01(\tR\x06result\x12;\n" + + "\vexecuted_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "executedAt\x12\x15\n" + + "\x06cmd_id\x18\x04 \x01(\tR\x05cmdId\"[\n" + + "\x1aListAgentsCommandsResponse\x12'\n" + + "\x04rows\x18\x01 \x03(\v2\x13.agent.AgentCommandR\x04rows\x12\x14\n" + + "\x05total\x18\x02 \x01(\x05R\x05total\"\xa1\x03\n" + + "\fAgentCommand\x129\n" + + "\n" + + "created_at\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x12\x19\n" + + "\bagent_id\x18\x03 \x01(\rR\aagentId\x12\x18\n" + + "\acommand\x18\x04 \x01(\tR\acommand\x12@\n" + + "\x0ecommand_status\x18\x05 \x01(\x0e2\x19.agent.AgentCommandStatusR\rcommandStatus\x12\x16\n" + + "\x06result\x18\x06 \x01(\tR\x06result\x12\x1f\n" + + "\vexecuted_by\x18\a \x01(\tR\n" + + "executedBy\x12\x15\n" + + "\x06cmd_id\x18\b \x01(\tR\x05cmdId\x12\x16\n" + + "\x06reason\x18\t \x01(\tR\x06reason\x12\x1f\n" + + "\vorigin_type\x18\n" + + " \x01(\tR\n" + + "originType\x12\x1b\n" + + "\torigin_id\x18\v \x01(\tR\boriginId*W\n" + + "\x12AgentCommandStatus\x12\x10\n" + + "\fNOT_EXECUTED\x10\x00\x12\t\n" + + "\x05QUEUE\x10\x01\x12\v\n" + + "\aPENDING\x10\x02\x12\f\n" + + "\bEXECUTED\x10\x03\x12\t\n" + + "\x05ERROR\x10\x042\x9c\x03\n" + + "\fAgentService\x12;\n" + + "\rRegisterAgent\x12\x13.agent.AgentRequest\x1a\x13.agent.AuthResponse\"\x00\x129\n" + + "\vUpdateAgent\x12\x13.agent.AgentRequest\x1a\x13.agent.AuthResponse\"\x00\x12:\n" + + "\vDeleteAgent\x12\x14.agent.DeleteRequest\x1a\x13.agent.AuthResponse\"\x00\x12=\n" + + "\n" + + "ListAgents\x12\x12.agent.ListRequest\x1a\x19.agent.ListAgentsResponse\"\x00\x12K\n" + + "\vAgentStream\x12\x1a.agent.BidirectionalStream\x1a\x1a.agent.BidirectionalStream\"\x00(\x010\x01\x12L\n" + + "\x11ListAgentCommands\x12\x12.agent.ListRequest\x1a!.agent.ListAgentsCommandsResponse\"\x002O\n" + + "\fPanelService\x12?\n" + + "\x0eProcessCommand\x12\x11.agent.UtmCommand\x1a\x14.agent.CommandResult\"\x00(\x010\x01B2Z0github.com/utmstack/UTMStack/agent-manager/agentb\x06proto3" var ( file_agent_proto_rawDescOnce sync.Once - file_agent_proto_rawDescData = file_agent_proto_rawDesc + file_agent_proto_rawDescData []byte ) func file_agent_proto_rawDescGZIP() []byte { file_agent_proto_rawDescOnce.Do(func() { - file_agent_proto_rawDescData = protoimpl.X.CompressGZIP(file_agent_proto_rawDescData) + file_agent_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_agent_proto_rawDesc), len(file_agent_proto_rawDesc))) }) return file_agent_proto_rawDescData } var file_agent_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_agent_proto_goTypes = []interface{}{ +var file_agent_proto_goTypes = []any{ (AgentCommandStatus)(0), // 0: agent.AgentCommandStatus (*AgentRequest)(nil), // 1: agent.AgentRequest (*ListAgentsResponse)(nil), // 2: agent.ListAgentsResponse @@ -1068,105 +991,7 @@ func file_agent_proto_init() { return } file_common_proto_init() - if !protoimpl.UnsafeEnabled { - file_agent_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AgentRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListAgentsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Agent); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BidirectionalStream); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UtmCommand); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandResult); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListAgentsCommandsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AgentCommand); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_agent_proto_msgTypes[3].OneofWrappers = []interface{}{ + file_agent_proto_msgTypes[3].OneofWrappers = []any{ (*BidirectionalStream_Command)(nil), (*BidirectionalStream_Result)(nil), } @@ -1174,7 +999,7 @@ func file_agent_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_agent_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_agent_proto_rawDesc), len(file_agent_proto_rawDesc)), NumEnums: 1, NumMessages: 8, NumExtensions: 0, @@ -1186,7 +1011,6 @@ func file_agent_proto_init() { MessageInfos: file_agent_proto_msgTypes, }.Build() File_agent_proto = out.File - file_agent_proto_rawDesc = nil file_agent_proto_goTypes = nil file_agent_proto_depIdxs = nil } diff --git a/agent-manager/agent/agent_grpc.pb.go b/agent-manager/agent/agent_grpc.pb.go index f75ba57b2..400673e17 100644 --- a/agent-manager/agent/agent_grpc.pb.go +++ b/agent-manager/agent/agent_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v3.21.12 +// - protoc v5.29.3 // source: agent.proto package agent diff --git a/agent-manager/agent/agent_imp.go b/agent-manager/agent/agent_imp.go index d9e09b870..fafa7d97d 100644 --- a/agent-manager/agent/agent_imp.go +++ b/agent-manager/agent/agent_imp.go @@ -75,17 +75,13 @@ func (s *AgentService) RegisterAgent(ctx context.Context, req *AgentRequest) (*A } oldAgent := &models.Agent{} - err := s.DBConnection.GetFirst(oldAgent, "hostname = ?", agent.Hostname) + err := s.DBConnection.GetFirst(oldAgent, "hostname = ? AND mac = ?", agent.Hostname, agent.Mac) if err == nil { - if oldAgent.Ip == agent.Ip { - return &AuthResponse{ - Id: uint32(oldAgent.ID), - Key: oldAgent.AgentKey, - }, nil - } else { - catcher.Error("agent already exists", err, map[string]any{"hostname": agent.Hostname, "process": "agent-manager"}) - return nil, status.Errorf(codes.AlreadyExists, "hostname has already been registered") - } + // Same machine re-registering, return existing agent + return &AuthResponse{ + Id: uint32(oldAgent.ID), + Key: oldAgent.AgentKey, + }, nil } key := uuid.New().String() @@ -334,6 +330,7 @@ func (s *AgentService) ProcessCommand(stream PanelService_ProcessCommandServer) AgentId: cmd.AgentId, Command: replaceSecretValues(cmd.Command), CmdId: cmdID, + Shell: cmd.Shell, }, }, }) diff --git a/agent-manager/agent/collector.pb.go b/agent-manager/agent/collector.pb.go index bcf8d63b7..be2fe7fa6 100644 --- a/agent-manager/agent/collector.pb.go +++ b/agent-manager/agent/collector.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 -// protoc v3.21.12 +// protoc-gen-go v1.36.10 +// protoc v5.29.3 // source: collector.proto package agent @@ -766,7 +766,7 @@ const file_collector_proto_rawDesc = "" + "\x0fCollectorStream\x12\x18.agent.CollectorMessages\x1a\x18.agent.CollectorMessages\"\x00(\x010\x01\x12D\n" + "\x12GetCollectorConfig\x12\x14.agent.ConfigRequest\x1a\x16.agent.CollectorConfig\"\x002d\n" + "\x15PanelCollectorService\x12K\n" + - "\x17RegisterCollectorConfig\x12\x16.agent.CollectorConfig\x1a\x16.agent.ConfigKnowledge\"\x00B5Z3github.com/utmstack/UTMStack/docker-collector/agentb\x06proto3" + "\x17RegisterCollectorConfig\x12\x16.agent.CollectorConfig\x1a\x16.agent.ConfigKnowledge\"\x00B2Z0github.com/utmstack/UTMStack/agent-manager/agentb\x06proto3" var ( file_collector_proto_rawDescOnce sync.Once diff --git a/agent-manager/agent/collector_grpc.pb.go b/agent-manager/agent/collector_grpc.pb.go index a924c7361..307c97b51 100644 --- a/agent-manager/agent/collector_grpc.pb.go +++ b/agent-manager/agent/collector_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v3.21.12 +// - protoc v5.29.3 // source: collector.proto package agent diff --git a/agent-manager/agent/common.pb.go b/agent-manager/agent/common.pb.go index 0d4ccf71d..4407bccf4 100644 --- a/agent-manager/agent/common.pb.go +++ b/agent-manager/agent/common.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 +// protoc-gen-go v1.36.10 +// protoc v5.29.3 // source: common.proto package agent @@ -11,6 +11,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -116,23 +117,20 @@ func (ConnectorType) EnumDescriptor() ([]byte, []int) { } type ListRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + PageNumber int32 `protobuf:"varint,1,opt,name=page_number,json=pageNumber,proto3" json:"page_number,omitempty"` + PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + SearchQuery string `protobuf:"bytes,3,opt,name=search_query,json=searchQuery,proto3" json:"search_query,omitempty"` + SortBy string `protobuf:"bytes,4,opt,name=sort_by,json=sortBy,proto3" json:"sort_by,omitempty"` unknownFields protoimpl.UnknownFields - - PageNumber int32 `protobuf:"varint,1,opt,name=page_number,json=pageNumber,proto3" json:"page_number,omitempty"` - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - SearchQuery string `protobuf:"bytes,3,opt,name=search_query,json=searchQuery,proto3" json:"search_query,omitempty"` - SortBy string `protobuf:"bytes,4,opt,name=sort_by,json=sortBy,proto3" json:"sort_by,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ListRequest) Reset() { *x = ListRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_common_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListRequest) String() string { @@ -143,7 +141,7 @@ func (*ListRequest) ProtoMessage() {} func (x *ListRequest) ProtoReflect() protoreflect.Message { mi := &file_common_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -187,21 +185,18 @@ func (x *ListRequest) GetSortBy() string { } type AuthResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` unknownFields protoimpl.UnknownFields - - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + sizeCache protoimpl.SizeCache } func (x *AuthResponse) Reset() { *x = AuthResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_common_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthResponse) String() string { @@ -212,7 +207,7 @@ func (*AuthResponse) ProtoMessage() {} func (x *AuthResponse) ProtoReflect() protoreflect.Message { mi := &file_common_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -242,20 +237,17 @@ func (x *AuthResponse) GetKey() string { } type DeleteRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + DeletedBy string `protobuf:"bytes,1,opt,name=deleted_by,json=deletedBy,proto3" json:"deleted_by,omitempty"` unknownFields protoimpl.UnknownFields - - DeletedBy string `protobuf:"bytes,1,opt,name=deleted_by,json=deletedBy,proto3" json:"deleted_by,omitempty"` + sizeCache protoimpl.SizeCache } func (x *DeleteRequest) Reset() { *x = DeleteRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_common_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DeleteRequest) String() string { @@ -266,7 +258,7 @@ func (*DeleteRequest) ProtoMessage() {} func (x *DeleteRequest) ProtoReflect() protoreflect.Message { mi := &file_common_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -290,50 +282,45 @@ func (x *DeleteRequest) GetDeletedBy() string { var File_common_proto protoreflect.FileDescriptor -var file_common_proto_rawDesc = []byte{ - 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x22, 0x87, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x65, - 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, - 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, - 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, - 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x22, - 0x30, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x22, 0x2e, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x42, - 0x79, 0x2a, 0x2e, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0a, 0x0a, 0x06, 0x4f, - 0x4e, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4f, 0x46, 0x46, 0x4c, 0x49, - 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x02, 0x2a, 0x29, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, - 0x09, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x10, 0x01, 0x42, 0x32, 0x5a, 0x30, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x74, 0x6d, 0x73, 0x74, - 0x61, 0x63, 0x6b, 0x2f, 0x55, 0x54, 0x4d, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +const file_common_proto_rawDesc = "" + + "\n" + + "\fcommon.proto\x12\x05agent\"\x87\x01\n" + + "\vListRequest\x12\x1f\n" + + "\vpage_number\x18\x01 \x01(\x05R\n" + + "pageNumber\x12\x1b\n" + + "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12!\n" + + "\fsearch_query\x18\x03 \x01(\tR\vsearchQuery\x12\x17\n" + + "\asort_by\x18\x04 \x01(\tR\x06sortBy\"0\n" + + "\fAuthResponse\x12\x0e\n" + + "\x02id\x18\x01 \x01(\rR\x02id\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\".\n" + + "\rDeleteRequest\x12\x1d\n" + + "\n" + + "deleted_by\x18\x01 \x01(\tR\tdeletedBy*.\n" + + "\x06Status\x12\n" + + "\n" + + "\x06ONLINE\x10\x00\x12\v\n" + + "\aOFFLINE\x10\x01\x12\v\n" + + "\aUNKNOWN\x10\x02*)\n" + + "\rConnectorType\x12\t\n" + + "\x05AGENT\x10\x00\x12\r\n" + + "\tCOLLECTOR\x10\x01B2Z0github.com/utmstack/UTMStack/agent-manager/agentb\x06proto3" var ( file_common_proto_rawDescOnce sync.Once - file_common_proto_rawDescData = file_common_proto_rawDesc + file_common_proto_rawDescData []byte ) func file_common_proto_rawDescGZIP() []byte { file_common_proto_rawDescOnce.Do(func() { - file_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_proto_rawDescData) + file_common_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_proto_rawDesc), len(file_common_proto_rawDesc))) }) return file_common_proto_rawDescData } var file_common_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_common_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_common_proto_goTypes = []interface{}{ +var file_common_proto_goTypes = []any{ (Status)(0), // 0: agent.Status (ConnectorType)(0), // 1: agent.ConnectorType (*ListRequest)(nil), // 2: agent.ListRequest @@ -353,49 +340,11 @@ func file_common_proto_init() { if File_common_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_common_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuthResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_common_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_proto_rawDesc), len(file_common_proto_rawDesc)), NumEnums: 2, NumMessages: 3, NumExtensions: 0, @@ -407,7 +356,6 @@ func file_common_proto_init() { MessageInfos: file_common_proto_msgTypes, }.Build() File_common_proto = out.File - file_common_proto_rawDesc = nil file_common_proto_goTypes = nil file_common_proto_depIdxs = nil } diff --git a/agent-manager/agent/ping.pb.go b/agent-manager/agent/ping.pb.go index 21ddaf763..b6f9787b9 100644 --- a/agent-manager/agent/ping.pb.go +++ b/agent-manager/agent/ping.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 +// protoc-gen-go v1.36.10 +// protoc v5.29.3 // source: ping.proto package agent @@ -11,6 +11,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -21,20 +22,17 @@ const ( ) type PingRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Type ConnectorType `protobuf:"varint,1,opt,name=type,proto3,enum=agent.ConnectorType" json:"type,omitempty"` unknownFields protoimpl.UnknownFields - - Type ConnectorType `protobuf:"varint,1,opt,name=type,proto3,enum=agent.ConnectorType" json:"type,omitempty"` + sizeCache protoimpl.SizeCache } func (x *PingRequest) Reset() { *x = PingRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_ping_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ping_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PingRequest) String() string { @@ -45,7 +43,7 @@ func (*PingRequest) ProtoMessage() {} func (x *PingRequest) ProtoReflect() protoreflect.Message { mi := &file_ping_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -68,20 +66,17 @@ func (x *PingRequest) GetType() ConnectorType { } type PingResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Received string `protobuf:"bytes,1,opt,name=received,proto3" json:"received,omitempty"` unknownFields protoimpl.UnknownFields - - Received string `protobuf:"bytes,1,opt,name=received,proto3" json:"received,omitempty"` + sizeCache protoimpl.SizeCache } func (x *PingResponse) Reset() { *x = PingResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_ping_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ping_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PingResponse) String() string { @@ -92,7 +87,7 @@ func (*PingResponse) ProtoMessage() {} func (x *PingResponse) ProtoReflect() protoreflect.Message { mi := &file_ping_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -116,40 +111,31 @@ func (x *PingResponse) GetReceived() string { var File_ping_proto protoreflect.FileDescriptor -var file_ping_proto_rawDesc = []byte{ - 0x0a, 0x0a, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x1a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x37, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, - 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2a, 0x0a, 0x0c, 0x50, 0x69, - 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x32, 0x42, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x2e, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x74, 0x6d, 0x73, 0x74, 0x61, 0x63, - 0x6b, 0x2f, 0x55, 0x54, 0x4d, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +const file_ping_proto_rawDesc = "" + + "\n" + + "\n" + + "ping.proto\x12\x05agent\x1a\fcommon.proto\"7\n" + + "\vPingRequest\x12(\n" + + "\x04type\x18\x01 \x01(\x0e2\x14.agent.ConnectorTypeR\x04type\"*\n" + + "\fPingResponse\x12\x1a\n" + + "\breceived\x18\x01 \x01(\tR\breceived2B\n" + + "\vPingService\x123\n" + + "\x04Ping\x12\x12.agent.PingRequest\x1a\x13.agent.PingResponse\"\x00(\x01B2Z0github.com/utmstack/UTMStack/agent-manager/agentb\x06proto3" var ( file_ping_proto_rawDescOnce sync.Once - file_ping_proto_rawDescData = file_ping_proto_rawDesc + file_ping_proto_rawDescData []byte ) func file_ping_proto_rawDescGZIP() []byte { file_ping_proto_rawDescOnce.Do(func() { - file_ping_proto_rawDescData = protoimpl.X.CompressGZIP(file_ping_proto_rawDescData) + file_ping_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ping_proto_rawDesc), len(file_ping_proto_rawDesc))) }) return file_ping_proto_rawDescData } var file_ping_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_ping_proto_goTypes = []interface{}{ +var file_ping_proto_goTypes = []any{ (*PingRequest)(nil), // 0: agent.PingRequest (*PingResponse)(nil), // 1: agent.PingResponse (ConnectorType)(0), // 2: agent.ConnectorType @@ -171,37 +157,11 @@ func file_ping_proto_init() { return } file_common_proto_init() - if !protoimpl.UnsafeEnabled { - file_ping_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PingRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ping_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PingResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_ping_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_ping_proto_rawDesc), len(file_ping_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, @@ -212,7 +172,6 @@ func file_ping_proto_init() { MessageInfos: file_ping_proto_msgTypes, }.Build() File_ping_proto = out.File - file_ping_proto_rawDesc = nil file_ping_proto_goTypes = nil file_ping_proto_depIdxs = nil } diff --git a/agent-manager/agent/ping_grpc.pb.go b/agent-manager/agent/ping_grpc.pb.go index f283dc80a..f3213500c 100644 --- a/agent-manager/agent/ping_grpc.pb.go +++ b/agent-manager/agent/ping_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v3.21.12 +// - protoc v5.29.3 // source: ping.proto package agent diff --git a/agent-manager/protos/agent.proto b/agent-manager/protos/agent.proto index 3f69e9a2b..c1db23819 100644 --- a/agent-manager/protos/agent.proto +++ b/agent-manager/protos/agent.proto @@ -79,6 +79,7 @@ message UtmCommand { string origin_type = 5; string origin_id = 6; string reason = 7; + string shell = 8; // Shell to execute command: "cmd", "powershell" (Windows), "sh", "bash" (Linux/macOS). Empty = default } message CommandResult { diff --git a/agent/agent/agent.pb.go b/agent/agent/agent.pb.go index 7c73da984..d5b52edb9 100644 --- a/agent/agent/agent.pb.go +++ b/agent/agent/agent.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 +// protoc-gen-go v1.36.10 +// protoc v5.29.3 // source: agent.proto package agent @@ -12,6 +12,7 @@ import ( timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -77,30 +78,27 @@ func (AgentCommandStatus) EnumDescriptor() ([]byte, []int) { } type AgentRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` - Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` - Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"` - Platform string `protobuf:"bytes,4,opt,name=platform,proto3" json:"platform,omitempty"` - Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` - RegisterBy string `protobuf:"bytes,6,opt,name=register_by,json=registerBy,proto3" json:"register_by,omitempty"` - Mac string `protobuf:"bytes,7,opt,name=mac,proto3" json:"mac,omitempty"` - OsMajorVersion string `protobuf:"bytes,8,opt,name=os_major_version,json=osMajorVersion,proto3" json:"os_major_version,omitempty"` - OsMinorVersion string `protobuf:"bytes,9,opt,name=os_minor_version,json=osMinorVersion,proto3" json:"os_minor_version,omitempty"` - Aliases string `protobuf:"bytes,10,opt,name=aliases,proto3" json:"aliases,omitempty"` - Addresses string `protobuf:"bytes,11,opt,name=addresses,proto3" json:"addresses,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` + Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` + Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"` + Platform string `protobuf:"bytes,4,opt,name=platform,proto3" json:"platform,omitempty"` + Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` + RegisterBy string `protobuf:"bytes,6,opt,name=register_by,json=registerBy,proto3" json:"register_by,omitempty"` + Mac string `protobuf:"bytes,7,opt,name=mac,proto3" json:"mac,omitempty"` + OsMajorVersion string `protobuf:"bytes,8,opt,name=os_major_version,json=osMajorVersion,proto3" json:"os_major_version,omitempty"` + OsMinorVersion string `protobuf:"bytes,9,opt,name=os_minor_version,json=osMinorVersion,proto3" json:"os_minor_version,omitempty"` + Aliases string `protobuf:"bytes,10,opt,name=aliases,proto3" json:"aliases,omitempty"` + Addresses string `protobuf:"bytes,11,opt,name=addresses,proto3" json:"addresses,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AgentRequest) Reset() { *x = AgentRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AgentRequest) String() string { @@ -111,7 +109,7 @@ func (*AgentRequest) ProtoMessage() {} func (x *AgentRequest) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -204,21 +202,18 @@ func (x *AgentRequest) GetAddresses() string { } type ListAgentsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Rows []*Agent `protobuf:"bytes,1,rep,name=rows,proto3" json:"rows,omitempty"` + Total int32 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` unknownFields protoimpl.UnknownFields - - Rows []*Agent `protobuf:"bytes,1,rep,name=rows,proto3" json:"rows,omitempty"` - Total int32 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ListAgentsResponse) Reset() { *x = ListAgentsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListAgentsResponse) String() string { @@ -229,7 +224,7 @@ func (*ListAgentsResponse) ProtoMessage() {} func (x *ListAgentsResponse) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -259,33 +254,30 @@ func (x *ListAgentsResponse) GetTotal() int32 { } type Agent struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` - Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` - Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"` - Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=agent.Status" json:"status,omitempty"` - Platform string `protobuf:"bytes,5,opt,name=platform,proto3" json:"platform,omitempty"` - Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` - AgentKey string `protobuf:"bytes,7,opt,name=agent_key,json=agentKey,proto3" json:"agent_key,omitempty"` - Id uint32 `protobuf:"varint,8,opt,name=id,proto3" json:"id,omitempty"` - LastSeen string `protobuf:"bytes,9,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` - Mac string `protobuf:"bytes,10,opt,name=mac,proto3" json:"mac,omitempty"` - OsMajorVersion string `protobuf:"bytes,11,opt,name=os_major_version,json=osMajorVersion,proto3" json:"os_major_version,omitempty"` - OsMinorVersion string `protobuf:"bytes,12,opt,name=os_minor_version,json=osMinorVersion,proto3" json:"os_minor_version,omitempty"` - Aliases string `protobuf:"bytes,13,opt,name=aliases,proto3" json:"aliases,omitempty"` - Addresses string `protobuf:"bytes,14,opt,name=addresses,proto3" json:"addresses,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` + Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` + Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"` + Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=agent.Status" json:"status,omitempty"` + Platform string `protobuf:"bytes,5,opt,name=platform,proto3" json:"platform,omitempty"` + Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` + AgentKey string `protobuf:"bytes,7,opt,name=agent_key,json=agentKey,proto3" json:"agent_key,omitempty"` + Id uint32 `protobuf:"varint,8,opt,name=id,proto3" json:"id,omitempty"` + LastSeen string `protobuf:"bytes,9,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + Mac string `protobuf:"bytes,10,opt,name=mac,proto3" json:"mac,omitempty"` + OsMajorVersion string `protobuf:"bytes,11,opt,name=os_major_version,json=osMajorVersion,proto3" json:"os_major_version,omitempty"` + OsMinorVersion string `protobuf:"bytes,12,opt,name=os_minor_version,json=osMinorVersion,proto3" json:"os_minor_version,omitempty"` + Aliases string `protobuf:"bytes,13,opt,name=aliases,proto3" json:"aliases,omitempty"` + Addresses string `protobuf:"bytes,14,opt,name=addresses,proto3" json:"addresses,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Agent) Reset() { *x = Agent{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Agent) String() string { @@ -296,7 +288,7 @@ func (*Agent) ProtoMessage() {} func (x *Agent) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -410,24 +402,21 @@ func (x *Agent) GetAddresses() string { } type BidirectionalStream struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to StreamMessage: + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to StreamMessage: // // *BidirectionalStream_Command // *BidirectionalStream_Result StreamMessage isBidirectionalStream_StreamMessage `protobuf_oneof:"stream_message"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *BidirectionalStream) Reset() { *x = BidirectionalStream{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BidirectionalStream) String() string { @@ -438,7 +427,7 @@ func (*BidirectionalStream) ProtoMessage() {} func (x *BidirectionalStream) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -453,23 +442,27 @@ func (*BidirectionalStream) Descriptor() ([]byte, []int) { return file_agent_proto_rawDescGZIP(), []int{3} } -func (m *BidirectionalStream) GetStreamMessage() isBidirectionalStream_StreamMessage { - if m != nil { - return m.StreamMessage +func (x *BidirectionalStream) GetStreamMessage() isBidirectionalStream_StreamMessage { + if x != nil { + return x.StreamMessage } return nil } func (x *BidirectionalStream) GetCommand() *UtmCommand { - if x, ok := x.GetStreamMessage().(*BidirectionalStream_Command); ok { - return x.Command + if x != nil { + if x, ok := x.StreamMessage.(*BidirectionalStream_Command); ok { + return x.Command + } } return nil } func (x *BidirectionalStream) GetResult() *CommandResult { - if x, ok := x.GetStreamMessage().(*BidirectionalStream_Result); ok { - return x.Result + if x != nil { + if x, ok := x.StreamMessage.(*BidirectionalStream_Result); ok { + return x.Result + } } return nil } @@ -491,26 +484,24 @@ func (*BidirectionalStream_Command) isBidirectionalStream_StreamMessage() {} func (*BidirectionalStream_Result) isBidirectionalStream_StreamMessage() {} type UtmCommand struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` + Command string `protobuf:"bytes,2,opt,name=command,proto3" json:"command,omitempty"` + ExecutedBy string `protobuf:"bytes,3,opt,name=executed_by,json=executedBy,proto3" json:"executed_by,omitempty"` + CmdId string `protobuf:"bytes,4,opt,name=cmd_id,json=cmdId,proto3" json:"cmd_id,omitempty"` + OriginType string `protobuf:"bytes,5,opt,name=origin_type,json=originType,proto3" json:"origin_type,omitempty"` + OriginId string `protobuf:"bytes,6,opt,name=origin_id,json=originId,proto3" json:"origin_id,omitempty"` + Reason string `protobuf:"bytes,7,opt,name=reason,proto3" json:"reason,omitempty"` + Shell string `protobuf:"bytes,8,opt,name=shell,proto3" json:"shell,omitempty"` // Shell to execute command: "cmd", "powershell" (Windows), "sh", "bash" (Linux/macOS). Empty = default unknownFields protoimpl.UnknownFields - - AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` - Command string `protobuf:"bytes,2,opt,name=command,proto3" json:"command,omitempty"` - ExecutedBy string `protobuf:"bytes,3,opt,name=executed_by,json=executedBy,proto3" json:"executed_by,omitempty"` - CmdId string `protobuf:"bytes,4,opt,name=cmd_id,json=cmdId,proto3" json:"cmd_id,omitempty"` - OriginType string `protobuf:"bytes,5,opt,name=origin_type,json=originType,proto3" json:"origin_type,omitempty"` - OriginId string `protobuf:"bytes,6,opt,name=origin_id,json=originId,proto3" json:"origin_id,omitempty"` - Reason string `protobuf:"bytes,7,opt,name=reason,proto3" json:"reason,omitempty"` + sizeCache protoimpl.SizeCache } func (x *UtmCommand) Reset() { *x = UtmCommand{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *UtmCommand) String() string { @@ -521,7 +512,7 @@ func (*UtmCommand) ProtoMessage() {} func (x *UtmCommand) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -585,24 +576,28 @@ func (x *UtmCommand) GetReason() string { return "" } +func (x *UtmCommand) GetShell() string { + if x != nil { + return x.Shell + } + return "" +} + type CommandResult struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` + Result string `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` + ExecutedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=executed_at,json=executedAt,proto3" json:"executed_at,omitempty"` + CmdId string `protobuf:"bytes,4,opt,name=cmd_id,json=cmdId,proto3" json:"cmd_id,omitempty"` unknownFields protoimpl.UnknownFields - - AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` - Result string `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` - ExecutedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=executed_at,json=executedAt,proto3" json:"executed_at,omitempty"` - CmdId string `protobuf:"bytes,4,opt,name=cmd_id,json=cmdId,proto3" json:"cmd_id,omitempty"` + sizeCache protoimpl.SizeCache } func (x *CommandResult) Reset() { *x = CommandResult{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CommandResult) String() string { @@ -613,7 +608,7 @@ func (*CommandResult) ProtoMessage() {} func (x *CommandResult) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -657,21 +652,18 @@ func (x *CommandResult) GetCmdId() string { } type ListAgentsCommandsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Rows []*AgentCommand `protobuf:"bytes,1,rep,name=rows,proto3" json:"rows,omitempty"` + Total int32 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` unknownFields protoimpl.UnknownFields - - Rows []*AgentCommand `protobuf:"bytes,1,rep,name=rows,proto3" json:"rows,omitempty"` - Total int32 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ListAgentsCommandsResponse) Reset() { *x = ListAgentsCommandsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListAgentsCommandsResponse) String() string { @@ -682,7 +674,7 @@ func (*ListAgentsCommandsResponse) ProtoMessage() {} func (x *ListAgentsCommandsResponse) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -712,10 +704,7 @@ func (x *ListAgentsCommandsResponse) GetTotal() int32 { } type AgentCommand struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` CreatedAt *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` AgentId uint32 `protobuf:"varint,3,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` @@ -727,15 +716,15 @@ type AgentCommand struct { Reason string `protobuf:"bytes,9,opt,name=reason,proto3" json:"reason,omitempty"` OriginType string `protobuf:"bytes,10,opt,name=origin_type,json=originType,proto3" json:"origin_type,omitempty"` OriginId string `protobuf:"bytes,11,opt,name=origin_id,json=originId,proto3" json:"origin_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AgentCommand) Reset() { *x = AgentCommand{} - if protoimpl.UnsafeEnabled { - mi := &file_agent_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_agent_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AgentCommand) String() string { @@ -746,7 +735,7 @@ func (*AgentCommand) ProtoMessage() {} func (x *AgentCommand) ProtoReflect() protoreflect.Message { mi := &file_agent_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -840,182 +829,116 @@ func (x *AgentCommand) GetOriginId() string { var File_agent_proto protoreflect.FileDescriptor -var file_agent_proto_rawDesc = []byte{ - 0x0a, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x02, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x73, 0x5f, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x73, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x73, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, - 0x73, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, - 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x4c, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x72, - 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x22, 0x88, 0x03, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x1a, 0x0a, - 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x18, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, - 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, - 0x61, 0x63, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x73, 0x5f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x73, - 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, - 0x6f, 0x73, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x73, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, - 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, - 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x0e, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x86, - 0x01, 0x0a, 0x13, 0x42, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x2d, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, - 0x55, 0x74, 0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x42, 0x10, 0x0a, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xcf, 0x01, 0x0a, 0x0a, 0x55, 0x74, 0x6d, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, - 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x15, 0x0a, 0x06, - 0x63, 0x6d, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6d, - 0x64, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, - 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x96, 0x01, 0x0a, 0x0d, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3b, - 0x0a, 0x0b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x63, - 0x6d, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6d, 0x64, - 0x49, 0x64, 0x22, 0x5b, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x27, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, - 0xa1, 0x03, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, - 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x40, 0x0a, 0x0e, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, - 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0d, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, - 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, - 0x64, 0x5f, 0x62, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x63, 0x6d, 0x64, 0x5f, 0x69, 0x64, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6d, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, - 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x72, 0x69, 0x67, - 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x49, 0x64, 0x2a, 0x57, 0x0a, 0x12, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x4f, 0x54, - 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x51, - 0x55, 0x45, 0x55, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, - 0x47, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x45, 0x44, 0x10, - 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x32, 0x9c, 0x03, 0x0a, - 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3b, 0x0a, - 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x13, - 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0b, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, - 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x67, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x3d, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, - 0x12, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x4b, 0x0a, 0x0b, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, - 0x1a, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x1a, 0x1a, 0x2e, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4c, 0x0a, - 0x11, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x73, 0x12, 0x12, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0x4f, 0x0a, 0x0c, 0x50, - 0x61, 0x6e, 0x65, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3f, 0x0a, 0x0e, 0x50, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x11, 0x2e, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x74, 0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x1a, 0x14, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x32, 0x5a, 0x30, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x74, 0x6d, 0x73, 0x74, - 0x61, 0x63, 0x6b, 0x2f, 0x55, 0x54, 0x4d, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +const file_agent_proto_rawDesc = "" + + "\n" + + "\vagent.proto\x12\x05agent\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\fcommon.proto\"\xbf\x02\n" + + "\fAgentRequest\x12\x0e\n" + + "\x02ip\x18\x01 \x01(\tR\x02ip\x12\x1a\n" + + "\bhostname\x18\x02 \x01(\tR\bhostname\x12\x0e\n" + + "\x02os\x18\x03 \x01(\tR\x02os\x12\x1a\n" + + "\bplatform\x18\x04 \x01(\tR\bplatform\x12\x18\n" + + "\aversion\x18\x05 \x01(\tR\aversion\x12\x1f\n" + + "\vregister_by\x18\x06 \x01(\tR\n" + + "registerBy\x12\x10\n" + + "\x03mac\x18\a \x01(\tR\x03mac\x12(\n" + + "\x10os_major_version\x18\b \x01(\tR\x0eosMajorVersion\x12(\n" + + "\x10os_minor_version\x18\t \x01(\tR\x0eosMinorVersion\x12\x18\n" + + "\aaliases\x18\n" + + " \x01(\tR\aaliases\x12\x1c\n" + + "\taddresses\x18\v \x01(\tR\taddresses\"L\n" + + "\x12ListAgentsResponse\x12 \n" + + "\x04rows\x18\x01 \x03(\v2\f.agent.AgentR\x04rows\x12\x14\n" + + "\x05total\x18\x02 \x01(\x05R\x05total\"\x88\x03\n" + + "\x05Agent\x12\x0e\n" + + "\x02ip\x18\x01 \x01(\tR\x02ip\x12\x1a\n" + + "\bhostname\x18\x02 \x01(\tR\bhostname\x12\x0e\n" + + "\x02os\x18\x03 \x01(\tR\x02os\x12%\n" + + "\x06status\x18\x04 \x01(\x0e2\r.agent.StatusR\x06status\x12\x1a\n" + + "\bplatform\x18\x05 \x01(\tR\bplatform\x12\x18\n" + + "\aversion\x18\x06 \x01(\tR\aversion\x12\x1b\n" + + "\tagent_key\x18\a \x01(\tR\bagentKey\x12\x0e\n" + + "\x02id\x18\b \x01(\rR\x02id\x12\x1b\n" + + "\tlast_seen\x18\t \x01(\tR\blastSeen\x12\x10\n" + + "\x03mac\x18\n" + + " \x01(\tR\x03mac\x12(\n" + + "\x10os_major_version\x18\v \x01(\tR\x0eosMajorVersion\x12(\n" + + "\x10os_minor_version\x18\f \x01(\tR\x0eosMinorVersion\x12\x18\n" + + "\aaliases\x18\r \x01(\tR\aaliases\x12\x1c\n" + + "\taddresses\x18\x0e \x01(\tR\taddresses\"\x86\x01\n" + + "\x13BidirectionalStream\x12-\n" + + "\acommand\x18\x01 \x01(\v2\x11.agent.UtmCommandH\x00R\acommand\x12.\n" + + "\x06result\x18\x02 \x01(\v2\x14.agent.CommandResultH\x00R\x06resultB\x10\n" + + "\x0estream_message\"\xe5\x01\n" + + "\n" + + "UtmCommand\x12\x19\n" + + "\bagent_id\x18\x01 \x01(\tR\aagentId\x12\x18\n" + + "\acommand\x18\x02 \x01(\tR\acommand\x12\x1f\n" + + "\vexecuted_by\x18\x03 \x01(\tR\n" + + "executedBy\x12\x15\n" + + "\x06cmd_id\x18\x04 \x01(\tR\x05cmdId\x12\x1f\n" + + "\vorigin_type\x18\x05 \x01(\tR\n" + + "originType\x12\x1b\n" + + "\torigin_id\x18\x06 \x01(\tR\boriginId\x12\x16\n" + + "\x06reason\x18\a \x01(\tR\x06reason\x12\x14\n" + + "\x05shell\x18\b \x01(\tR\x05shell\"\x96\x01\n" + + "\rCommandResult\x12\x19\n" + + "\bagent_id\x18\x01 \x01(\tR\aagentId\x12\x16\n" + + "\x06result\x18\x02 \x01(\tR\x06result\x12;\n" + + "\vexecuted_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "executedAt\x12\x15\n" + + "\x06cmd_id\x18\x04 \x01(\tR\x05cmdId\"[\n" + + "\x1aListAgentsCommandsResponse\x12'\n" + + "\x04rows\x18\x01 \x03(\v2\x13.agent.AgentCommandR\x04rows\x12\x14\n" + + "\x05total\x18\x02 \x01(\x05R\x05total\"\xa1\x03\n" + + "\fAgentCommand\x129\n" + + "\n" + + "created_at\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x12\x19\n" + + "\bagent_id\x18\x03 \x01(\rR\aagentId\x12\x18\n" + + "\acommand\x18\x04 \x01(\tR\acommand\x12@\n" + + "\x0ecommand_status\x18\x05 \x01(\x0e2\x19.agent.AgentCommandStatusR\rcommandStatus\x12\x16\n" + + "\x06result\x18\x06 \x01(\tR\x06result\x12\x1f\n" + + "\vexecuted_by\x18\a \x01(\tR\n" + + "executedBy\x12\x15\n" + + "\x06cmd_id\x18\b \x01(\tR\x05cmdId\x12\x16\n" + + "\x06reason\x18\t \x01(\tR\x06reason\x12\x1f\n" + + "\vorigin_type\x18\n" + + " \x01(\tR\n" + + "originType\x12\x1b\n" + + "\torigin_id\x18\v \x01(\tR\boriginId*W\n" + + "\x12AgentCommandStatus\x12\x10\n" + + "\fNOT_EXECUTED\x10\x00\x12\t\n" + + "\x05QUEUE\x10\x01\x12\v\n" + + "\aPENDING\x10\x02\x12\f\n" + + "\bEXECUTED\x10\x03\x12\t\n" + + "\x05ERROR\x10\x042\x9c\x03\n" + + "\fAgentService\x12;\n" + + "\rRegisterAgent\x12\x13.agent.AgentRequest\x1a\x13.agent.AuthResponse\"\x00\x129\n" + + "\vUpdateAgent\x12\x13.agent.AgentRequest\x1a\x13.agent.AuthResponse\"\x00\x12:\n" + + "\vDeleteAgent\x12\x14.agent.DeleteRequest\x1a\x13.agent.AuthResponse\"\x00\x12=\n" + + "\n" + + "ListAgents\x12\x12.agent.ListRequest\x1a\x19.agent.ListAgentsResponse\"\x00\x12K\n" + + "\vAgentStream\x12\x1a.agent.BidirectionalStream\x1a\x1a.agent.BidirectionalStream\"\x00(\x010\x01\x12L\n" + + "\x11ListAgentCommands\x12\x12.agent.ListRequest\x1a!.agent.ListAgentsCommandsResponse\"\x002O\n" + + "\fPanelService\x12?\n" + + "\x0eProcessCommand\x12\x11.agent.UtmCommand\x1a\x14.agent.CommandResult\"\x00(\x010\x01B2Z0github.com/utmstack/UTMStack/agent-manager/agentb\x06proto3" var ( file_agent_proto_rawDescOnce sync.Once - file_agent_proto_rawDescData = file_agent_proto_rawDesc + file_agent_proto_rawDescData []byte ) func file_agent_proto_rawDescGZIP() []byte { file_agent_proto_rawDescOnce.Do(func() { - file_agent_proto_rawDescData = protoimpl.X.CompressGZIP(file_agent_proto_rawDescData) + file_agent_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_agent_proto_rawDesc), len(file_agent_proto_rawDesc))) }) return file_agent_proto_rawDescData } var file_agent_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_agent_proto_goTypes = []interface{}{ +var file_agent_proto_goTypes = []any{ (AgentCommandStatus)(0), // 0: agent.AgentCommandStatus (*AgentRequest)(nil), // 1: agent.AgentRequest (*ListAgentsResponse)(nil), // 2: agent.ListAgentsResponse @@ -1068,105 +991,7 @@ func file_agent_proto_init() { return } file_common_proto_init() - if !protoimpl.UnsafeEnabled { - file_agent_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AgentRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListAgentsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Agent); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BidirectionalStream); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UtmCommand); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommandResult); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListAgentsCommandsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_agent_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AgentCommand); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_agent_proto_msgTypes[3].OneofWrappers = []interface{}{ + file_agent_proto_msgTypes[3].OneofWrappers = []any{ (*BidirectionalStream_Command)(nil), (*BidirectionalStream_Result)(nil), } @@ -1174,7 +999,7 @@ func file_agent_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_agent_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_agent_proto_rawDesc), len(file_agent_proto_rawDesc)), NumEnums: 1, NumMessages: 8, NumExtensions: 0, @@ -1186,7 +1011,6 @@ func file_agent_proto_init() { MessageInfos: file_agent_proto_msgTypes, }.Build() File_agent_proto = out.File - file_agent_proto_rawDesc = nil file_agent_proto_goTypes = nil file_agent_proto_depIdxs = nil } diff --git a/agent/agent/agent_grpc.pb.go b/agent/agent/agent_grpc.pb.go index f75ba57b2..400673e17 100644 --- a/agent/agent/agent_grpc.pb.go +++ b/agent/agent/agent_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v3.21.12 +// - protoc v5.29.3 // source: agent.proto package agent diff --git a/agent/agent/common.pb.go b/agent/agent/common.pb.go index 0d4ccf71d..4407bccf4 100644 --- a/agent/agent/common.pb.go +++ b/agent/agent/common.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 +// protoc-gen-go v1.36.10 +// protoc v5.29.3 // source: common.proto package agent @@ -11,6 +11,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -116,23 +117,20 @@ func (ConnectorType) EnumDescriptor() ([]byte, []int) { } type ListRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + PageNumber int32 `protobuf:"varint,1,opt,name=page_number,json=pageNumber,proto3" json:"page_number,omitempty"` + PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + SearchQuery string `protobuf:"bytes,3,opt,name=search_query,json=searchQuery,proto3" json:"search_query,omitempty"` + SortBy string `protobuf:"bytes,4,opt,name=sort_by,json=sortBy,proto3" json:"sort_by,omitempty"` unknownFields protoimpl.UnknownFields - - PageNumber int32 `protobuf:"varint,1,opt,name=page_number,json=pageNumber,proto3" json:"page_number,omitempty"` - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - SearchQuery string `protobuf:"bytes,3,opt,name=search_query,json=searchQuery,proto3" json:"search_query,omitempty"` - SortBy string `protobuf:"bytes,4,opt,name=sort_by,json=sortBy,proto3" json:"sort_by,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ListRequest) Reset() { *x = ListRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_common_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListRequest) String() string { @@ -143,7 +141,7 @@ func (*ListRequest) ProtoMessage() {} func (x *ListRequest) ProtoReflect() protoreflect.Message { mi := &file_common_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -187,21 +185,18 @@ func (x *ListRequest) GetSortBy() string { } type AuthResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` unknownFields protoimpl.UnknownFields - - Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + sizeCache protoimpl.SizeCache } func (x *AuthResponse) Reset() { *x = AuthResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_common_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthResponse) String() string { @@ -212,7 +207,7 @@ func (*AuthResponse) ProtoMessage() {} func (x *AuthResponse) ProtoReflect() protoreflect.Message { mi := &file_common_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -242,20 +237,17 @@ func (x *AuthResponse) GetKey() string { } type DeleteRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + DeletedBy string `protobuf:"bytes,1,opt,name=deleted_by,json=deletedBy,proto3" json:"deleted_by,omitempty"` unknownFields protoimpl.UnknownFields - - DeletedBy string `protobuf:"bytes,1,opt,name=deleted_by,json=deletedBy,proto3" json:"deleted_by,omitempty"` + sizeCache protoimpl.SizeCache } func (x *DeleteRequest) Reset() { *x = DeleteRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_common_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_common_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DeleteRequest) String() string { @@ -266,7 +258,7 @@ func (*DeleteRequest) ProtoMessage() {} func (x *DeleteRequest) ProtoReflect() protoreflect.Message { mi := &file_common_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -290,50 +282,45 @@ func (x *DeleteRequest) GetDeletedBy() string { var File_common_proto protoreflect.FileDescriptor -var file_common_proto_rawDesc = []byte{ - 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x22, 0x87, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x65, - 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, - 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, - 0x68, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, - 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x22, - 0x30, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x22, 0x2e, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x42, - 0x79, 0x2a, 0x2e, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0a, 0x0a, 0x06, 0x4f, - 0x4e, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4f, 0x46, 0x46, 0x4c, 0x49, - 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x02, 0x2a, 0x29, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, - 0x09, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x10, 0x01, 0x42, 0x32, 0x5a, 0x30, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x74, 0x6d, 0x73, 0x74, - 0x61, 0x63, 0x6b, 0x2f, 0x55, 0x54, 0x4d, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +const file_common_proto_rawDesc = "" + + "\n" + + "\fcommon.proto\x12\x05agent\"\x87\x01\n" + + "\vListRequest\x12\x1f\n" + + "\vpage_number\x18\x01 \x01(\x05R\n" + + "pageNumber\x12\x1b\n" + + "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12!\n" + + "\fsearch_query\x18\x03 \x01(\tR\vsearchQuery\x12\x17\n" + + "\asort_by\x18\x04 \x01(\tR\x06sortBy\"0\n" + + "\fAuthResponse\x12\x0e\n" + + "\x02id\x18\x01 \x01(\rR\x02id\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\".\n" + + "\rDeleteRequest\x12\x1d\n" + + "\n" + + "deleted_by\x18\x01 \x01(\tR\tdeletedBy*.\n" + + "\x06Status\x12\n" + + "\n" + + "\x06ONLINE\x10\x00\x12\v\n" + + "\aOFFLINE\x10\x01\x12\v\n" + + "\aUNKNOWN\x10\x02*)\n" + + "\rConnectorType\x12\t\n" + + "\x05AGENT\x10\x00\x12\r\n" + + "\tCOLLECTOR\x10\x01B2Z0github.com/utmstack/UTMStack/agent-manager/agentb\x06proto3" var ( file_common_proto_rawDescOnce sync.Once - file_common_proto_rawDescData = file_common_proto_rawDesc + file_common_proto_rawDescData []byte ) func file_common_proto_rawDescGZIP() []byte { file_common_proto_rawDescOnce.Do(func() { - file_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_proto_rawDescData) + file_common_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_proto_rawDesc), len(file_common_proto_rawDesc))) }) return file_common_proto_rawDescData } var file_common_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_common_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_common_proto_goTypes = []interface{}{ +var file_common_proto_goTypes = []any{ (Status)(0), // 0: agent.Status (ConnectorType)(0), // 1: agent.ConnectorType (*ListRequest)(nil), // 2: agent.ListRequest @@ -353,49 +340,11 @@ func file_common_proto_init() { if File_common_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_common_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuthResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_common_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_common_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_proto_rawDesc), len(file_common_proto_rawDesc)), NumEnums: 2, NumMessages: 3, NumExtensions: 0, @@ -407,7 +356,6 @@ func file_common_proto_init() { MessageInfos: file_common_proto_msgTypes, }.Build() File_common_proto = out.File - file_common_proto_rawDesc = nil file_common_proto_goTypes = nil file_common_proto_depIdxs = nil } diff --git a/agent/agent/incident_response.go b/agent/agent/incident_response.go index 3c9927d45..a7b87eec2 100644 --- a/agent/agent/incident_response.go +++ b/agent/agent/incident_response.go @@ -48,7 +48,7 @@ func IncidentResponseStream(cnf *config.Config, ctx context.Context) { switch msg := in.StreamMessage.(type) { case *BidirectionalStream_Command: - err = commandProcessor(path, stream, cnf, []string{msg.Command.Command, in.GetCommand().CmdId}) + err = commandProcessor(path, stream, cnf, msg.Command.Command, msg.Command.CmdId, msg.Command.Shell) if err != nil { action := HandleGRPCStreamError(err, "error sending result to server", &streamErrLogged) if action == ActionReconnect { @@ -62,31 +62,41 @@ func IncidentResponseStream(cnf *config.Config, ctx context.Context) { } } -func commandProcessor(path string, stream AgentService_AgentStreamClient, cnf *config.Config, commandPair []string) error { +func commandProcessor(path string, stream AgentService_AgentStreamClient, cnf *config.Config, command, cmdId, shell string) error { var result string var errB bool - utils.Logger.LogF(100, "Received command: %s", commandPair[0]) + utils.Logger.LogF(100, "Received command: %s (shell: %s)", command, shell) switch runtime.GOOS { case "windows": - result, errB = utils.ExecuteWithResult("cmd.exe", path, "/C", commandPair[0]) + if shell == "powershell" { + result, errB = utils.ExecuteWithResult("powershell.exe", path, "-Command", command) + } else { + // Default to cmd.exe (also handles shell == "" or shell == "cmd") + result, errB = utils.ExecuteWithResult("cmd.exe", path, "/C", command) + } case "linux", "darwin": - result, errB = utils.ExecuteWithResult("sh", path, "-c", commandPair[0]) + if shell == "bash" { + result, errB = utils.ExecuteWithResult("bash", path, "-c", command) + } else { + // Default to sh (also handles shell == "" or shell == "sh") + result, errB = utils.ExecuteWithResult("sh", path, "-c", command) + } default: utils.Logger.ErrorF("unsupported operating system: %s", runtime.GOOS) return fmt.Errorf("unsupported operating system: %s", runtime.GOOS) } if errB { - utils.Logger.ErrorF("error executing command %s: %s", commandPair[0], result) + utils.Logger.ErrorF("error executing command %s: %s", command, result) } else { - utils.Logger.LogF(100, "Result when executing the command %s: %s", commandPair[0], result) + utils.Logger.LogF(100, "Result when executing the command %s: %s", command, result) } if err := stream.Send(&BidirectionalStream{ StreamMessage: &BidirectionalStream_Result{ - Result: &CommandResult{Result: result, AgentId: strconv.Itoa(int(cnf.AgentID)), ExecutedAt: timestamppb.Now(), CmdId: commandPair[1]}, + Result: &CommandResult{Result: result, AgentId: strconv.Itoa(int(cnf.AgentID)), ExecutedAt: timestamppb.Now(), CmdId: cmdId}, }, }); err != nil { return err diff --git a/agent/agent/ping.pb.go b/agent/agent/ping.pb.go index 21ddaf763..b6f9787b9 100644 --- a/agent/agent/ping.pb.go +++ b/agent/agent/ping.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 +// protoc-gen-go v1.36.10 +// protoc v5.29.3 // source: ping.proto package agent @@ -11,6 +11,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -21,20 +22,17 @@ const ( ) type PingRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Type ConnectorType `protobuf:"varint,1,opt,name=type,proto3,enum=agent.ConnectorType" json:"type,omitempty"` unknownFields protoimpl.UnknownFields - - Type ConnectorType `protobuf:"varint,1,opt,name=type,proto3,enum=agent.ConnectorType" json:"type,omitempty"` + sizeCache protoimpl.SizeCache } func (x *PingRequest) Reset() { *x = PingRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_ping_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ping_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PingRequest) String() string { @@ -45,7 +43,7 @@ func (*PingRequest) ProtoMessage() {} func (x *PingRequest) ProtoReflect() protoreflect.Message { mi := &file_ping_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -68,20 +66,17 @@ func (x *PingRequest) GetType() ConnectorType { } type PingResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Received string `protobuf:"bytes,1,opt,name=received,proto3" json:"received,omitempty"` unknownFields protoimpl.UnknownFields - - Received string `protobuf:"bytes,1,opt,name=received,proto3" json:"received,omitempty"` + sizeCache protoimpl.SizeCache } func (x *PingResponse) Reset() { *x = PingResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_ping_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_ping_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PingResponse) String() string { @@ -92,7 +87,7 @@ func (*PingResponse) ProtoMessage() {} func (x *PingResponse) ProtoReflect() protoreflect.Message { mi := &file_ping_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -116,40 +111,31 @@ func (x *PingResponse) GetReceived() string { var File_ping_proto protoreflect.FileDescriptor -var file_ping_proto_rawDesc = []byte{ - 0x0a, 0x0a, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x1a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x37, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, - 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2a, 0x0a, 0x0c, 0x50, 0x69, - 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x32, 0x42, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x2e, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x13, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x74, 0x6d, 0x73, 0x74, 0x61, 0x63, - 0x6b, 0x2f, 0x55, 0x54, 0x4d, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +const file_ping_proto_rawDesc = "" + + "\n" + + "\n" + + "ping.proto\x12\x05agent\x1a\fcommon.proto\"7\n" + + "\vPingRequest\x12(\n" + + "\x04type\x18\x01 \x01(\x0e2\x14.agent.ConnectorTypeR\x04type\"*\n" + + "\fPingResponse\x12\x1a\n" + + "\breceived\x18\x01 \x01(\tR\breceived2B\n" + + "\vPingService\x123\n" + + "\x04Ping\x12\x12.agent.PingRequest\x1a\x13.agent.PingResponse\"\x00(\x01B2Z0github.com/utmstack/UTMStack/agent-manager/agentb\x06proto3" var ( file_ping_proto_rawDescOnce sync.Once - file_ping_proto_rawDescData = file_ping_proto_rawDesc + file_ping_proto_rawDescData []byte ) func file_ping_proto_rawDescGZIP() []byte { file_ping_proto_rawDescOnce.Do(func() { - file_ping_proto_rawDescData = protoimpl.X.CompressGZIP(file_ping_proto_rawDescData) + file_ping_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ping_proto_rawDesc), len(file_ping_proto_rawDesc))) }) return file_ping_proto_rawDescData } var file_ping_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_ping_proto_goTypes = []interface{}{ +var file_ping_proto_goTypes = []any{ (*PingRequest)(nil), // 0: agent.PingRequest (*PingResponse)(nil), // 1: agent.PingResponse (ConnectorType)(0), // 2: agent.ConnectorType @@ -171,37 +157,11 @@ func file_ping_proto_init() { return } file_common_proto_init() - if !protoimpl.UnsafeEnabled { - file_ping_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PingRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_ping_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PingResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_ping_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_ping_proto_rawDesc), len(file_ping_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, @@ -212,7 +172,6 @@ func file_ping_proto_init() { MessageInfos: file_ping_proto_msgTypes, }.Build() File_ping_proto = out.File - file_ping_proto_rawDesc = nil file_ping_proto_goTypes = nil file_ping_proto_depIdxs = nil } diff --git a/agent/agent/ping_grpc.pb.go b/agent/agent/ping_grpc.pb.go index f283dc80a..f3213500c 100644 --- a/agent/agent/ping_grpc.pb.go +++ b/agent/agent/ping_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v3.21.12 +// - protoc v5.29.3 // source: ping.proto package agent diff --git a/agent/protos/agent.proto b/agent/protos/agent.proto index 5495bb3be..39d98fdd4 100644 --- a/agent/protos/agent.proto +++ b/agent/protos/agent.proto @@ -9,6 +9,7 @@ import "common.proto"; service AgentService { rpc RegisterAgent(AgentRequest) returns (AuthResponse) {} + rpc UpdateAgent(AgentRequest) returns (AuthResponse) {} rpc DeleteAgent(DeleteRequest) returns (AuthResponse) {} rpc ListAgents (ListRequest) returns (ListAgentsResponse) {} rpc AgentStream(stream BidirectionalStream) returns (stream BidirectionalStream) {} @@ -78,6 +79,7 @@ message UtmCommand { string origin_type = 5; string origin_id = 6; string reason = 7; + string shell = 8; // Shell to execute command: "cmd", "powershell" (Windows), "sh", "bash" (Linux/macOS). Empty = default } message CommandResult { diff --git a/agent/version.json b/agent/version.json index 685be4c14..b60b61712 100644 --- a/agent/version.json +++ b/agent/version.json @@ -1,4 +1,4 @@ { - "version": "11.1.3", - "updater_version": "1.0.2" + "version": "11.1.4", + "updater_version": "1.0.4" } From 5823657f2f1949eb12897f3463ff95ee31004ce6 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 04:13:35 -0500 Subject: [PATCH 034/115] fix(agent): download version.json during install before agent registration --- agent/cmd/install.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/agent/cmd/install.go b/agent/cmd/install.go index ad9f9b076..1625015cc 100644 --- a/agent/cmd/install.go +++ b/agent/cmd/install.go @@ -10,6 +10,8 @@ import ( "github.com/utmstack/UTMStack/agent/config" "github.com/utmstack/UTMStack/agent/serv" "github.com/utmstack/UTMStack/agent/utils" + "github.com/utmstack/UTMStack/shared/fs" + "github.com/utmstack/UTMStack/shared/http" ) var installCmd = &cobra.Command{ @@ -34,6 +36,14 @@ var installCmd = &cobra.Command{ } fmt.Println("[OK]") + fmt.Print("Downloading version info ... ") + versionURL := fmt.Sprintf(config.DependUrl, cnf.Server, config.DependenciesPort, "version.json") + if err := http.DownloadFile(versionURL, nil, "version.json", fs.GetExecutablePath(), cnf.SkipCertValidation); err != nil { + fmt.Println("\nError downloading version.json: ", err) + os.Exit(1) + } + fmt.Println("[OK]") + fmt.Print("Configuring agent ... ") if err := pb.RegisterAgent(cnf, utmKey); err != nil { fmt.Println("\nError registering agent: ", err) From 069c6ae6577d2e085176f08eb2a02b8f6c2e9e12 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 04:49:45 -0500 Subject: [PATCH 035/115] fix(updater,frontend): add legacy binary migration and fix agent search filter --- agent/updater/updates/update.go | 22 +++++++++++++++++++ .../agent-sidebar/agent-sidebar.component.ts | 5 ++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/agent/updater/updates/update.go b/agent/updater/updates/update.go index 0ac08ef37..fecaad400 100644 --- a/agent/updater/updates/update.go +++ b/agent/updater/updates/update.go @@ -24,6 +24,15 @@ type Version struct { Version string `json:"version"` } +// legacyServiceFile returns the old naming convention for the agent binary. +// This is used for migration from old agents that don't have OS/arch suffix. +func legacyServiceFile() string { + if runtime.GOOS == "windows" { + return "utmstack_agent_service.exe" + } + return "utmstack_agent_service" +} + var currentVersion = Version{} func UpdateDependencies(cnf *config.Config) { @@ -99,6 +108,19 @@ func runUpdateProcess(basePath string) error { time.Sleep(10 * time.Second) + // Migration: check if old naming convention exists and migrate to new naming + oldBinPath := filepath.Join(basePath, oldBin) + if !fs.Exists(oldBinPath) { + legacyBin := legacyServiceFile() + legacyBinPath := filepath.Join(basePath, legacyBin) + if fs.Exists(legacyBinPath) { + logger.Info("Migrating legacy binary from %s to %s", legacyBin, oldBin) + if err := os.Rename(legacyBinPath, oldBinPath); err != nil { + return fmt.Errorf("error migrating legacy binary: %v", err) + } + } + } + backupPath := filepath.Join(basePath, backupBin) if fs.Exists(backupPath) { logger.Info("Removing previous backup: %s", backupPath) 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 b1e39fd91..c3dac6220 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 @@ -24,10 +24,13 @@ export class AgentSidebarComponent implements OnInit { } searchAgent($event: string | number) { + const searchValue = $event.toString().trim(); + const searchQuery = searchValue ? `hostname.Contain=${searchValue}` : ''; + this.agentSidebarService.loadData({ ...this.request, page: 0, - searchQuery: $event.toString() + searchQuery }); } From dcbf36cc5bde86bf334a831b1833a8d63e317c3a Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 05:16:35 -0500 Subject: [PATCH 036/115] fix(agent): return errors from low-level packages instead of calling Fatal/Exit --- agent/agent/logprocessor.go | 63 +++++++++++++++++++++++++++++-------- agent/cmd/clean_logs.go | 6 +++- agent/database/db.go | 23 ++++++++------ agent/serv/service.go | 13 +++++--- 4 files changed, 78 insertions(+), 27 deletions(-) diff --git a/agent/agent/logprocessor.go b/agent/agent/logprocessor.go index fe59bf1aa..b67fff502 100644 --- a/agent/agent/logprocessor.go +++ b/agent/agent/logprocessor.go @@ -3,7 +3,6 @@ package agent import ( "context" "errors" - "os" "strconv" "strings" "sync" @@ -27,29 +26,47 @@ type LogProcessor struct { } var ( - processor LogProcessor - processorOnce sync.Once - LogQueue = make(chan *plugins.Log, 10000) - timeCLeanLogs = 10 * time.Minute + processor LogProcessor + processorOnce sync.Once + processorInitErr error + LogQueue = make(chan *plugins.Log, 10000) + timeCLeanLogs = 10 * time.Minute + + // ErrAgentUninstalled is returned when the agent uninstalls itself due to invalid key + ErrAgentUninstalled = errors.New("agent uninstalled due to invalid key") ) -func GetLogProcessor() LogProcessor { +func GetLogProcessor() (*LogProcessor, error) { processorOnce.Do(func() { + db, err := database.GetDB() + if err != nil { + processorInitErr = err + return + } processor = LogProcessor{ - db: database.GetDB(), + db: db, connErrWritten: false, ackErrWritten: false, sendErrWritten: false, } }) - return processor + if processorInitErr != nil { + return nil, processorInitErr + } + return &processor, nil } func (l *LogProcessor) ProcessLogs(cnf *config.Config, ctx context.Context) { go l.CleanCountedLogs() for { - ctxEof, cancelEof := context.WithCancel(context.Background()) + select { + case <-ctx.Done(): + utils.Logger.Info("ProcessLogs stopping due to context cancellation") + return + default: + } + connection, err := GetCorrelationConnection(cnf) if err != nil { if !l.connErrWritten { @@ -61,9 +78,23 @@ func (l *LogProcessor) ProcessLogs(cnf *config.Config, ctx context.Context) { } client := plugins.NewIntegrationClient(connection) - plClient := createClient(client, ctx) + plClient, err := createClient(client, ctx) + if err != nil { + if errors.Is(err, ErrAgentUninstalled) { + utils.Logger.Info("Agent uninstalled, stopping log processor") + return + } + if errors.Is(err, context.Canceled) { + utils.Logger.Info("ProcessLogs stopping due to context cancellation") + return + } + utils.Logger.ErrorF("error creating client: %v", err) + continue + } l.connErrWritten = false + // Create context only after successful client creation to avoid leaks + ctxEof, cancelEof := context.WithCancel(context.Background()) go l.handleAcknowledgements(plClient, ctxEof, cancelEof) l.processLogs(plClient, ctxEof, cancelEof) } @@ -173,13 +204,19 @@ func (l *LogProcessor) CleanCountedLogs() { } } -func createClient(client plugins.IntegrationClient, ctx context.Context) plugins.Integration_ProcessLogClient { +func createClient(client plugins.IntegrationClient, ctx context.Context) (plugins.Integration_ProcessLogClient, error) { var connErrMsgWritten bool invalidKeyCounter := 0 invalidKeyDelay := timeToSleep maxInvalidKeyDelay := 5 * time.Minute maxInvalidKeyAttempts := 100 // ~8+ hours with backoff before uninstall for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + plClient, err := client.ProcessLog(ctx) if err != nil { if strings.Contains(err.Error(), "invalid agent key") { @@ -188,7 +225,7 @@ func createClient(client plugins.IntegrationClient, ctx context.Context) plugins if invalidKeyCounter >= maxInvalidKeyAttempts { utils.Logger.ErrorF("uninstalling agent after %d consecutive invalid key errors", maxInvalidKeyAttempts) _ = UninstallAll() - os.Exit(1) + return nil, ErrAgentUninstalled } time.Sleep(invalidKeyDelay) invalidKeyDelay = utils.IncrementReconnectDelay(invalidKeyDelay, maxInvalidKeyDelay) @@ -204,7 +241,7 @@ func createClient(client plugins.IntegrationClient, ctx context.Context) plugins time.Sleep(timeToSleep) continue } - return plClient + return plClient, nil } } diff --git a/agent/cmd/clean_logs.go b/agent/cmd/clean_logs.go index fcd422c19..56ce119a9 100644 --- a/agent/cmd/clean_logs.go +++ b/agent/cmd/clean_logs.go @@ -18,7 +18,11 @@ var cleanLogsCmd = &cobra.Command{ PreRunE: requireInstalled, RunE: func(cmd *cobra.Command, args []string) error { fmt.Println("Cleaning old logs ...") - db := database.GetDB() + db, err := database.GetDB() + if err != nil { + fmt.Println("Error initializing database: ", err) + os.Exit(1) + } datR, err := agent.GetDataRetention() if err != nil { fmt.Println("Error getting retention: ", err) diff --git a/agent/database/db.go b/agent/database/db.go index 73ebca493..a9e2af5b6 100644 --- a/agent/database/db.go +++ b/agent/database/db.go @@ -3,7 +3,6 @@ package database import ( "errors" "fmt" - "log" "os" "path/filepath" "sync" @@ -18,6 +17,7 @@ import ( var ( dbInstance *Database dbOnce sync.Once + dbInitErr error ) type Database struct { @@ -112,18 +112,20 @@ func (d *Database) DeleteOld(data interface{}, retentionMegabytes int) (int, err return rowsAffected, nil } -func GetDB() *Database { +func GetDB() (*Database, error) { dbOnce.Do(func() { path := filepath.Join(fs.GetExecutablePath(), "logs_process") - err := fs.CreateDirIfNotExist(path) - if err != nil { - log.Fatalf("error creating database path: %v", err) + if err := fs.CreateDirIfNotExist(path); err != nil { + dbInitErr = fmt.Errorf("creating database path: %w", err) + return } + path = config.LogsDBFile if _, err := os.Stat(path); os.IsNotExist(err) { file, err := os.Create(path) if err != nil { - log.Fatalf("error creating database file: %v", err) + dbInitErr = fmt.Errorf("creating database file: %w", err) + return } file.Close() } @@ -132,14 +134,17 @@ func GetDB() *Database { Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { - log.Fatalf("error connecting with database: %v", err) + dbInitErr = fmt.Errorf("connecting with database: %w", err) + return } dbInstance = &Database{db: conn} - }) - return dbInstance + if dbInitErr != nil { + return nil, dbInitErr + } + return dbInstance, nil } func GetDatabaseSizeInMB() (int, error) { diff --git a/agent/serv/service.go b/agent/serv/service.go index c90849e16..ced41abf9 100644 --- a/agent/serv/service.go +++ b/agent/serv/service.go @@ -63,7 +63,7 @@ func (p *program) Stop(_ service.Service) error { } // Close database - if db := database.GetDB(); db != nil { + if db, err := database.GetDB(); err == nil && db != nil { if err := db.Close(); err != nil { utils.Logger.ErrorF("error closing database: %v", err) } @@ -94,9 +94,10 @@ func (p *program) run() { utils.Logger.Fatal("error getting config: %v", err) } - db := database.GetDB() - err = db.Migrate(models.Log{}) + db, err := database.GetDB() if err != nil { + utils.Logger.ErrorF("error initializing database: %v", err) + } else if err = db.Migrate(models.Log{}); err != nil { utils.Logger.ErrorF("error migrating logs table: %v", err) } @@ -125,7 +126,11 @@ func (p *program) run() { }) p.goSafe("ProcessLogs", func() { - logProcessor := pb.GetLogProcessor() + logProcessor, err := pb.GetLogProcessor() + if err != nil { + utils.Logger.ErrorF("error initializing log processor: %v", err) + return + } logProcessor.ProcessLogs(cnf, ctx) }) From f11a76504125286945cc5cc96353e1af3ade6389 Mon Sep 17 00:00:00 2001 From: Yorjander Hernandez Vergara Date: Wed, 18 Feb 2026 05:22:40 -0500 Subject: [PATCH 037/115] refactor[agent](collector): use fsnotify for config changes instead of polling --- agent/collector/configwatcher/watcher.go | 95 ++++++++++++++++++++++++ agent/collector/file/file.go | 25 ++----- agent/collector/netflow/netflow.go | 18 ++--- agent/collector/syslog/syslog.go | 19 +---- agent/go.mod | 1 + agent/go.sum | 2 + 6 files changed, 115 insertions(+), 45 deletions(-) create mode 100644 agent/collector/configwatcher/watcher.go diff --git a/agent/collector/configwatcher/watcher.go b/agent/collector/configwatcher/watcher.go new file mode 100644 index 000000000..dc1149682 --- /dev/null +++ b/agent/collector/configwatcher/watcher.go @@ -0,0 +1,95 @@ +// Package configwatcher provides a shared config file watcher using fsnotify. +package configwatcher + +import ( + "context" + "path/filepath" + "time" + + "github.com/fsnotify/fsnotify" + "github.com/utmstack/UTMStack/agent/config" + "github.com/utmstack/UTMStack/agent/utils" +) + +const ( + // FallbackInterval is used as a safety net in case fsnotify misses events + FallbackInterval = 5 * time.Minute +) + +// Watch monitors the collector config file for changes and calls onConfigChange +// when the file is modified. It also calls onConfigChange periodically as a fallback. +// This function blocks until ctx is cancelled. +func Watch(ctx context.Context, name string, onConfigChange func()) { + // Initial call + onConfigChange() + + // Set up fsnotify watcher + watcher, err := fsnotify.NewWatcher() + if err != nil { + utils.Logger.ErrorF("%s: failed to create fsnotify watcher: %v, falling back to polling", name, err) + runPollingFallback(ctx, name, onConfigChange) + return + } + defer watcher.Close() + + // Watch the directory containing the config file + configDir := filepath.Dir(config.CollectorFileName) + configBase := filepath.Base(config.CollectorFileName) + + if err := watcher.Add(configDir); err != nil { + utils.Logger.ErrorF("%s: failed to watch config directory: %v, falling back to polling", name, err) + runPollingFallback(ctx, name, onConfigChange) + return + } + + utils.Logger.Info("%s: watching config file for changes", name) + + // Fallback timer as safety net + fallbackTicker := time.NewTicker(FallbackInterval) + defer fallbackTicker.Stop() + + for { + select { + case <-ctx.Done(): + utils.Logger.Info("%s: stopping config watcher", name) + return + + case event, ok := <-watcher.Events: + if !ok { + return + } + if filepath.Base(event.Name) == configBase { + if event.Op&(fsnotify.Write|fsnotify.Create) != 0 { + utils.Logger.Info("%s: config file changed, reconciling", name) + onConfigChange() + } + } + + case err, ok := <-watcher.Errors: + if !ok { + return + } + utils.Logger.ErrorF("%s: fsnotify error: %v", name, err) + + case <-fallbackTicker.C: + onConfigChange() + } + } +} + +// runPollingFallback is used when fsnotify cannot be initialized. +func runPollingFallback(ctx context.Context, name string, onConfigChange func()) { + utils.Logger.Info("%s: using polling fallback", name) + ticker := time.NewTicker(FallbackInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + utils.Logger.Info("%s: stopping config watcher", name) + return + case <-ticker.C: + onConfigChange() + } + } +} diff --git a/agent/collector/file/file.go b/agent/collector/file/file.go index 4dccc1e2e..7083e3991 100644 --- a/agent/collector/file/file.go +++ b/agent/collector/file/file.go @@ -11,15 +11,13 @@ import ( "github.com/threatwinds/go-sdk/entities" "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/collector/configwatcher" "github.com/utmstack/UTMStack/agent/collector/schema" "github.com/utmstack/UTMStack/agent/config" "github.com/utmstack/UTMStack/agent/utils" ) -const ( - reconcileInterval = 10 * time.Second - pollInterval = 1 * time.Second -) +const pollInterval = 1 * time.Second // fileWatcher represents an active file being tailed. type fileWatcher struct { @@ -62,21 +60,14 @@ func (fc *FileCollector) Stop() { } } -// Start begins the reconciliation loop. It reads the collector config every -// 10 seconds and adjusts file watchers as needed. +// Start begins watching for configuration changes using fsnotify. +// It performs an initial reconciliation and then reacts to config file changes. func (fc *FileCollector) Start(ctx context.Context, queue chan *plugins.Log) { fc.queue = queue - for { - select { - case <-ctx.Done(): - utils.Logger.Info("file collector stopping due to context cancellation") - fc.Stop() - return - default: - fc.reconcile(ctx) - time.Sleep(reconcileInterval) - } - } + configwatcher.Watch(ctx, "file collector", func() { + fc.reconcile(ctx) + }) + fc.Stop() } func (fc *FileCollector) reconcile(ctx context.Context) { diff --git a/agent/collector/netflow/netflow.go b/agent/collector/netflow/netflow.go index d41386586..9e26e5628 100644 --- a/agent/collector/netflow/netflow.go +++ b/agent/collector/netflow/netflow.go @@ -17,15 +17,15 @@ import ( tehmaze "github.com/tehmaze/netflow" "github.com/tehmaze/netflow/session" "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/collector/configwatcher" "github.com/utmstack/UTMStack/agent/collector/schema" "github.com/utmstack/UTMStack/agent/config" "github.com/utmstack/UTMStack/agent/utils" ) const ( - reconcileInterval = 10 * time.Second cacheCleanupInterval = 5 * time.Minute - cacheTTL = 30 * time.Minute + cacheTTL = 30 * time.Minute ) // templateSystem implements netflow.NetFlowTemplateSystem for goflow2 @@ -122,7 +122,8 @@ func (nc *NetflowCollector) Stop() { nc.disablePort() } -// Start begins the reconciliation loop. +// Start begins watching for configuration changes using fsnotify. +// It performs an initial reconciliation and then reacts to config file changes. func (nc *NetflowCollector) Start(ctx context.Context, queue chan *plugins.Log) { nc.queue = queue @@ -147,16 +148,7 @@ func (nc *NetflowCollector) Start(ctx context.Context, queue chan *plugins.Log) } }() - for { - select { - case <-ctx.Done(): - utils.Logger.Info("netflow collector stopping due to context cancellation") - return - default: - nc.reconcile() - time.Sleep(reconcileInterval) - } - } + configwatcher.Watch(ctx, "netflow collector", nc.reconcile) } func (nc *NetflowCollector) reconcile() { diff --git a/agent/collector/syslog/syslog.go b/agent/collector/syslog/syslog.go index ba318febc..60435263f 100644 --- a/agent/collector/syslog/syslog.go +++ b/agent/collector/syslog/syslog.go @@ -6,13 +6,12 @@ import ( "time" "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/agent/collector/configwatcher" "github.com/utmstack/UTMStack/agent/collector/schema" "github.com/utmstack/UTMStack/agent/config" "github.com/utmstack/UTMStack/agent/utils" ) -const reconcileInterval = 10 * time.Second - // SyslogCollector manages all syslog instances. It reads the config file // periodically and reconciles port state internally. type SyslogCollector struct { @@ -41,21 +40,11 @@ func (sc *SyslogCollector) Stop() { } } -// Start begins the reconciliation loop. It reads the collector config every -// 10 seconds and adjusts listening ports as needed. This is the only entry -// point for syslog port management — no external code touches ports. +// Start begins watching for configuration changes using fsnotify. +// It performs an initial reconciliation and then reacts to config file changes. func (sc *SyslogCollector) Start(ctx context.Context, queue chan *plugins.Log) { sc.queue = queue - for { - select { - case <-ctx.Done(): - utils.Logger.Info("syslog collector stopping due to context cancellation") - return - default: - sc.reconcile() - time.Sleep(reconcileInterval) - } - } + configwatcher.Watch(ctx, "syslog collector", sc.reconcile) } func (sc *SyslogCollector) reconcile() { diff --git a/agent/go.mod b/agent/go.mod index aec39b5d0..b90c5ee3e 100644 --- a/agent/go.mod +++ b/agent/go.mod @@ -31,6 +31,7 @@ require ( github.com/cloudwego/base64x v0.1.6 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/elastic/go-windows v1.0.2 // indirect + github.com/fsnotify/fsnotify v1.9.0 // 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 diff --git a/agent/go.sum b/agent/go.sum index 9f727d900..cfb551e58 100644 --- a/agent/go.sum +++ b/agent/go.sum @@ -25,6 +25,8 @@ github.com/elastic/go-sysinfo v1.15.4 h1:A3zQcunCxik14MgXu39cXFXcIw2sFXZ0zL886ey github.com/elastic/go-sysinfo v1.15.4/go.mod h1:ZBVXmqS368dOn/jvijV/zHLfakWTYHBZPk3G244lHrU= github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI= github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 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= From 83c6dfea8d850b8010bc8c8cc52634f38973516e Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 18 Feb 2026 08:47:06 -0600 Subject: [PATCH 038/115] feat(agent): add shell field to UtmCommand for enhanced command execution context --- .../service/grpc/AgentManagerGrpc.java | 65 ++++----- .../utmstack/service/grpc/UtmCommand.java | 136 ++++++++++++++++++ .../service/grpc/UtmCommandOrBuilder.java | 12 ++ backend/src/main/proto/agent.proto | 1 + 4 files changed, 182 insertions(+), 32 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/grpc/AgentManagerGrpc.java b/backend/src/main/java/com/park/utmstack/service/grpc/AgentManagerGrpc.java index d48b04bf1..b72898c05 100644 --- a/backend/src/main/java/com/park/utmstack/service/grpc/AgentManagerGrpc.java +++ b/backend/src/main/java/com/park/utmstack/service/grpc/AgentManagerGrpc.java @@ -92,39 +92,40 @@ public static void registerAllExtensions( "\n\taddresses\030\016 \001(\t\"u\n\023BidirectionalStream" + "\022$\n\007command\030\001 \001(\0132\021.agent.UtmCommandH\000\022&" + "\n\006result\030\002 \001(\0132\024.agent.CommandResultH\000B\020" + - "\n\016stream_message\"\214\001\n\nUtmCommand\022\020\n\010agent" + + "\n\016stream_message\"\233\001\n\nUtmCommand\022\020\n\010agent" + "_id\030\001 \001(\t\022\017\n\007command\030\002 \001(\t\022\023\n\013executed_b" + "y\030\003 \001(\t\022\016\n\006cmd_id\030\004 \001(\t\022\023\n\013origin_type\030\005" + - " \001(\t\022\021\n\torigin_id\030\006 \001(\t\022\016\n\006reason\030\007 \001(\t\"" + - "r\n\rCommandResult\022\020\n\010agent_id\030\001 \001(\t\022\016\n\006re" + - "sult\030\002 \001(\t\022/\n\013executed_at\030\003 \001(\0132\032.google" + - ".protobuf.Timestamp\022\016\n\006cmd_id\030\004 \001(\t\"N\n\032L" + - "istAgentsCommandsResponse\022!\n\004rows\030\001 \003(\0132" + - "\023.agent.AgentCommand\022\r\n\005total\030\002 \001(\005\"\261\002\n\014" + - "AgentCommand\022.\n\ncreated_at\030\001 \001(\0132\032.googl" + - "e.protobuf.Timestamp\022.\n\nupdated_at\030\002 \001(\013" + - "2\032.google.protobuf.Timestamp\022\020\n\010agent_id" + - "\030\003 \001(\r\022\017\n\007command\030\004 \001(\t\0221\n\016command_statu" + - "s\030\005 \001(\0162\031.agent.AgentCommandStatus\022\016\n\006re" + - "sult\030\006 \001(\t\022\023\n\013executed_by\030\007 \001(\t\022\016\n\006cmd_i" + - "d\030\010 \001(\t\022\016\n\006reason\030\t \001(\t\022\023\n\013origin_type\030\n" + - " \001(\t\022\021\n\torigin_id\030\013 \001(\t*W\n\022AgentCommandS" + - "tatus\022\020\n\014NOT_EXECUTED\020\000\022\t\n\005QUEUE\020\001\022\013\n\007PE" + - "NDING\020\002\022\014\n\010EXECUTED\020\003\022\t\n\005ERROR\020\0042\234\003\n\014Age" + - "ntService\0229\n\013UpdateAgent\022\023.agent.AgentRe" + - "quest\032\023.agent.AuthResponse\"\000\022;\n\rRegister" + - "Agent\022\023.agent.AgentRequest\032\023.agent.AuthR" + - "esponse\"\000\022:\n\013DeleteAgent\022\024.agent.DeleteR" + - "equest\032\023.agent.AuthResponse\"\000\022=\n\nListAge" + - "nts\022\022.agent.ListRequest\032\031.agent.ListAgen" + - "tsResponse\"\000\022K\n\013AgentStream\022\032.agent.Bidi" + - "rectionalStream\032\032.agent.BidirectionalStr" + - "eam\"\000(\0010\001\022L\n\021ListAgentCommands\022\022.agent.L" + - "istRequest\032!.agent.ListAgentsCommandsRes" + - "ponse\"\0002O\n\014PanelService\022?\n\016ProcessComman" + - "d\022\021.agent.UtmCommand\032\024.agent.CommandResu" + - "lt\"\000(\0010\001B7\n\036com.park.utmstack.service.gr" + - "pcB\020AgentManagerGrpcP\001\210\001\001b\006proto3" + " \001(\t\022\021\n\torigin_id\030\006 \001(\t\022\016\n\006reason\030\007 \001(\t\022" + + "\r\n\005shell\030\010 \001(\t\"r\n\rCommandResult\022\020\n\010agent" + + "_id\030\001 \001(\t\022\016\n\006result\030\002 \001(\t\022/\n\013executed_at" + + "\030\003 \001(\0132\032.google.protobuf.Timestamp\022\016\n\006cm" + + "d_id\030\004 \001(\t\"N\n\032ListAgentsCommandsResponse" + + "\022!\n\004rows\030\001 \003(\0132\023.agent.AgentCommand\022\r\n\005t" + + "otal\030\002 \001(\005\"\261\002\n\014AgentCommand\022.\n\ncreated_a" + + "t\030\001 \001(\0132\032.google.protobuf.Timestamp\022.\n\nu" + + "pdated_at\030\002 \001(\0132\032.google.protobuf.Timest" + + "amp\022\020\n\010agent_id\030\003 \001(\r\022\017\n\007command\030\004 \001(\t\0221" + + "\n\016command_status\030\005 \001(\0162\031.agent.AgentComm" + + "andStatus\022\016\n\006result\030\006 \001(\t\022\023\n\013executed_by" + + "\030\007 \001(\t\022\016\n\006cmd_id\030\010 \001(\t\022\016\n\006reason\030\t \001(\t\022\023" + + "\n\013origin_type\030\n \001(\t\022\021\n\torigin_id\030\013 \001(\t*W" + + "\n\022AgentCommandStatus\022\020\n\014NOT_EXECUTED\020\000\022\t" + + "\n\005QUEUE\020\001\022\013\n\007PENDING\020\002\022\014\n\010EXECUTED\020\003\022\t\n\005" + + "ERROR\020\0042\234\003\n\014AgentService\0229\n\013UpdateAgent\022" + + "\023.agent.AgentRequest\032\023.agent.AuthRespons" + + "e\"\000\022;\n\rRegisterAgent\022\023.agent.AgentReques" + + "t\032\023.agent.AuthResponse\"\000\022:\n\013DeleteAgent\022" + + "\024.agent.DeleteRequest\032\023.agent.AuthRespon" + + "se\"\000\022=\n\nListAgents\022\022.agent.ListRequest\032\031" + + ".agent.ListAgentsResponse\"\000\022K\n\013AgentStre" + + "am\022\032.agent.BidirectionalStream\032\032.agent.B" + + "idirectionalStream\"\000(\0010\001\022L\n\021ListAgentCom" + + "mands\022\022.agent.ListRequest\032!.agent.ListAg" + + "entsCommandsResponse\"\0002O\n\014PanelService\022?" + + "\n\016ProcessCommand\022\021.agent.UtmCommand\032\024.ag" + + "ent.CommandResult\"\000(\0010\001B7\n\036com.park.utms" + + "tack.service.grpcB\020AgentManagerGrpcP\001\210\001\001" + + "b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -161,7 +162,7 @@ public static void registerAllExtensions( internal_static_agent_UtmCommand_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_agent_UtmCommand_descriptor, - new java.lang.String[] { "AgentId", "Command", "ExecutedBy", "CmdId", "OriginType", "OriginId", "Reason", }); + new java.lang.String[] { "AgentId", "Command", "ExecutedBy", "CmdId", "OriginType", "OriginId", "Reason", "Shell", }); internal_static_agent_CommandResult_descriptor = getDescriptor().getMessageTypes().get(5); internal_static_agent_CommandResult_fieldAccessorTable = new diff --git a/backend/src/main/java/com/park/utmstack/service/grpc/UtmCommand.java b/backend/src/main/java/com/park/utmstack/service/grpc/UtmCommand.java index 83b39c5a5..84c4f287c 100644 --- a/backend/src/main/java/com/park/utmstack/service/grpc/UtmCommand.java +++ b/backend/src/main/java/com/park/utmstack/service/grpc/UtmCommand.java @@ -34,6 +34,7 @@ private UtmCommand() { originType_ = ""; originId_ = ""; reason_ = ""; + shell_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -322,6 +323,45 @@ public java.lang.String getReason() { } } + public static final int SHELL_FIELD_NUMBER = 8; + @SuppressWarnings("serial") + private volatile java.lang.Object shell_ = ""; + /** + * string shell = 8; + * @return The shell. + */ + @java.lang.Override + public java.lang.String getShell() { + java.lang.Object ref = shell_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + shell_ = s; + return s; + } + } + /** + * string shell = 8; + * @return The bytes for shell. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getShellBytes() { + java.lang.Object ref = shell_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + shell_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -357,6 +397,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(reason_)) { com.google.protobuf.GeneratedMessage.writeString(output, 7, reason_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(shell_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 8, shell_); + } getUnknownFields().writeTo(output); } @@ -387,6 +430,9 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(reason_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(7, reason_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(shell_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(8, shell_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -416,6 +462,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getOriginId())) return false; if (!getReason() .equals(other.getReason())) return false; + if (!getShell() + .equals(other.getShell())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -441,6 +489,8 @@ public int hashCode() { hash = (53 * hash) + getOriginId().hashCode(); hash = (37 * hash) + REASON_FIELD_NUMBER; hash = (53 * hash) + getReason().hashCode(); + hash = (37 * hash) + SHELL_FIELD_NUMBER; + hash = (53 * hash) + getShell().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -579,6 +629,7 @@ public Builder clear() { originType_ = ""; originId_ = ""; reason_ = ""; + shell_ = ""; return this; } @@ -633,6 +684,9 @@ private void buildPartial0(com.park.utmstack.service.grpc.UtmCommand result) { if (((from_bitField0_ & 0x00000040) != 0)) { result.reason_ = reason_; } + if (((from_bitField0_ & 0x00000080) != 0)) { + result.shell_ = shell_; + } } @java.lang.Override @@ -682,6 +736,11 @@ public Builder mergeFrom(com.park.utmstack.service.grpc.UtmCommand other) { bitField0_ |= 0x00000040; onChanged(); } + if (!other.getShell().isEmpty()) { + shell_ = other.shell_; + bitField0_ |= 0x00000080; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -743,6 +802,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000040; break; } // case 58 + case 66: { + shell_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000080; + break; + } // case 66 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -1264,6 +1328,78 @@ public Builder setReasonBytes( return this; } + private java.lang.Object shell_ = ""; + /** + * string shell = 8; + * @return The shell. + */ + public java.lang.String getShell() { + java.lang.Object ref = shell_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + shell_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string shell = 8; + * @return The bytes for shell. + */ + public com.google.protobuf.ByteString + getShellBytes() { + java.lang.Object ref = shell_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + shell_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string shell = 8; + * @param value The shell to set. + * @return This builder for chaining. + */ + public Builder setShell( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + shell_ = value; + bitField0_ |= 0x00000080; + onChanged(); + return this; + } + /** + * string shell = 8; + * @return This builder for chaining. + */ + public Builder clearShell() { + shell_ = getDefaultInstance().getShell(); + bitField0_ = (bitField0_ & ~0x00000080); + onChanged(); + return this; + } + /** + * string shell = 8; + * @param value The bytes for shell to set. + * @return This builder for chaining. + */ + public Builder setShellBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + shell_ = value; + bitField0_ |= 0x00000080; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:agent.UtmCommand) } diff --git a/backend/src/main/java/com/park/utmstack/service/grpc/UtmCommandOrBuilder.java b/backend/src/main/java/com/park/utmstack/service/grpc/UtmCommandOrBuilder.java index 7a7305596..215ccc7ad 100644 --- a/backend/src/main/java/com/park/utmstack/service/grpc/UtmCommandOrBuilder.java +++ b/backend/src/main/java/com/park/utmstack/service/grpc/UtmCommandOrBuilder.java @@ -92,4 +92,16 @@ public interface UtmCommandOrBuilder extends */ com.google.protobuf.ByteString getReasonBytes(); + + /** + * string shell = 8; + * @return The shell. + */ + java.lang.String getShell(); + /** + * string shell = 8; + * @return The bytes for shell. + */ + com.google.protobuf.ByteString + getShellBytes(); } diff --git a/backend/src/main/proto/agent.proto b/backend/src/main/proto/agent.proto index c9dc990ef..d5ee2df98 100644 --- a/backend/src/main/proto/agent.proto +++ b/backend/src/main/proto/agent.proto @@ -82,6 +82,7 @@ message UtmCommand { string origin_type = 5; string origin_id = 6; string reason = 7; + string shell = 8; } message CommandResult { From 3f3de424d852a30d7961644dd1ab0683adfa3782 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Wed, 18 Feb 2026 17:58:03 +0300 Subject: [PATCH 039/115] feat(fortinet): update fortinet filter --- filters/fortinet/fortinet.yml | 922 +++++++++++++++++++++++++++++++++- 1 file changed, 921 insertions(+), 1 deletion(-) diff --git a/filters/fortinet/fortinet.yml b/filters/fortinet/fortinet.yml index b973d3c23..f2cd81998 100644 --- a/filters/fortinet/fortinet.yml +++ b/filters/fortinet/fortinet.yml @@ -1,4 +1,4 @@ -# Fortinet firewall module filter, version 3.0.1 +# Fortinet firewall module filter, version 3.0.2 # Based in docs and samples provided # # Documentations @@ -101,6 +101,66 @@ pipeline: substring: '>' fields: - log.priority + - trim: + function: prefix + substring: '"' + fields: + - log.devname + - log.devid + - log.logid + - log.type + - log.subtype + - log.eventtype + - log.level + - log.devid + - log.vd + - log.srccountry + - log.dstcountry + - log.srcintf + - log.srcintfrole + - log.dstintf + - log.dstintfrole + - log.direction + - log.poluuid + - log.policytype + - action + - log.appcat + - log.app + - log.hostname + - log.url + - log.apprisk + - log.scertcname + - log.scertissuer + - trim: + function: suffix + substring: '"' + fields: + - log.devname + - log.devid + - log.logid + - log.type + - log.subtype + - log.eventtype + - log.level + - log.devid + - log.vd + - log.srccountry + - log.dstcountry + - log.srcintf + - log.srcintfrole + - log.dstintf + - log.dstintfrole + - log.direction + - log.poluuid + - log.policytype + - action + - log.appcat + - log.app + - log.hostname + - log.url + - log.apprisk + - log.scertcname + - log.scertissuer # Adding geolocation to origin.ip - dynamic: @@ -118,6 +178,866 @@ pipeline: destination: target.geolocation where: exists("target.ip") + # Adding protocol field based on IANA protocol numbers + - add: + function: "string" + params: + key: protocol + value: "HOPOPT" + where: equals("log.proto", "0") + - add: + function: "string" + params: + key: protocol + value: "ICMP" + where: equals("log.proto", "1") + - add: + function: "string" + params: + key: protocol + value: "IGMP" + where: equals("log.proto", "2") + - add: + function: "string" + params: + key: protocol + value: "GGP" + where: equals("log.proto", "3") + - add: + function: "string" + params: + key: protocol + value: "IP-in-IP" + where: equals("log.proto", "4") + - add: + function: "string" + params: + key: protocol + value: "ST" + where: equals("log.proto", "5") + - add: + function: "string" + params: + key: protocol + value: "TCP" + where: equals("log.proto", "6") + - add: + function: "string" + params: + key: protocol + value: "CBT" + where: equals("log.proto", "7") + - add: + function: "string" + params: + key: protocol + value: "EGP" + where: equals("log.proto", "8") + - add: + function: "string" + params: + key: protocol + value: "IGP" + where: equals("log.proto", "9") + - add: + function: "string" + params: + key: protocol + value: "BBN-RCC-MON" + where: equals("log.proto", "10") + - add: + function: "string" + params: + key: protocol + value: "NVP-II" + where: equals("log.proto", "11") + - add: + function: "string" + params: + key: protocol + value: "PUP" + where: equals("log.proto", "12") + - add: + function: "string" + params: + key: protocol + value: "ARGUS" + where: equals("log.proto", "13") + - add: + function: "string" + params: + key: protocol + value: "EMCON" + where: equals("log.proto", "14") + - add: + function: "string" + params: + key: protocol + value: "XNET" + where: equals("log.proto", "15") + - add: + function: "string" + params: + key: protocol + value: "CHAOS" + where: equals("log.proto", "16") + - add: + function: "string" + params: + key: protocol + value: "UDP" + where: equals("log.proto", "17") + - add: + function: "string" + params: + key: protocol + value: "MUX" + where: equals("log.proto", "18") + - add: + function: "string" + params: + key: protocol + value: "DCN-MEAS" + where: equals("log.proto", "19") + - add: + function: "string" + params: + key: protocol + value: "HMP" + where: equals("log.proto", "20") + - add: + function: "string" + params: + key: protocol + value: "PRM" + where: equals("log.proto", "21") + - add: + function: "string" + params: + key: protocol + value: "XNS-IDP" + where: equals("log.proto", "22") + - add: + function: "string" + params: + key: protocol + value: "TRUNK-1" + where: equals("log.proto", "23") + - add: + function: "string" + params: + key: protocol + value: "TRUNK-2" + where: equals("log.proto", "24") + - add: + function: "string" + params: + key: protocol + value: "LEAF-1" + where: equals("log.proto", "25") + - add: + function: "string" + params: + key: protocol + value: "LEAF-2" + where: equals("log.proto", "26") + - add: + function: "string" + params: + key: protocol + value: "RDP" + where: equals("log.proto", "27") + - add: + function: "string" + params: + key: protocol + value: "IRTP" + where: equals("log.proto", "28") + - add: + function: "string" + params: + key: protocol + value: "ISO-TP4" + where: equals("log.proto", "29") + - add: + function: "string" + params: + key: protocol + value: "NETBLT" + where: equals("log.proto", "30") + - add: + function: "string" + params: + key: protocol + value: "MFE-NSP" + where: equals("log.proto", "31") + - add: + function: "string" + params: + key: protocol + value: "MERIT-INP" + where: equals("log.proto", "32") + - add: + function: "string" + params: + key: protocol + value: "DCCP" + where: equals("log.proto", "33") + - add: + function: "string" + params: + key: protocol + value: "3PC" + where: equals("log.proto", "34") + - add: + function: "string" + params: + key: protocol + value: "IDPR" + where: equals("log.proto", "35") + - add: + function: "string" + params: + key: protocol + value: "XTP" + where: equals("log.proto", "36") + - add: + function: "string" + params: + key: protocol + value: "DDP" + where: equals("log.proto", "37") + - add: + function: "string" + params: + key: protocol + value: "IDPR-CMTP" + where: equals("log.proto", "38") + - add: + function: "string" + params: + key: protocol + value: "TP++" + where: equals("log.proto", "39") + - add: + function: "string" + params: + key: protocol + value: "IL" + where: equals("log.proto", "40") + - add: + function: "string" + params: + key: protocol + value: "IPv6" + where: equals("log.proto", "41") + - add: + function: "string" + params: + key: protocol + value: "SDRP" + where: equals("log.proto", "42") + - add: + function: "string" + params: + key: protocol + value: "IPv6-Route" + where: equals("log.proto", "43") + - add: + function: "string" + params: + key: protocol + value: "IPv6-Frag" + where: equals("log.proto", "44") + - add: + function: "string" + params: + key: protocol + value: "IDRP" + where: equals("log.proto", "45") + - add: + function: "string" + params: + key: protocol + value: "RSVP" + where: equals("log.proto", "46") + - add: + function: "string" + params: + key: protocol + value: "GRE" + where: equals("log.proto", "47") + - add: + function: "string" + params: + key: protocol + value: "DSR" + where: equals("log.proto", "48") + - add: + function: "string" + params: + key: protocol + value: "BNA" + where: equals("log.proto", "49") + - add: + function: "string" + params: + key: protocol + value: "ESP" + where: equals("log.proto", "50") + - add: + function: "string" + params: + key: protocol + value: "AH" + where: equals("log.proto", "51") + - add: + function: "string" + params: + key: protocol + value: "I-NLSP" + where: equals("log.proto", "52") + - add: + function: "string" + params: + key: protocol + value: "SwIPe" + where: equals("log.proto", "53") + - add: + function: "string" + params: + key: protocol + value: "NARP" + where: equals("log.proto", "54") + - add: + function: "string" + params: + key: protocol + value: "MOBILE" + where: equals("log.proto", "55") + - add: + function: "string" + params: + key: protocol + value: "TLSP" + where: equals("log.proto", "56") + - add: + function: "string" + params: + key: protocol + value: "SKIP" + where: equals("log.proto", "57") + - add: + function: "string" + params: + key: protocol + value: "IPv6-ICMP" + where: equals("log.proto", "58") + - add: + function: "string" + params: + key: protocol + value: "IPv6-NoNxt" + where: equals("log.proto", "59") + - add: + function: "string" + params: + key: protocol + value: "IPv6-Opts" + where: equals("log.proto", "60") + - add: + function: "string" + params: + key: protocol + value: "CFTP" + where: equals("log.proto", "62") + - add: + function: "string" + params: + key: protocol + value: "SAT-EXPAK" + where: equals("log.proto", "64") + - add: + function: "string" + params: + key: protocol + value: "KRYPTOLAN" + where: equals("log.proto", "65") + - add: + function: "string" + params: + key: protocol + value: "RVD" + where: equals("log.proto", "66") + - add: + function: "string" + params: + key: protocol + value: "IPPC" + where: equals("log.proto", "67") + - add: + function: "string" + params: + key: protocol + value: "SAT-MON" + where: equals("log.proto", "69") + - add: + function: "string" + params: + key: protocol + value: "VISA" + where: equals("log.proto", "70") + - add: + function: "string" + params: + key: protocol + value: "IPCU" + where: equals("log.proto", "71") + - add: + function: "string" + params: + key: protocol + value: "CPNX" + where: equals("log.proto", "72") + - add: + function: "string" + params: + key: protocol + value: "CPHB" + where: equals("log.proto", "73") + - add: + function: "string" + params: + key: protocol + value: "WSN" + where: equals("log.proto", "74") + - add: + function: "string" + params: + key: protocol + value: "PVP" + where: equals("log.proto", "75") + - add: + function: "string" + params: + key: protocol + value: "BR-SAT-MON" + where: equals("log.proto", "76") + - add: + function: "string" + params: + key: protocol + value: "SUN-ND" + where: equals("log.proto", "77") + - add: + function: "string" + params: + key: protocol + value: "WB-MON" + where: equals("log.proto", "78") + - add: + function: "string" + params: + key: protocol + value: "WB-EXPAK" + where: equals("log.proto", "79") + - add: + function: "string" + params: + key: protocol + value: "ISO-IP" + where: equals("log.proto", "80") + - add: + function: "string" + params: + key: protocol + value: "VMTP" + where: equals("log.proto", "81") + - add: + function: "string" + params: + key: protocol + value: "SECURE-VMTP" + where: equals("log.proto", "82") + - add: + function: "string" + params: + key: protocol + value: "VINES" + where: equals("log.proto", "83") + - add: + function: "string" + params: + key: protocol + value: "IPTM" + where: equals("log.proto", "84") + - add: + function: "string" + params: + key: protocol + value: "NSFNET-IGP" + where: equals("log.proto", "85") + - add: + function: "string" + params: + key: protocol + value: "DGP" + where: equals("log.proto", "86") + - add: + function: "string" + params: + key: protocol + value: "TCF" + where: equals("log.proto", "87") + - add: + function: "string" + params: + key: protocol + value: "EIGRP" + where: equals("log.proto", "88") + - add: + function: "string" + params: + key: protocol + value: "OSPF" + where: equals("log.proto", "89") + - add: + function: "string" + params: + key: protocol + value: "Sprite-RPC" + where: equals("log.proto", "90") + - add: + function: "string" + params: + key: protocol + value: "LARP" + where: equals("log.proto", "91") + - add: + function: "string" + params: + key: protocol + value: "MTP" + where: equals("log.proto", "92") + - add: + function: "string" + params: + key: protocol + value: "AX.25" + where: equals("log.proto", "93") + - add: + function: "string" + params: + key: protocol + value: "OS" + where: equals("log.proto", "94") + - add: + function: "string" + params: + key: protocol + value: "MICP" + where: equals("log.proto", "95") + - add: + function: "string" + params: + key: protocol + value: "SCC-SP" + where: equals("log.proto", "96") + - add: + function: "string" + params: + key: protocol + value: "ETHERIP" + where: equals("log.proto", "97") + - add: + function: "string" + params: + key: protocol + value: "ENCAP" + where: equals("log.proto", "98") + - add: + function: "string" + params: + key: protocol + value: "GMTP" + where: equals("log.proto", "100") + - add: + function: "string" + params: + key: protocol + value: "IFMP" + where: equals("log.proto", "101") + - add: + function: "string" + params: + key: protocol + value: "PNNI" + where: equals("log.proto", "102") + - add: + function: "string" + params: + key: protocol + value: "PIM" + where: equals("log.proto", "103") + - add: + function: "string" + params: + key: protocol + value: "ARIS" + where: equals("log.proto", "104") + - add: + function: "string" + params: + key: protocol + value: "SCPS" + where: equals("log.proto", "105") + - add: + function: "string" + params: + key: protocol + value: "QNX" + where: equals("log.proto", "106") + - add: + function: "string" + params: + key: protocol + value: "A/N" + where: equals("log.proto", "107") + - add: + function: "string" + params: + key: protocol + value: "IPComp" + where: equals("log.proto", "108") + - add: + function: "string" + params: + key: protocol + value: "SNP" + where: equals("log.proto", "109") + - add: + function: "string" + params: + key: protocol + value: "Compaq-Peer" + where: equals("log.proto", "110") + - add: + function: "string" + params: + key: protocol + value: "IPX-in-IP" + where: equals("log.proto", "111") + - add: + function: "string" + params: + key: protocol + value: "VRRP" + where: equals("log.proto", "112") + - add: + function: "string" + params: + key: protocol + value: "PGM" + where: equals("log.proto", "113") + - add: + function: "string" + params: + key: protocol + value: "L2TP" + where: equals("log.proto", "115") + - add: + function: "string" + params: + key: protocol + value: "DDX" + where: equals("log.proto", "116") + - add: + function: "string" + params: + key: protocol + value: "IATP" + where: equals("log.proto", "117") + - add: + function: "string" + params: + key: protocol + value: "STP" + where: equals("log.proto", "118") + - add: + function: "string" + params: + key: protocol + value: "SRP" + where: equals("log.proto", "119") + - add: + function: "string" + params: + key: protocol + value: "UTI" + where: equals("log.proto", "120") + - add: + function: "string" + params: + key: protocol + value: "SMP" + where: equals("log.proto", "121") + - add: + function: "string" + params: + key: protocol + value: "SM" + where: equals("log.proto", "122") + - add: + function: "string" + params: + key: protocol + value: "PTP" + where: equals("log.proto", "123") + - add: + function: "string" + params: + key: protocol + value: "IS-IS" + where: equals("log.proto", "124") + - add: + function: "string" + params: + key: protocol + value: "FIRE" + where: equals("log.proto", "125") + - add: + function: "string" + params: + key: protocol + value: "CRTP" + where: equals("log.proto", "126") + - add: + function: "string" + params: + key: protocol + value: "CRUDP" + where: equals("log.proto", "127") + - add: + function: "string" + params: + key: protocol + value: "SSCOPMCE" + where: equals("log.proto", "128") + - add: + function: "string" + params: + key: protocol + value: "IPLT" + where: equals("log.proto", "129") + - add: + function: "string" + params: + key: protocol + value: "SPS" + where: equals("log.proto", "130") + - add: + function: "string" + params: + key: protocol + value: "PIPE" + where: equals("log.proto", "131") + - add: + function: "string" + params: + key: protocol + value: "SCTP" + where: equals("log.proto", "132") + - add: + function: "string" + params: + key: protocol + value: "FC" + where: equals("log.proto", "133") + - add: + function: "string" + params: + key: protocol + value: "RSVP-E2E-IGNORE" + where: equals("log.proto", "134") + - add: + function: "string" + params: + key: protocol + value: "Mobility-Header" + where: equals("log.proto", "135") + - add: + function: "string" + params: + key: protocol + value: "UDPLite" + where: equals("log.proto", "136") + - add: + function: "string" + params: + key: protocol + value: "MPLS-in-IP" + where: equals("log.proto", "137") + - add: + function: "string" + params: + key: protocol + value: "manet" + where: equals("log.proto", "138") + - add: + function: "string" + params: + key: protocol + value: "HIP" + where: equals("log.proto", "139") + - add: + function: "string" + params: + key: protocol + value: "Shim6" + where: equals("log.proto", "140") + - add: + function: "string" + params: + key: protocol + value: "WESP" + where: equals("log.proto", "141") + - add: + function: "string" + params: + key: protocol + value: "ROHC" + where: equals("log.proto", "142") + - add: + function: "string" + params: + key: protocol + value: "Ethernet" + where: equals("log.proto", "143") + - add: + function: "string" + params: + key: protocol + value: "AGGFRAG" + where: equals("log.proto", "144") + - add: + function: "string" + params: + key: protocol + value: "NSH" + where: equals("log.proto", "145") + - add: + function: "string" + params: + key: protocol + value: "Homa" + where: equals("log.proto", "146") + - add: + function: "string" + params: + key: protocol + value: "BIT-EMU" + where: equals("log.proto", "147") + # Removing unused fields - delete: fields: From aea60bbe574e7e7941ed761a9d0ccd85a41f12e9 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 18 Feb 2026 14:06:10 -0600 Subject: [PATCH 040/115] chore(cleanup): remove unused integrations (Redis, Nginx, PostgreSQL, Apache, MySQL, MongoDB, Elastic, Logstash, Kibana, Kafka, NATS, Traefik, Audit, HAP, IIS, OSQuery) --- .../20260218001_remove_redis_integration.xml | 40 +++++++++++++++++++ .../20260218002_remove_nginx_integration.xml | 40 +++++++++++++++++++ ...60218003_remove_postgresql_integration.xml | 40 +++++++++++++++++++ .../20260218004_remove_apache_integration.xml | 40 +++++++++++++++++++ .../20260218005_remove_mysql_integration.xml | 40 +++++++++++++++++++ ...20260218006_remove_mongodb_integration.xml | 40 +++++++++++++++++++ ...20260218007_remove_elastic_integration.xml | 40 +++++++++++++++++++ ...0260218008_remove_logstash_integration.xml | 40 +++++++++++++++++++ .../20260218009_remove_kibana_integration.xml | 40 +++++++++++++++++++ .../20260218010_remove_kafka_integration.xml | 40 +++++++++++++++++++ .../20260218011_remove_nats_integration.xml | 40 +++++++++++++++++++ ...20260218012_remove_traefik_integration.xml | 40 +++++++++++++++++++ .../20260218013_remove_audit_integration.xml | 40 +++++++++++++++++++ .../20260218014_remove_hap_integration.xml | 40 +++++++++++++++++++ .../20260218015_remove_iis_integration.xml | 40 +++++++++++++++++++ ...0260218016_remove_os_query_integration.xml | 40 +++++++++++++++++++ .../resources/config/liquibase/master.xml | 32 +++++++++++++++ 17 files changed, 672 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218001_remove_redis_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218002_remove_nginx_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218003_remove_postgresql_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218004_remove_apache_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218005_remove_mysql_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218006_remove_mongodb_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218007_remove_elastic_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218008_remove_logstash_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218009_remove_kibana_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218010_remove_kafka_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218011_remove_nats_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218012_remove_traefik_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218013_remove_audit_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218014_remove_hap_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218015_remove_iis_integration.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260218016_remove_os_query_integration.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218001_remove_redis_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218001_remove_redis_integration.xml new file mode 100644 index 000000000..56b04a54a --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218001_remove_redis_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_redis(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218002_remove_nginx_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218002_remove_nginx_integration.xml new file mode 100644 index 000000000..92547ba6d --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218002_remove_nginx_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_nginx(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218003_remove_postgresql_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218003_remove_postgresql_integration.xml new file mode 100644 index 000000000..ce4554d1e --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218003_remove_postgresql_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_postgresql(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218004_remove_apache_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218004_remove_apache_integration.xml new file mode 100644 index 000000000..0d9c2acea --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218004_remove_apache_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_apache(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218005_remove_mysql_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218005_remove_mysql_integration.xml new file mode 100644 index 000000000..9b23fd9f0 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218005_remove_mysql_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_mysql(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218006_remove_mongodb_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218006_remove_mongodb_integration.xml new file mode 100644 index 000000000..2f2b334d0 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218006_remove_mongodb_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_mongodb(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218007_remove_elastic_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218007_remove_elastic_integration.xml new file mode 100644 index 000000000..78c192062 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218007_remove_elastic_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_elasticsearch(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218008_remove_logstash_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218008_remove_logstash_integration.xml new file mode 100644 index 000000000..700fd1663 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218008_remove_logstash_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_logstash(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218009_remove_kibana_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218009_remove_kibana_integration.xml new file mode 100644 index 000000000..4a9b51019 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218009_remove_kibana_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_kibana(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218010_remove_kafka_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218010_remove_kafka_integration.xml new file mode 100644 index 000000000..bddee7300 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218010_remove_kafka_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_kafka(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218011_remove_nats_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218011_remove_nats_integration.xml new file mode 100644 index 000000000..920edef51 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218011_remove_nats_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_nats(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218012_remove_traefik_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218012_remove_traefik_integration.xml new file mode 100644 index 000000000..d463ee01b --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218012_remove_traefik_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_traefik(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218013_remove_audit_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218013_remove_audit_integration.xml new file mode 100644 index 000000000..235698d86 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218013_remove_audit_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_ad_audit(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218014_remove_hap_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218014_remove_hap_integration.xml new file mode 100644 index 000000000..07ff2c475 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218014_remove_hap_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_hap(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218015_remove_iis_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218015_remove_iis_integration.xml new file mode 100644 index 000000000..2b691f116 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218015_remove_iis_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_iis(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260218016_remove_os_query_integration.xml b/backend/src/main/resources/config/liquibase/changelog/20260218016_remove_os_query_integration.xml new file mode 100644 index 000000000..d5c178137 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260218016_remove_os_query_integration.xml @@ -0,0 +1,40 @@ + + + + + + + + + DROP FUNCTION IF EXISTS register_integration_osquery(integer); + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 8fd4434f5..7a162fe6e 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -433,6 +433,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 9b75b97309b485d694564bcfe943ba5c1a0c19bd Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 18 Feb 2026 14:36:07 -0600 Subject: [PATCH 041/115] chore(cleanup): remove integrations (Redis, Nginx, PostgreSQL, Apache, MySQL, MongoDB, Elastic, Logstash, Kibana, Kafka, NATS, Traefik, Audit, HAP, IIS, OSQuery) --- .../factory/impl/ModuleApache.java | 48 ------------------- .../factory/impl/ModuleAuditD.java | 48 ------------------- .../factory/impl/ModuleElasticsearch.java | 48 ------------------- .../factory/impl/ModuleHaProxy.java | 48 ------------------- .../factory/impl/ModuleIIS.java | 48 ------------------- .../factory/impl/ModuleKafka.java | 48 ------------------- .../factory/impl/ModuleKibana.java | 48 ------------------- .../factory/impl/ModuleLogstash.java | 48 ------------------- .../factory/impl/ModuleMongoDb.java | 48 ------------------- .../factory/impl/ModuleMySql.java | 48 ------------------- .../factory/impl/ModuleNats.java | 48 ------------------- .../factory/impl/ModuleNginx.java | 48 ------------------- .../factory/impl/ModuleOsQuery.java | 48 ------------------- .../factory/impl/ModulePostgreSql.java | 48 ------------------- .../factory/impl/ModuleRedis.java | 48 ------------------- .../factory/impl/ModuleTraefik.java | 48 ------------------- 16 files changed, 768 deletions(-) delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleApache.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleAuditD.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleElasticsearch.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleHaProxy.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleIIS.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleKafka.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleKibana.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleLogstash.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleMongoDb.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleMySql.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleNats.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleNginx.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleOsQuery.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModulePostgreSql.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleRedis.java delete mode 100644 backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleTraefik.java diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleApache.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleApache.java deleted file mode 100644 index 99926b557..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleApache.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleApache implements IModule { - private static final String CLASSNAME = "ModuleApache"; - - private final UtmModuleService moduleService; - - public ModuleApache(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.APACHE); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.APACHE; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleAuditD.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleAuditD.java deleted file mode 100644 index f284c3cd0..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleAuditD.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleAuditD implements IModule { - private static final String CLASSNAME = "ModuleAuditD"; - - private final UtmModuleService moduleService; - - public ModuleAuditD(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.AUDITD); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.AUDITD; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleElasticsearch.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleElasticsearch.java deleted file mode 100644 index 2027641cb..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleElasticsearch.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleElasticsearch implements IModule { - private static final String CLASSNAME = "ModuleElasticsearch"; - - private final UtmModuleService moduleService; - - public ModuleElasticsearch(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.ELASTICSEARCH); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.ELASTICSEARCH; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleHaProxy.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleHaProxy.java deleted file mode 100644 index 82af81dbc..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleHaProxy.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleHaProxy implements IModule { - private static final String CLASSNAME = "ModuleHaProxy"; - - private final UtmModuleService moduleService; - - public ModuleHaProxy(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.HAPROXY); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.HAPROXY; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleIIS.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleIIS.java deleted file mode 100644 index ae40db0a2..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleIIS.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleIIS implements IModule { - private static final String CLASSNAME = "ModuleIIS"; - - private final UtmModuleService moduleService; - - public ModuleIIS(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.IIS); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.IIS; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleKafka.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleKafka.java deleted file mode 100644 index fb79ed7bb..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleKafka.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleKafka implements IModule { - private static final String CLASSNAME = "ModuleKafka"; - - private final UtmModuleService moduleService; - - public ModuleKafka(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.KAFKA); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.KAFKA; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleKibana.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleKibana.java deleted file mode 100644 index 37a6b8583..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleKibana.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleKibana implements IModule { - private static final String CLASSNAME = "ModuleKibana"; - - private final UtmModuleService moduleService; - - public ModuleKibana(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.KIBANA); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.KIBANA; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleLogstash.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleLogstash.java deleted file mode 100644 index cef8dde4f..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleLogstash.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleLogstash implements IModule { - private static final String CLASSNAME = "ModuleLogstash"; - - private final UtmModuleService moduleService; - - public ModuleLogstash(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.LOGSTASH); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.LOGSTASH; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleMongoDb.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleMongoDb.java deleted file mode 100644 index 66e55819a..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleMongoDb.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleMongoDb implements IModule { - private static final String CLASSNAME = "ModuleMongoDb"; - - private final UtmModuleService moduleService; - - public ModuleMongoDb(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.MONGODB); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.MONGODB; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleMySql.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleMySql.java deleted file mode 100644 index c0728dc34..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleMySql.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleMySql implements IModule { - private static final String CLASSNAME = "ModuleMySql"; - - private final UtmModuleService moduleService; - - public ModuleMySql(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.MYSQL); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.MYSQL; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleNats.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleNats.java deleted file mode 100644 index 8bf67719c..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleNats.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleNats implements IModule { - private static final String CLASSNAME = "ModuleNats"; - - private final UtmModuleService moduleService; - - public ModuleNats(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.NATS); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.NATS; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleNginx.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleNginx.java deleted file mode 100644 index 72c865ac1..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleNginx.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleNginx implements IModule { - private static final String CLASSNAME = "ModuleNginx"; - - private final UtmModuleService moduleService; - - public ModuleNginx(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.NGINX); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.NGINX; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleOsQuery.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleOsQuery.java deleted file mode 100644 index d8aa48f02..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleOsQuery.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleOsQuery implements IModule { - private static final String CLASSNAME = "ModuleOsQuery"; - - private final UtmModuleService moduleService; - - public ModuleOsQuery(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.OSQUERY); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.OSQUERY; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModulePostgreSql.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModulePostgreSql.java deleted file mode 100644 index 095990766..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModulePostgreSql.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModulePostgreSql implements IModule { - private static final String CLASSNAME = "ModulePostgreSql"; - - private final UtmModuleService moduleService; - - public ModulePostgreSql(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.POSTGRESQL); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.POSTGRESQL; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleRedis.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleRedis.java deleted file mode 100644 index 516ec2cff..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleRedis.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleRedis implements IModule { - private static final String CLASSNAME = "ModuleRedis"; - - private final UtmModuleService moduleService; - - public ModuleRedis(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.REDIS); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.REDIS; - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleTraefik.java b/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleTraefik.java deleted file mode 100644 index 2c45d2306..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/application_modules/factory/impl/ModuleTraefik.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.park.utmstack.domain.application_modules.factory.impl; - -import com.park.utmstack.domain.application_modules.UtmModule; -import com.park.utmstack.domain.application_modules.enums.ModuleName; -import com.park.utmstack.domain.application_modules.factory.IModule; -import com.park.utmstack.domain.application_modules.types.ModuleConfigurationKey; -import com.park.utmstack.domain.application_modules.types.ModuleRequirement; -import com.park.utmstack.service.application_modules.UtmModuleService; -import org.springframework.stereotype.Component; - -import java.util.Collections; -import java.util.List; - -@Component -public class ModuleTraefik implements IModule { - private static final String CLASSNAME = "ModuleTraefik"; - - private final UtmModuleService moduleService; - - public ModuleTraefik(UtmModuleService moduleService) { - this.moduleService = moduleService; - } - - @Override - public UtmModule getDetails(Long serverId) throws Exception { - final String ctx = CLASSNAME + ".getDetails"; - try { - return moduleService.findByServerIdAndModuleName(serverId, ModuleName.TRAEFIK); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } - } - - @Override - public List checkRequirements(Long serverId) throws Exception { - return Collections.emptyList(); - } - - @Override - public List getConfigurationKeys(Long groupId) throws Exception { - return Collections.emptyList(); - } - - @Override - public ModuleName getName() { - return ModuleName.TRAEFIK; - } -} From 03938884af908f15347b465a41d3c0ea60d8bbcf Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 18 Feb 2026 15:00:21 -0600 Subject: [PATCH 042/115] feat(logstash): enhance logstash stats retrieval with improved error handling and pipeline status management --- .../UtmLogstashPipelineService.java | 89 ++++++++++--------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/logstash_pipeline/UtmLogstashPipelineService.java b/backend/src/main/java/com/park/utmstack/service/logstash_pipeline/UtmLogstashPipelineService.java index 125926afb..4b2383177 100644 --- a/backend/src/main/java/com/park/utmstack/service/logstash_pipeline/UtmLogstashPipelineService.java +++ b/backend/src/main/java/com/park/utmstack/service/logstash_pipeline/UtmLogstashPipelineService.java @@ -20,6 +20,7 @@ import com.park.utmstack.service.logstash_pipeline.response.pipeline.PipelineStats; import com.park.utmstack.service.logstash_pipeline.response.statistic.StatisticDocument; import com.park.utmstack.service.web_clients.rest_template.RestTemplateService; +import com.park.utmstack.util.exceptions.ApiException; import com.park.utmstack.web.rest.vm.UtmLogstashPipelineVM; import com.utmstack.opensearch_connector.parsers.TermAggregateParser; import com.utmstack.opensearch_connector.types.BucketAggregation; @@ -38,6 +39,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.http.HttpStatus; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -267,57 +269,56 @@ public ApiEngineResponse logstashJvmApiResponse() { * Getting active pipelines stats from DB, general jvm stats from logstash */ public ApiStatsResponse getLogstashStats() throws Exception { - final String ctx = CLASSNAME + ".getLogstashStats"; - ApiStatsResponse statsResponse = new ApiStatsResponse(); + final String ctx = CLASSNAME + ".getLogstashStats"; - // Variables used to set the general pipeline's status - AtomicInteger activePipelinesCount = new AtomicInteger(); - AtomicInteger upPipelinesCount = new AtomicInteger(); + try { + ApiStatsResponse statsResponse = new ApiStatsResponse(); boolean isCorrelationUp = isEngineUp(); - try { - // Getting Jvm information (not used) - ApiEngineResponse jvmData = logstashJvmApiResponse(); - if (jvmData != null) { - statsResponse.setGeneral(jvmData); - } - // List to store stats mapped from DB - List infoStats; - - // Getting the active pipelines statistics - infoStats = activePipelinesList().stream().map(activePip -> { - - // Calculating stats for pipelines - // Setting stats for non-logstash pipelines (correlation engine) - if (isCorrelationUp) { - activePipelinesCount.getAndIncrement(); // Total pipelines that have to be active - if (activePip.getPipelineStatus().equals(PipelineStatus.PIPELINE_STATUS_UP.get())) { - upPipelinesCount.getAndIncrement(); - } - } else { - activePip.setPipelineStatus(PipelineStatus.PIPELINE_STATUS_DOWN.get()); - } - // } - // Mapping stats from DB pipeline - return PipelineStats.getPipelineStats(activePip); - }).collect(Collectors.toList()); - - // Setting the final global status of pipelines - if (isCorrelationUp) { - if (upPipelinesCount.get() == 0) { - jvmData.setStatus(PipelineStatus.ENGINE_STATUS_RED.get()); - } else if (upPipelinesCount.get() == activePipelinesCount.get()) { - jvmData.setStatus(PipelineStatus.ENGINE_STATUS_GREEN.get()); - } else { - jvmData.setStatus(PipelineStatus.ENGINE_STATUS_YELLOW.get()); - } + ApiEngineResponse jvmData = logstashJvmApiResponse(); + if (jvmData != null) { + statsResponse.setGeneral(jvmData); + } + + List activePipelines = activePipelinesList(); + + if (!isCorrelationUp) { + activePipelines.forEach(p -> + p.setPipelineStatus(PipelineStatus.PIPELINE_STATUS_DOWN.get()) + ); + } + + List pipelineStatsList = activePipelines.stream() + .map(PipelineStats::getPipelineStats) + .sorted( Comparator.comparing(PipelineStats::getPipelineStatus).reversed()) + .collect(Collectors.toList()); + + statsResponse.setPipelines(pipelineStatsList); + + if (isCorrelationUp && jvmData != null) { + long upCount = activePipelines.stream() + .filter(p -> PipelineStatus.PIPELINE_STATUS_UP.get() + .equals(p.getPipelineStatus())) + .count(); + + int totalCount = activePipelines.size(); + + if (upCount == 0) { + jvmData.setStatus(PipelineStatus.ENGINE_STATUS_RED.get()); + } else if (upCount == totalCount) { + jvmData.setStatus(PipelineStatus.ENGINE_STATUS_GREEN.get()); + } else { + jvmData.setStatus(PipelineStatus.ENGINE_STATUS_YELLOW.get()); } - statsResponse.setPipelines(infoStats); - } catch (Exception ex) { - throw new Exception(ctx + ": " + ex.getMessage()); } + return statsResponse; + + } catch (Exception ex) { + log.error("{}: An error occurred while fetching logstash stats: {}", ctx, ex.getMessage(), ex); + throw new ApiException(String.format("%s: An error occurred while fetching logstash stats", ctx), HttpStatus.INTERNAL_SERVER_ERROR); } +} /** * Method to set the DB pipelines status From 34d8fadf70da7e356f910d34283a6bdaa15b695e Mon Sep 17 00:00:00 2001 From: Yadian Llada Lopez Date: Fri, 20 Feb 2026 12:25:05 -0500 Subject: [PATCH 043/115] fix(system_linux): update filter with enhanced JSON parsing and field normalization --- filters/filebeat/system_linux_module.yml | 393 ++++++++++++++++++++--- 1 file changed, 344 insertions(+), 49 deletions(-) diff --git a/filters/filebeat/system_linux_module.yml b/filters/filebeat/system_linux_module.yml index ba96d3e21..5bbbac822 100644 --- a/filters/filebeat/system_linux_module.yml +++ b/filters/filebeat/system_linux_module.yml @@ -1,79 +1,374 @@ -# System linux filter, version 3.0.0 -# Fields based on https://www.elastic.co/guide/en/beats/filebeat/7.13/filebeat-module-system.html -# and filebeat fields.yml version 7.13.4 oss -# As the docs says this module work with one event per line, filebeat must ensure to send one event per line. - -# Filter Input requirements -> fileset: datatype -# syslog: plain text +# System Linux filter version 4.0.0 +# Support for systemd/journald JSON format from filebeat/journald +# Converts SCREAMINGSNAKECASE and snakecase to camelCase +# Maps to UTMStack Standard Event Schema +# Optimized: Direct mapping to standard schema (no intermediate steps) + pipeline: - dataTypes: - linux steps: + # ======================================== + # PHASE 1: EXTRACTION + # ======================================== + + # Parse JSON from systemd/journald - json: source: raw + where: 'startsWith("raw", "{")' + + # ======================================== + # PHASE 2: FIELD NORMALIZATION (camelCase conversion) + # ======================================== + + # Convert SCREAMINGSNAKECASE to camelCase - rename: from: - - log.url - to: origin.url + - log.MESSAGE + to: log.message + where: exists("log.MESSAGE") + - rename: from: - - log.log.file.path - to: origin.file + - log.PRIORITY + to: log.priority + where: exists("log.PRIORITY") + - rename: from: - - log.host.ip - to: log.local.ips + - log.SYSLOGIDENTIFIER + to: log.syslogIdentifier + where: exists("log.SYSLOGIDENTIFIER") + - rename: from: - - log.host.mac - to: log.local.macs + - log.SYSLOGTIMESTAMP + to: log.syslogTimestamp + where: exists("log.SYSLOGTIMESTAMP") + - rename: from: - - log.host.hostname - to: origin.host + - log.SYSLOGFACILITY + to: log.syslogFacility + where: exists("log.SYSLOGFACILITY") + - rename: from: - - log.host.name - to: origin.user + - log.SYSLOGPID + to: log.syslogPid + where: exists("log.SYSLOGPID") + + # Convert snakecase to camelCase (only for fields staying in log.*) + - rename: + from: + - log.PID + to: log.pid + where: exists("log.PID") + + - rename: + from: + - log.UID + to: log.uid + where: exists("log.UID") + + - rename: + from: + - log.GID + to: log.gid + where: exists("log.GID") + + - rename: + from: + - log.TID + to: log.tid + where: exists("log.TID") + + - rename: + from: + - log.EXE + to: log.exe + where: exists("log.EXE") + + - rename: + from: + - log.UNIT + to: log.unit + where: exists("log.UNIT") + + - rename: + from: + - log.SYSTEMDUNIT + to: log.systemdUnit + where: exists("log.SYSTEMDUNIT") + + - rename: + from: + - log.SYSTEMDSLICE + to: log.systemdSlice + where: exists("log.SYSTEMDSLICE") + + - rename: + from: + - log.SYSTEMDUSERSLICE + to: log.systemdUserSlice + where: exists("log.SYSTEMDUSERSLICE") + + - rename: + from: + - log.SYSTEMDSESSION + to: log.systemdSession + where: exists("log.SYSTEMDSESSION") + + - rename: + from: + - log.SESSIONID + to: log.sessionId + where: exists("log.SESSIONID") + + - rename: + from: + - log.LEADER + to: log.leader + where: exists("log.LEADER") + + - rename: + from: + - log.SYSTEMDOWNERUID + to: log.systemdOwnerUid + where: exists("log.SYSTEMDOWNERUID") + + - rename: + from: + - log.SYSTEMDCGROUP + to: log.systemdCgroup + where: exists("log.SYSTEMDCGROUP") + + - rename: + from: + - log.BOOTID + to: log.bootId + where: exists("log.BOOTID") + + - rename: + from: + - log.MACHINEID + to: log.machineId + where: exists("log.MACHINEID") + + - rename: + from: + - log.TRANSPORT + to: log.transport + where: exists("log.TRANSPORT") + + - rename: + from: + - log.SELINUXCONTEXT + to: log.selinuxContext + where: exists("log.SELINUXCONTEXT") + - rename: from: - - log.host.id - to: log.hostId + - log.AUDITSESSION + to: log.auditSession + where: exists("log.AUDITSESSION") + - rename: from: - - log.event.dataset - to: action + - log.AUDITLOGINUID + to: log.auditLoginUid + where: exists("log.AUDITLOGINUID") + - rename: from: - - log.agent.version - to: log.agentVersion + - log.CAPEFFECTIVE + to: log.capEffective + where: exists("log.CAPEFFECTIVE") + - rename: from: - - log.host.os.kernel - to: log.osVersion + - log.REALTIMETIMESTAMP + to: log.realtimeTimestamp + where: exists("log.REALTIMETIMESTAMP") + + - rename: + from: + - log.SOURCEREALTIMETIMESTAMP + to: log.sourceRealtimeTimestamp + where: exists("log.SOURCEREALTIMETIMESTAMP") + + - rename: + from: + - log.MONOTONICTIMESTAMP + to: log.monotonicTimestamp + where: exists("log.MONOTONICTIMESTAMP") + + - rename: + from: + - log.CURSOR + to: log.cursor + where: exists("log.CURSOR") + + - rename: + from: + - log.SEQNUM + to: log.seqnum + where: exists("log.SEQNUM") + + - rename: + from: + - log.SEQNUMID + to: log.seqnumId + where: exists("log.SEQNUMID") + + - rename: + from: + - log.RUNTIMESCOPE + to: log.runtimeScope + where: exists("log.RUNTIMESCOPE") + + - rename: + from: + - log.STREAMID + to: log.streamId + where: exists("log.STREAMID") + + - rename: + from: + - log.SYSTEMDINVOCATIONID + to: log.systemdInvocationId + where: exists("log.SYSTEMDINVOCATIONID") + + - rename: + from: + - log.CODEFILE + to: log.codeFile + where: exists("log.CODEFILE") + + - rename: + from: + - log.CODELINE + to: log.codeLine + where: exists("log.CODELINE") + + - rename: + from: + - log.CODEFUNC + to: log.codeFunc + where: exists("log.CODEFUNC") + + - rename: + from: + - log.INVOCATIONID + to: log.invocationId + where: exists("log.INVOCATIONID") + + - rename: + from: + - log.JOBID + to: log.jobId + where: exists("log.JOBID") + + - rename: + from: + - log.JOBRESULT + to: actionResult + where: exists("log.JOBRESULT") + + - rename: + from: + - log.JOBTYPE + to: log.jobType + where: exists("log.JOBTYPE") + + - rename: + from: + - log.MESSAGEID + to: log.messageId + where: exists("log.MESSAGEID") + + # ======================================== + # PHASE 3: STANDARD SCHEMA MAPPING + # ======================================== + + # Map directly to Standard Event Schema (no intermediate camelCase step) + - rename: + from: + - log.HOSTNAME + to: origin.host + where: exists("log.HOSTNAME") + + - rename: + from: + - log.USERID + to: origin.user + where: exists("log.USERID") + - rename: from: - - log.host.os.type - to: log.osType + - log.COMM + to: origin.process + where: exists("log.COMM") + - rename: from: - - log.host.architecture - to: log.cpuArchitecture - - cast: - to: '[]string' - fields: - - log.local.ips - - cast: - to: '[]string' - fields: - - log.local.macs - - delete: - fields: - - log.service - - log.metadata - - log.agent - - log.host - - log.event - - log.ecs - - log.log - - log.fileset \ No newline at end of file + - log.CMDLINE + to: origin.command + where: exists("log.CMDLINE") + + # Map syslog priority (0-7) to severity labels + - add: + function: string + params: + key: severity + value: "emergency" + where: 'equals("log.priority", "0")' + + - add: + function: string + params: + key: severity + value: "alert" + where: 'equals("log.priority", "1")' + + - add: + function: string + params: + key: severity + value: "critical" + where: 'equals("log.priority", "2")' + + - add: + function: string + params: + key: severity + value: "error" + where: 'equals("log.priority", "3")' + + - add: + function: string + params: + key: severity + value: "warning" + where: 'equals("log.priority", "4")' + + - add: + function: string + params: + key: severity + value: "notice" + where: 'equals("log.priority", "5")' + + - add: + function: string + params: + key: severity + value: "info" + where: 'equals("log.priority", "6")' + + - add: + function: string + params: + key: severity + value: "debug" + where: 'equals("log.priority", "7")' \ No newline at end of file From b4466fab254e63b685c50aebb6ad405326e214dd Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 20 Feb 2026 13:59:50 -0600 Subject: [PATCH 044/115] feat(filter): add Linux filter update with enhanced JSON parsing and field normalization --- .../20260220001_update_filter_linux.xml | 393 ++++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 395 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260220001_update_filter_linux.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260220001_update_filter_linux.xml b/backend/src/main/resources/config/liquibase/changelog/20260220001_update_filter_linux.xml new file mode 100644 index 000000000..7169e2b19 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260220001_update_filter_linux.xml @@ -0,0 +1,393 @@ + + + + + + + + + + \ 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 7a162fe6e..7ad19b973 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -465,6 +465,8 @@ + + From ec0cfedb4e2b3345ed6053d16a67c52cdf20c22d Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 20 Feb 2026 15:05:45 -0600 Subject: [PATCH 045/115] feat(visualization): add update for Linux visualizations to normalize field names and improve dataset consistency --- ...0260220002_update_linux_visualizations.xml | 154 ++++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 156 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260220002_update_linux_visualizations.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260220002_update_linux_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260220002_update_linux_visualizations.xml new file mode 100644 index 000000000..60c0959b8 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260220002_update_linux_visualizations.xml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 7ad19b973..b9fbb258a 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -467,6 +467,8 @@ + + From ff25941e14716d496922c8124ddfba9c97ac2a15 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Mon, 23 Feb 2026 18:23:33 +0300 Subject: [PATCH 046/115] feat(windows): update windows filter --- filters/windows/windows-events.yml | 503 +++++++++++++---------------- 1 file changed, 221 insertions(+), 282 deletions(-) diff --git a/filters/windows/windows-events.yml b/filters/windows/windows-events.yml index 2457dea08..534a26076 100644 --- a/filters/windows/windows-events.yml +++ b/filters/windows/windows-events.yml @@ -1,4 +1,4 @@ -# Windows_Agent filter, version 3.0.4 +# Windows_Agent filter, version 3.1.0 # Based on winlogbeat fields, reference [8.15] # See https://www.elastic.co/guide/en/beats/winlogbeat/current/exported-fields-winlog.html @@ -12,333 +12,277 @@ pipeline: # Renaming useful fields - rename: from: - - log.host.ip - to: log.origin.ips - - - rename: - from: - - log.host.mac - to: log.origin.macs - - - rename: - from: - - log.event.action - to: log.action - - - rename: - from: - - log.event.outcome - to: actionResult - - - rename: - from: - - log.host.hostname - to: origin.host - - - rename: - from: - - log.event.created - to: log.deviceTime - - - rename: - from: - - log.host.os.name - to: log.os - - - rename: - from: - - log.host.os.kernel - to: log.osVersion - - - rename: - from: - - log.host.architecture - to: log.cpuArchitecture - - - rename: - from: - - log.winlog.provider_guid + - log.providerguid to: log.providerGuid - rename: from: - - log.winlog.event_data.PrivilegeList - to: log.winlogEventDataPrivilegeList + - log.data.PrivilegeList + to: log.eventDataPrivilegeList - rename: from: - - log.winlog.event_data.ServiceName - to: log.winlogEventDataServiceName + - log.data.ServiceName + to: log.eventDataServiceName - rename: from: - - log.winlog.event_data.SubjectDomainName - to: log.winlogEventDataSubjectDomainName + - log.data.SubjectDomainName + to: log.eventDataSubjectDomainName - rename: from: - - log.winlog.event_data.SubjectLogonId - to: log.winlogEventDataSubjectLogonId + - log.data.SubjectLogonId + to: log.eventDataSubjectLogonId - rename: from: - - log.winlog.event_data.SubjectUserName - to: log.winlogEventDataSubjectUserName + - log.data.SubjectUserName + to: log.eventDataSubjectUserName - rename: from: - - log.winlog.event_data.SubjectUserSid - to: log.winlogEventDataSubjectUserSid + - log.data.SubjectUserSid + to: log.eventDataSubjectUserSid - rename: from: - - log.winlog.event_data.SubjectUserSid - to: log.winlogEventDataSubjectUserSid + - log.data.SubjectUserSid + to: log.eventDataSubjectUserSid - rename: from: - - log.winlog.event_data.PrivilegeList - to: log.winlogEventDataPrivilegeList + - log.data.PrivilegeList + to: log.eventDataPrivilegeList - rename: from: - - log.winlog.event_data.ClientProcessId - to: log.winlogEventDataClientProcessId + - log.data.ClientProcessId + to: log.eventDataClientProcessId - rename: from: - - log.winlog.event_data.Flags - to: log.winlogEventDataFlags + - log.data.Flags + to: log.eventDataFlags - rename: from: - - log.winlog.event_data.Identity - to: log.winlogEventDataIdentity + - log.data.Identity + to: log.eventDataIdentity - rename: from: - - log.winlog.event_data.ProcessCreationTime - to: log.winlogEventDataProcessCreationTime + - log.data.ProcessCreationTime + to: log.eventDataProcessCreationTime - rename: from: - - log.winlog.event_id - to: log.winlogEventId + - log.id + to: log.eventId - rename: from: - - log.winlog.event_data.Resource - to: log.winlogEventDataResource + - log.data.Resource + to: log.eventDataResource - rename: from: - - log.winlog.event_data.ReturnCode - to: log.winlogEventDataReturnCode + - log.data.ReturnCode + to: log.eventDataReturnCode - rename: from: - - log.winlog.event_data.Resource - to: log.winlogEventDataResource + - log.data.Resource + to: log.eventDataResource - rename: from: - - log.winlog.event_data.Schema - to: log.winlogEventDataSchema + - log.data.Schema + to: log.eventDataSchema - rename: from: - - log.winlog.event_data.SchemaFriendlyName - to: log.winlogEventDataSchemaFriendlyName + - log.data.SchemaFriendlyName + to: log.eventDataSchemaFriendlyName - rename: from: - - log.winlog.event_data.Resource - to: log.winlogEventDataResource + - log.data.Resource + to: log.eventDataResource - rename: from: - - log.winlog.event_data.AuthenticationPackageName - to: log.winlogEventDataAuthenticationPackageName + - log.data.AuthenticationPackageName + to: log.eventDataAuthenticationPackageName - rename: from: - - log.winlog.event_data.ElevatedToken - to: log.winlogEventDataElevatedToken + - log.data.ElevatedToken + to: log.eventDataElevatedToken - rename: from: - - log.winlog.event_data.ImpersonationLevel - to: log.winlogEventDataImpersonationLevel + - log.data.ImpersonationLevel + to: log.eventDataImpersonationLevel - rename: from: - - log.wineventlog.event_data.FailureReason - to: log.winlogEventDataFailureReason + - log.wineventlog.data.FailureReason + to: log.eventDataFailureReason - rename: from: - - log.winlog.event_data.IpAddress - to: log.winlogEventDataIpAddress + - log.data.IpAddress + to: log.eventDataIpAddress - rename: from: - - log.winlog.event_data.IpPort - to: log.winlogEventDataIpPort + - log.data.IpPort + to: log.eventDataIpPort - rename: from: - - log.winlog.event_data.Resource - to: log.winlogEventDataResource + - log.data.Resource + to: log.eventDataResource - rename: from: - - log.winlog.event_data.KeyLength - to: log.winlogEventDataKeyLength + - log.data.KeyLength + to: log.eventDataKeyLength - rename: from: - - log.winlog.event_data.LmPackageName - to: log.winlogEventDataLmPackageName + - log.data.LmPackageName + to: log.eventDataLmPackageName - rename: from: - - log.winlog.event_data.LogonProcessName - to: log.winlogEventDataLogonProcessName + - log.data.LogonProcessName + to: log.eventDataLogonProcessName - rename: from: - - log.winlog.event_data.LogonType - to: log.winlogEventDataLogonType - + - log.data.LogonType + to: log.eventDataLogonType - rename: from: - - log.winlog.event_data.Resource - to: log.winlogEventDataResource + - log.data.Resource + to: log.eventDataResource - rename: from: - - log.winlog.event_data.ProcessId - to: log.winlogEventDataProcessId + - log.data.ProcessId + to: log.eventDataProcessId - rename: from: - - log.winlog.event_data.ProcessName - to: log.winlogEventDataProcessName + - log.data.ProcessName + to: log.eventDataProcessName - rename: from: - - log.winlog.event_data.RestrictedAdminMode - to: log.winlogEventDataRestrictedAdminMode + - log.data.RestrictedAdminMode + to: log.eventDataRestrictedAdminMode - rename: from: - - log.winlog.event_data.TargetDomainName + - log.data.TargetDomainName to: target.domain - rename: from: - - log.winlog.event_data.TargetLinkedLogonId - to: log.winlogEventDataTargetLinkedLogonId + - log.data.TargetLinkedLogonId + to: log.eventDataTargetLinkedLogonId - rename: from: - - log.winlog.event_data.Resource - to: log.winlogEventDataResource + - log.data.Resource + to: log.eventDataResource - rename: from: - - log.winlog.event_data.TargetLogonId - to: log.winlogEventDataTargetLogonId + - log.data.TargetLogonId + to: log.eventDataTargetLogonId - rename: from: - - log.winlog.event_data.TargetOutboundDomainName - to: log.winlogEventDataTargetOutboundDomainName + - log.data.TargetOutboundDomainName + to: log.eventDataTargetOutboundDomainName - rename: from: - - log.winlog.event_data.TargetOutboundUserName - to: log.winlogEventDataTargetOutboundUserName + - log.data.TargetOutboundUserName + to: log.eventDataTargetOutboundUserName - rename: from: - - log.winlog.event_data.TargetUserName + - log.data.TargetUserName to: target.user - rename: from: - - log.winlog.event_data.TargetUserSid - to: log.winlogEventDataTargetUserSid - - - rename: - from: - - log.winlog.event_data.TransmittedServices - to: log.winlogEventDataTransmittedServices + - log.data.TargetUserSid + to: log.eventDataTargetUserSid - rename: from: - - log.winlog.event_data.VirtualAccount - to: log.winlogEventDataVirtualAccount + - log.data.TransmittedServices + to: log.eventDataTransmittedServices - rename: from: - - log.winlog.event_data.WorkstationName - to: log.winlogEventDataWorkstationName + - log.data.VirtualAccount + to: log.eventDataVirtualAccount - rename: from: - - log.winlog.event_data.FailureReason - to: log.winlogEventDataFailureReason + - log.data.WorkstationName + to: log.eventDataWorkstationName - rename: from: - - log.winlog.event_data.AccessMask - to: log.winlogEeventDataAccessMask + - log.data.FailureReason + to: log.eventDataFailureReason - rename: from: - - log.winlog.event_data.Status - to: log.winlogEventDataStatus + - log.data.AccessMask + to: log.eventDataAccessMask - rename: from: - - log.winlog.opcode - to: log.winlogOpcode + - log.data.Status + to: log.eventDataStatus - rename: from: - - log.winlog.process.thread - to: log.winlogProcessThread + - log.process.thread + to: log.processThread - rename: from: - - log.winlog.task - to: log.winlogOpcode + - log.execution.ThreadID + to: log.processThreadId - rename: from: - - log.winlog.process.thread.id - to: log.winlogProcessThreadId + - log.process.pid + to: log.processPid - rename: from: - - log.winlog.process.pid - to: log.winlogProcessPid + - log.providername + to: log.providerName - rename: from: - - log.winlog.provider_name - to: log.winlogProviderName + - log.record_id + to: log.recordId - rename: from: - - log.winlog.record_id - to: log.winlogRecordId - - - rename: - from: - - log.winlog.task - to: log.winlogTask + - log.task + to: log.task - rename: from: @@ -362,13 +306,13 @@ pipeline: - rename: from: - - log.winlog.api - to: log.winlogApi + - log.api + to: log.api - rename: from: - - log.winlog.channel - to: log.winlogChannel + - log.channel + to: log.channel - rename: from: @@ -377,23 +321,18 @@ pipeline: - rename: from: - - log.winlog.activity_id + - log.correlation.ActivityID to: log.activityId - rename: from: - - log.winlog.event_data.LogonGuid + - log.data.LogonGuid to: log.logonGuid - - cast: - to: "[]string" - fields: - - log.origin.ips - - - cast: - to: "[]string" - fields: - - log.origin.macs + - rename: + from: + - log.execution.ProcessID + to: log.executionProcessID - cast: to: "int" @@ -407,8 +346,8 @@ pipeline: - log.providerGuid - log.activityId - log.logonGuid - - log.winlogEventDataSchema - - log.winlogProcessThread + - log.eventDataSchema + - log.processThread - trim: function: suffix @@ -417,8 +356,8 @@ pipeline: - log.providerGuid - log.activityId - log.logonGuid - - log.winlogEventDataSchema - - log.winlogProcessThread + - log.eventDataSchema + - log.processThread # Drop unnecessary events - drop: @@ -2293,49 +2232,49 @@ pipeline: params: key: log.failureReasonDescription value: 'The specified user account has expired.' - where: equals("log.winlogEventDataFailureReason", "%%2305") && equals("log.eventCode", 4625) + where: equals("log.eventDataFailureReason", "%%2305") && equals("log.eventCode", 4625) - add: function: 'string' params: key: log.failureReasonDescription value: 'The password for the specified account has expired' - where: equals("log.winlogEventDataFailureReason", "%%2309") && equals("log.eventCode", 4625) + where: equals("log.eventDataFailureReason", "%%2309") && equals("log.eventCode", 4625) - add: function: 'string' params: key: log.failureReasonDescription value: 'Account currently disabled' - where: equals("log.winlogEventDataFailureReason", "%%2310") && equals("log.eventCode", 4625) + where: equals("log.eventDataFailureReason", "%%2310") && equals("log.eventCode", 4625) - add: function: 'string' params: key: log.failureReasonDescription value: 'Account logon time restriction violation' - where: equals("log.winlogEventDataFailureReason", "%%2311") && equals("log.eventCode", 4625) + where: equals("log.eventDataFailureReason", "%%2311") && equals("log.eventCode", 4625) - add: function: 'string' params: key: log.failureReasonDescription value: 'User not allowed to logon at this computer' - where: equals("log.winlogEventDataFailureReason", "%%2312") && equals("log.eventCode", 4625) + where: equals("log.eventDataFailureReason", "%%2312") && equals("log.eventCode", 4625) - add: function: 'string' params: key: log.failureReasonDescription value: 'Unknown user name or bad password' - where: equals("log.winlogEventDataFailureReason", "%%2313") && equals("log.eventCode", 4625) + where: equals("log.eventDataFailureReason", "%%2313") && equals("log.eventCode", 4625) - add: function: 'string' params: key: log.failureReasonDescription value: 'An Error occurred during Logon' - where: equals("log.winlogEventDataFailureReason", "%%2304") && equals("log.eventCode", 4625) + where: equals("log.eventDataFailureReason", "%%2304") && equals("log.eventCode", 4625) # Decoding the "AccessMask" field when the event code is 4663 and adding the "accessType" and "accessDescription" field - add: @@ -2343,210 +2282,210 @@ pipeline: params: key: log.accessType value: 'read' - where: equals("log.winlogEeventDataAccessMask", "0x1") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x1") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'For a file object, the right to read the corresponding file data. For a directory object, the right to read the corresponding directory data.\n For a directory, the right to list the contents of the directory.\n For registry objects, this is, Query key value.' - where: equals("log.winlogEeventDataAccessMask", "0x1") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x1") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'write' - where: equals("log.winlogEeventDataAccessMask", "0x2") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x2") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'For a file object, the right to write data to the file.\n For a directory object, the right to create a file in the directory.\n For registry objects, this is, Set key value.' - where: equals("log.winlogEeventDataAccessMask", "0x2") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x2") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'append' - where: equals("log.winlogEeventDataAccessMask", "0x4") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x4") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'For a file object, the right to append data to the file. (For local files, write operations will not overwrite existing data if this flag is specified without FILE_WRITE_DATA.)\n For a directory object, the right to create a subdirectory.\n For a named pipe, the right to create a pipe.' - where: equals("log.winlogEeventDataAccessMask", "0x4") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x4") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'read_extended_attributes' - where: equals("log.winlogEeventDataAccessMask", "0x8") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x8") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'The right to read extended file attributes.\n For registry objects, this is, Enumerate sub-keys.' - where: equals("log.winlogEeventDataAccessMask", "0x8") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x8") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'write_extended_attributes' - where: equals("log.winlogEeventDataAccessMask", "0x10") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x10") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'The right to write extended file attributes.' - where: equals("log.winlogEeventDataAccessMask", "0x10") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x10") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'execute' - where: equals("log.winlogEeventDataAccessMask", "0x20") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x20") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'For a native code file, the right to execute the file. This access right given to scripts may cause the script to be executable, depending on the script interpreter.\n For a directory, the right to traverse the directory. By default, users are assigned the BYPASS_TRAVERSE_CHECKING privilege, which ignores the FILE_TRAVERSE access right.' - where: equals("log.winlogEeventDataAccessMask", "0x20") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x20") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'delete_child' - where: equals("log.winlogEeventDataAccessMask", "0x40") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x40") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'For a directory, the right to delete a directory and all the files it contains, including read-only files.' - where: equals("log.winlogEeventDataAccessMask", "0x40") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x40") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'read_attributes' - where: equals("log.winlogEeventDataAccessMask", "0x80") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x80") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'The right to read file attributes.' - where: equals("log.winlogEeventDataAccessMask", "0x80") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x80") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'write_attributes' - where: equals("log.winlogEeventDataAccessMask", "0x100") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x100") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'The right to write file attributes.' - where: equals("log.winlogEeventDataAccessMask", "0x100") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x100") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'delete' - where: equals("log.winlogEeventDataAccessMask", "0x10000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x10000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'The right to delete the object.' - where: equals("log.winlogEeventDataAccessMask", "0x10000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x10000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'read_control' - where: equals("log.winlogEeventDataAccessMask", "0x20000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x20000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'The right to read information in the security descriptor object, without including the information in the system access control list (SACL).' - where: equals("log.winlogEeventDataAccessMask", "0x20000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x20000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'write_dac' - where: equals("log.winlogEeventDataAccessMask", "0x40000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x40000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'The right to modify the discretionary access control list (DACL) in the security descriptor object.' - where: equals("log.winlogEeventDataAccessMask", "0x40000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x40000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'write_owner' - where: equals("log.winlogEeventDataAccessMask", "0x80000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x80000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'The right to change the owner in the security descriptor object' - where: equals("log.winlogEeventDataAccessMask", "0x80000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x80000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'synchronize' - where: equals("log.winlogEeventDataAccessMask", "0x100000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x100000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'The right to use the object for synchronization. This enables a thread to wait until the object is in the signaled state. Some object types do not support this access right.' - where: equals("log.winlogEeventDataAccessMask", "0x100000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x100000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessType value: 'access_sys_sec' - where: equals("log.winlogEeventDataAccessMask", "0x1000000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x1000000") && equals("log.eventCode", 4663) - add: function: 'string' params: key: log.accessDescription value: 'The ACCESS_SYS_SEC access right controls the ability to get or set the SACL in an security descriptor object.' - where: equals("log.winlogEeventDataAccessMask", "0x1000000") && equals("log.eventCode", 4663) + where: equals("log.eeventDataAccessMask", "0x1000000") && equals("log.eventCode", 4663) # Decoding the "eventStatus" field - add: @@ -2554,91 +2493,91 @@ pipeline: params: key: log.statusDescription value: 'Account locked out' - where: equals("log.winlogEventDataStatus", "0xC0000234") + where: equals("log.eventDataStatus", "0xC0000234") - add: function: 'string' params: key: log.statusDescription value: 'Account expired' - where: equals("log.winlogEventDataStatus", "0xC0000193") + where: equals("log.eventDataStatus", "0xC0000193") - add: function: 'string' params: key: log.statusDescription value: 'Clocks out of sync' - where: equals("log.winlogEventDataStatus", "0xC0000133") + where: equals("log.eventDataStatus", "0xC0000133") - add: function: 'string' params: key: log.statusDescription value: 'Password change required' - where: equals("log.winlogEventDataStatus", "0xC0000224") + where: equals("log.eventDataStatus", "0xC0000224") - add: function: 'string' params: key: log.statusDescription value: 'User does not have logon right' - where: equals("log.winlogEventDataStatus", "0xc000015b") + where: equals("log.eventDataStatus", "0xc000015b") - add: function: 'string' params: key: log.statusDescription value: 'Logon failure' - where: equals("log.winlogEventDataStatus", "0xc000006d") + where: equals("log.eventDataStatus", "0xc000006d") - add: function: 'string' params: key: log.statusDescription value: 'Account restriction' - where: equals("log.winlogEventDataStatus", "0xc000006e") + where: equals("log.eventDataStatus", "0xc000006e") - add: function: 'string' params: key: log.statusDescription value: 'An error occurred during logon' - where: equals("log.winlogEventDataStatus", "0xc00002ee") + where: equals("log.eventDataStatus", "0xc00002ee") - add: function: 'string' params: key: log.statusDescription value: 'Password expired' - where: equals("log.winlogEventDataStatus", "0xC0000071") + where: equals("log.eventDataStatus", "0xC0000071") - add: function: 'string' params: key: log.statusDescription value: 'Account disabled' - where: equals("log.winlogEventDataStatus", "0xC0000072") + where: equals("log.eventDataStatus", "0xC0000072") - add: function: 'string' params: key: log.statusDescription value: 'Authentication firewall prohibits logon' - where: equals("log.winlogEventDataStatus", "0xC0000413") + where: equals("log.eventDataStatus", "0xC0000413") - add: function: 'string' params: key: log.statusDescription value: 'Incorrect password' - where: equals("log.winlogEventDataStatus", "0xc000006a") + where: equals("log.eventDataStatus", "0xc000006a") - add: function: 'string' params: key: log.statusDescription value: 'Account does not exist' - where: equals("log.winlogEventDataStatus", "0xc0000064") + where: equals("log.eventDataStatus", "0xc0000064") # Decoding the "eventStatus" field when the event code is 4771 and adding the "statusDescription" - add: @@ -2646,330 +2585,330 @@ pipeline: params: key: log.statusDescription value: 'Customer database entry has expired' - where: equals("log.winlogEventDataStatus", "0x1") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x1") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'The server database entry has expired' - where: equals("log.winlogEventDataStatus", "0x2") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x2") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Requested protocol version not supported' - where: equals("log.winlogEventDataStatus", "0x3") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x3") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Customer key encrypted in old master key' - where: equals("log.winlogEventDataStatus", "0x4") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x4") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Server key encrypted with old master key' - where: equals("log.winlogEventDataStatus", "0x5") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x5") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Client not found in Kerberos database' - where: equals("log.winlogEventDataStatus", "0x6") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x6") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Server not found in Kerberos database' - where: equals("log.winlogEventDataStatus", "0x7") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x7") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Multiple principal entries in database' - where: equals("log.winlogEventDataStatus", "0x8") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x8") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'The client or server has a null key' - where: equals("log.winlogEventDataStatus", "0x9") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x9") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Ticket not eligible for postdating' - where: equals("log.winlogEventDataStatus", "0xA") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0xA") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Requested start time is later than end time' - where: equals("log.winlogEventDataStatus", "0xB") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0xB") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'KDC policy rejects request' - where: equals("log.winlogEventDataStatus", "0xC") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0xC") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'KDC cannot accommodate requested option' - where: equals("log.winlogEventDataStatus", "0xD") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0xD") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'KDC has no support for encryption type' - where: equals("log.winlogEventDataStatus", "0xE") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0xE") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'KDC has no support for checksum type' - where: equals("log.winlogEventDataStatus", "0xF") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0xF") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'KDC has no support for padata type' - where: equals("log.winlogEventDataStatus", "0x10") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x10") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'KDC has no support for transited type' - where: equals("log.winlogEventDataStatus", "0x11") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x11") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Clients credentials have been revoked' - where: equals("log.winlogEventDataStatus", "0x12") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x12") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Credentials for server have been revoked' - where: equals("log.winlogEventDataStatus", "0x13") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x13") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'TGT has been revoked' - where: equals("log.winlogEventDataStatus", "0x14") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x14") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Client not yet valid - try again later' - where: equals("log.winlogEventDataStatus", "0x15") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x15") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Server not yet valid - try again later' - where: equals("log.winlogEventDataStatus", "0x16") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x16") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Password has expired' - where: equals("log.winlogEventDataStatus", "0x17") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x17") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Pre-authentication information was invalid' - where: equals("log.winlogEventDataStatus", "0x18") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x18") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Additional pre-authentication required' - where: equals("log.winlogEventDataStatus", "0x19") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x19") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Integrity check on decrypted field failed' - where: equals("log.winlogEventDataStatus", "0x1F") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x1F") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Ticket expired' - where: equals("log.winlogEventDataStatus", "0x20") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x20") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Ticket not yet valid' - where: equals("log.winlogEventDataStatus", "0x21") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x21") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Request is a replay' - where: equals("log.winlogEventDataStatus", "0x22") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x22") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'The ticket is not for us' - where: equals("log.winlogEventDataStatus", "0x23") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x23") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Ticket and authenticator is not match' - where: equals("log.winlogEventDataStatus", "0x24") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x24") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Clock skew too great' - where: equals("log.winlogEventDataStatus", "0x25") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x25") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Incorrect net address' - where: equals("log.winlogEventDataStatus", "0x26") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x26") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Protocol version mismatch' - where: equals("log.winlogEventDataStatus", "0x27") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x27") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Invalid msg type' - where: equals("log.winlogEventDataStatus", "0x28") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x28") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Message stream modified' - where: equals("log.winlogEventDataStatus", "0x29") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x29") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Message out of order' - where: equals("log.winlogEventDataStatus", "0x2A") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x2A") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Specified version of key is not available' - where: equals("log.winlogEventDataStatus", "0x2C") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x2C") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Service key not available' - where: equals("log.winlogEventDataStatus", "0x2D") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x2D") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Mutual authentication failed' - where: equals("log.winlogEventDataStatus", "0x2E") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x2E") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Incorrect message direction' - where: equals("log.winlogEventDataStatus", "0x2F") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x2F") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Alternative authentication method required' - where: equals("log.winlogEventDataStatus", "0x30") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x30") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Incorrect sequence number in message' - where: equals("log.winlogEventDataStatus", "0x31") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x31") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Inappropriate type of checksum in message' - where: equals("log.winlogEventDataStatus", "0x32") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x32") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Generic error (description in e-text)' - where: equals("log.winlogEventDataStatus", "0x3C") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x3C") && equals("log.eventCode", 4771) - add: function: 'string' params: key: log.statusDescription value: 'Field is too long for this implementation' - where: equals("log.winlogEventDataStatus", "0x3D") && equals("log.eventCode", 4771) + where: equals("log.eventDataStatus", "0x3D") && equals("log.eventCode", 4771) - delete: fields: - log.agent - log.host - - log.winlog.computer_name - - log.winlog.event_data - - log.winlog.process + - log.computer_name + - log.data + - log.process - log.metadata - log.event - log.ecs From b2562e73140b88c01412c141b97caac2f78e0fc0 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 23 Feb 2026 09:13:02 -0600 Subject: [PATCH 047/115] feat(saml): enhance SAML registration with improved error handling and environment variable validation --- ...amlRelyingPartyRegistrationRepository.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java index 49c063cc6..ad5bf1574 100644 --- a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java @@ -4,18 +4,21 @@ import com.park.utmstack.domain.idp_provider.IdentityProviderConfig; import com.park.utmstack.repository.idp_provider.IdentityProviderConfigRepository; import com.park.utmstack.util.CipherUtil; +import com.park.utmstack.util.exceptions.ApiException; import com.park.utmstack.util.saml.PemUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +@Slf4j public class SamlRelyingPartyRegistrationRepository implements RelyingPartyRegistrationRepository { private final Map registrations = new ConcurrentHashMap<>(); @@ -42,11 +45,15 @@ private void loadProviders(IdentityProviderConfigRepository jpaProviderRepositor } private RelyingPartyRegistration buildRelyingPartyRegistration(IdentityProviderConfig entity) { + try { + String encryptionKey = System.getenv(Constants.ENV_ENCRYPTION_KEY); + if (encryptionKey == null || encryptionKey.isBlank()) { + throw new IllegalStateException( + "Environment variable " + Constants.ENV_ENCRYPTION_KEY + " not configured"); + } - PrivateKey spKey = PemUtils.parsePrivateKey(CipherUtil.decrypt( - entity.getSpPrivateKeyPem(), - System.getenv(Constants.ENV_ENCRYPTION_KEY) - )); + String decryptedKey = CipherUtil.decrypt(entity.getSpPrivateKeyPem(), encryptionKey); + PrivateKey spKey = PemUtils.parsePrivateKey(decryptedKey); X509Certificate spCert = PemUtils.parseCertificate(entity.getSpCertificatePem()); return RelyingPartyRegistrations @@ -56,6 +63,10 @@ private RelyingPartyRegistration buildRelyingPartyRegistration(IdentityProviderC .assertionConsumerServiceLocation(entity.getSpAcsUrl()) .signingX509Credentials(c -> c.add(Saml2X509Credential.signing(spKey, spCert))) .build(); + } catch (Exception e) { + log.error("Failed to build SAML registration for provider: {}", entity.getName(), e); + throw new ApiException(String.format("Failed to build SAML registration for provider: %s", entity.getName()), HttpStatus.INTERNAL_SERVER_ERROR); } +} } \ No newline at end of file From bc21339c393ba30969e16508855a9f7ecf8e7f14 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 23 Feb 2026 09:16:51 -0600 Subject: [PATCH 048/115] feat(saml): improve SAML provider loading with enhanced error handling and logging --- ...amlRelyingPartyRegistrationRepository.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java index ad5bf1574..4c5744056 100644 --- a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java @@ -15,6 +15,7 @@ import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -38,11 +39,29 @@ public void reloadProviders(IdentityProviderConfigRepository jpaProviderReposito } private void loadProviders(IdentityProviderConfigRepository jpaProviderRepository) { - jpaProviderRepository.findAllByActiveTrue().forEach(entity -> { - RelyingPartyRegistration registration = buildRelyingPartyRegistration(entity); - registrations.put(entity.getProviderType().name().toLowerCase(), registration); + try { + List activeProviders = jpaProviderRepository.findAllByActiveTrue(); + + if (activeProviders.isEmpty()) { + return; + } + + activeProviders.forEach(entity -> { + try { + RelyingPartyRegistration registration = buildRelyingPartyRegistration(entity); + registrations.put(entity.getProviderType().name().toLowerCase(), registration); + log.info("Loaded SAML provider: {} (type: {})", entity.getName(), entity.getProviderType()); + } catch (Exception e) { + log.error("Failed to load SAML provider: {}", entity.getName(), e); + } }); + + log.info("Successfully loaded {} SAML provider(s)", registrations.size()); + } catch (Exception e) { + log.error("Failed to load SAML providers: {}", e.getMessage(), e); + throw new ApiException(String.format("Failed to load SAML providers: %s", e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR); } +} private RelyingPartyRegistration buildRelyingPartyRegistration(IdentityProviderConfig entity) { try { From 36231237adcbebc83517d47473fddbd5ee59a745 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 23 Feb 2026 09:32:08 -0600 Subject: [PATCH 049/115] feat(saml): enhance SAML provider loading with improved error handling and logging --- ...amlRelyingPartyRegistrationRepository.java | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java index 4c5744056..d1dd4b13e 100644 --- a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java @@ -39,53 +39,53 @@ public void reloadProviders(IdentityProviderConfigRepository jpaProviderReposito } private void loadProviders(IdentityProviderConfigRepository jpaProviderRepository) { - try { - List activeProviders = jpaProviderRepository.findAllByActiveTrue(); + try { + List activeProviders = jpaProviderRepository.findAllByActiveTrue(); - if (activeProviders.isEmpty()) { - return; - } - - activeProviders.forEach(entity -> { - try { - RelyingPartyRegistration registration = buildRelyingPartyRegistration(entity); - registrations.put(entity.getProviderType().name().toLowerCase(), registration); - log.info("Loaded SAML provider: {} (type: {})", entity.getName(), entity.getProviderType()); - } catch (Exception e) { - log.error("Failed to load SAML provider: {}", entity.getName(), e); + if (activeProviders.isEmpty()) { + return; } - }); - log.info("Successfully loaded {} SAML provider(s)", registrations.size()); - } catch (Exception e) { - log.error("Failed to load SAML providers: {}", e.getMessage(), e); - throw new ApiException(String.format("Failed to load SAML providers: %s", e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR); + activeProviders.forEach(entity -> { + try { + RelyingPartyRegistration registration = buildRelyingPartyRegistration(entity); + registrations.put(entity.getProviderType().name().toLowerCase(), registration); + log.info("Loaded SAML provider: {} (type: {})", entity.getName(), entity.getProviderType()); + } catch (Exception e) { + log.error("Failed to load SAML provider: {}", entity.getName(), e); + } + }); + + log.info("Successfully loaded {} SAML provider(s)", registrations.size()); + } catch (Exception e) { + log.error("Failed to load SAML providers: {}", e.getMessage(), e); + throw new ApiException(String.format("Failed to load SAML providers: %s", e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR); + } } -} private RelyingPartyRegistration buildRelyingPartyRegistration(IdentityProviderConfig entity) { - try { - String encryptionKey = System.getenv(Constants.ENV_ENCRYPTION_KEY); - if (encryptionKey == null || encryptionKey.isBlank()) { - throw new IllegalStateException( - "Environment variable " + Constants.ENV_ENCRYPTION_KEY + " not configured"); - } + try { + String encryptionKey = System.getenv(Constants.ENV_ENCRYPTION_KEY); + if (encryptionKey == null || encryptionKey.isBlank()) { + throw new IllegalStateException( + "Environment variable " + Constants.ENV_ENCRYPTION_KEY + " not configured"); + } - String decryptedKey = CipherUtil.decrypt(entity.getSpPrivateKeyPem(), encryptionKey); - PrivateKey spKey = PemUtils.parsePrivateKey(decryptedKey); - X509Certificate spCert = PemUtils.parseCertificate(entity.getSpCertificatePem()); + String decryptedKey = CipherUtil.decrypt(entity.getSpPrivateKeyPem(), encryptionKey); + PrivateKey spKey = PemUtils.parsePrivateKey(decryptedKey); + X509Certificate spCert = PemUtils.parseCertificate(entity.getSpCertificatePem()); - return RelyingPartyRegistrations - .fromMetadataLocation(entity.getMetadataUrl()) - .registrationId(entity.getName()) - .entityId(entity.getSpEntityId()) - .assertionConsumerServiceLocation(entity.getSpAcsUrl()) - .signingX509Credentials(c -> c.add(Saml2X509Credential.signing(spKey, spCert))) - .build(); - } catch (Exception e) { - log.error("Failed to build SAML registration for provider: {}", entity.getName(), e); - throw new ApiException(String.format("Failed to build SAML registration for provider: %s", entity.getName()), HttpStatus.INTERNAL_SERVER_ERROR); + return RelyingPartyRegistrations + .fromMetadataLocation(entity.getMetadataUrl()) + .registrationId(entity.getName()) + .entityId(entity.getSpEntityId()) + .assertionConsumerServiceLocation(entity.getSpAcsUrl()) + .signingX509Credentials(c -> c.add(Saml2X509Credential.signing(spKey, spCert))) + .build(); + } catch (Exception e) { + log.error("Failed to build SAML registration for provider: {}", entity.getName(), e); + throw new ApiException(String.format("Failed to build SAML registration for provider: %s", entity.getName()), HttpStatus.INTERNAL_SERVER_ERROR); + } } -} } \ No newline at end of file From b99c596ae11e5d167dbb16d3598c568ac33fced6 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 23 Feb 2026 10:35:01 -0600 Subject: [PATCH 050/115] feat(correlation): add updates for winevent correlation rules --- ...3002_update_winevent_correlation_rules.xml | 35 + .../data/20260223/utm_correlation_rules.sql | 2314 +++++++++++++++++ .../20260223/utm_group_rules_data_type.sql | 221 ++ 3 files changed, 2570 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260223002_update_winevent_correlation_rules.xml create mode 100644 backend/src/main/resources/config/liquibase/data/20260223/utm_correlation_rules.sql create mode 100644 backend/src/main/resources/config/liquibase/data/20260223/utm_group_rules_data_type.sql diff --git a/backend/src/main/resources/config/liquibase/changelog/20260223002_update_winevent_correlation_rules.xml b/backend/src/main/resources/config/liquibase/changelog/20260223002_update_winevent_correlation_rules.xml new file mode 100644 index 000000000..77917533f --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260223002_update_winevent_correlation_rules.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/data/20260223/utm_correlation_rules.sql b/backend/src/main/resources/config/liquibase/data/20260223/utm_correlation_rules.sql new file mode 100644 index 000000000..287c15d2f --- /dev/null +++ b/backend/src/main/resources/config/liquibase/data/20260223/utm_correlation_rules.sql @@ -0,0 +1,2314 @@ +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (879, 'Windows: Signed Proxy Execution via MS Work Folders', 1, 2, 3, 'Defense Evasion', 'T1218 - System Binary Proxy Execution', 'Identifies the use of Windows Work Folders to execute a potentially masqueraded control.exe file in the current working directory. Misuse of Windows Work Folders could indicate malicious activity.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1218/"]', 'contains("log.eventDataProcessName", "control.exe") && contains("log.eventDataParentProcessName", "workfolders.exe") && !regexMatch("log.eventDataProcessName", "(:\\Windows\\(System32|SysWOW64)\\control.exe)")', '2026-02-23 16:15:10.139988', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (880, 'Windows: Wireless Credential Dumping using Netsh Command', 3, 3, 2, 'Credential Access', 'T1003 - OS Credential Dumping', 'Identifies attempts to dump Wireless saved access keys in clear text using the Windows built-in utility Netsh.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/"]', 'contains("log.message", "wlan") && regexMatch("log.message", "(key(.+)clear)") && contains("log.eventDataProcessName", "netsh.exe")', '2026-02-23 16:15:11.418994', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (881, 'Windows: Unusual Process Network Connection', 3, 3, 2, 'Defense Evasion', 'Trusted Developer Utilities Proxy Execution', 'Identifies network activity from unexpected system applications. This may indicate adversarial activity as these applications are often leveraged by adversaries to execute code and evade detection.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1127/"]', 'regexMatch("log.eventDataProcessName", "(Microsoft.Workflow.Compiler.exe|bginfo.exe|cdb.exe|cmstp.exe|csi.exe|dnx.exe|fsi.exe|ieexec.exe|iexpress.exe|odbcconf.exe|rcsi.exe|xwizard.exe)")', '2026-02-23 16:15:12.474670', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataProcessID","operator":"filter_term","value":"{{log.eventDataProcessID}}"}],"or":null,"within":"now-5m","count":3}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (882, 'Windows: Unusual Child Process of dns.exe', 1, 3, 2, 'Initial Access', 'T1133 - External Remote Services', 'Identifies an unexpected process spawning from dns.exe, the process responsible for Windows DNS server services, which may indicate activity related to remote code execution or other forms of exploitation.', '["https://attack.mitre.org/tactics/TA0001/","https://attack.mitre.org/techniques/T1133/"]', 'contains("log.eventDataProcessName", "dns.exe") && !contains("log.eventDataParentProcessName", "conhost.exe")', '2026-02-23 16:15:13.474465', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (883, 'Windows: System Shells via Services', 1, 3, 2, 'Persistence', 'T1543.003 - Create or Modify System Process: Windows Service', 'Windows services typically run as SYSTEM and can be used as a privilege escalation opportunity. Malware or penetration testers may run a shell as a service to gain SYSTEM permissions.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1543/003/"]', e'regexMatch("log.eventDataProcessName", "(cmd.exe|powershell.exe|pwsh.exe|powershell_ise.exe)") && + contains("log.eventDataParentProcessName", "services.exe") && + !(regexMatch("log.message", "(NVDisplay.ContainerLocalSystem)")) +', '2026-02-23 16:15:14.566192', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (884, 'Windows: Symbolic Link to Shadow Copy Created', 1, 3, 2, 'Credential Access', 'T1003 - OS Credential Dumping', 'Detects creation of a symbolic link to a volume shadow copy. Adversaries may use this technique to access and exfiltrate sensitive data such as NTDS.dit or SAM database from shadow copies.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/"]', 'regexMatch("log.eventDataProcessName", "(cmd.exe|powershell.exe|pwsh.exe|powershell_ise.exe)") && regexMatch("log.message", "(?i)(mklink|New-Item.*SymbolicLink)") && contains("log.message", "HarddiskVolumeShadowCopy")', '2026-02-23 16:15:15.645080', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (885, 'Windows: Suspicious RDP ActiveX Client Loaded', 1, 3, 2, 'Lateral Movement', 'T1021 - Remote Services', 'Identifies suspicious Image Loading of the Remote Desktop Services ActiveX Client (mstscax), this may indicate the presence of RDP lateral movement capability.', '["https://attack.mitre.org/tactics/TA0008/","https://attack.mitre.org/techniques/T1021/"]', e'!(regexMatch("log.eventDataProcessName", "(C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\mstsc\\\\.exe|C:\\\\\\\\Windows\\\\\\\\SysWOW64\\\\\\\\mstsc\\\\.exe)")) && +regexMatch("log.eventDataProcessName", "(C:\\\\\\\\Windows\\\\\\\\|C:\\\\\\\\Users\\\\\\\\Public\\\\\\\\|C:\\\\\\\\Users\\\\\\\\Default\\\\\\\\|C:\\\\\\\\Intel\\\\\\\\|C:\\\\\\\\PerfLogs\\\\\\\\|C:\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Device\\\\\\\\Mup\\\\\\\\|\\\\\\\\\\\\\\\\)") && +contains("log.message", "mstscax.dll") +', '2026-02-23 16:15:16.831651', true, true, 'target', null, '[]', '["target.host","target.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (886, 'Windows: Suspicious Process Execution via Renamed PsExec Executable', 1, 3, 2, 'Execution', 'T1569 - System Services', 'Identifies suspicious psexec activity which is executing from the psexec service that has been renamed, possibly to vade detection.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1569/"]', 'equals("log.eventDataEventType", "start") && (contains("log.eventDataProcessName", "PSEXESVC.exe") || contains("log.eventDataOriginalFileName", "psexesvc.exe"))', '2026-02-23 16:15:17.881604', true, true, 'target', null, '[]', '["target.host","target.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (887, 'Windows: Suspicious Execution via Scheduled Task', 1, 3, 2, 'Persistence', 'T1053.005 - Scheduled Task', 'Identifies execution of a suspicious program via scheduled tasks by looking at process lineage and command line usage.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1053/005/"]', e'equals("log.eventDataEventType", "start") && contains("log.eventDataProcessParentName", "svchost.exe") && contains("log.eventDataProcessParentArgs", "Schedule") && +regexMatch("log.eventDataOriginalFileName", "(cscript.exe|wscript.exe|PowerShell.EXE|Cmd.Exe|MSHTA.EXE|RUNDLL32.EXE|REGSVR32.EXE|MSBuild.exe|InstallUtil.exe|RegAsm.exe|RegSvcs.exe|msxsl.exe|CONTROL.EXE|EXPLORER.EXE|Microsoft.Workflow.Compiler.exe|msiexec.exe)") && +regexMatch("log.eventDataProcessArgs", "(C:\\\\\\\\Users\\\\\\\\|C:\\\\\\\\ProgramData\\\\\\\\|C:\\\\\\\\Windows\\\\\\\\Temp\\\\\\\\|C:\\\\\\\\Windows\\\\\\\\Tasks\\\\\\\\|C:\\\\\\\\PerfLogs\\\\\\\\|C:\\\\\\\\Intel\\\\\\\\|C:\\\\\\\\Windows\\\\\\\\Debug\\\\\\\\|C:\\\\\\\\HP\\\\\\\\)") && +!regexMatch("log.eventDataProcessName", "(cmd.exe|cscript.exe|powershell.exe|msiexec.exe)") && +!regexMatch("log.eventDataProcessArgs", "(:\\\\\\\\(.+).bat|:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\calluxxprovider.vbs|-File|-PSConsoleFile)") && +!regexMatch("log.eventDataUserId", "(S-1-5-18)") && !regexMatch("log.eventDataWorkingDirectory", "(:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\)") +', '2026-02-23 16:15:18.952423', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (888, 'Windows: Suspicious PowerShell Engine ImageLoad', 1, 3, 2, 'Execution', 'T1059 - Command and Scripting Interpreter', 'Identifies the PowerShell engine being invoked by unexpected processes. Rather than executing PowerShell functionality with powershell.exe, some attackers do this to operate more stealthily.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1059/"]', e'!(oneOf("log.eventDataProcessName", ["Altaro.SubAgent.exe", "AppV_Manage.exe", "azureadconnect.exe", "CcmExec.exe", "configsyncrun.exe", "choco.exe", "ctxappvservice.exe", "DVLS.Console.exe", "edgetransport.exe", "exsetup.exe", "forefrontactivedirectoryconnector.exe", "InstallUtil.exe", "JenkinsOnDesktop.exe", "Microsoft.EnterpriseManagement.ServiceManager.UI.Console.exe", "mmc.exe", "mscorsvw.exe", "msexchangedelivery.exe", "msexchangefrontendtransport.exe", "msexchangehmworker.exe", "msexchangesubmission.exe", "msiexec.exe", "MsiExec.exe", "noderunner.exe", "NServiceBus.Host.exe", "NServiceBus.Host32.exe", "NServiceBus.Hosting.Azure.HostProcess.exe", "OuiGui.WPF.exe", "powershell.exe", "powershell_ise.exe", "pwsh.exe", "SCCMCliCtrWPF.exe", "ScriptEditor.exe", "ScriptRunner.exe", "sdiagnhost.exe", "servermanager.exe", "setup100.exe", "ServiceHub.VSDetouredHost.exe", "SPCAF.Client.exe", "SPCAF.SettingsEditor.exe", "SQLPS.exe", "telemetryservice.exe", "UMWokerProcess.exe", "w3wp.exe", "wsmprovhost.exe"])) && +!(regexMatch("log.eventDataProcessName", "(C:\\\\Windows\\\\System32\\\\RemoteFXvGPUDisablement.exe|C:\\\\Windows\\\\System32\\\\sdiagnhost.exe|C:\\\\Program Files( \\\\(x86\\\\))?\\\\(.+)\\\\.exe)")) && +oneOf("log.message", ["System.Management.Automation.ni.dll", "System.Management.Automation.dll"]) +', '2026-02-23 16:15:19.969203', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (889, 'Windows: Suspicious Process Access via Direct System Call', 1, 3, 2, 'Defense Evasion', 'T1055 - Process Injection', 'Identifies suspicious process access events from an unknown memory region. Endpoint security solutions usually hook userland Windows APIs in order to decide if the code that is being executed is malicious or not. It''s possible to bypass hooked functions by writing malicious functions that call syscalls directly.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1055/"]', e'equals("log.eventCode", 10) && !(regexMatch("log.eventDataCallTrace", "(:\\\\WINDOWS\\\\SYSTEM32\\\\ntdll.dll|:\\\\WINDOWS\\\\SysWOW64\\\\ntdll.dll|:\\\\Windows\\\\System32\\\\wow64cpu.dll|:\\\\WINDOWS\\\\System32\\\\wow64win.dll|:\\\\Windows\\\\System32\\\\win32u.dll)")) && +!(regexMatch("log.eventDataTargetImage", "(:\\\\WINDOWS\\\\system32\\\\lsass.exe|:\\\\Program Files (x86)\\\\Malwarebytes Anti-Exploit\\\\mbae-svc.exe|:\\\\Program Files\\\\Cisco\\\\AMP\\\\(.+)\\\\sfc.exe|:\\\\Program Files (x86)\\\\Microsoft\\\\EdgeWebView\\\\Application\\\\(.+)\\\\msedgewebview2.exe|:\\\\Program Files\\\\Adobe\\\\Acrobat DC\\\\Acrobat\\\\(.+)\\\\AcroCEF.exe)")) && +!(regexMatch("log.eventDataProcessName", "(:\\\\Program Files\\\\Adobe\\\\Acrobat DC\\\\Acrobat\\\\Acrobat.exe|:\\\\Program Files (x86)\\\\World of Warcraft\\\\_classic_\\\\WowClassic.exe)")) +', '2026-02-23 16:15:20.977194', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (890, 'Windows: Suspicious PDF Reader Child Process', 1, 1, 2, 'Execution', 'T1204 - User Execution', 'Identifies suspicious child processes of PDF reader applications. These child processes are often launched via exploitation of PDF applications or social engineering.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1204/"]', 'oneOf("log.eventDataParentProcessName", ["AcroRd32.exe", "Acrobat.exe", "FoxitPhantomPDF.exe", "FoxitReader.exe"]) && oneOf("log.eventDataProcessName", ["arp.exe", "dsquery.exe", "dsget.exe", "gpresult.exe", "hostname.exe", "ipconfig.exe", "nbtstat.exe", "net.exe", "net1.exe", "netsh.exe", "netstat.exe", "nltest.exe", "ping.exe", "qprocess.exe", "quser.exe", "qwinsta.exe", "reg.exe", "sc.exe", "systeminfo.exe", "tasklist.exe", "tracert.exe", "whoami.exe", "bginfo.exe", "cdb.exe", "cmstp.exe", "csi.exe", "dnx.exe", "fsi.exe", "ieexec.exe", "iexpress.exe", "installutil.exe", "Microsoft.Workflow.Compiler.exe", "msbuild.exe", "mshta.exe", "msxsl.exe", "odbcconf.exe", "rcsi.exe", "regsvr32.exe", "xwizard.exe", "atbroker.exe", "forfiles.exe", "schtasks.exe", "regasm.exe", "regsvcs.exe", "cmd.exe", "cscript.exe", "powershell.exe", "pwsh.exe", "wmic.exe", "wscript.exe", "bitsadmin.exe", "certutil.exe", "ftp.exe"])', '2026-02-23 16:15:22.100610', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (891, 'Windows: Suspicious MS Outlook Child Process', 1, 3, 2, 'Initial Access', 'T1566.001 - Phishing: Spearphishing Attachment', 'Identifies suspicious child processes of Microsoft Outlook. These child processes are often associated with spear phishing activity.', '["https://attack.mitre.org/tactics/TA0001/","https://attack.mitre.org/techniques/T1566/001/"]', 'oneOf("log.eventDataProcessName", ["Microsoft.Workflow.Compiler.exe", "arp.exe", "atbroker.exe", "bginfo.exe", "bitsadmin.exe", "cdb.exe", "certutil.exe", "cmd.exe", "cmstp.exe", "cscript.exe", "csi.exe", "dnx.exe", "dsget.exe", "dsquery.exe", "forfiles.exe", "fsi.exe", "ftp.exe", "gpresult.exe", "hostname.exe", "ieexec.exe", "iexpress.exe", "installutil.exe", "ipconfig.exe", "mshta.exe", "msxsl.exe", "nbtstat.exe", "net.exe", "net1.exe", "netsh.exe", "netstat.exe", "nltest.exe", "odbcconf.exe", "ping.exe", "powershell.exe", "pwsh.exe", "qprocess.exe", "quser.exe", "qwinsta.exe", "rcsi.exe", "reg.exe", "regasm.exe", "regsvcs.exe", "regsvr32.exe", "sc.exe", "schtasks.exe", "systeminfo.exe", "tasklist.exe", "tracert.exe", "whoami.exe", "wmic.exe", "wscript.exe", "xwizard.exe"]) && contains("log.eventDataParentProcessName", "outlook.exe")', '2026-02-23 16:15:23.157906', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (892, 'Windows: Suspicious MS Office Child Process', 1, 2, 3, 'Initial Access', 'T1566.001 - Phishing: Spearphishing Attachment', 'Identifies suspicious child processes of frequently targeted Microsoft Office applications (Word, PowerPoint, Excel). These child processes are often launched during exploitation of Office applications or from documents with malicious macros.', '["https://attack.mitre.org/tactics/TA0001/","https://attack.mitre.org/techniques/T1566/001/"]', 'oneOf("log.eventDataProcessName", ["Microsoft.Workflow.Compiler.exe", "arp.exe", "atbroker.exe", "bginfo.exe", "bitsadmin.exe", "cdb.exe", "certutil.exe", "cmd.exe", "cmstp.exe", "control.exe", "cscript.exe", "csi.exe", "dnx.exe", "dsget.exe", "dsquery.exe", "forfiles.exe", "fsi.exe", "ftp.exe", "gpresult.exe", "hostname.exe", "ieexec.exe", "iexpress.exe", "installutil.exe", "ipconfig.exe", "mshta.exe", "msxsl.exe", "nbtstat.exe", "net.exe", "net1.exe", "netsh.exe", "netstat.exe", "nltest.exe", "odbcconf.exe", "ping.exe", "powershell.exe", "pwsh.exe", "qprocess.exe", "quser.exe", "qwinsta.exe", "rcsi.exe", "reg.exe", "regasm.exe", "regsvcs.exe", "regsvr32.exe", "sc.exe", "schtasks.exe", "systeminfo.exe", "tasklist.exe", "tracert.exe", "whoami.exe", "wmic.exe", "wscript.exe", "xwizard.exe", "explorer.exe", "rundll32.exe", "hh.exe", "msdt.exe"]) && oneOf("log.eventDataParentProcessName", ["eqnedt32.exe", "excel.exe", "fltldr.exe", "msaccess.exe", "mspub.exe", "powerpnt.exe", "winword.exe", "outlook.exe"])', '2026-02-23 16:15:24.176084', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (893, 'Windows: Microsoft Exchange Worker Spawning Suspicious Processes', 2, 3, 2, 'Initial Access', 'T1190 - Exploit Public-Facing Application', 'Identifies suspicious processes being spawned by the Microsoft Exchange Server worker process (w3wp). This activity may indicate exploitation activity or access to an existing web shell backdoor.', '["https://attack.mitre.org/tactics/TA0001/","https://attack.mitre.org/techniques/T1190/"]', 'contains("log.eventDataParentProcessName", "w3wp.exe") && regexMatch("log.message", "(MSExchange(.+)AppPool)") && regexMatch("log.eventDataProcessName", "(md.exe|powershell.exe|pwsh.dll|powershell_ise.exe)")', '2026-02-23 16:15:25.158589', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (894, 'Windows: Microsoft Exchange Worker Spawning Suspicious Processes', 2, 3, 2, 'Initial Access', 'T1190 - Exploit Public-Facing Application', 'Identifies suspicious processes being spawned by the Microsoft Exchange Server worker process (w3wp). This activity may indicate exploitation activity or access to an existing web shell backdoor.', '["https://attack.mitre.org/tactics/TA0001/","https://attack.mitre.org/techniques/T1190/"]', 'regexMatch("log.eventDataParentProcessName", "(UMService.exe|UMWorkerProcess.exe)") && !(regexMatch("log.eventDataProcessName", "(:\\Windows\\System32\\werfault.exe|:\\Windows\\System32\\wermgr.exe|:\\Program Files\\Microsoft\\Exchange Server\\V(.+)\\Bin\\UMWorkerProcess.exe|D:\\Exchange 2016\\Bin\\UMWorkerProcess.exe|E:\\ExchangeServer\\Bin\\UMWorkerProcess.exe)"))', '2026-02-23 16:15:26.215882', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (895, 'Windows: Suspicious Managed Code Hosting Process', 3, 3, 2, 'Defense Evasion', 'T1055 - Process Injection', 'Identifies a suspicious managed code hosting process which could indicate code injection or other form of suspicious code execution.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1055/"]', 'regexMatch("log.eventDataProcessName", "(wscript.exe|cscript.exe|mshta.exe|wmic.exe|regsvr32.exe|svchost.exe|dllhost.exe|cmstp.exe)")', '2026-02-23 16:15:27.311555', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataProcessName","operator":"filter_term","value":"{{log.eventDataProcessName}}"},{"field":"log.eventdataProcessID","operator":"filter_term","value":"{{log.eventdataProcessID}}"}],"or":null,"within":"now-5m","count":3}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (896, 'Windows: Potential LSASS Memory Dump via PssCaptureSnapShot', 3, 3, 2, 'Credential Access', 'T1003 - OS Credential Dumping', 'Identifies suspicious access to an LSASS handle via PssCaptureSnapShot where two successive process accesses are performed by the same process and target two different instances of LSASS. This may indicate an attempt to evade detection and dump LSASS memory for credential access.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/"]', 'equals("log.eventId", "10") && regexMatch("log.eventDataTargetImage", "([Cc]:\\Windows\\[Ss]ystem32\\lsass.exe)")', '2026-02-23 16:15:28.444660', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (897, 'Windows: Potential Credential Access via LSASS Memory Dump', 3, 3, 2, 'Credential Access', 'T1003 - OS Credential Dumping', 'Identifies suspicious access to LSASS handle from a call trace pointing to DBGHelp.dll or DBGCore.dll, which both export the MiniDumpWriteDump method that can be used to dump LSASS memory content in preparation for credential access.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/"]', 'equals("log.eventId", "10") && regexMatch("log.eventDataTargetImage", "(C:\\\\WINDOWS\\\\system32\\\\lsass.exe)") && regexMatch("log.eventDataCallTrace", "(dbghelp|dbgcore)") && !regexMatch("log.eventDataProcessName", "(C:\\\\Windows\\\\System32\\\\WerFault.exe|C:\\\\Windows\\\\System32\\\\WerFaultSecure.exe)")', '2026-02-23 16:15:29.628201', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (898, 'Windows: Remote Computer Account DnsHostName Update', 3, 3, 2, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Identifies the remote update to a computer account''s DnsHostName attribute. If the new value set is a valid domain controller DNS hostname and the subject computer name is not a domain controller, then it''s highly likely a preparation step to exploit CVE-2022-26923 in an attempt to elevate privileges from a standard domain user to domain admin privileges.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1068/"]', 'equals("log.action", "logged-in") && regexMatch("actionResult", "success") && !contains("log.userName", "ANONYMOUS LOGON") && !contains("log.eventDataSubjectUserName", "ANONYMOUS LOGON") && startsWith("log.eventDataSubjectUserName", "$") && !contains("log.userDomain", "NT AUTHORITY")', '2026-02-23 16:15:30.803228', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (899, 'Windows: Potential Credential Access via Renamed COM+ Services DLL', 3, 3, 2, 'Credential Access', 'T1003 - OS Credential Dumping', 'Identifies suspicious renamed COMSVCS.DLL Image Load, which exports the MiniDump function that can be used to dump a process memory. This may indicate an attempt to dump LSASS memory while bypassing command-line based detection in preparation for credential access.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/001/"]', 'contains("log.eventCategory", "process") && contains("log.eventProcessName", "rundll32.exe") && regexMatch("log.eventDataset", "(windows.sysmon_operational)") && equals("log.eventId", "7") && !equals("log.fileName", "COMSVCS.DLL") && (regexMatch("log.filePeOriginalFileName", "(COMSVCS.DLL)") || regexMatch("log.filePeImphash", "(EADBCCBB324829ACB5F2BBE87E5549A8)"))', '2026-02-23 16:15:31.977408', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.processEntityId","operator":"filter_term","value":"{{log.processEntityId}}"},{"field":"log.eventCategory","operator":"filter_term","value":"process"}],"or":null,"within":"now-5m","count":3}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (900, 'Windows: Service Control Spawned via Script Interpreter', 2, 3, 2, 'Lateral Movement', 'T1021 - Remote Services', 'Identifies Service Control (sc.exe) spawning from script interpreter processes to create, modify, or start services. This could be indicative of adversary lateral movement but will be noisy if commonly done by admins.', '["https://attack.mitre.org/tactics/TA0008/","https://attack.mitre.org/techniques/T1021/"]', 'oneOf("log.eventDataParentProcessName", ["cmd.exe", "wscript.exe", "rundll32.exe", "regsvr32.exe", "wmic.exe", "mshta.exe", "powershell.exe", "pwsh.exe"]) && oneOf("log.message", ["config", "create", "start", "delete", "stop", "pause"]) && !equals("log.eventDataSubjectUserName", "S-1-5-18") && contains("log.eventDataProcessName", "sc.exe")', '2026-02-23 16:15:33.127722', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (901, 'Windows: Sensitive Privilege SeEnableDelegationPrivilege assigned to a User', 3, 3, 1, 'Credential Access', 'T1212 - Exploitation for Credential Access', 'Identifies the assignment of the SeEnableDelegationPrivilege sensitive user right to a user. The SeEnableDelegationPrivilege user right enables computer and user accounts to be trusted for delegation. Attackers can abuse this right to compromise Active Directory accounts and elevate their privileges.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1212/"]', 'regexMatch("action", "([Aa]uthorization [Pp]olicy [Cc]hange)") && equals("log.eventCode", 4704) && contains("log.eventDataPrivilegeList", "SeEnableDelegationPrivilege")', '2026-02-23 16:15:34.306254', true, true, 'target', null, '[]', '["target.host","target.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (902, 'Windows: Security Software Discovery using WMIC', 3, 2, 1, 'Discovery', 'T1518.001 - Software Discovery: Security Software Discovery', 'Identifies the use of Windows Management Instrumentation Command (WMIC) to discover certain System Security Settings such as AntiVirus or Host Firewall details.', '["https://attack.mitre.org/tactics/TA0007/","https://attack.mitre.org/techniques/T1518/001/"]', 'regexMatch("log.message", "(namespace:\\\\root\\SecurityCenter2)") && contains("log.message", "Get") && contains("log.eventDataProcessName", "wmic.exe")', '2026-02-23 16:15:35.458891', true, true, 'origin', '["adversary.user","adversary.ip"]', '[]', null); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (903, 'Windows: Potential Secure File Deletion via SDelete Utility', 3, 2, 2, 'Defense Evasion', 'T1070.004 - Indicator Removal: File Deletion', 'Detects file name patterns generated by the use of Sysinternals SDelete utility to securely delete a file via multiple file overwrite and rename operations.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1070/004/"]', 'equals("log.eventDataEventType", "change") && contains("log.eventDataFileName", "AAA.AAA")', '2026-02-23 16:15:36.546981', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (979, 'Windows: SeDebugPrivilege Enabled by a Suspicious Process', 1, 3, 2, 'Privilege Escalation', 'T1134 - Access Token Manipulation', 'Identifies the creation of a process running as SYSTEM and impersonating a Windows core binary privileges. Adversaries may create a new process with a different token to escalate privileges and bypass access controls.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1134/"]', 'regexMatch("log.action", "(Token Right Adjusted Events)") && regexMatch("log.eventProvider", "(Microsoft-Windows-Security-Auditing)") && regexMatch("log.eventDataEnabledPrivilegeList", "(SeDebugPrivilege)") && !(oneOf("log.eventDataSubjectUserSid", ["S-1-5-18", "S-1-5-19", "S-1-5-20"])) && !(regexMatch("log.eventDataProcessName", "(:\\Windows\\System32\\msiexec.exe|:\\Windows\\SysWOW64\\msiexec.exe|:\\Windows\\System32\\lsass.exe|:\\Windows\\WinSxS\\|:\\Program Files\\|:\\Program Files (x86)\\|:\\Windows\\System32\\MRT.exe|:\\Windows\\System32\\cleanmgr.exe|:\\Windows\\System32\\taskhostw.exe|:\\Windows\\System32\\mmc.exe|:\\Users\\(.+)\\AppData\\Local\\Temp\\(.+)-(.+)\\DismHost.exe|:\\Windows\\System32\\auditpol.exe|:\\Windows\\System32\\wbem\\WmiPrvSe.exe|:\\Windows\\SysWOW64\\wbem\\WmiPrvSe.exe)"))', '2026-02-23 16:17:01.932948', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (980, 'Windows: Suspicious WMIC XSL Script Execution', 2, 3, 2, 'Defense Evasion', 'T1220 - XSL Script Processing', 'Identifies WMIC allowlist bypass techniques by alerting on suspicious execution of scripts. When WMIC loads scripting libraries it may be indicative of an allowlist bypass.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1220/"]', 'regexMatch("log.message", "(?i)wmic.*format") && !contains("log.message", "/format:table") && regexMatch("log.message", "(?i)(jscript\\.dll|vbscript\\.dll)")', '2026-02-23 16:17:03.063606', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (981, 'Windows: Microsoft Exchange Server UM Writing Suspicious Files', 2, 3, 2, 'Initial Access', 'T1190 - Exploit Public-Facing Application', 'Identifies suspicious files being written by the Microsoft Exchange Server Unified Messaging (UM) service. This activity has been observed exploiting CVE-2021-26858.', '["https://attack.mitre.org/tactics/TA0001/","https://attack.mitre.org/techniques/T1190/"]', 'regexMatch("log.eventDataProcessName", "(UMWorkerProcess.exe|umservice.exe)") && regexMatch("log.message", "(php|jsp|js|aspx|asmx|asax|cfm|shtml)") && regexMatch("log.message", "(:\\\\inetpub\\\\wwwroot\\\\aspnet_client\\\\|:\\\\(.+)\\\\Microsoft\\\\Exchange Server(.+)\\\\FrontEnd\\\\HttpProxy\\\\owa\\\\auth\\\\)") && !regexMatch("log.message", "(:\\\\(.+)\\\\Microsoft\\\\Exchange Server(.+)\\\\FrontEnd\\\\HttpProxy\\\\(owa|ecp)\\\\auth\\\\version\\\\)")', '2026-02-23 16:17:04.093307', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (982, 'Windows: Suspicious WMI Image Load from MS Office', 1, 2, 3, 'Execution', 'T1047 - Windows Management Instrumentation', 'Identifies a suspicious image load (wmiutils.dll) from Microsoft Office processes. This behavior may indicate adversarial activity where child processes are spawned via Windows Management Instrumentation (WMI). This technique can be used to execute code and evade traditional parent/child processes spawned from Microsoft Office products.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1047/"]', 'regexMatch("log.eventDataProcessName", "(WINWORD.EXE|EXCEL.EXE|POWERPNT.EXE|MSPUB.EXE|MSACCESS.EXE)") && contains("log.message", "wmiutils.dll")', '2026-02-23 16:17:05.147941', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (983, 'Windows: Suspicious Image Load (taskschd.dll) from MS Office', 1, 2, 3, 'Persistence', 'T1053 - Scheduled Task/Job', 'Identifies a suspicious image load (taskschd.dll) from Microsoft Office processes. This behavior may indicate adversarial activity where a scheduled task is configured via Windows Component Object Model (COM). This technique can be used to configure persistence and evade monitoring by avoiding the usage of the traditional Windows binary (schtasks.exe) used to manage scheduled tasks.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1053/"]', 'regexMatch("log.eventDataProcessName", "(WINWORD.EXE|EXCEL.EXE|POWERPNT.EXE|MSPUB.EXE|MSACCESS.EXE)") && contains("log.message", "taskschd.dll")', '2026-02-23 16:17:06.220563', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (984, 'Windows: Suspicious Execution from mounted device', 1, 2, 3, 'Defense Evasion', 'T1055 - Process Injection', 'Identifies suspicious process access events from an unknown memory region. Endpoint security solutions usually hook userland Windows APIs in order to decide if the code that is being executed is malicious or not. It''s possible to bypass hooked functions by writing malicious functions that call syscalls directly.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1055/"]', 'equals("log.eventType", "start") && regexMatch("log.process.executable", "(^C:\\\\)") && regexMatch("log.processWorkingDirectory", "((^\\w:\\\\)") && !regexMatch("log.processWorkingDirectory", "(^C:\\\\)") && contains("log.processParentName", "explorer.exe") && oneOf("log.processName", ["rundll32.exe", "mshta.exe", "powershell.exe", "pwsh.exe", "cmd.exe", "regsvr32.exe", "cscript.exe", "wscript.exe"])', '2026-02-23 16:17:07.496247', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (985, 'Windows: New Windows Service Created to start from windows root path. Suspicious event as the binary may have been dropped using Windows Admin Shares', 1, 2, 3, 'Execution', 'T1021.002 - Remote Services: SMB/Windows Admin Shares', 'Adversaries may use Valid Accounts to interact with a remote network share using Server Message Block (SMB). The adversary may then perform actions as the logged-on user.', '["https://attack.mitre.org/techniques/T1021/002/"]', 'regexMatch("log.eventDataImagePath", "(^%systemroot%\\(.+)\\(.+).exe)") && equals("log.eventCode", 7045) && oneOf("log.logName", ["system", "System"])', '2026-02-23 16:17:08.698420', true, true, 'target', null, '[]', '["target.host","target.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (904, 'Windows Script Interpreter Executing Process via WMI', 2, 2, 2, 'Initial Access', 'T1566.001 - Phishing: Spearphishing Attachment', 'Identifies use of the built-in Windows script interpreters (cscript.exe or wscript.exe) being used to execute a process via Windows Management Instrumentation (WMI). This may be indicative of malicious activity.', '["https://attack.mitre.org/tactics/TA0001/","https://attack.mitre.org/techniques/T1566/001/"]', 'oneOf("log.processName", ["wscript.exe", "cscript.exe"]) && contains("log.message", "wmiutils.dll")', '2026-02-23 16:15:37.737643', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (905, 'Windows: Remote Scheduled Task Creation', 2, 2, 2, 'Lateral Movement', 'T1021 - Remote Services', 'Identifies remote scheduled task creations on a target host. This could be indicative of adversary lateral movement.', '["https://attack.mitre.org/tactics/TA0008/","https://attack.mitre.org/techniques/T1021/"]', 'contains("log.processName", "svchost.exe") && oneOf("log.eventDataSourceNetworkAddress", ["incoming", "ingress"]) && greaterOrEqual("origin.port", 49152) && greaterOrEqual("target.port", 49152) && !oneOf("origin.ip", ["127.0.0.1", "::1"])', '2026-02-23 16:15:38.847150', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (906, 'Windows Script Executing PowerShell', 2, 3, 2, 'Initial Access', 'T1566.001 - Phishing: Spearphishing Attachment', 'Identifies a PowerShell process launched by either cscript.exe or wscript.exe. Observing Windows scripting processes executing a PowerShell script, may be indicative of malicious activity.', '["https://attack.mitre.org/tactics/TA0001/","https://attack.mitre.org/techniques/T1566/001/"]', 'equals("log.eventDataEventType", "start") && oneOf("log.eventDataParentProcessName", ["cscript.exe", "wscript.exe"]) && contains("log.eventDataProcessName", "powershell.exe")', '2026-02-23 16:15:40.032098', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (907, 'Windows: Searching for Saved Credentials via VaultCmd', 3, 2, 1, 'Credential Access', 'T1555 - Credentials from Password Stores', 'Windows Credential Manager allows you to create, view, or delete saved credentials for signing into websites, connected applications, and networks. An adversary may abuse this to list or dump credentials stored in the Credential Manager for saved usernames and passwords. This may also be performed in preparation of lateral movement.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1555/"]', 'contains("log.eventDataProcessName", "vaultcmd.exe") && contains("log.message", "/list")', '2026-02-23 16:15:41.126817', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (908, 'Windows: Multiple Vault Web Credentials Read', 2, 3, 2, 'Credential Access', 'T1555.004 - Credentials from Password Stores: Windows Credential Manager', 'Windows Credential Manager allows you to create, view, or delete saved credentials for signing into websites, connected applications, and networks. An adversary may abuse this to list or dump credentials stored in the Credential Manager for saved usernames and passwords. This may also be performed in preparation of lateral movement.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1555/004/"]', 'equals("log.eventCode", 5382) && !equals("log.eventDataSubjectLogonId", "0x3e7") && (contains("log.eventDataSchemaFriendlyName", "Windows Web Password Credential") || contains("log.eventDataResource", "http"))', '2026-02-23 16:15:42.273418', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataProcessPid","operator":"filter_term","value":"{{log.eventDataProcessPid}}"},{"field":"log.eventCode","operator":"filter_term","value":"5382"},{"field":"log.eventDataSubjectLogonId","operator":"filter_not_match","value":"0x3e7"}],"or":[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataSchemaFriendlyName","operator":"filter_term","value":"Windows Web Password Credential"},{"field":"log.eventDataResource","operator":"filter_term","value":"http"}],"or":null,"within":"now-60s","count":1}],"within":"now-60s","count":1}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (909, 'Windows: Outbound Scheduled Task Activity via PowerShell', 2, 3, 2, 'Execution', 'T1053.005 - Scheduled Task', 'Identifies the PowerShell process loading the Task Scheduler COM DLL followed by an outbound RPC network connection within a short time period. This may indicate lateral movement or remote discovery via scheduled tasks.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1053/005/"]', 'oneOf("log.eventDataProcessName", ["powershell.exe", "pwsh.exe", "powershell_ise.exe"]) && regexMatch("log.message", "(powershell.exe|pwsh.exe|powershell_ise.exe)")', '2026-02-23 16:15:43.406951', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (910, 'Windows: Potential Privileged Escalation via SamAccountName Spoofing', 2, 3, 1, 'Privilege Escalation', 'T1078 - Valid Accounts', 'Identifies a suspicious computer account name rename event, which may indicate an attempt to exploit CVE-2021-42278 to elevate privileges from a standard domain user to a user with domain admin privileges. CVE-2021-42278 is a security vulnerability that allows potential attackers to impersonate a domain controller via samAccountName attribute spoofing.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1078/"]', 'equals("action", "renamed-user-account") && endsWith("target.user", "$") && !endsWith("log.eventDataNewTargetUserName", "$")', '2026-02-23 16:15:44.589033', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (911, 'Windows: Execution of Persistent Suspicious Program', 2, 3, 2, 'Persistence', 'T1547 - Boot or Logon Autostart Execution', 'Identifies execution of suspicious persistent programs (scripts, rundll32, etc.) by looking at process lineage and command line usage', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1547/"]', 'contains("log.eventDataProcessName", "explorer.exe")', '2026-02-23 16:15:45.647899', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (912, 'Windows: Unusual Child Processes of RunDLL32', 2, 3, 2, 'Defense Evasion', 'T1218.011 - System Binary Proxy Execution: Rundll32', 'Identifies child processes of unusual instances of RunDLL32 where the command line parameters were suspicious. Misuse of RunDLL32 could indicate malicious activity', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1218/011/"]', 'contains("log.eventDataProcessName", "rundll32.exe")', '2026-02-23 16:15:46.787111', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (913, 'Windows: Remote Logon followed by Scheduled Task Creation', 3, 3, 2, 'Lateral Movement', 'T1021 - Remote Services', 'Identifies a remote logon followed by a scheduled task creation on the target host. This could be indicative of adversary lateral movement.', '["https://attack.mitre.org/tactics/TA0008/","https://attack.mitre.org/techniques/T1021/"]', 'equals("log.action", "logged-in") && equals("actionResult", "success") && !contains("log.UserName", "ANONYMOUS LOGON") && !contains("log.UserDomain", "NT AUTHORITY")', '2026-02-23 16:15:47.931134', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.hostId","operator":"filter_term","value":"{{log.hostId}}"},{"field":"log.eventDataSubjectLogonId","operator":"filter_term","value":"scheduled-task-created"}],"or":null,"within":"now-60s","count":3}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (914, 'Windows: Remote System Discovery Commands', 3, 1, 2, 'Discovery', 'T1018 - Remote System Discovery', 'Discovery of remote system information using built-in commands, which may be used to move laterally.', '["https://attack.mitre.org/tactics/TA0007/","https://attack.mitre.org/techniques/T1018/"]', 'regexMatch("log.message", "(-n|-s|-a)") && regexMatch("log.eventDataProcessName", "(nbtstat.exe|arp.exe)")', '2026-02-23 16:15:48.940939', true, true, 'origin', '["adversary.user","adversary.ip"]', '[]', null); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (915, 'Windows: Remote File Download via MpCmdRun', 2, 3, 1, 'Command and Control', 'T1105 - Ingress Tool Transfer', 'Identifies the Windows Defender configuration utility (MpCmdRun.exe) being used to download a remote file.', '["https://attack.mitre.org/tactics/TA0011/","https://attack.mitre.org/techniques/T1105/"]', 'contains("log.eventDataProcessName", "MpCmdRun.exe") && contains("log.message", "-url") && contains("log.message", "-DownloadFile") && contains("log.message", "-path")', '2026-02-23 16:15:49.924130', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (916, 'Windows: Remote File Copy to a Hidden Share', 3, 2, 1, 'Lateral Movement', 'T1021 - Remote Services', 'Identifies a remote file copy attempt to a hidden network share. This may indicate lateral movement or data staging activity.', '["https://attack.mitre.org/tactics/TA0008/","https://attack.mitre.org/techniques/T1021/"]', 'oneOf("log.eventDataProcessName", ["cmd.exe", "powershell.exe", "robocopy.exe", "xcopy.exe"]) && contains("log.message", "$") && (contains("log.message", "copy") || contains("log.message", "move") || contains("log.message", " cp ") || contains("log.message", " mv "))', '2026-02-23 16:15:50.930626', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (917, 'Windows: Possible ransomware attack detected. Ransomware Note Creation.', 3, 3, 2, 'Impact', 'T1486 - Data Encrypted for Impact', 'Ransomware, is a type of malware that prevents users from accessing their system or personal files and requires payment of a ransom in order to gain access to them again. Identifies ransomware attempts. A known ransomware note file has been detected, potentially indicating an active ransomware infection.', '["https://attack.mitre.org/tactics/TA0040/"]', 'equals("log.eventCode", 4663) && regexMatch("log.EventDataFileName", "(README_TO_RESTORE_FILES|INSTRUCTION_TO_GET_FILES_BACK|HOW_TO_DECRYPT_FILES|DECRYPT_INSTRUCTION|RECOVER_INSTRUCTION|RESTORE_FILES|READ_ME_NOW|YOUR_FILES_ARE_ENCRYPTED|IMPORTANT_INSTRUCTIONS|NOTICE|DECRYPT_YOUR_FILES|HOW_TO_RESTORE_FILES|HELP_DECRYPT|RECOVERY_FILE|RECOVER-FILES|INSTRUCTION)\\.(txt|html|php)$")', '2026-02-23 16:15:51.934608', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":"{{log.eventCode}}"},{"field":"log.EventDataFileName","operator":"filter_term","value":"{{log.EventDataFileName}}"}],"or":null,"within":"now-60s","count":5}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (918, 'Windows: Remote File Download via Desktopimgdownldr Utility', 2, 3, 1, 'Command and Control', 'T1105 - Ingress Tool Transfer', 'Identifies the desktopimgdownldr utility being used to download a remote file. An adversary may use desktopimgdownldr to download arbitrary files as an alternative to certutil.', '["https://attack.mitre.org/tactics/TA0011/","https://attack.mitre.org/techniques/T1105/"]', 'contains("log.eventDataProcessName", "desktopimgdownldr.exe")', '2026-02-23 16:15:52.940394', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (919, 'Windows: Possible ransomware attack detected. Multiple File Deletion.', 1, 3, 2, 'Impact', 'T1486 - Data Encrypted for Impact', 'Detects potential ransomware activity by monitoring multiple file write/modification events (Event ID 4663) with write access masks in user directories within a short timeframe. Modern ransomware typically encrypts files in-place rather than deleting them, making write access monitoring more effective than deletion monitoring alone.', '["https://attack.mitre.org/tactics/TA0040/"]', e'equals("log.eventCode", 4663) && +oneOf("log.eventDataAccessMask", ["0x2", "0x4", "0x6"]) && +!(regexMatch("log.eventDataProcessName", "(?i).*(trustedinstaller|svchost|wuauclt|msiexec|windows10upgrade|setuphost|tiworker|dism).*")) && +regexMatch("log.eventDataObjectName", "(?i).*\\\\\\\\(users|documents|desktop|downloads|pictures|videos|music)\\\\\\\\.*") && +!(regexMatch("log.eventDataObjectName", "(?i).*(\\\\\\\\windows\\\\\\\\|\\\\\\\\program files|\\\\\\\\programdata\\\\\\\\|\\\\\\\\temp\\\\\\\\|\\\\\\\\appdata\\\\\\\\local\\\\\\\\temp|\\\\\\\\softwaredistribution\\\\\\\\|\\\\\\\\winsxs\\\\\\\\|\\\\\\\\logs\\\\\\\\|\\\\\\\\prefetch\\\\\\\\).*")) && +!(regexMatch("log.eventDataObjectName", "(?i).*\\\\.(tmp|log|etl|dmp|pf|evtx|cache|dat|bak)$")) && +regexMatch("log.eventDataObjectName", "(?i).*\\\\.(doc[x]?|xls[x]?|ppt[x]?|pdf|txt|jpg|jpeg|png|gif|bmp|mp4|avi|mp3|zip|rar|7z)$") +', '2026-02-23 16:15:54.025061', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":"4663"},{"field":"origin.user","operator":"filter_term","value":"{{origin.user}}"}],"or":null,"within":"now-5m","count":50}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (920, 'Windows: Suspicious Microsoft Diagnostics Wizard Execution with args IT_RebrowseForFile=, ms-msdt:/id, ms-msdt:-id or FromBase64', 2, 3, 1, 'Defense Evasion', 'T1218 - System Binary Proxy Execution', 'Identifies potential abuse of the Microsoft Diagnostics Troubleshooting Wizard (MSDT) to proxy malicious command or binary execution via malicious process arguments', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1218/"]', 'contains("log.eventDataProcessName", "msdt.exe") && regexMatch("log.message", "(IT_RebrowseForFile=|ms-msdt:/id|ms-msdt:-id|FromBase64)")', '2026-02-23 16:15:55.116584', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (921, 'Windows: Privilege Escalation via Rogue Named Pipe Impersonation', 1, 3, 2, 'Privilege Escalation', 'T1134 - Access Token Manipulation', 'Identifies a privilege escalation attempt via rogue named pipe impersonation. An adversary may abuse this technique by masquerading as a known named pipe and manipulating a privileged process to connect to it.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1134/"]', 'regexMatch("log.eventDataProcessName", "(\\(.+)\\Pipe\\)") && contains("log.action", "Pipe Created")', '2026-02-23 16:15:56.315789', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (922, 'Windows: Potential Modification of Accessibility Binaries', 2, 3, 1, 'Persistence', 'T1546.008 - Event Triggered Execution: Accessibility Features', 'Windows contains accessibility features that may be launched with a key combination before a user has logged in. An adversary can modify the way these programs are launched to get a command prompt or backdoor without logging in to the system.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1546/008/"]', e'!regexMatch("log.eventDataProcessName", "(osk.exe|sethc.exe|utilman2.exe|DisplaySwitch.exe|ATBroker.exe|ScreenMagnifier.exe|SR.exe|Narrator.exe|magnify.exe|MAGNIFY.EXE)") && + regexMatch("log.eventDataParentProcessName", "(Utilman.exe|on.exe)") && + contains("log.eventDataSubjectUserName", "SYSTEM") && + regexMatch("log.message", "(C:\\\\Windows\\\\System32\\\\osk.exe|C:\\\\Windows\\\\System32\\\\Magnify.exe|C:\\\\Windows\\\\System32\\\\Narrator.exe|C:\\\\Windows\\\\System32\\\\Sethc.exe|utilman.exe|ATBroker.exe|DisplaySwitch.exe|sethc.exe)") +', '2026-02-23 16:15:57.522165', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (923, 'Windows: Suspicious PrintSpooler Service Executable File Creation', 2, 3, 1, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects attempts to exploit privilege escalation vulnerabilities related to the Print Spooler service. For more information refer to the following CVE''s - CVE-2020-1048, CVE-2020-1337 and CVE-2020-1300 and verify that the impacted system is patched', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1068/"]', e'!regexMatch("log.file.path", "(\\\\Windows\\\\System32\\\\spool\\\\|:\\\\Windows\\\\Temp\\\\|:\\\\Users\\\\)") && contains("log.eventDataProcessName", "spoolsv.exe") +', '2026-02-23 16:15:58.649751', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (924, 'Windows Firewall Disabled via PowerShell', 1, 2, 3, 'Defense Evasion', 'T1562.004 - Impair Defenses: Disable or Modify System Firewall', 'Identifies when the Windows Firewall is disabled using PowerShell cmdlets, which can help attackers evade network constraints, like internet and network lateral communication restrictions.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/004/"]', 'regexMatch("log.message", "(-All|Public|Domain|Private)") && contains("log.message", "False") && contains("log.message", "-Enabled") && contains("log.message", "Set-NetFirewallProfile") && regexMatch("log.eventDataProcessName", "(powershell.exe|pwsh.exe|powershell_ise.exe)")', '2026-02-23 16:15:59.848021', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (925, 'Windows: Probable Password guessing', 2, 2, 3, 'Credential Access', 'T1110.001 - Brute Force: Password Guessing', 'Adversaries with no prior knowledge of legitimate credentials within the system or environment may guess passwords to attempt access to accounts. Without knowledge of the password for an account, an adversary may opt to systematically guess the password using a repetitive or iterative mechanism. An adversary may guess login credentials without prior knowledge of system or environment passwords during an operation by using a list of common passwords. Password guessing may or may not take into account the target''s policies on password complexity or use policies that may lock accounts out after a number of failed attempts.', '["https://attack.mitre.org/tactics/TA0006","https://attack.mitre.org/techniques/T1110/001/"]', 'oneOf("log.eventCode", [4625, 529, 530, 531, 532, 533, 534, 535, 536, 537, 539])', '2026-02-23 16:16:01.017499', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":"{{log.eventCode}}"},{"field":"target.user","operator":"filter_term","value":"{{target.user}}"}],"or":null,"within":"now-5m","count":10}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (926, 'Windows: Persistence via WMI Event Subscription', 2, 3, 2, 'Persistence', 'T1546.003 - Event Triggered Execution: WMI Event Subscription', 'An adversary can use Windows Management Instrumentation (WMI) to install event filters, providers, consumers, and bindings that execute code when a defined event occurs. Adversaries may use the capabilities of WMI to subscribe to an event and execute arbitrary code when that event occurs, providing persistence on a system.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1546/003/"]', 'contains("log.message", "create") && regexMatch("log.message", "(ActiveScriptEventConsumer|CommandLineEventConsumer)") && contains("log.eventDataProcessName", "wmic.exe")', '2026-02-23 16:16:02.184850', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (927, 'Windows: Suspicious Print Spooler SPL File Created', 1, 3, 2, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects attempts to exploit privilege escalation vulnerabilities related to the Print Spooler service including CVE-2020-1048 and CVE-2020-1337.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1068/"]', e'!regexMatch("log.eventDataProcessName", "(spoolsv.exe|printfilterpipelinesvc.exe|PrintIsolationHost.exe|splwow64.exe|msiexec.exe|poqexec.exe)") && regexMatch("log.eventDataProcessName", "(:\\\\Windows\\\\System32\\\\spool\\\\PRINTERS\\\\)") +', '2026-02-23 16:16:03.392862', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (928, 'Windows: Persistence via Update Orchestrator Service Hijack', 2, 3, 2, 'Persistence', 'T1543.003 - Create or Modify System Process: Windows Service', 'Identifies potential hijacking of the Microsoft Update Orchestrator Service to establish persistence with an integrity level of SYSTEM.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1543/003/"]', e'contains("log.message", "UsoSvc") && + regexMatch("log.eventDataParentProcessName", "C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\svchost\\\\.exe") && + !regexMatch("log.eventDataProcessName", "MoUsoCoreWorker\\\\.exe|OfficeC2RClient\\\\.exe") && + !regexMatch("log.eventDataProcessName", "ProgramData\\\\\\\\Microsoft\\\\\\\\Windows\\\\\\\\UUS\\\\\\\\Packages\\\\\\\\.*\\\\\\\\amd64\\\\\\\\MoUsoCoreWorker\\\\.exe|Windows\\\\\\\\System32\\\\\\\\UsoClient\\\\.exe|Windows\\\\\\\\System32\\\\\\\\MusNotification\\\\.exe|Windows\\\\\\\\System32\\\\\\\\MusNotificationUx\\\\.exe|Windows\\\\\\\\System32\\\\\\\\MusNotifyIcon\\\\.exe|Windows\\\\\\\\System32\\\\\\\\WerFault\\\\.exe|Windows\\\\\\\\System32\\\\\\\\WerMgr\\\\.exe|Windows\\\\\\\\UUS\\\\\\\\amd64\\\\\\\\MoUsoCoreWorker\\\\.exe|Windows\\\\\\\\System32\\\\\\\\MoUsoCoreWorker\\\\.exe|Windows\\\\\\\\UUS\\\\\\\\amd64\\\\\\\\UsoCoreWorker\\\\.exe|Windows\\\\\\\\System32\\\\\\\\UsoCoreWorker\\\\.exe|Program Files\\\\\\\\Common Files\\\\\\\\microsoft shared\\\\\\\\ClickToRun\\\\\\\\OfficeC2RClient\\\\.exe") +', '2026-02-23 16:16:04.566964', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (929, 'Windows: Persistence via TelemetryController Scheduled Task Hijack', 2, 3, 2, 'Persistence', 'T1053.005 - Scheduled Task', 'Detects the successful hijack of Microsoft Compatibility Appraiser scheduled task to establish persistence with an integrity level of system.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1053/005/"]', 'contains("log.message", "-cv") && contains("log.eventDataParentProcessName", "CompatTelRunner.exe") && !(regexMatch("log.eventDataProcessName", "(conhost.exe|DeviceCensus.exe|CompatTelRunner.exe|DismHost.exe|rundll32.exe|powershell.exe)"))', '2026-02-23 16:16:05.746209', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (930, 'Windows: Persistence via BITS Job Notify Cmdline', 2, 3, 2, 'Persistence', 'T1197 - BITS Jobs', 'An adversary can use the Background Intelligent Transfer Service (BITS) SetNotifyCmdLine method to execute a program that runs after a job finishes transferring data or after a job enters a specified state in order to persist on a system.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1197/"]', 'contains("log.message", "BITS") && contains("log.eventDataParentProcessName", "svchost.exe") && !(regexMatch("log.eventDataProcessName", "(:\\Windows\\System32\\WerFaultSecure.exe|:\\Windows\\System32\\WerFault.exe|:\\Windows\\System32\\wermgr.exe|:\\WINDOWS\\system32\\directxdatabaseupdater.exe)"))', '2026-02-23 16:16:06.933567', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (931, 'Windows: Privilege Escalation via Named Pipe Impersonation', 3, 3, 2, 'Privilege Escalation', 'T1134 - Access Token Manipulation', 'Identifies a privilege escalation attempt via named pipe impersonation. An adversary may abuse this technique by utilizing a framework such Metasploit''s meterpreter getsystem command.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1134/"]', 'regexMatch("log.eventDataProcessName", "(Cmd.Exe|PowerShell.EXE|powershell.exe|cmd.exe)") && contains("log.message", ">") && regexMatch("log.message", "(\\\\.\\pipe\\)")', '2026-02-23 16:16:08.132646', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (932, 'Windows: User logged using Remote Desktop Connection from loopback address, possible exploit over reverse tunneling using stolen credentials', 3, 2, 1, 'Credential Access', 'T1021.001 - Remote Services: Remote Desktop Protocol', 'Adversaries may use Valid Accounts to log into a computer using the Remote Desktop Protocol (RDP). The adversary may then perform actions as the logged-on user.', '["https://attack.mitre.org/techniques/T1021/001/"]', 'equals("log.eventDataLogonType", "10") && oneOf("log.origin.ips", ["::1", "127.0.0.1"]) && oneOf("log.eventCode", [528, 540, 673, 4624, 4769])', '2026-02-23 16:16:09.305307', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (933, 'Windows: Multiple failed attempts to perform a privileged operation by the same user', 1, 2, 3, 'Privilege Escalation', 'T1110 - Brute Force', 'Adversaries may use brute force techniques to gain access to accounts when passwords are unknown or when password hashes are obtained.', '["https://attack.mitre.org/techniques/T1110/"]', 'equals("log.eventCode", 577) || equals("log.eventCode", 4673)', '2026-02-23 16:16:10.480165', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"target.user","operator":"filter_term","value":"{{target.user}}"}],"or":null,"within":"now-10m","count":10}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (934, 'Windows: Modification of Boot Configuration', 1, 3, 3, 'Impact', 'T1490 - Inhibit System Recovery', 'Identifies use of bcdedit.exe to delete boot configuration data. This tactic is sometimes used as by malware or an attacker as a destructive technique.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1490/"]', 'contains("log.eventDataProcessName", "bcdedit.exe") && regexMatch("log.message", "((ignoreallfailures(.+)bootstatuspolicy(.+)/set)|(ignoreallfailures(.+)/set(.+)bootstatuspolicy)|(/set(.+)bootstatuspolicy(.+)ignoreallfailures)|(/set(.+)ignoreallfailures(.+)bootstatuspolicy)|(bootstatuspolicy(.+)set(.+)ignoreallfailures)|(bootstatuspolicy(.+)ignoreallfailures(.+)/set)|(no(.+)recoveryenabled)|(recoveryenabled(.+)no))")', '2026-02-23 16:16:11.487276', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (935, 'Windows: Mounting Hidden or WebDav Remote Shares', 3, 2, 1, 'Lateral Movement', 'T1021.002 - Remote Services: SMB/Windows Admin Shares', 'Identifies the use of net.exe to mount a WebDav or hidden remote share. This may indicate lateral movement or preparation for data exfiltration.', '["https://attack.mitre.org/tactics/TA0008/","https://attack.mitre.org/techniques/T1021/002/"]', 'regexMatch("log.eventDataProcessName", "(net.exe|net1.exe)") && regexMatch("log.message", "(\\\\(.+)\\(.+)$|\\\\(.+)@SSL\\|http)") && contains("log.message", "/d") && contains("log.message", "use") && !(contains("log.eventDataParentProcessName", "net.exe"))', '2026-02-23 16:16:12.673631', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (936, 'Windows: Microsoft security essentials - Virus detected', 3, 3, 2, 'Privilege Escalation', 'T1055 - Process Injection', 'Detect the presence of a virus or malware on the system using Microsoft Security Essentials. The rule correlates different threat detection events, represented by various Event IDs, to identify virus detection on the system.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1055/"]', 'oneOf("log.eventCode", [1107, 1117, 1116, 1118, 1119]) && equals("log.providerName", "Microsoft Antimalware")', '2026-02-23 16:16:13.843357', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (937, 'Windows: LSASS Memory Dump Handle Access', 2, 3, 3, 'Credential Access', 'T1003.001 - OS Credential Dumping: LSASS Memory', 'Identifies handle requests for the Local Security Authority Subsystem Service (LSASS) object access with specific access masks that many tools with a capability to dump memory to disk use (0x1fffff, 0x1010, 0x120089). This rule is tool agnostic as it has been validated against a host of various LSASS dump tools such as SharpDump, Procdump, Mimikatz, Comsvcs etc. It detects this behavior at a low level and does not depend on a specific tool or dump file name.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/","https://attack.mitre.org/techniques/T1003/001/"]', 'equals("log.eventCode", 4656) && regexMatch("log.eventDataObjectName", "(:\\Windows\\System32\\lsass.exe|\\Device\\HarddiskVolume[A-Za-z?:\\]([A-Za-z?])?\\Windows\\System32\\lsass.exe)") && !regexMatch("log.eventDataProcessName", "(:\\Program Files\\(.+).exe|:\\Program Files (x86)\\(.+).exe|:\\Windows\\system32\\wbem\\WmiPrvSE.exe|:\\Windows\\System32\\dllhost.exe|:\\Windows\\System32\\svchost.exe|:\\Windows\\System32\\msiexec.exe|:\\ProgramData\\Microsoft\\Windows Defender\\(.+).exe|:\\Windows\\explorer.exe)") && oneOf("log.eventDataAccessMask", ["0x1fffff", "0x1010", "0x120089", "0x1F3FFF"])', '2026-02-23 16:16:14.943525', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (938, 'Windows: IIS HTTP Logging Disabled', 3, 2, 3, 'Defense Evasion', 'T1562.002 - Impair Defenses: Disable Windows Event Logging', 'Identifies when Internet Information Services (IIS) HTTP Logging is disabled on a server. An attacker with IIS server access via a webshell or other mechanism can disable HTTP Logging as an effective anti-forensics measure.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/002/"]', 'contains("log.eventDataProcessName", "appcmd.exe") && regexMatch("log.message", "(/dontLog(.+):(.+)True)") && !(contains("log.eventDataParentProcessName", "iissetup.exe"))', '2026-02-23 16:16:16.133286', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (939, 'Windows: Microsoft IIS Connection Strings Decryption', 2, 3, 1, 'Credential Access', 'T1003 - OS Credential Dumping', 'Identifies use of aspnet_regiis to decrypt Microsoft IIS connection strings. An attacker with Microsoft IIS web server access via a webshell or alike can decrypt and dump any hardcoded connection strings, such as the MSSQL service account password using aspnet_regiis command.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/"]', 'regexMatch("log.eventDataProcessName", "aspnet_regiis.exe") && regexMatch("log.message", "(connectionStrings)") && regexMatch("log.message", "(-pdf)")', '2026-02-23 16:16:17.307172', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (940, 'Windows: Microsoft IIS Service Account Password Dumped', 3, 2, 2, 'Credential Access', 'T1003 - OS Credential Dumping', 'Identifies the Internet Information Services (IIS) command-line tool, AppCmd, being used to list passwords. An attacker with IIS web server access via a web shell can decrypt and dump the IIS AppPool service account password using AppCmd.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/"]', 'regexMatch("log.eventDataProcessName", "appcmd.exe") && regexMatch("log.message", "(/list)") && regexMatch("log.message", "(/text(.+)password)")', '2026-02-23 16:16:18.489030', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (941, 'Windows: Execution via MSSQL xp_cmdshell Stored Procedure', 3, 3, 2, 'Execution', 'T1059 - Command and Scripting Interpreter', 'Identifies execution via MSSQL xp_cmdshell stored procedure. Malicious users may attempt to elevate their privileges by using xp_cmdshell, which is disabled by default, thus, it''s important to review the context of it''s use.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1059/"]', 'oneOf("log.message", ["diskfree", "rmdir", "mkdir", "dir", "del", "rename", "bcp", "XMLNAMESPACES"]) && contains("log.eventDataProcessName", "cmd.exe") && contains("log.eventDataParentProcessName", "sqlservr.exe")', '2026-02-23 16:16:19.539166', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (942, 'Windows: Process Activity via Compiled HTML File', 3, 3, 1, 'Execution', 'T1204.002 - User Execution: Malicious File', 'Compiled HTML files (.chm) are commonly distributed as part of the Microsoft HTML Help system. Adversaries may conceal malicious code in a CHM file and deliver it to a victim for execution. CHM content is loaded by the HTML Help executable program (hh.exe).', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1204/002/"]', 'regexMatch("log.eventDataProcessName", "(mshta.exe|cmd.exe|powershell.exe|pwsh.exe|powershell_ise.exe|cscript.exe|wscript.exe)") && regexMatch("log.eventDataParentProcessName", "hh.exe")', '2026-02-23 16:16:20.713047', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (943, 'Windows: Possible sideloading of DLL via Microsoft antimalware service executable with MsMpEng process', 3, 3, 3, 'Defense Evasion', 'T1574 - Hijack Execution Flow', 'Identifies a Windows trusted program that is known to be vulnerable to DLL Search Order Hijacking starting after being renamed or from a non-standard path. This is uncommon behavior and may indicate an attempt to evade defenses via side-loading a malicious DLL within the memory space of one of those processes.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1574/"]', 'contains("log.eventDataProcessName", "MsMpEng.exe") && !regexMatch("log.eventDataProcessPath", "(:\\ProgramData\\Microsoft\\Windows Defender\\(.+).exe|:\\Program Files\\Windows Defender\\(.+).exe|:\\Program Files (x86)\\Windows Defender\\(.+).exe|:\\Program Files\\Microsoft Security Client\\(.+).exe|:\\Program Files (x86)\\Microsoft Security Client\\(.+).exe)")', '2026-02-23 16:16:21.846333', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (944, 'Windows: Microsoft Build Engine Started by a System Process', 3, 3, 2, 'Defense Evasion', 'T1127.001 - Trusted Developer Utilities Proxy Execution: MSBuild', 'An instance of MSBuild, the Microsoft Build Engine, was started by Explorer or the WMI (Windows Management Instrumentation) subsystem. This behavior is unusual and is sometimes used by malicious payloads.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1127/001/"]', 'regexMatch("log.eventDataProcessName", "MSBuild.exe") && regexMatch("log.eventDataParentProcessName", "(explorer.exe|wmiprvse.exe)")', '2026-02-23 16:16:22.853607', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (945, 'Windows: Microsoft Build Engine Started an Unusual Process', 3, 3, 2, 'Defense Evasion', 'T1027 - Obfuscated Files or Information', 'An instance of MSBuild, the Microsoft Build Engine, started a PowerShell script or the Visual C# Command Line Compiler. This technique is sometimes used to deploy a malicious payload using the Build Engine.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1027/"]', 'regexMatch("log.eventDataProcessName", "(csc.exe|iexplore.exe|powershell.exe)") && regexMatch("log.eventDataParentProcessName", "MSBuild.exe")', '2026-02-23 16:16:23.959123', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (946, 'Windows: Microsoft Build Engine Started by a Script Process', 3, 3, 2, 'Defense Evasion', 'T1127.001 - Trusted Developer Utilities Proxy Execution: MSBuild', 'An instance of MSBuild, the Microsoft Build Engine, was started by a script or the Windows command interpreter. This behavior is unusual and is sometimes used by malicious payloads.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1127/001/"]', 'regexMatch("log.eventDataProcessName", "MSBuild.exe") && regexMatch("log.eventDataParentProcessName", "(cmd.exe|powershell.exe|wscript.exe|cscript.exe)")', '2026-02-23 16:16:24.937434', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (947, 'Windows: Microsoft Build Engine Started by an Office Application', 3, 3, 1, 'Defense Evasion', 'T1127.001 - Trusted Developer Utilities Proxy Execution: MSBuild', 'An instance of MSBuild, the Microsoft Build Engine, was started by Excel or Word. This is unusual behavior for the Build Engine and could have been caused by an Excel or Word document executing a malicious script payload.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1127/001/"]', 'regexMatch("log.eventDataProcessName", "MSBuild.exe") && regexMatch("log.eventDataParentProcessName", "(eqnedt32.exe|excel.exe|fltldr.exe|msaccess.exe|mspub.exe|outlook.exe|powerpnt.exe|winword.exe)")', '2026-02-23 16:16:26.000190', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (948, 'Windows: Remote Desktop Enabled in Windows Firewall by Netsh', 3, 3, 1, 'Defense Evasion', 'T1562.004 - Impair Defenses: Disable or Modify System Firewall', 'Identifies use of the network shell utility (netsh.exe) to enable inbound Remote Desktop Protocol (RDP) connections in the Windows Firewall.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/004/"]', 'regexMatch("log.eventDataProcessName", "netsh.exe") && regexMatch("log.message", "(action=allow|enable=Yes|enable)") && regexMatch("log.message", "(localport=3389|RemoteDesktop|group=(.+)remote desktop(.+))")', '2026-02-23 16:16:27.028894', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (949, 'Windows: Detection of Exchange mail exported through PowerShell', 3, 1, 1, 'Collection', 'T1005 - Data from Local System', 'This rule identifies the use of an Exchange PowerShell cmdlet, which is used to export the contents of a core file. mailbox or archive to a .pst file. Adversaries can target user email to collect sensitive information.', '["https://attack.mitre.org/tactics/TA0009/","https://attack.mitre.org/techniques/T1005/","https://attack.mitre.org/techniques/T1114/","https://attack.mitre.org/techniques/T1114/002/"]', 'regexMatch("log.eventDataProcessName", "(powershell.exe|pwsh.exe|powershell_ise.exe)") && regexMatch("log.message", "(New-MailboxExportRequest)")', '2026-02-23 16:16:28.087132', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (950, 'Windows: Credential Acquisition via Registry Hive Dumping', 3, 1, 1, 'Credential Access', 'T1003.002 - OS Credential Dumping: Security Account Manager', 'Identifies attempts to export a registry hive which may contain credentials using the Windows reg.exe tool.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/","https://attack.mitre.org/techniques/T1003/002/"]', 'regexMatch("log.eventDataProcessName", "reg.exe") && regexMatch("log.message", "(save|export)") && regexMatch("log.message", "(hklm\\sam|hklm\\security)")', '2026-02-23 16:16:29.177259', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (951, 'Windows: Disabling Windows Defender Security Settings via PowerShell', 3, 3, 3, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', 'Identifies use of the Set-MpPreference PowerShell command to disable or weaken certain Windows Defender settings.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/001/"]', 'regexMatch("log.message", "(Set-MpPreference)") && regexMatch("log.message", "(-Disable|Disabled|NeverSend|-Exclusion)") && regexMatch("log.eventDataProcessName", "(powershell.exe|pwsh.dll|powershell_ise.exe)")', '2026-02-23 16:16:30.280741', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (952, 'Windows: Disable Windows Firewall Rules via Netsh', 2, 2, 3, 'Defense Evasion', 'T1562.004 - Impair Defenses: Disable or Modify System Firewall', 'Identifies use of netsh.exe to disable Windows Firewall rules or turn off the firewall entirely. Adversaries may disable the Windows Firewall to enable network connections for lateral movement or command and control.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/004/"]', 'regexMatch("log.message", "(disable(.+)firewall(.+)set|disable(.+)set(.+)firewall|firewall(.+)disable(.+)set|firewall(.+)set(.+)disable|set(.+)disable(.+)firewall|set(.+)firewall(.+)disable|state(.+)advfirewall(.+)off|state(.+)off(.+)advfirewall|advfirewall(.+)state(.+)off|advfirewall(.+)off(.+)state|off(.+)state(.+)advfirewall|off(.+)advfirewall(.+)state)") && regexMatch("log.eventDataProcessName", "netsh.exe")', '2026-02-23 16:16:31.291436', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (953, 'Windows: PowerShell Keylogging Script', 3, 2, 2, 'Collection', 'T1056.001 - Input Capture: Keylogging', 'Detects the use of Win32 API Functions that can be used to capture user keystrokes in PowerShell scripts. Attackers use this technique to capture user input, looking for credentials and/or other valuable data.', '["https://attack.mitre.org/tactics/TA0009/","https://attack.mitre.org/techniques/T1056/","https://attack.mitre.org/techniques/T1056/001/"]', 'regexMatch("log.message", "(GetAsyncKeyState|NtUserGetAsyncKeyState|GetKeyboardState|Get-Keystrokes|SetWindowsHookA|SetWindowsHookW|SetWindowsHookEx|SetWindowsHookExA|NtUserSetWindowsHookEx|GetForegroundWindow|GetWindowTextA|GetWindowTextW|WM_KEYBOARD_LL)") && regexMatch("log.eventDataProcessName", "(powershell.exe|pwsh.exe|powershell_ise.exe)")', '2026-02-23 16:16:32.462863', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (954, 'Windows: Deleting Backup Catalogs with Wbadmin', 1, 2, 3, 'Impact', 'T1490 - Inhibit System Recovery', 'Identifies use of the wbadmin.exe to delete the backup catalog. Ransomware and other malware may do this to prevent system recovery.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1490/"]', 'regexMatch("log.message", "(delete(.+)catalog|catalog(.+)delete)") && regexMatch("log.eventDataProcessName", "(wbadmin.exe|WBADMIN.EXE)")', '2026-02-23 16:16:33.638347', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (955, 'Windows: Delete Volume USN Journal with Fsutil', 1, 2, 3, 'Defense Evasion', 'T1070.004 - Indicator Removal: File Deletion', 'Identifies use of the fsutil.exe to delete the volume USNJRNL. This technique is used by attackers to eliminate evidence of files created during post-exploitation activities.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1070/004/"]', 'regexMatch("log.message", "(deletejournal(.+)usn|usn(.+)deletejournal)") && regexMatch("log.eventDataProcessName", "fsutil.exe")', '2026-02-23 16:16:34.810106', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (956, 'Windows Defender Exclusions Added via PowerShell', 2, 2, 3, 'Defense Evasion', 'T1562 - Impair Defenses', 'Identifies modifications to the Windows Defender configuration settings using PowerShell to add exclusions at the folder directory or process level.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/"]', 'regexMatch("log.message", "(-Exclusion(.+)(Add-MpPreference|Set-MpPreference)|(Add-MpPreference|Set-MpPreference)(.+)-Exclusion)") && regexMatch("log.eventDataProcessName", "(powershell.exe|pwsh.exe|powershell_ise.exe)")', '2026-02-23 16:16:35.961367', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (957, 'Windows: Potential Credential Access via Trusted Developer Utility', 2, 2, 3, 'Credential Access', 'T1003 - OS Credential Dumping', 'An instance of MSBuild, the Microsoft Build Engine, loaded DLLs (dynamically linked libraries) responsible for Windows credential management. This technique is sometimes used for credential dumping.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/"]', 'regexMatch("log.eventDataProcessName", "(MSBuild.exe|msbuild.exe)") && regexMatch("log.message", "(vaultcli.dll|SAMLib.DLL)")', '2026-02-23 16:16:37.137901', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataProcessId","operator":"filter_term","value":"{{log.eventDataProcessId}}"}],"or":null,"within":"now-1m","count":1}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (958, 'Windows: NTDS or SAM Database File Copied', 3, 3, 3, 'Credential Access', 'T1003.002 - OS Credential Dumping: Security Account Manager', 'Identifies a copy operation of the Active Directory Domain Database or Security Account Manager (SAM) files. Those files contain sensitive information including hashed domain and local credentials.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/","https://attack.mitre.org/techniques/T1003/002/"]', 'regexMatch("log.eventDataProcessName", "(?i)(cmd\\.exe|powershell\\.exe|xcopy\\.exe|esentutl\\.exe)") && regexMatch("log.message", "(copy|xcopy|Copy-Item|move|cp|mv|/y|/vss|/d)") && regexMatch("log.message", "(\\ntds.dit|\\config\\SAM|\\(.+)\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy(.+)\\|/system32/config/SAM)")', '2026-02-23 16:16:38.300804', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (959, 'Clearing Windows Event Logs with wevtutil', 1, 2, 3, 'Defense Evasion', 'T1070.001 - Indicator Removal: Clear Windows Event Logs', 'Identifies attempts to clear or disable Windows event log stores using Windows wevetutil command. This is often done by attackers in an attempt to evade detection or destroy forensic evidence on a system.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1070/001/"]', 'regexMatch("log.message", "(/e:false|cl|clear-log|Clear-EventLog)") && regexMatch("log.eventDataLogonProcessName", "wevtutil.exe")', '2026-02-23 16:16:39.433313', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (960, 'Windows Service Installed via an Unusual Client', 3, 3, 2, 'Privilege Escalation', 'T1543.003 - Create or Modify System Process: Windows Service', 'Identifies the creation of a Windows service by an unusual client process. Services may be created with administrator privileges but are executed under SYSTEM privileges, so an adversary may also use a service to escalate privileges from administrator to SYSTEM.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1543/003/"]', 'equals("action", "service-installed") && equals("clientProcessId", "0") && equals("parentProcessId", "0")', '2026-02-23 16:16:40.564801', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (961, 'Windows: Whoami Process Activity', 1, 1, 0, 'Discovery', 'T1033 - System Owner/User Discovery', 'Identifies suspicious use of whoami.exe which displays user, group, and privileges information for the user who is currently logged on to the local system.', '["https://attack.mitre.org/tactics/TA0007/","https://attack.mitre.org/techniques/T1033/"]', 'contains("log.eventDataProcessName", "whoami.exe")', '2026-02-23 16:16:41.781533', true, true, 'origin', '["adversary.user","adversary.ip"]', '[]', null); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (962, 'Windows Defender: Antimalware engine found malware or other potentially unwanted software', 1, 2, 3, 'Execution', 'T1546 - Event Triggered Execution', 'This rule is triggered when the antimalware engine detects malware or potentially unwanted software on the system. This alert is critical to identify the presence of threats and unwanted software that may compromise system security and performance.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1546/"]', 'oneOf("log.eventCode", [1006, 1015, 1116]) && equals("log.providerName", "SecurityCenter")', '2026-02-23 16:16:42.945957', true, true, 'target', null, '[]', '["target.host","target.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (963, 'Windows: Unusual Service Host Child Process - Childless Service', 1, 3, 2, 'Privilege Escalation', 'T1055 - Process Injection', 'Identifies unusual child processes of Service Host (svchost.exe) that traditionally do not spawn any child processes. This may indicate a code injection or an equivalent form of exploitation.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1055/"]', 'contains("log.eventDataParentProcessName", "svchost.exe") && regexMatch("log.message", "(WdiSystemHost|LicenseManager|StorSvc|CDPSvc|cdbhsvc|BthAvctpSvc|SstpSvc|WdiServiceHost|imgsvc|TrkWks|WpnService|IKEEXT|PolicyAgent|CryptSvc|netprofm|ProfSvc|StateRepository|camsvc|LanmanWorkstation|NlaSvc|EventLog|hidserv|DisplayEnhancementService|ShellHWDetection|AppHostSvc|fhsvc|CscService|PushToInstall)") && !regexMatch("log.eventDataProcessName", "(WerFault.exe|WerFaultSecure.exe|wermgr.exe|rundll32.exe)") && !regexMatch("log.eventDataProcessName", "(:\\Windows\\System32\\RelPost.exe|:\\Program Files\\|:\\Program Files (x86)\\|:\\Windows\\System32\\Kodak\\kds_i4x50\\lib\\lexexe.exe)") && !regexMatch("log.message", "(WdiSystemHost|WdiServiceHost|imgsvc)") && !regexMatch("log.message", "(:\\WINDOWS\\System32\\winethc.dll,ForceProxyDetectionOnNextRun)")', '2026-02-23 16:16:44.121771', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (964, 'Windows: Unusual Print Spooler Child Process', 1, 3, 2, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects unusual Print Spooler service (spoolsv.exe) child processes. This may indicate an attempt to exploit privilege escalation vulnerabilities related to the Printing Service on Windows.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1068/"]', 'contains("log.eventDataParentProcessName", "spoolsv.exe") && !regexMatch("log.eventDataProcessName", "(splwow64.exe|PDFCreator.exe|acrodist.exe|spoolsv.exe|msiexec.exe|route.exe|WerFault.exe|net.exe|cmd.exe|powershell.exe|netsh.exe|regsvr32.exe)") && !regexMatch("log.message", "(\\WINDOWS\\system32\\spool\\DRIVERS|stop|start|.spl|\\program files(.+)route add|add portopening|rule name|PrintConfig.dll)") && equals("log.logName", "System")', '2026-02-23 16:16:45.295328', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (965, 'Windows: Unusual Network Connection via DllHost or via RunDLL32', 2, 3, 2, 'Defense Evasion', 'T1218 - System Binary Proxy Execution', 'Identifies unusual instances of dllhost.exe making outbound network connections. This may indicate adversarial Command and Control activity. Identifies unusual instances of rundll32.exe making outbound network connections. This may indicate adversarial Command and Control activity.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1218/"]', 'regexMatch("log.eventDataProcessName", "(dllhost.exe|rundll32.exe)") && regexMatch("log.origin.ips", "((10.0.0.0/8,127.0.0.0/8,169.254.0.0/16,172.16.0.0/12,192.0.0.0/24,192.0.0.0/29,192.0.0.8/32,192.0.0.9/32,192.0.0.10/32,192.0.0.170/32,192.0.0.171/32,192.0.2.0/24,192.31.196.0/24,192.52.193.0/24,192.168.0.0/16,192.88.99.0/24,224.0.0.0/4,100.64.0.0/10,192.175.48.0/24,198.18.0.0/15,198.51.100.0/24,203.0.113.0/24,240.0.0.0/4,::1,FE80::/10,FF00::/8)")', '2026-02-23 16:16:46.427287', true, true, 'target', null, '[]', '["target.host","target.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (966, 'Windows: Unusual File Modification by dns.exe', 1, 3, 2, 'Initial Access', 'T1133 - External Remote Services', 'Identifies an unexpected file being modified by dns.exe, the process responsible for Windows DNS Server services, which may indicate activity related to remote code execution or other forms of exploitation.', '["https://attack.mitre.org/tactics/TA0001/","https://attack.mitre.org/techniques/T1133/"]', 'contains("log.eventDataProcessName", "dns.exe") && !contains("log.eventDataFileName", "dns.log")', '2026-02-23 16:16:47.600711', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (967, 'Windows: UAC Bypass via Windows Firewall Snap-In Hijack', 1, 3, 2, 'Privilege Escalation', 'T1548.002 - Abuse Elevation Control Mechanism: Bypass User Account Control', 'Identifies attempts to bypass User Account Control (UAC) by hijacking the Microsoft Management Console (MMC) Windows Firewall snap-in. Attackers bypass UAC to stealthily execute code with elevated permissions.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1548/002/"]', 'contains("log.eventDataProcessName", "mmc.exe") && !contains("log.message", "WerFault.exe") && contains("log.message", "WF.msc")', '2026-02-23 16:16:48.750297', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (968, 'Windows: Unusual File Creation - Alternate Data Stream', 1, 3, 2, 'Defense Evasion', 'T1564 - Hide Artifacts', 'Identifies suspicious creation of Alternate Data Streams on highly targeted files. This is uncommon for legitimate files and sometimes done by adversaries to hide malware.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1564/"]', 'regexMatch("log.eventDataFileName", "(^C:\\(.+):(.+))") && !regexMatch("log.eventDataFileName", "(C:\\(.+):zone.identifier)") && regexMatch("log.message", "(pdf|dll|png|exe|dat|com|bat|cmd|sys|vbs|ps1|hta|txt|vbe|js|wsh|docx|doc|xlsx|xls|pptx|ppt|rtf|gif|jpg|png|bmp|img|iso)") && !regexMatch("log.eventDataProcessName", "(:\\windows\\System32\\svchost.exe|:\\Windows\\System32\\inetsrv\\w3wp.exe|:\\Windows\\explorer.exe|:\\Windows\\System32\\sihost.exe|:\\Windows\\System32\\PickerHost.exe|:\\Windows\\System32\\SearchProtocolHost.exe|:\\Program Files (x86)\\Dropbox\\Client\\Dropbox.exe|:\\Program Files\\Rivet Networks\\SmartByte\\SmartByteNetworkService.exe|:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe|:\\Program Files\\ExpressConnect\\ExpressConnectNetworkService.exe|:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe|:\\Program Files\\Google\\Chrome\\Application\\chrome.exe|:\\Program Files\\Mozilla Firefox\\firefox.exe)")', '2026-02-23 16:16:49.933149', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (969, 'Windows Web Shell Detection: Script Process Child of Common Web Processes', 1, 3, 2, 'Persistence', 'T1505.003 - Server Software Component: Web Shell', 'Identifies suspicious commands executed via a web server, which may suggest a vulnerability and remote shell access.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1505/003/"]', 'regexMatch("log.eventDataProcessName", "(cmd.exe|cscript.exe|powershell.exe|pwsh.exe|powershell_ise.exe|wmic.exe|wscript.exe)") && regexMatch("log.eventDataParentProcessName", "(w3wp.exe|httpd.exe|nginx.exe|php.exe|php-cgi.exe|tomcat.exe)")', '2026-02-23 16:16:51.108857', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (970, 'Windows: Volume Shadow Copy Deletion via WMIC', 1, 2, 3, 'Impact', 'T1490 - Inhibit System Recovery', 'Identifies use of wmic.exe for shadow copy deletion on endpoints. This commonly occurs in tandem with ransomware or other destructive attacks.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1490/"]', 'regexMatch("log.message", "(delete(.+)shadowcopy|shadowcopy(.+)delete)") && contains("log.eventDataProcessName", "WMIC.exe")', '2026-02-23 16:16:52.173672', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (971, 'Windows: Clearing Windows Console History', 1, 2, 3, 'Defense Evasion', 'T1070.003 - Indicator Removal: Clear Command History', 'Identifies when a user attempts to clear console history. An adversary may clear the command history of a compromised account to conceal the actions undertaken during an intrusion.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1070/003/"]', 'regexMatch("log.message", "(Clear-History|(Remove-Item|rm)(.+)(ConsoleHost_history.txt|\\(Get-PSReadlineOption\\)\\.HistorySavePath)|(ConsoleHost_history.txt|\\(Get-PSReadlineOption\\)\\.HistorySavePath)(.+)(Remove-Item|rm)|Set-PSReadlineOption(.+)SaveNothing|SaveNothing(.+)PSReadlineOption)") && equals("log.providerName", "PowerShell")', '2026-02-23 16:16:53.352646', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (972, 'Windows: Volume Shadow Copy Deletion via PowerShell', 1, 2, 3, 'Impact', 'T1490 - Inhibit System Recovery', 'Identifies the use of the Win32_ShadowCopy class and related cmdlets to achieve shadow copy deletion. This commonly occurs in tandem with ransomware or other destructive attacks.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1490/"]', 'regexMatch("log.eventDataProcessName", "(powershell.exe|pwsh.exe|powershell_ise.exe)") && regexMatch("log.message", "(Get-WmiObject|gwmi|Get-CimInstance|gcim)") && regexMatch("log.message", "(Win32_ShadowCopy)") && regexMatch("log.message", "(\\.Delete\\(\\)|Remove-WmiObject|rwmi|Remove-CimInstance|rcim)")', '2026-02-23 16:16:54.461749', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (973, 'Windows: Volume Shadow Copy Deleted or Resized via VssAdmin', 1, 2, 3, 'Impact', 'T1490 - Inhibit System Recovery', 'Identifies use of vssadmin.exe for shadow copy deletion or resizing on endpoints. This commonly occurs in tandem with ransomware or other destructive attacks.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1490/"]', 'regexMatch("log.message", "((delete|resize)(.+)shadows|shadows(.+)(delete|resize))") && contains("log.eventDataProcessName", "vssadmin.exe")', '2026-02-23 16:16:55.552078', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (974, 'Windows: Bypass UAC via Event Viewer', 1, 3, 2, 'Privilege Escalation', 'T1548.002 - Abuse Elevation Control Mechanism: Bypass User Account Control', 'Identifies User Account Control (UAC) bypass via eventvwr.exe. Attackers bypass UAC to stealthily execute code with elevated permissions.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1548/002/"]', 'contains("log.eventDataParentProcessName", "eventvwr.exe") && !regexMatch("log.eventDataProcessName", "(:\\Windows\\SysWOW64\\mmc.exe|:\\Windows\\System32\\mmc.exe|:\\Windows\\SysWOW64\\WerFault.exe|:\\Windows\\System32\\WerFault.exe)")', '2026-02-23 16:16:56.691201', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (975, 'Windows: UAC Bypass Attempt via Privileged IFileOperation COM Interface', 1, 3, 2, 'Privilege Escalation', 'T1548.002 - Abuse Elevation Control Mechanism: Bypass User Account Control', 'Identifies attempts to bypass User Account Control (UAC) via DLL side-loading. Attackers may attempt to bypass UAC to stealthily execute code with elevated permissions.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1548/002/"]', 'regexMatch("log.eventDataFileName", "(wow64log.dll|comctl32.dll|DismCore.dll|OskSupport.dll|duser.dll|Accessibility.ni.dll)") && contains("log.eventDataProcessName", "dllhost.exe") && !regexMatch("log.eventDataFileName", "(C:\\Windows\\SoftwareDistribution\\|C:\\Windows\\WinSxS\\)")', '2026-02-23 16:16:57.641068', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (976, 'Windows: UAC Bypass via ICMLuaUtil Elevated COM Interface', 1, 3, 2, 'Privilege Escalation', 'T1548.002 - Abuse Elevation Control Mechanism: Bypass User Account Control', 'Identifies User Account Control (UAC) bypass attempts via the ICMLuaUtil Elevated COM interface. Attackers may attempt to bypass UAC to stealthily execute code with elevated permissions', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1548/002/"]', 'contains("log.eventDataProcessName", "dllhost.exe") && !contains("log.message", "WerFault.exe") && regexMatch("log.message", "(/Processid:\\{3E5FC7F9-9A51-4367-9063-A120244FBEC7\\}|/Processid:\\{D2E7041B-2927-42FB-8E9F-7CE93B6DC937\\})")', '2026-02-23 16:16:58.684760', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (977, 'Windows: UAC Bypass Attempt via Elevated COM Internet Explorer Add-On Installer', 1, 3, 2, 'Privilege Escalation', 'T1548.002 - Abuse Elevation Control Mechanism: Bypass User Account Control', 'Identifies User Account Control (UAC) bypass attempts by abusing an elevated COM Interface to launch a malicious program. Attackers may attempt to bypass UAC to stealthily execute code with elevated permissions.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1548/002/"]', 'regexMatch("log.message", "(C:\\(.+)\\AppData\\(.+)\\Temp\\IDC(.+).tmp\\(.+).exe)") && contains("log.processParentName", "ieinstall.exe") && regexMatch("log.message", "(-Embedding)")', '2026-02-23 16:16:59.795356', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (978, 'Windows: UAC Bypass Attempt with IEditionUpgradeManager Elevated COM Interface', 1, 3, 2, 'Privilege Escalation', 'T1548.002 - Abuse Elevation Control Mechanism: Bypass User Account Control', 'Identifies attempts to bypass User Account Control (UAC) by abusing an elevated COM Interface to launch a rogue Windows ClipUp program. Attackers may attempt to bypass UAC to stealthily execute code with elevated permissions.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1548/002/"]', 'contains("log.eventDataProcessName", "Clipup.exe") && !regexMatch("log.message", "(C:\\Windows\\System32\\ClipUp.exe)") && contains("log.eventDataParentProcessName", "dllhost.exe") && regexMatch("log.message", "(/Processid:{BD54C901-076B-434E-B6C7-17C531F4AB41)")', '2026-02-23 16:17:00.827262', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (986, 'Windows: Suspicious Cmd Execution via WMI', 1, 2, 3, 'Execution', 'T1047 - Windows Management Instrumentation', 'Identifies suspicious command execution (cmd) via Windows Management Instrumentation (WMI) on a remote host. This could be indicative of adversary lateral movement.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1047/"]', 'contains("log.eventDataParentProcessName", "WmiPrvSE.exe") && contains("log.eventDataProcessName", "cmd.exe") && contains("log.message", "\\\\127.0.0.1\\") && regexMatch("log.message", "(2>&1|1>)")', '2026-02-23 16:17:09.962165', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (987, 'Windows: Suspicious CertUtil Commands', 3, 2, 1, 'Defense Evasion', 'T1140 - Deobfuscate/Decode Files or Information', 'Identifies suspicious commands being used with certutil.exe. CertUtil is a native Windows component which is part of Certificate Services. CertUtil is often abused by attackers to live off the land for stealthier command and control or data exfiltration.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1140/"]', 'regexMatch("log.message", "(decode|encode|urlcache|verifyctl|encodehex|decodehex|exportPFX)") && contains("log.eventDataProcessName", "certutil.exe")', '2026-02-23 16:17:11.217733', true, true, 'target', null, '[]', '["target.host","target.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (988, 'Windows: Detection of SUNBURST command and control activity', 3, 3, 2, 'Command and Control', 'T1195 - Supply Chain Compromise', 'This rule detects post-exploitation command and control activity of the SUNBURST backdoor.', '["https://attack.mitre.org/tactics/TA0011/","https://attack.mitre.org/techniques/T1195/"]', 'regexMatch("log.eventDataProcessName", "(ConfigurationWizard.exe|NetFlowService.exe|NetflowDatabaseMaintenance.exe|SolarWinds.Administration.exe|SolarWinds.BusinessLayerHost.exe|SolarWinds.BusinessLayerHostx64.exe|SolarWinds.Collector.Service.exe|SolarwindsDiagnostics.exe)") && regexMatch("log.message", "(/swip/Upload.ashx(.+)(POST|PUT)|(POST|PUT)(.+)/swip/Upload.ashx|/swip/SystemDescription(.+)(GET|HEAD)|(GET|HEAD)(.+)/swip/SystemDescription)")', '2026-02-23 16:17:12.492026', true, true, 'target', null, '[]', '["target.host","target.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (989, 'Windows: Startup Persistence by a Suspicious Process', 1, 2, 3, 'Persistence', 'T1547.001 - Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder', 'Identifies files written to or modified in the startup folder by commonly abused processes. Adversaries may use this technique to maintain persistence.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1547/001/"]', 'contains("log.message", "delet") && regexMatch("log.eventDataProcessName", "(cmd.exe|powershell.exe|wmic.exe|mshta.exe|pwsh.exe|cscript.exe|wscript.exe|regsvr32.exe|RegAsm.exe|rundll32.exe|EQNEDT32.EXE|WINWORD.EXE|EXCEL.EXE|POWERPNT.EXE|MSPUB.EXE|MSACCESS.EXE|iexplore.exe|InstallUtil.exe)") && regexMatch("log.message", "(C:\\Users\\(.+)\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\|C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\StartUp\\)") && !contains("target.domain", "NT AUTHORITY")', '2026-02-23 16:17:13.507086', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (990, 'Windows: User account exposed to Kerberoasting', 3, 3, 2, 'Credential Access', 'T1558.003 - Steal or Forge Kerberos Tickets: Kerberoasting', 'Detects when a user account has the servicePrincipalName attribute modified. Attackers can abuse write privileges over a user to configure Service Principle Names (SPNs) so that they can perform Kerberoasting. Administrators can also configure this for legitimate purposes, exposing the account to Kerberoasting.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1558/003/"]', 'regexMatch("log.action", "([Dd]irectory [Ss]ervice [Cc]hanges)") && equals("log.eventCode", 5136) && regexMatch("log.message", "(servicePrincipalName)")', '2026-02-23 16:17:14.733203', true, true, 'target', null, '[]', '["target.host","target.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (991, 'Windows: SIP Provider Modification', 1, 2, 3, 'Defense Evasion', 'T1553.003 - Subvert Trust Controls: SIP and Trust Provider Hijacking', 'Identifies modifications to the registered Subject Interface Package (SIP) providers. SIP providers are used by the Windows cryptographic system to validate file signatures on the system. This may be an attempt to bypass signature validation checks or inject code into critical processes.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1553/003/"]', 'equals("log.event.type", "change") && contains("log.registryDataStrings", ".dll") && regexMatch("log.registryPath", "(HKLM\\\\SOFTWARE(\\\\WOW6432Node)?\\\\Microsoft\\\\Cryptography\\\\OID\\\\EncodingType 0\\\\CryptSIPDllPutSignedDataMsg\\\\(.+)\\\\Dll|HKLM\\\\SOFTWARE(\\\\WOW6432Node)?\\\\Microsoft\\\\Cryptography\\\\Providers\\\\Trust\\\\FinalPolicy\\\\(.+)\\\\\\$Dll)")', '2026-02-23 16:17:15.971963', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (992, 'Windows: Execution via local SxS Shared Modules', 1, 2, 3, 'Execution', 'T1129 - Shared Modules', 'Identifies the creation, change, or deletion of a DLL module within a Windows SxS local folder. Adversaries may abuse shared modules to execute malicious payloads by instructing the Windows module loader to load DLLs from arbitrary local paths.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1129/"]', 'contains("log.file.extension", "dll") && regexMatch("log.file.path", "(C:\\\\(.+)\\\\.exe.local\\\\(.+).dll)")', '2026-02-23 16:17:17.159441', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (993, 'Windows: Potential Shadow Credentials added to AD Object', 3, 3, 2, 'Credential Access', 'T1556 - Modify Authentication Process', 'Identify the modification of the msDS-KeyCredentialLink attribute in an Active Directory Computer or User Object. Attackers can abuse control over the object and create a key pair, append to raw public key in the attribute, and obtain persistent and stealthy access to the target user or computer object.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1556/"]', 'regexMatch("log.action", "([Dd]irectory [Ss]ervice [Cc]hanges)") && equals("log.eventCode", 5136) && regexMatch("log.message", "(msDS-KeyCredentialLink)") && contains("log.message", ":828")', '2026-02-23 16:17:18.355353', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (994, 'Windows: Detection of a suspicious PowerShell Script with screenshot capabilities', 3, 2, 1, 'Collection', 'T1113 - Screen Capture', 'Detects PowerShell scripts that can take screenshots, which is a common feature in post-exploitation kits and remote access tools', '["https://attack.mitre.org/tactics/TA0009/","https://attack.mitre.org/techniques/T1113/"]', 'regexMatch("log.message", "(CopyFromScreen(.+)Drawing.Bitmap|Drawing.Bitmap(.+)CopyFromScreen)")', '2026-02-23 16:17:19.371469', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (995, 'Windows: Privilege Escalation via Windir Environment Variable', 2, 3, 2, 'Privilege Escalation', 'T1574.007 - Hijack Execution Flow: Path Interception by PATH Environment Variable', 'Identifies a privilege escalation attempt via a rogue Windows directory (Windir) environment variable. This is a known primitive that is often combined with other vulnerabilities to elevate privileges.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1574/007/"]', 'regexMatch("log.message", "(C:\\windows|%SystemRoot%)") && regexMatch("log.message", "(HKEY_USERS\\(.+)\\Environment\\windir|HKEY_USERS\\(.+)\\Environment\\systemroot)")', '2026-02-23 16:17:20.577929', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (996, 'Windows: Possible ransomware attack detected. Unusual File Extensions.', 3, 3, 3, 'Impact', 'T1486 - Data Encrypted for Impact', 'Ransomware, is a type of malware that prevents users from accessing their system or personal files and requires payment of a ransom in order to gain access to them again. Identifies ransomware attempts. Files with unusual file extensions have been detected, potentially indicating encrypted files created by ransomware.', '["https://attack.mitre.org/tactics/TA0040/"]', 'equals("log.eventCode", 4663) && regexMatch("log.eventDataFileName", "\\.(7z\\.encrypted|aaa|abcd|abtc|acc|aes256|aes_ni|aes256ctr|aes256encrypted|aes_gcm|ajp|alcatraz_locked|alfa|amnesia[1-9]?|amsi|apocalypse666|armadillo|arrow|asasin|asi|atom128|auditor|aurora|autoit|avastvirusinfo|avenger|av666|azero|barak|barrax|bart|beef|beetle|bip|bit|bitcoin|bl2r|blackblock|blackmail|blast|blind|bmw|boot|braincrypt[3-8]?|broken|btcware|budak|bull|buydecryptor|cakl|calipso|calum|carote|cats|cbf|cccmn|ccrypt|cerber[3-9]?|chifrator|chimera|ciphered|crypted|clover|cobra|codnat3|combo|comrade|conficker|coot|cpt|crash|crimson|cry|crypt\\d{3,4}|crypt38|crypt72|crypt888|cryptinfinite|cryptolocker|cryptowall|cryptxxx|crypz|csfr|csone|ctb[1-4]|ctb-locker|ctrsa|cryptowin|cube|dcrpt|ddtf|dr2|dragon|dried|druid|ducky|ecrpt|eeta|etr|ee|f[0-9]{3,4}|flock|grt|grt[0-9]+|grtlock|gwz|h3ll|hades|hakunamatata|hallucinating|happy|harmful|harrow|havoc|headdesk|helpdecrypt|hermes|hidden|hideous|hijack|hilda|hitler|hjg|hmpl|hrosas|hsdf|hushed|hwrm|ihsdj|ikarus|ikasir|ikayed|ill|imbtc|img|encrypted|improved|indrik|injected|innocent|insane|interesante|jungle|kaos|karl|katana|kimcilware|kin|kiratos|kiss|kjh|locked|locky|lokf|losers|lukitus|m3g4c0rtx|m4n1fest0|m4s4g3|maas|madmax|mafi|magic|maktub|malware|manamecrypt|mandelbrot|manic|matrix|max|md5|medusa|mega|melme|merry|mesmerize|metropolitan|mikey|mikibackup|milarepa.lotos@aol.com|mirror|mmnn|mole|monro|mosk|muslat|n1n1n1|nabr|napoleon|narrow|nasoh|nataniel|neitrino|neras|nlah|nosu|novasof|nozelesn|nuclear|nwa|nymaim|obelisk|off|offwhite|ogdo|omega|omerta|onion|ooss|opencode|openme|opqz|osiris|otx|p3rf0rm4|pabluk|pack14|packagetrackr@india.com|packrat|pahd|panda|pandemic|pandora|pansy|paradise|paris|paym3|paymer|payms|pcap|pclock|peet|pelikan|penis|petya|pewcrypt|phoenix|photominr|phobos|phps|pirated|pluto|po1|point|poop|potato|pr0tect|preppy|princesa|princess|prosper|prosperity|prq|pshy|pumas|pumax|pure|purple|purpler|pwnd|pysa|q9q9|qbtex|qiuu|qkkd|qscx|qtyop|quimera|r2d2|r5a|rabit|radman|raid10|rainbow|rakhni|rambo|ramses|rat|rcrypted|react|reactor|realtek|reaper|redlion|redmat|redrum|rekt|remk|removal|remsec|remy|renaming|revenge|rezuc|rhino|ribd|rich|rip|rire|rizonesoft@protonmail.ch|rk|rmdir|robinhood|rocke|rogue|roldat|rolin|ronzware|rosenquist|rotten|roza|rpcminer|rsalive|rumba|run|rxx|uak|udjvu|unlock92|unlckr|upd|urcp|usam|usbc|v8|vag|vandt|varasto|vault|vauw|vb|ve|vendetta|venom|veracrypt|versiegelt|veton|vhd|vindows|violate|virus|vivin|vk_677|vma|vmx|volcano|vorasto|vorphal|vos|vscrypt|vxl|w4b|wakanda|wannacash|wannacry|wanted|war|wasted|wcry|weapologize|webmafia|weird|weui|whatthefuck|whistler|white|whitenoise|whiterabbit|whorus|why!decryptor|wicked|wildfire|windows10|windows7|windows8|windowsupdate|winlock|wipe|wisconsin|wizard|wlu|woolger|worm|wormfubuki|wow|wpencrypt|wq!decryptor|wrui|wtg|x1881|x3m|xampug|xdata|xencrypt|xfiles|xhelper|xlr|xman|xmd|xmd|xtbl|xtbl|xtr|xtt|xtz|xyz|yakuza|yatron|ybn|year|yellow|yheq|yty|yuke|yxo|yyto|z3|zatrov|zax|zbot|zbt|zbt|zeppelin|zerber|zet|zet|zfj|zfj|zimbra|zip|zix|zlz|zobm|zoh|zorro|zphs)$")', '2026-02-23 16:17:21.651801', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":"4663"},{"field":"origin.user","operator":"filter_term","value":"{{origin.user}}"}],"or":null,"within":"now-5m","count":20}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (997, 'Windows: Persistence via PowerShell profile', 2, 3, 1, 'Persistence', 'T1546.013 - Event Triggered Execution: PowerShell Profile', 'Identifies the creation or modification of a PowerShell profile. PowerShell profile is a script that is executed when PowerShell starts to customize the user environment, which can be abused by attackers to persist in a environment where PowerShell is common.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1098/002/"]', 'regexMatch("log.eventDataProcessName", "(:\\Users\\(.+)\\Documents\\WindowsPowerShell\\|:\\Users\\(.+)\\Documents\\PowerShell\\|:\\Windows\\System32\\WindowsPowerShell\\)") && regexMatch("log.eventDataProcessName", "(profile.ps1|Microsoft.Powershell_profile.ps1)")', '2026-02-23 16:17:22.748939', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (998, 'Windows: New ActiveSyncAllowedDeviceID Added via PowerShell', 3, 2, 1, 'Persistence', 'T1098.002 - Account Manipulation: Additional Email Delegate Permissions', 'Identifies the use of the Exchange PowerShell cmdlet, Set-CASMailbox, to add a new ActiveSync allowed device. Adversaries may target user email to collect sensitive information.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1098/002/"]', 'oneOf("log.eventDataProcessName", ["powershell.exe", "pwsh.exe", "powershell_ise.exe"]) && regexMatch("log.message", "(Set-CASMailbox(.+)ActiveSyncAllowedDeviceIDs)")', '2026-02-23 16:17:23.881249', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (999, 'Windows: Potential Credential Access via DuplicateHandle in LSASS', 3, 1, 2, 'Credential Access', 'T1003 - OS Credential Dumping', 'Identifies suspicious access to an LSASS handle via DuplicateHandle. This may indicate an attempt to bypass the NtOpenProcess API to evade detection and dump LSASS memory for credential access.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/"]', 'equals("log.eventCode", 10) && contains("log.eventDataProcessName", "lsass.exe") && equals("log.eventDataGrantedAccess", "0x40") && regexMatch("log.eventDataCallTrace", "(UNKNOWN)")', '2026-02-23 16:17:25.034075', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1000, 'Windows: Potential DNS Tunneling via NsLookup', 3, 2, 1, 'Command and Control', 'T1071 - Application Layer Protocol', 'This rule identifies a large number of nslookup.exe executions with an explicit query type from the same host. This may indicate command and control activity utilizing the DNS protocol.', '["https://attack.mitre.org/tactics/TA0011/","https://attack.mitre.org/techniques/T1071/"]', 'contains("log.eventDataProcessName", "nslookup.exe") && regexMatch("log.message", "(-querytype=|-qt=|-q=|-type=)")', '2026-02-23 16:17:26.125392', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1001, 'Windows: Printer driver failed to load, possible remote code execution using PrinterNightmare exploit: CVE-2021-34527', 3, 2, 1, 'Lateral Movement', 'T1210 - Exploitation of Remote Services', 'Adversaries may exploit remote services to gain unauthorized access to internal systems once inside of a network. Exploitation of a software vulnerability occurs when an adversary takes advantage of a programming error in a program, service, or within the operating system software or kernel itself to execute adversary-controlled code. A common goal for post-compromise exploitation of remote services is for lateral movement to enable access to a remote system.', '["https://attack.mitre.org/techniques/T1210/"]', 'equals("log.eventCode", 808) && oneOf("log.severityLabel", ["Error", "error"])', '2026-02-23 16:17:27.300525', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1002, 'Windows: Possible addition of new item to Windows startup registry', 2, 3, 1, 'Persistence', 'T1547.001 - Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder', 'Adversaries may achieve persistence by adding a program to a startup folder or referencing it with a Registry run key. Adding an entry to the run keys in the Registry or startup folder will cause the program referenced to be executed when a user logs in. These programs will be executed under the context of the user and will have the account''s associated permissions level.', '["https://attack.mitre.org/techniques/T1547/001/"]', 'regexMatch("log.message", "(SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run)")', '2026-02-23 16:17:28.355757', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1003, 'Windows: PowerShell Script with Token Impersonation Capabilities', 3, 3, 2, 'Privilege Escalation', 'Token Impersonation/Theft', 'Detects scripts that contain PowerShell functions, structures, or Windows API functions related to token impersonation/theft. Attackers may duplicate then impersonate another user''s token to escalate privileges and bypass access controls.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1134/001/"]', e'oneOf("log.message", ["Invoke-TokenManipulation", "ImpersonateNamedPipeClient", "NtImpersonateThread"]) || +(regexMatch("log.message", "(UpdateProcThreadAttribute(.+)STARTUPINFOEX|STARTUPINFOEX(.+)UpdateProcThreadAttribute)") && +regexMatch("log.message", "(AdjustTokenPrivileges(.+)SeDebugPrivilege|SeDebugPrivilege(.+)AdjustTokenPrivileges)")) || +regexMatch("log.message", "((SetThreadToken|ImpersonateLoggedOnUser|CreateProcessWithTokenW|CreatePRocessAsUserW|CreateProcessAsUserA)(.+)(DuplicateToken|DuplicateTokenEx)|(DuplicateToken|DuplicateTokenEx)(.+)(SetThreadToken|ImpersonateLoggedOnUser|CreateProcessWithTokenW|CreatePRocessAsUserW|CreateProcessAsUserA))") +', '2026-02-23 16:17:29.491501', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1004, 'Windows: PowerShell Suspicious Discovery Related Windows API Functions', 2, 1, 3, 'Discovery', 'T1069 - Permission Groups Discovery', 'This rule detects the use of discovery-related Windows API functions in PowerShell Scripts. Attackers can use these functions to perform various situational awareness related activities, like enumerating users, shares, sessions, domain trusts, groups, etc.', '["https://attack.mitre.org/tactics/TA0007/","https://attack.mitre.org/techniques/T1069/"]', e'contains("log.message", ["NetShareEnum", "NetWkstaUserEnum", "NetSessionEnum", "NetLocalGroupEnum", "NetLocalGroupGetMembers", "DsGetSiteName", "DsEnumerateDomainTrusts", "WTSEnumerateSessionsEx", "WTSQuerySessionInformation", "LsaGetLogonSessionData", "QueryServiceObjectSecurity"]) +', '2026-02-23 16:17:30.600742', true, true, 'origin', '["adversary.user","adversary.ip"]', '[]', null); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1005, 'Windows: PowerShell Kerberos Ticket Request', 3, 2, 1, 'Credential Access', 'T1059 - Command and Scripting Interpreter', 'Detects PowerShell scripts that have the capability of requesting kerberos tickets, which is a common step in Kerberoasting toolkits to crack service accounts.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1059/"]', 'regexMatch("log.message", "(KerberosRequestorSecurityToken)")', '2026-02-23 16:17:31.704687', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1006, 'Windows: PowerShell PSReflect Script', 2, 3, 1, 'Execution', 'T1059 - Command and Scripting Interpreter', 'Detects the use of PSReflect in PowerShell scripts. Attackers leverage PSReflect as a library that enables PowerShell to access win32 API functions.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1059/"]', 'regexMatch("log.message", "(New-InMemoryModule|Add-Win32Type|psenum|DefineDynamicAssembly|DefineDynamicModule|Reflection.TypeAttributes|Reflection.Emit.OpCodes|Reflection.Emit.CustomAttributeBuilder|Runtime.InteropServices.DllImportAttribute)")', '2026-02-23 16:17:32.923911', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1007, 'Windows: Potential Process Injection via PowerShell', 3, 3, 2, 'Defense Evasion', 'T1055 - Process Injection', 'Detects the use of Windows API functions that are commonly abused by malware and security tools to load malicious code or inject it into remote processes.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1055/"]', 'regexMatch("log.message", "(WriteProcessMemory|CreateRemoteThread|NtCreateThreadEx|CreateThread|QueueUserAPC|SuspendThread|ResumeThread|GetDelegateForFunctionPointer)") && regexMatch("log.message", "(VirtualAlloc|VirtualAllocEx|VirtualProtect|LdrLoadDll|LoadLibrary|LoadLibraryA|LoadLibraryEx|GetProcAddress|OpenProcess|OpenProcessToken|AdjustTokenPrivileges)")', '2026-02-23 16:17:33.882169', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1008, 'Windows: Suspicious Portable Executable Encoded in Powershell Script', 2, 3, 1, 'Execution', 'T1059 - Command and Scripting Interpreter', 'Detects the presence of a portable executable (PE) in a PowerShell script by looking for its encoded header. Attackers embed PEs into PowerShell scripts to inject them into memory, avoiding defences by not writing to disk.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1059/"]', 'contains("log.message", "TVqQAAMAAAAEAAAA")', '2026-02-23 16:17:34.931900', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1009, 'Windows: PowerShell MiniDump Script', 3, 3, 2, 'Credential Access', 'T1059.001 - Command and Scripting Interpreter: PowerShell', 'This rule detects PowerShell scripts capable of dumping process memory using WindowsErrorReporting or Dbghelp.dll MiniDumpWriteDump. Attackers can use this tooling to dump LSASS and get access to credentials.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1059/001/"]', 'regexMatch("log.message", "(MiniDumpWriteDump|MiniDumpWithFullMemory|pmuDetirWpmuDiniM)")', '2026-02-23 16:17:36.075967', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1010, 'Windows: PowerShell Share Enumeration Script', 3, 2, 1, 'Discovery', 'T1135 - Network Share Discovery', 'Detects scripts that contain PowerShell functions, structures, or Windows API functions related to windows share enumeration activities. Attackers, mainly ransomware groups, commonly identify and inspect network shares, looking for critical information for encryption and/or exfiltration.', '["https://attack.mitre.org/tactics/TA0007/","https://attack.mitre.org/techniques/T1135/"]', 'regexMatch("log.message", "(Invoke-ShareFinder|Invoke-ShareFinderThreaded|(shi1_netname(.+)shi1_remark)|shi1_remark(.+)shi1_netname|(NetShareEnum(.+)NetApiBufferFree)|(NetApiBufferFree(.+)NetShareEnum))")', '2026-02-23 16:17:37.216444', true, true, 'origin', '["adversary.user","adversary.ip"]', '[]', null); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1011, 'Windows: PowerShell Suspicious Payload Encoded and Compressed', 2, 3, 1, 'Defense Evasion', 'T1027 - Obfuscated Files or Information', 'Identifies the use of .NET functionality for decompression and base64 decoding combined in PowerShell scripts, which malware and security tools heavily use to deobfuscate payloads and load them directly in memory to bypass defenses.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1027/"]', 'regexMatch("log.message", "(FromBase64String)") && regexMatch("log.message", "(System.IO.Compression.DeflateStream|System.IO.Compression.GzipStream|IO.Compression.DeflateStream|IO.Compression.GzipStream)")', '2026-02-23 16:17:38.251105', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1012, 'Windows: Suspicious .NET Reflection via PowerShell', 3, 2, 1, 'Defense Evasion', 'T1055 - Process Injection', 'Detects the use of Reflection.Assembly to load PEs and DLLs in memory in PowerShell scripts. Attackers use this method to load executables and DLLs without writing to the disk, bypassing security solutions.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1055/"]', 'regexMatch("log.message", "(\\[Reflection.Assembly\\]::Load)")', '2026-02-23 16:17:39.236449', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1013, 'Windows: Outlook add-in was loaded by powershell, possible use for email collection', 3, 2, 1, 'Execution', 'T1114.001 - Email Collection: Local Email Collection', 'Adversaries may target user email on local systems to collect sensitive information. Files containing email data can be acquired from a user is local system, such as Outlook storage or cache files.', '["https://attack.mitre.org/techniques/T1114/001/"]', 'contains("log.message", "Microsoft.Office.Interop.Outlook")', '2026-02-23 16:17:40.238844', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1014, 'Windows: Multiple TS Gateway login failures', 3, 3, 2, 'Credential Access', 'T1110 - Brute Force', 'Adversaries may use brute force techniques to gain access to accounts when passwords are unknown or when password hashes are obtained.', '["https://attack.mitre.org/techniques/T1110/"]', 'equals("log.eventCode", 1001) && contains("log.message", "Microsoft-Windows-TerminalServices")', '2026-02-23 16:17:41.284851', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.ip","operator":"filter_term","value":"{{origin.ip}}"}],"or":null,"within":"now-15m","count":10}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1015, 'Windows: Multiple remote access login failures', 3, 2, 2, 'Credential Access', 'T1110 - Brute Force', 'Adversaries may use brute force techniques to gain access to accounts when passwords are unknown or when password hashes are obtained.', '["https://attack.mitre.org/techniques/T1110/"]', e'oneOf("log.eventCode", [20187, 20014, 20078, 20050, 20049, 2018]) && +regexMatch("log.message", "(?i)(authentication failed|login failed|access denied|authentication error|invalid credentials)") +', '2026-02-23 16:17:42.344133', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.ip","operator":"filter_term","value":"{{origin.ip}}"}],"or":null,"within":"now-15m","count":10}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1016, 'Windows: Potential Invoke-Mimikatz PowerShell Script', 3, 3, 2, 'Credential Access', 'T1003.001 - OS Credential Dumping: LSASS Memory', 'Mimikatz is a credential dumper capable of obtaining plaintext Windows account logins and passwords, along with many other features that make it useful for testing the security of networks. This rule detects Invoke-Mimikatz PowerShell script and alike.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1003/","https://attack.mitre.org/techniques/T1003/001/"]', 'regexMatch("log.message", "(DumpCreds(.+)DumpCerts|DumpCerts(.+)DumpCreds|sekurlsa::logonpasswords|crypto::certificates(.+)CERT_SYSTEM_STORE_LOCAL_MACHINE|CERT_SYSTEM_STORE_LOCAL_MACHINE(.+)crypto::certificates)")', '2026-02-23 16:17:43.473800', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1017, 'Windows: Kerberos Pre-authentication Disabled for User', 2, 2, 3, 'Credential Access', 'T1558.004 - Steal or Forge Kerberos Tickets: AS-REP Roasting', 'Identifies the modification of an accounts Kerberos pre-authentication options. An adversary with GenericWrite/GenericAll rights over the account can maliciously modify these settings to perform offline password cracking attacks such as AS-REP roasting', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1558/","https://attack.mitre.org/techniques/T1558/004/"]', 'regexMatch("log.message", "(('')?[Dd]on('')?t [Rr]equire [Pp]reauth('')?(\\s)?(-)?(\\s)?[Ee]nabled)") && equals("log.eventCode", 4738)', '2026-02-23 16:17:44.457366', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1018, 'Windows: Multiple Logon Failure Followed by Logon Success', 2, 2, 3, 'Credential Access', 'T1110 - Brute Force', 'This rule is triggered when a sequence of multiple failed login attempts followed immediately by a successful login from the same IP address or source is detected. This unusual sequence of events may indicate a possible unauthorized access attempt using a brute force or password guessing technique. The purpose of this rule is to identify suspicious patterns of login activity and alert you to potential unauthorized access attempts.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1110/"]', 'equals("log.eventCode", 4624)', '2026-02-23 16:17:45.550673', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":4625},{"field":"target.user","operator":"filter_term","value":"{{target.user}}"}],"or":null,"within":"now-5m","count":10}]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1050, 'RDP Brute Force Attack', 3, 2, 2, 'Credential Access', 'T1110.001 - Brute Force: Password Guessing', e'Detects multiple failed RDP login attempts from the same source IP address, indicating a potential brute force attack. This rule monitors Windows Event ID 4625 (failed logon) with focus on network logon types (type 3) which are commonly used for RDP connections. The rule triggers when 10 or more failed attempts occur from the same IP within 15 minutes. + +Next Steps: +1. Investigate the source IP address for malicious indicators and geolocation +2. Check if the targeted user accounts are legitimate and active +3. Review successful logons from the same IP after failed attempts +4. Implement IP blocking or rate limiting for the source address +5. Enable account lockout policies if not already configured +6. Consider implementing multi-factor authentication for RDP access +7. Review RDP access logs for any successful connections during the attack timeframe +', '["https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4625","https://attack.mitre.org/techniques/T1110/001/"]', 'equals("log.eventCode", "4625") && equals("log.eventDataLogonType", "3") && exists("log.eventDataIpAddress") && !equals("log.eventDataIpAddress", "-") && !equals("log.eventDataIpAddress", "::1") && !equals("log.eventDataIpAddress", "127.0.0.1")', '2026-02-23 16:19:04.956285', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataIpAddress","operator":"filter_term","value":"{{log.eventDataIpAddress}}"},{"field":"log.eventCode","operator":"filter_term","value":"4625"},{"field":"log.eventDataLogonType","operator":"filter_term","value":"3"}],"or":null,"within":"now-15m","count":10}]', '["adversary.ip","target.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1051, 'PsExec Lateral Movement Detection', 3, 3, 2, 'Lateral Movement', 'T1569.002 - System Services: Service Execution', e'Detects PsExec-style lateral movement by monitoring for the creation of the PSEXESVC service +(Event ID 7045), PsExec named pipe creation, and process execution patterns characteristic +of PsExec and its variants (Impacket PSExec, Metasploit PSExec). PsExec is the most common +tool for lateral movement in enterprise environments. + +Next Steps: +1. Identify the source host that initiated the PsExec connection +2. Verify if this is authorized administrative activity +3. Check what command was executed via PsExec on the target host +4. Review the service account credentials used for PsExec authentication +5. Search for evidence of PsExec usage across other hosts in the environment +6. Check for credential theft that may have enabled the lateral movement +7. Block PsExec at the network level if not authorized +', '["https://attack.mitre.org/techniques/T1569/002/","https://jpcertcc.github.io/ToolAnalysisResultSheet/details/PsExec.htm","https://www.sans.org/blog/psexec-and-lateral-movement/"]', e'( + equals("log.eventCode", "7045") && + ( + regexMatch("log.eventDataServiceName", "(?i)^(PSEXESVC|psexec|PAExec|csexec|remcom)") || + regexMatch("log.eventDataServiceFileName", "(?i)(PSEXESVC|psexec|PAExec)\\\\.exe") || + (regexMatch("log.eventDataServiceFileName", "(?i)%SystemRoot%\\\\\\\\[a-zA-Z]{8}\\\\.exe") && + regexMatch("log.eventDataServiceName", "(?i)^[a-zA-Z]{8}$")) || + regexMatch("log.eventDataServiceFileName", "(?i)cmd\\\\.exe\\\\s+/c.*\\\\\\\\\\\\\\\\[0-9]+\\\\.[0-9]+\\\\.[0-9]+\\\\.[0-9]+\\\\\\\\") + ) +) || +( + equals("log.eventCode", "17") && + equals("log.providerName", "Microsoft-Windows-Sysmon") && + regexMatch("log.eventDataPipeName", "(?i)\\\\\\\\(psexesvc|paexec|csexec|remcom)") +) || +( + equals("log.eventCode", "4688") && + regexMatch("log.eventDataNewProcessName", "(?i)\\\\\\\\PSEXESVC\\\\.exe$") && + exists("log.eventDataParentProcessName") +) +', '2026-02-23 16:19:06.129872', true, true, 'origin', null, '[]', '["adversary.host","target.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1052, 'Process Injection Techniques Detection', 3, 3, 2, 'Defense Evasion, Privilege Escalation', 'T1055 - Process Injection', e'Detects various process injection techniques including CreateRemoteThread, SetWindowsHookEx, and other methods used by malware to inject code into legitimate processes. This rule monitors for suspicious cross-process activities targeting critical Windows processes and detects PowerShell/command line usage of injection APIs. + +Next Steps: +1. Investigate the source process and command line arguments for suspicious activity +2. Examine the target process for signs of compromise or abnormal behavior +3. Review process tree and parent-child relationships for the involved processes +4. Check for additional suspicious network connections or file system activities from the same host +5. Analyze memory dumps of target processes if available to confirm injection +6. Review system logs for privilege escalation attempts around the same timeframe +7. Correlate with threat intelligence to identify known malware families using similar techniques +8. Consider isolating the affected system if malicious activity is confirmed +', '["https://attack.mitre.org/techniques/T1055/","https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon","https://www.elastic.co/blog/ten-process-injection-techniques-technical-survey-common-and-trending-process"]', e'( + equals("log.eventCode", "8") && + ( + regexMatch("log.eventDataTargetImage", "(?i)(lsass\\\\.exe|csrss\\\\.exe|services\\\\.exe|on\\\\.exe|svchost\\\\.exe|explorer\\\\.exe)") || + ( + regexMatch("log.eventDataSourceImage", "(?i)(powershell\\\\.exe|cmd\\\\.exe|rundll32\\\\.exe|regsvr32\\\\.exe)") && + !regexMatch("log.eventDataTargetImage", "(?i)(conhost\\\\.exe)") + ) + ) +) || +( + equals("log.eventCode", "10") && + regexMatch("log.eventDataTargetImage", "(?i)(lsass\\\\.exe|csrss\\\\.exe|services\\\\.exe)") && + oneOf("log.eventDataGrantedAccess", ["0x1F0FFF", "0x1F1FFF", "0x1FFFFF", "0x1F3FFF"]) && + !regexMatch("log.eventDataSourceImage", "(?i)(taskmgr\\\\.exe|procexp\\\\.exe|procmon\\\\.exe|svchost\\\\.exe)") +) || +( + (equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && + ( + regexMatch("log.eventDataCommandLine", "(?i)VirtualAllocEx") || + regexMatch("log.eventDataCommandLine", "(?i)WriteProcessMemory") || + regexMatch("log.eventDataCommandLine", "(?i)CreateRemoteThread") || + regexMatch("log.eventDataCommandLine", "(?i)NtQueueApcThread") || + regexMatch("log.eventDataCommandLine", "(?i)SetWindowsHookEx") || + regexMatch("log.eventDataCommandLine", "(?i)RtlCreateUserThread") + ) +) || +( + (equals("log.eventCode", "4104") || equals("log.eventId", 4104)) && + ( + regexMatch("log.eventDataScriptBlockText", "(?i)\\\\[Kernel32\\\\]::(VirtualAllocEx|WriteProcessMemory|CreateRemoteThread)") || + regexMatch("log.eventDataScriptBlockText", "(?i)\\\\[ntdll\\\\]::(NtQueueApcThread|RtlCreateUserThread)") || + contains("log.eventDataScriptBlockText", "Invoke-ReflectivePEInjection") || + contains("log.eventDataScriptBlockText", "Invoke-ProcessHollowing") + ) +) +', '2026-02-23 16:19:07.218671', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host","operator":"filter_term","value":"{{origin.host}}"}],"or":null,"within":"now-15m","count":3}]', '["adversary.host","adversary.process"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1019, 'Windows: Possible Brute Force Attack', 2, 2, 3, 'Credential Access', 'T1110 - Brute Force', 'This rule is triggered when a pattern of repeated and rapid login attempts from the same IP address or source is detected. These login attempts may target specific user accounts or services in an attempt to crack passwords through automated brute force. The purpose of this rule is to identify possible malicious unauthorized access attempts and prevent a brute force attack against the system.', '["https://attack.mitre.org/tactics/TA0006/","https://attack.mitre.org/techniques/T1110/"]', 'equals("log.eventCode", 4625)', '2026-02-23 16:17:46.729364', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":"{{log.eventCode}}"},{"field":"target.user","operator":"filter_term","value":"{{target.user}}"}],"or":null,"within":"now-5m","count":10}]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1020, 'Windows audit log was cleared', 1, 2, 3, 'Defense Evasion', 'T1070.001 - Indicator Removal: Clear Windows Event Logs', 'Detects when the Windows audit log (Security event log) has been cleared. Adversaries may clear event logs to remove evidence of an intrusion.', '["https://attack.mitre.org/techniques/T1070/001/"]', 'equals("log.eventCode", 1102)', '2026-02-23 16:17:47.943411', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1021, 'Windows Defender: Protection Disabled', 1, 2, 3, 'Defense Evasion', 'T1562 - Impair Defenses', 'This rule is triggered when it detects that Windows Defender protection has been turned off or disabled on the system. The alert is crucial to identify any unauthorized or malicious actions that may leave the system vulnerable to security threats.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/"]', 'oneOf("log.eventCode", [5001, 5012])', '2026-02-23 16:17:49.046642', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1022, 'Windows: An attempt was made to set the Directory Services Restore Mode', 1, 2, 3, 'Collection', 'T1005 - Data from Local System', 'This event generates on every attempt made to set the Directory Services Restore Mode. Is a function on Active Directory Domain Controllers to take the server offline for emergency maintenance, particularly restoring backups of AD objects. It is accessed on Windows Server via the advanced startup menu, similarly to safe mode.', '["https://attack.mitre.org/tactics/TA0009/","https://attack.mitre.org/techniques/T1005"]', 'equals("log.eventCode", 4794)', '2026-02-23 16:17:50.216828', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1023, 'Windows: Hiding files with attrib.exe', 1, 2, 3, 'Defense Evasion', 'T1564 - Hide Artifacts', 'This correlation rule detects events where the attrib.exe tool is used to hide files on the system. Cloaking files using this tool can be an indicator of suspicious or malicious activity, as it could be used by malicious actors to evade detection and hide their presence on the system.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1564/"]', 'regexMatch("log.message", "(attrib +h|attrib.exe +h)")', '2026-02-23 16:17:51.320773', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1024, 'Windows: Changes to AdminSDHolder', 1, 2, 3, 'Privilege Escalation', 'T1087 - Account Discovery', 'This rule detects changes to the AdminSDHolder object within the Active Directory. The AdminSDHolder object is critical to maintaining the ACLs of highly privileged accounts and groups in the domain. Any unexpected modification to this object could indicate a privilege escalation attempt or malicious action.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1069/"]', 'regexMatch("log.action", "Directory Service Changes") && equals("log.eventCode", 5136) && regexMatch("log.eventDataObjectName", "AdminSDHolder")', '2026-02-23 16:17:52.517738', true, true, 'origin', null, '[]', '["adversary.ip","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1066, 'Malicious PowerShell Execution', 3, 3, 2, 'Execution', 'T1059.001 - Command and Scripting Interpreter: PowerShell', e'Detects malicious PowerShell execution patterns including encoded commands, download cradles, +AMSI bypass attempts, execution policy bypasses, and common offensive PowerShell techniques. +These patterns are frequently used by attackers for initial access, lateral movement, and +payload delivery. + +Next Steps: +1. Isolate the affected host immediately +2. Decode any Base64-encoded command content for analysis +3. Check parent process - unexpected parents (e.g., Word, Excel) indicate macro-based attacks +4. Review network connections for download cradle destinations +5. Examine PowerShell transcription and script block logs for full command content +6. Search for persistence mechanisms created by the script +7. Check if AMSI was successfully bypassed and what payload was executed +8. Correlate with any recent phishing emails received by the user +', '["https://attack.mitre.org/techniques/T1059/001/","https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging","https://www.fireeye.com/blog/threat-research/2016/02/greater_visibility_t.html"]', e'( + (equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && + ( + regexMatch("log.eventDataNewProcessName", "(?i)powershell\\\\.exe$|pwsh\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)powershell\\\\.exe$|pwsh\\\\.exe$") + ) && + ( + regexMatch("log.eventDataCommandLine", "(?i)-[eE][nN][cC]\\\\s+[A-Za-z0-9+/=]{50,}") || + regexMatch("log.eventDataCommandLine", "(?i)(-nop|-noni|-w\\\\s+hidden|-ep\\\\s+bypass|-exec\\\\s+bypass)") || + regexMatch("log.eventDataCommandLine", "(?i)(DownloadString|DownloadFile|DownloadData|WebClient|Invoke-WebRequest|wget|curl|Start-BitsTransfer)") || + regexMatch("log.eventDataCommandLine", "(?i)(Invoke-Expression|IEX|Invoke-Command)\\\\s*[\\\\(\\\\{]") || + regexMatch("log.eventDataCommandLine", "(?i)(Net\\\\.WebClient|IO\\\\.MemoryStream|IO\\\\.StreamReader|IO\\\\.Compression)") || + regexMatch("log.eventDataCommandLine", "(?i)(FromBase64String|ToBase64String|\\\\[Convert\\\\]::)") || + regexMatch("log.eventDataCommandLine", "(?i)-[eE][nN][cC]\\\\s+[A-Za-z0-9+/=]{50,}") || + regexMatch("log.eventDataCommandLine", "(?i)(-nop|-noni|-w\\\\s+hidden|-ep\\\\s+bypass|-exec\\\\s+bypass)") || + regexMatch("log.eventDataCommandLine", "(?i)(DownloadString|DownloadFile|DownloadData|WebClient|Invoke-WebRequest)") + ) +) || +( + (equals("log.eventCode", "4104") || equals("log.eventId", 4104)) && + equals("log.providerName", "Microsoft-Windows-PowerShell") && + ( + contains("log.eventDataScriptBlockText", "AmsiInitFailed") || + contains("log.eventDataScriptBlockText", "amsiContext") || + contains("log.eventDataScriptBlockText", "AmsiUtils") || + contains("log.eventDataScriptBlockText", "amsi.dll") || + regexMatch("log.eventDataScriptBlockText", "(?i)(Invoke-Obfuscation|Invoke-CradleCrafter|Out-EncodedCommand)") || + regexMatch("log.eventDataScriptBlockText", "(?i)(New-Object\\\\s+Net\\\\.Sockets\\\\.TCPClient|Net\\\\.WebClient\\\\)?\\\\.DownloadString)") || + regexMatch("log.eventDataScriptBlockText", "(?i)(\\\\$env:COMSPEC|cmd\\\\.exe.*/c)") || + regexMatch("log.eventDataScriptBlockText", "(?i)(Add-Type.*DllImport|\\\\[DllImport)") || + contains("log.eventDataScriptBlockText", "Reflection.Assembly") || + contains("log.eventDataScriptBlockText", "Invoke-Shellcode") || + contains("log.eventDataScriptBlockText", "AmsiInitFailed") || + contains("log.eventDataScriptBlockText", "AmsiUtils") || + regexMatch("log.eventDataScriptBlockText", "(?i)(Invoke-Obfuscation|Invoke-CradleCrafter|Out-EncodedCommand)") + ) +) +', '2026-02-23 16:19:22.800322', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host","operator":"filter_term","value":"{{origin.host}}"}],"or":null,"within":"now-10m","count":2}]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1067, 'LSASS Memory Dump Alternatives', 3, 3, 1, 'Credential Access', 'T1003.001 - OS Credential Dumping: LSASS Memory', e'Detects alternatives to Mimikatz for dumping LSASS process memory, including procdump.exe, +comsvcs.dll MiniDump via rundll32, Task Manager LSASS dump, and direct process access to LSASS. +These tools are commonly used by attackers who avoid Mimikatz to extract credentials from memory. + +Next Steps: +1. Isolate the affected host immediately to prevent lateral movement +2. Identify the user account and parent process that initiated the dump +3. Check if procdump or comsvcs.dll was used and from what directory +4. Review for any exfiltration of the dump file to external destinations +5. Reset all credentials that were potentially exposed on the affected host +6. Investigate how the attacker obtained the privileges needed for LSASS access +7. Search for evidence of credential reuse across the environment +', '["https://attack.mitre.org/techniques/T1003/001/","https://www.microsoft.com/en-us/security/blog/2022/10/05/detecting-and-preventing-lsass-credential-dumping-attacks/","https://redcanary.com/threat-detection-report/techniques/lsass-memory/"]', e'( + (equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && + ( + (regexMatch("log.eventDataCommandLine", "(?i)procdump.*lsass") || + regexMatch("log.eventDataCommandLine", "(?i)procdump.*lsass")) || + (regexMatch("log.eventDataCommandLine", "(?i)rundll32.*comsvcs\\\\.dll.*MiniDump") || + regexMatch("log.eventDataCommandLine", "(?i)rundll32.*comsvcs\\\\.dll.*MiniDump")) || + (regexMatch("log.eventDataCommandLine", "(?i)Out-Minidump.*lsass") || + regexMatch("log.eventDataCommandLine", "(?i)Out-Minidump.*lsass")) || + (regexMatch("log.eventDataCommandLine", "(?i)taskmgr.*lsass") || + regexMatch("log.eventDataCommandLine", "(?i)taskmgr.*lsass")) || + (regexMatch("log.eventDataCommandLine", "(?i)sqldumper\\\\.exe.*lsass") || + regexMatch("log.eventDataCommandLine", "(?i)sqldumper\\\\.exe.*lsass")) || + (regexMatch("log.eventDataCommandLine", "(?i)createdump\\\\.exe.*lsass") || + regexMatch("log.eventDataCommandLine", "(?i)createdump\\\\.exe.*lsass")) || + (regexMatch("log.eventDataCommandLine", "(?i)rdrleakdiag\\\\.exe.*lsass") || + regexMatch("log.eventDataCommandLine", "(?i)rdrleakdiag\\\\.exe.*lsass")) + ) +) || +( + equals("log.eventCode", "10") && + regexMatch("log.eventDataTargetImage", "(?i)lsass\\\\.exe$") && + oneOf("log.eventDataGrantedAccess", ["0x1010", "0x1038", "0x1fffff", "0x1410", "0x143a"]) +) +', '2026-02-23 16:19:23.774229', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1025, 'WMI Remote Command Execution Detection', 3, 3, 2, 'Lateral Movement', 'T1047 - Windows Management Instrumentation', e'Detects WMI-based remote command execution using wmic.exe /node: parameter or PowerShell +Invoke-WmiMethod / Invoke-CimMethod for lateral movement. Attackers use WMI to execute +commands on remote systems without dropping files to disk, making it a stealthy lateral +movement technique that leverages built-in Windows functionality. + +Next Steps: +1. Identify the target host specified in the /node: parameter +2. Verify if this is authorized administrative WMI usage +3. Check what process or command was executed on the remote host +4. Review the credentials used for the WMI connection +5. Search for WMI execution evidence on the target host +6. Check for additional lateral movement from the same source +7. Monitor for process creation events on the target system +', '["https://attack.mitre.org/techniques/T1047/","https://www.fireeye.com/content/dam/fireeye-www/global/en/current-threats/pdfs/wp-windows-management-instrumentation.pdf","https://www.sans.org/blog/wmi-for-detection-and-response/"]', e'(equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && +( + (regexMatch("log.eventDataCommandLine", "(?i)wmic(.exe)?\\\\s+/node:") || + regexMatch("log.eventDataCommandLine", "(?i)wmic(.exe)?\\\\s+/node:")) || + (regexMatch("log.eventDataCommandLine", "(?i)Invoke-WmiMethod.*-ComputerName") || + regexMatch("log.eventDataCommandLine", "(?i)Invoke-WmiMethod.*-ComputerName")) || + (regexMatch("log.eventDataCommandLine", "(?i)Invoke-CimMethod.*-ComputerName") || + regexMatch("log.eventDataCommandLine", "(?i)Invoke-CimMethod.*-ComputerName")) || + (regexMatch("log.eventDataCommandLine", "(?i)Get-WmiObject.*-ComputerName.*Win32_Process") || + regexMatch("log.eventDataCommandLine", "(?i)Get-WmiObject.*-ComputerName.*Win32_Process")) || + (regexMatch("log.eventDataCommandLine", "(?i)wmic(.exe)?.*process\\\\s+call\\\\s+create") || + regexMatch("log.eventDataCommandLine", "(?i)wmic(.exe)?.*process\\\\s+call\\\\s+create")) +) +', '2026-02-23 16:18:36.664953', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1026, 'WMI Event Subscription Persistence Detection', 2, 3, 2, 'Persistence', 'T1546.003 - Event Triggered Execution: Windows Management Instrumentation Event Subscription', e'Detects creation of WMI event subscriptions used for stealthy persistence. Attackers create +WMI event filters and consumers that execute commands when specific system events occur. +This is a highly effective persistence mechanism because it survives reboots and is difficult +to detect without specific monitoring. The rule monitors for wmic.exe commands creating +event subscriptions and PowerShell WMI subscription creation patterns. + +Next Steps: +1. Enumerate all WMI event subscriptions using Get-WMIObject or wmic +2. Examine the event filter conditions and consumer actions +3. Identify the command or script executed by the consumer +4. Remove malicious WMI subscriptions immediately +5. Check for related persistence mechanisms on the same host +6. Review the timeline to identify the initial compromise +7. Search for similar WMI persistence across other endpoints +', '["https://attack.mitre.org/techniques/T1546/003/","https://www.fireeye.com/blog/threat-research/2016/08/wmi_vs_wmi_monitor.html","https://pentestlab.blog/2020/01/21/persistence-wmi-event-subscription/"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + ( + regexMatch("log.eventDataCommandLine", "(?i)wmic.*(/namespace|__EventFilter|__EventConsumer|__FilterToConsumerBinding)") || + regexMatch("log.eventDataCommandLine", "(?i)wmic.*(/namespace|__EventFilter|__EventConsumer|__FilterToConsumerBinding)") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)(Set-WmiInstance|New-CimInstance).*(__EventFilter|__EventConsumer|CommandLineEventConsumer|ActiveScriptEventConsumer)") || + regexMatch("log.eventDataCommandLine", "(?i)(Set-WmiInstance|New-CimInstance).*(__EventFilter|__EventConsumer|CommandLineEventConsumer|ActiveScriptEventConsumer)") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)Register-WmiEvent") || + regexMatch("log.eventDataCommandLine", "(?i)Register-WmiEvent") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)mofcomp\\\\.exe") || + regexMatch("log.eventDataCommandLine", "(?i)mofcomp\\\\.exe") + ) +) || +( + (equals("log.eventCode", "4104") || equals("log.eventId", 4104)) && + equals("log.providerName", "Microsoft-Windows-PowerShell") && + ( + regexMatch("log.eventDataScriptBlockText", "(?i)(__EventFilter|__EventConsumer|__FilterToConsumerBinding)") || + regexMatch("log.eventDataScriptBlockText", "(?i)(CommandLineEventConsumer|ActiveScriptEventConsumer)") || + contains("log.eventDataScriptBlockText", "Register-WmiEvent") + ) +) +', '2026-02-23 16:18:37.849462', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1027, 'Windows Script Host Suspicious Execution', 3, 3, 2, 'Execution', 'T1059.005 - Command and Scripting Interpreter: Visual Basic', e'Detects suspicious execution of wscript.exe and cscript.exe with potentially malicious script +files or command-line arguments. Attackers commonly use Windows Script Host to execute VBScript +and JScript payloads delivered via phishing emails, drive-by downloads, or as secondary +payloads during an intrusion. The rule focuses on scripts executed from suspicious locations +and with suspicious arguments. + +Next Steps: +1. Identify the script file being executed and analyze its contents +2. Check the parent process (e.g., Outlook, Word, browser) for phishing indicators +3. Review the script file location for legitimacy +4. Analyze any network connections made by the script +5. Check if the script downloads or executes additional payloads +6. Search for the same script across other endpoints +7. Block the script and remove any persistence mechanisms it created +', '["https://attack.mitre.org/techniques/T1059/005/","https://lolbas-project.github.io/lolbas/Binaries/Wscript/","https://lolbas-project.github.io/lolbas/Binaries/Cscript/"]', e'(equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && +( + regexMatch("log.eventDataNewProcessName", "(?i)\\\\\\\\(wscript|cscript)\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)\\\\\\\\(wscript|cscript)\\\\.exe$") +) && +( + (regexMatch("log.eventDataCommandLine", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\)") || + regexMatch("log.eventDataCommandLine", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\)")) || + (regexMatch("log.eventDataCommandLine", "(?i)//[eE]:(vbscript|jscript)") || + regexMatch("log.eventDataCommandLine", "(?i)//[eE]:(vbscript|jscript)")) || + (regexMatch("log.eventDataCommandLine", "(?i)(http://|https://|ftp://|\\\\\\\\\\\\\\\\[0-9])") || + regexMatch("log.eventDataCommandLine", "(?i)(http://|https://|ftp://|\\\\\\\\\\\\\\\\[0-9])")) || + (regexMatch("log.eventDataCommandLine", "(?i)\\\\.(vbs|vbe|js|jse|wsf|wsh)\\\\s+(//B|//nologo)") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\.(vbs|vbe|js|jse|wsf|wsh)\\\\s+(//B|//nologo)")) || + (regexMatch("log.eventDataCommandLine", "(?i)(CreateObject|WScript\\\\.Shell|Scripting\\\\.FileSystemObject|ADODB\\\\.Stream)") || + regexMatch("log.eventDataCommandLine", "(?i)(CreateObject|WScript\\\\.Shell|Scripting\\\\.FileSystemObject|ADODB\\\\.Stream)")) +) +', '2026-02-23 16:18:39.016715', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1028, 'Windows Remote Management (WinRM) Abuse', 3, 3, 2, 'Lateral Movement', 'T1021.006 - Remote Services: Windows Remote Management', e'Detects potential abuse of Windows Remote Management (WinRM) for lateral movement. Monitors for successful logon events (4624) with network logon type 3 combined with privilege escalation (4672) and WinRM-related process activity, indicating remote command execution via WinRM. + +Next Steps: +1. Investigate the source IP address and verify if it\'s an authorized administrative workstation +2. Review the target user account for any signs of compromise or unusual privilege usage +3. Examine recent PowerShell execution logs and command history on the target system +4. Check for concurrent suspicious activities from the same source IP across other systems +5. Verify if the WinRM connection aligns with scheduled maintenance or authorized administrative tasks +6. Review network traffic patterns between source and target systems for data exfiltration indicators +7. Validate the legitimacy of any processes spawned through the WinRM session +8. Consider implementing additional monitoring for WinRM usage if this represents unexpected activity +', '["https://jpcertcc.github.io/ToolAnalysisResultSheet/details/WinRM.htm","https://attack.mitre.org/techniques/T1021/006/"]', 'equals("log.eventCode", "4624") && equals("log.eventDataLogonType", "3") && exists("log.eventDataProcessName") && (contains("log.eventDataProcessName", "wsmprovhost.exe") || contains("log.eventDataProcessName", "winrshost.exe") || contains("log.eventDataProcessName", "powershell.exe"))', '2026-02-23 16:18:40.204416', true, true, 'origin', null, '[]', '["lastEvent.target.user","adversary.host","adversary.ip"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1029, 'Windows Defender Tampering Detection', 2, 3, 3, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', e'Detects attempts to disable or tamper with Windows Defender components through registry modifications, service stops, or exclusion additions. This includes registry changes to disable antivirus features, stopping Windows Defender services, or using PowerShell commands to modify protection settings. + +Next Steps: +1. Investigate the user account and process responsible for the tampering attempt +2. Check if this is part of authorized maintenance or if it\'s malicious activity +3. Review recent logon events and process execution history on the affected host +4. Verify Windows Defender configuration and ensure it\'s properly restored +5. Scan the system for malware that may have disabled protection +6. Check for other security tool tampering attempts across the environment +', '["https://attack.mitre.org/techniques/T1562/001/","https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/troubleshoot-windows-defender-antivirus"]', e'(equals("log.eventId", "4657") && + contains("log.eventDataObjectName", "\\\\Windows Defender\\\\") && + (contains("log.eventDataObjectValueName", "DisableAntiSpyware") || + contains("log.eventDataObjectValueName", "DisableAntiVirus") || + contains("log.eventDataObjectValueName", "DisableRealtimeMonitoring"))) || +(equals("log.eventId", "7040") && + contains("log.eventDataServiceName", "WinDefend") && + equals("log.eventDataNewState", "disabled")) || +(equals("log.eventId", "4688") && + contains("log.eventDataCommandLine", "Set-MpPreference") && + (contains("log.eventDataCommandLine", "DisableRealtimeMonitoring") || + contains("log.eventDataCommandLine", "DisableIOAVProtection") || + contains("log.eventDataCommandLine", "DisableBehaviorMonitoring"))) +', '2026-02-23 16:18:41.336143', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1030, 'Suspicious Admin Share Access Detection', 3, 3, 2, 'Lateral Movement', 'T1021.002 - Remote Services: SMB/Windows Admin Shares', e'Detects suspicious access to Windows administrative shares (C$, ADMIN$, IPC$) which is +commonly used for lateral movement. While admin share access can be legitimate, attackers +use these shares to copy payloads, execute remote commands, and move laterally across the +network. The rule monitors Event ID 5140 (network share access) and Event ID 5145 (detailed +share access) for admin share connections from non-standard sources. + +Next Steps: +1. Identify the source IP and user account accessing the admin share +2. Verify if this is authorized administrative access or IT operations +3. Check what files were read or written to the admin share +4. Review if tools or malware were copied via the share +5. Check for service creation or scheduled task creation on the target +6. Correlate with authentication events from the same source IP +7. Block unauthorized admin share access via Group Policy +', '["https://attack.mitre.org/techniques/T1021/002/","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=5140","https://www.sans.org/blog/detecting-lateral-movement-using-windows-admin-shares/"]', e'( + equals("log.eventCode", "5140") && + equals("log.channel", "Security") && + regexMatch("log.eventDataShareName", "(?i)\\\\\\\\\\\\*(C\\\\$|ADMIN\\\\$)$") && + exists("log.eventDataIpAddress") && + !equals("log.eventDataIpAddress", "::1") && + !equals("log.eventDataIpAddress", "127.0.0.1") && + !regexMatch("log.eventDataSubjectUserName", "(?i)^(SYSTEM|LOCAL SERVICE|NETWORK SERVICE|\\\\$)") && + !regexMatch("log.eventDataSubjectUserName", "(?i)\\\\$$") +) || +( + equals("log.eventCode", "5145") && + equals("log.channel", "Security") && + regexMatch("log.eventDataShareName", "(?i)\\\\\\\\\\\\*(C\\\\$|ADMIN\\\\$)$") && + regexMatch("log.eventDataRelativeTargetName", "(?i)\\\\.(exe|dll|bat|cmd|ps1|vbs|hta)$") && + exists("log.eventDataIpAddress") +) +', '2026-02-23 16:18:42.527359', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataIpAddress","operator":"filter_term","value":"{{log.eventDataIpAddress}}"}],"or":null,"within":"now-30m","count":3}]', '["lastEvent.log.eventDataIpAddress","target.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1031, 'Vulnerable Driver Loading Detection (BYOVD)', 3, 3, 3, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', e'Detects Bring Your Own Vulnerable Driver (BYOVD) attacks where adversaries load known vulnerable +kernel drivers to disable security tools, escalate privileges, or gain kernel-level access. +This technique is increasingly used by ransomware groups and APTs to bypass endpoint protection. +The rule monitors for service installations loading known vulnerable drivers and suspicious +driver-related command patterns. + +Next Steps: +1. Identify the loaded driver and check against known vulnerable driver lists (loldrivers.io) +2. Examine the service installation that loaded the driver +3. Check if endpoint security tools were disabled after driver loading +4. Review the process that installed the vulnerable driver +5. Remove the vulnerable driver and restore security tools +6. Block the driver hash via WDAC or application control policies +7. Check for privilege escalation or security tool tampering after driver load +8. Search for the same driver across other endpoints +', '["https://attack.mitre.org/techniques/T1068/","https://www.loldrivers.io/","https://github.com/magicsword-io/LOLDrivers"]', e'( + equals("log.eventCode", "4697") && + equals("log.channel", "Security") && + ( + regexMatch("log.eventDataServiceFileName", "(?i)(dbutil_2_3|dbutildrv2|rtcore64|gdrv|asio|ene\\\\.sys|procexp|viragt64|aswarpot|hw64|cpuz|gmer64|mhyprot2|kdmapper|iqvw64e|echo_driver|winio|amifldrv64|elby|vboxdrv|gpu_temp|nicm|phymemx64|micio64|directio64|blacklotus|irec|zemana)") || + regexMatch("log.eventDataServiceFileName", "(?i)\\\\.sys\\\\s*$") + ) +) || +( + (equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && + ( + regexMatch("log.eventDataCommandLine", "(?i)(sc\\\\.exe|sc)\\\\s+(create|start)\\\\s+.*type=\\\\s*kernel") || + regexMatch("log.eventDataCommandLine", "(?i)(sc\\\\.exe|sc)\\\\s+(create|start)\\\\s+.*type=\\\\s*kernel") || + regexMatch("log.eventDataCommandLine", "(?i)(sc\\\\.exe|sc)\\\\s+create\\\\s+.*binPath=.*\\\\.sys") || + regexMatch("log.eventDataCommandLine", "(?i)(sc\\\\.exe|sc)\\\\s+create\\\\s+.*binPath=.*\\\\.sys") + ) +) +', '2026-02-23 16:18:43.714091', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1032, 'UAC Bypass Attempt Detection', 3, 3, 2, 'Privilege Escalation, Defense Evasion', 'T1548.002 - Abuse Elevation Control Mechanism: Bypass User Account Control', e'Detects potential UAC bypass attempts by monitoring for processes with elevated privileges that were not launched through the proper UAC consent mechanism. This rule identifies processes running with elevated token types (TokenElevationTypeVirtual) without proper UAC consent flow, which may indicate an attempt to bypass User Account Control security measures. + +Next Steps: +1. Investigate the process that triggered the alert - examine the process name, command line arguments, and parent process +2. Check if the process is known legitimate software that should have elevated privileges +3. Verify the user account that launched the process and their normal privilege level +4. Look for other suspicious activities on the same host around the same time +5. Check for known UAC bypass techniques such as DLL hijacking, registry manipulation, or abuse of auto-elevated processes +6. Examine the process execution chain to identify the attack vector +7. Consider isolating the affected system if malicious activity is confirmed +8. Review security policies and UAC settings to prevent similar bypasses +', '["https://attack.mitre.org/techniques/T1548/002/","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=4688"]', e'equals("log.eventId", "4688") && +equals("log.eventDataTokenElevationType", "2") && +!contains("log.eventDataParentProcessName", "consent.exe") && +!contains("log.eventDataProcessName", "TrustedInstaller.exe") && +!contains("log.eventDataSubjectUserName", "SYSTEM") && +(contains("log.eventDataParentProcessName", "fodhelper.exe") || + contains("log.eventDataParentProcessName", "computerdefaults.exe") || + contains("log.eventDataParentProcessName", "sdclt.exe") || + contains("log.eventDataParentProcessName", "slui.exe") || + contains("log.eventDataParentProcessName", "eventvwr.exe") || + contains("log.eventDataParentProcessName", "cmstp.exe") || + contains("log.eventDataParentProcessName", "msconfig.exe") || + contains("log.eventDataParentProcessName", "dccw.exe") || + (contains("log.eventDataProcessName", "cmd.exe") || contains("log.eventDataProcessName", "powershell.exe") || contains("log.eventDataProcessName", "pwsh.exe"))) +', '2026-02-23 16:18:44.818132', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1033, 'Volume Shadow Copy Deletion', 2, 3, 3, 'Impact', 'T1490 - Inhibit System Recovery', e'Detects deletion of Volume Shadow Copies which is commonly performed by ransomware to prevent recovery of encrypted files. This rule identifies various methods attackers use to delete shadow copies including vssadmin, wmic, PowerShell, wbadmin, and bcdedit commands. + +Next Steps: +1. Immediately investigate the affected host for signs of ransomware activity +2. Check for recent file encryption or unusual file modifications +3. Review process execution timeline around the shadow copy deletion +4. Verify if this was legitimate administrative activity or malicious +5. Examine network connections and lateral movement indicators +6. Check backup integrity and consider isolating the system if ransomware is confirmed +7. Review user accounts and privileges involved in the activity +', '["https://attack.mitre.org/techniques/T1490/","https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/vssadmin"]', e'equals("log.eventId", "4688") && +((endsWith("log.eventDataProcessName", "vssadmin.exe") && + (contains("log.eventDataCommandLine", "delete shadows") || + (contains("log.eventDataCommandLine", "resize shadowstorage") && + contains("log.eventDataCommandLine", "maxsize=")))) || + (endsWith("log.eventDataProcessName", "wmic.exe") && + contains("log.eventDataCommandLine", "shadowcopy") && + contains("log.eventDataCommandLine", "delete")) || + (endsWith("log.eventDataProcessName", "powershell.exe") && + (contains("log.eventDataCommandLine", "Get-WmiObject") || + contains("log.eventDataCommandLine", "gwmi")) && + contains("log.eventDataCommandLine", "Win32_ShadowCopy") && + contains("log.eventDataCommandLine", "Delete()")) || + (endsWith("log.eventDataProcessName", "wbadmin.exe") && + contains("log.eventDataCommandLine", "delete") && + (contains("log.eventDataCommandLine", "catalog") || + contains("log.eventDataCommandLine", "backup"))) || + (endsWith("log.eventDataProcessName", "bcdedit.exe") && + contains("log.eventDataCommandLine", "recoveryenabled") && + contains("log.eventDataCommandLine", "no"))) +', '2026-02-23 16:18:45.831954', true, true, 'origin', null, '[]', '["lastEvent.log.eventDataProcessName","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1034, 'Tunneling Service C2 Detection', 3, 2, 2, 'Command and Control', 'T1572 - Protocol Tunneling', e'Detects execution of tunneling services (ngrok, cloudflared, devtunnels, localtonet, bore, chisel) +that create reverse tunnels for command and control or to expose internal services to the internet. +These tools are increasingly abused by threat actors for persistent C2 channels that bypass +network security controls because traffic appears as legitimate HTTPS connections. + +Next Steps: +1. Identify the tunneling tool being used and its configuration +2. Determine what internal service or port is being exposed +3. Check if this is authorized usage (dev/test) or malicious +4. Review the tunnel endpoint for C2 activity +5. Block the tunneling tool and its associated domains at the firewall +6. Terminate the tunnel process and remove the tool +7. Check for data exfiltration through the tunnel +8. Hunt for the same tunneling tool across other endpoints +', '["https://attack.mitre.org/techniques/T1572/","https://www.microsoft.com/en-us/security/blog/2023/05/24/volt-typhoon-targets-us-critical-infrastructure-with-living-off-the-land-techniques/","https://ngrok.com/abuse"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + regexMatch("log.eventDataNewProcessName", "(?i)(ngrok|cloudflared|devtunnels|localtonet|bore|chisel|frpc|rathole)\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)(ngrok|cloudflared|devtunnels|localtonet|bore|chisel|frpc|rathole)\\\\.exe$") || + regexMatch("log.eventDataCommandLine", "(?i)ngrok\\\\s+(http|tcp|tls|start)") || + regexMatch("log.eventDataCommandLine", "(?i)ngrok\\\\s+(http|tcp|tls|start)") || + regexMatch("log.eventDataCommandLine", "(?i)cloudflared\\\\s+tunnel\\\\s+(run|--url)") || + regexMatch("log.eventDataCommandLine", "(?i)cloudflared\\\\s+tunnel\\\\s+(run|--url)") || + regexMatch("log.eventDataCommandLine", "(?i)devtunnels\\\\s+(host|create|port)") || + regexMatch("log.eventDataCommandLine", "(?i)devtunnels\\\\s+(host|create|port)") || + regexMatch("log.eventDataCommandLine", "(?i)chisel\\\\s+(client|server)") || + regexMatch("log.eventDataCommandLine", "(?i)chisel\\\\s+(client|server)") || + regexMatch("log.eventDataCommandLine", "(?i)frpc\\\\.exe.*-c") || + regexMatch("log.eventDataCommandLine", "(?i)frpc\\\\.exe.*-c") +) +', '2026-02-23 16:18:46.920824', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1035, 'Windows Token Manipulation', 3, 3, 3, 'Defense Evasion, Privilege Escalation', 'Access Token Manipulation', 'Detects potential token manipulation attacks used for privilege escalation. Monitors for suspicious combinations of privileged service calls (4673) and operations on privileged objects (4674) along with special privilege assignments (4672) that include sensitive privileges like SeDebugPrivilege, SeImpersonatePrivilege, or SeTcbPrivilege commonly abused for token manipulation.', '["https://medium.com/palantir/windows-privilege-abuse-auditing-detection-and-defense-3078a403d74e","https://attack.mitre.org/techniques/T1134/"]', 'equals("log.eventCode", "4672") && exists("log.eventDataPrivilegeList") && (contains("log.eventDataPrivilegeList", "SeDebugPrivilege") || contains("log.eventDataPrivilegeList", "SeImpersonatePrivilege") || contains("log.eventDataPrivilegeList", "SeTcbPrivilege") || contains("log.eventDataPrivilegeList", "SeAssignPrimaryTokenPrivilege") || contains("log.eventDataPrivilegeList", "SeLoadDriverPrivilege") || contains("log.eventDataPrivilegeList", "SeRestorePrivilege"))', '2026-02-23 16:18:47.970943', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host","operator":"filter_term","value":"{{origin.host}}"},{"field":"log.eventDataTargetLogonId","operator":"filter_term","value":"{{log.eventDataTargetLogonId}}"},{"field":"log.eventCode","operator":"should_terms","value":"4673,4674"}],"or":null,"within":"now-10m","count":3}]', '["lastEvent.log.eventDataTargetLogonId","lastEvent.target.user","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1036, 'File Timestomping Detection', 1, 3, 1, 'Defense Evasion', 'T1070.006 - Indicator Removal: Timestomp', e'Detects timestomping activities where attackers modify file creation or modification timestamps +to blend malicious files with legitimate system files. Common tools include PowerShell +Set-ItemProperty, timestomp.exe, NirCmd, and direct API calls to SetFileTime. This technique +is used to evade forensic timeline analysis and make malicious files appear older. + +Next Steps: +1. Identify which files had their timestamps modified +2. Check if the modified timestamps match legitimate system file dates (common pattern) +3. Analyze the files with modified timestamps for malicious content +4. Review the process and user that performed the timestamp modification +5. Investigate what the attacker was trying to hide with timestomping +6. Use $MFT analysis to detect original timestamps vs modified ones +7. Search for additional anti-forensic techniques on the same host +', '["https://attack.mitre.org/techniques/T1070/006/","https://www.inversecos.com/2022/04/defence-evasion-technique-timestomping.html","https://andreafortuna.org/2017/10/06/timestomping-what-it-is-and-how-to-detect-it/"]', e'( + equals("log.eventCode", "2") && + equals("log.providerName", "Microsoft-Windows-Sysmon") && + exists("log.eventDataPreviousCreationUtcTime") && + exists("log.eventDataCreationUtcTime") && + !regexMatch("log.eventDataImage", "(?i)(setup|install|update|patch|msiexec|TiWorker|TrustedInstaller|svchost)\\\\.exe$") +) || +( + (equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && + ( + regexMatch("log.eventDataCommandLine", "(?i)Set-ItemProperty.*CreationTime|Set-ItemProperty.*LastWriteTime|Set-ItemProperty.*LastAccessTime") || + regexMatch("log.eventDataCommandLine", "(?i)Set-ItemProperty.*CreationTime|Set-ItemProperty.*LastWriteTime|Set-ItemProperty.*LastAccessTime") || + regexMatch("log.eventDataCommandLine", "(?i)timestomp|SetFileTime|NirCmd.*setfiletime") || + regexMatch("log.eventDataCommandLine", "(?i)timestomp|SetFileTime|NirCmd.*setfiletime") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\[IO\\\\.File\\\\]::SetCreationTime|\\\\[IO\\\\.File\\\\]::SetLastWriteTime") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\[IO\\\\.File\\\\]::SetCreationTime|\\\\[IO\\\\.File\\\\]::SetLastWriteTime") + ) +) +', '2026-02-23 16:18:49.103144', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1037, 'Sysmon Service Tampering Detection', 3, 3, 3, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', e'Detects attempts to tamper with the Sysmon monitoring service including unloading the Sysmon +driver, stopping or deleting the Sysmon service, modifying Sysmon configuration, or renaming +the Sysmon binary. Attackers target Sysmon specifically because it provides rich endpoint +telemetry that enables threat detection. Disabling Sysmon is a critical defense evasion step. + +Next Steps: +1. Immediately investigate the process and user that tampered with Sysmon +2. Restore Sysmon service and verify it is running correctly +3. Check if the Sysmon driver is still loaded in the kernel +4. Review what malicious activity occurred during the Sysmon outage +5. Re-apply the Sysmon configuration from a known good backup +6. Investigate the full attack chain that led to Sysmon tampering +7. Consider implementing tamper protection for Sysmon +', '["https://attack.mitre.org/techniques/T1562/001/","https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon","https://undev.ninja/sysmon-evasion-techniques/"]', e'( + (equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && + ( + (regexMatch("log.eventDataCommandLine", "(?i)sysmon.*-u") || + regexMatch("log.eventDataCommandLine", "(?i)sysmon.*-u")) || + (regexMatch("log.eventDataCommandLine", "(?i)sc(.exe)?\\\\s+(stop|delete|config)\\\\s+sysmon") || + regexMatch("log.eventDataCommandLine", "(?i)sc(.exe)?\\\\s+(stop|delete|config)\\\\s+sysmon")) || + (regexMatch("log.eventDataCommandLine", "(?i)net(.exe)?\\\\s+stop\\\\s+sysmon") || + regexMatch("log.eventDataCommandLine", "(?i)net(.exe)?\\\\s+stop\\\\s+sysmon")) || + (regexMatch("log.eventDataCommandLine", "(?i)fltMC(.exe)?\\\\s+unload\\\\s+sysmon") || + regexMatch("log.eventDataCommandLine", "(?i)fltMC(.exe)?\\\\s+unload\\\\s+sysmon")) || + (regexMatch("log.eventDataCommandLine", "(?i)taskkill.*/f.*/im\\\\s+sysmon") || + regexMatch("log.eventDataCommandLine", "(?i)taskkill.*/f.*/im\\\\s+sysmon")) + ) +) || +( + equals("log.eventCode", "7045") && + regexMatch("log.eventDataServiceName", "(?i)sysmon") && + !equals("log.eventDataServiceType", "kernel mode driver") +) +', '2026-02-23 16:18:50.172615', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1038, 'Suspicious Service Installation Detection', 3, 3, 2, 'Persistence', 'T1543.003 - Create or Modify System Process: Windows Service', e'Detects installation of suspicious Windows services matching patterns from Cobalt Strike, Metasploit, +Impacket PSExec, and Meterpreter payloads. Attackers commonly install malicious services for persistence, +privilege escalation (getsystem), and lateral movement. The rule monitors Event ID 4697 for service +installations with characteristic names, binary paths, or command-line patterns used by offensive frameworks. + +Next Steps: +1. Examine the service binary path for malicious executables or scripts +2. Check if the service name matches known offensive tool patterns +3. Verify the installing user account and their authorization level +4. Review the service configuration for suspicious startup types +5. Stop and disable the suspicious service immediately +6. Analyze the service binary in a sandbox environment +7. Search for lateral movement indicators from the source host +8. Check for other persistence mechanisms installed by the same actor +', '["https://attack.mitre.org/techniques/T1543/003/","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4697","https://www.sans.org/blog/red-team-tactics-hiding-windows-services/"]', e'equals("log.eventCode", "4697") && +equals("log.channel", "Security") && +( + regexMatch("log.eventDataServiceFileName", "(?i)(cmd\\\\.exe\\\\s*/c|powershell|pwsh|%COMSPEC%)") || + regexMatch("log.eventDataServiceFileName", "(?i)(\\\\\\\\ADMIN\\\\$|\\\\\\\\C\\\\$|127\\\\.0\\\\.0\\\\.1)") || + regexMatch("log.eventDataServiceFileName", "(?i)(rundll32|msiexec|regsvr32|mshta|certutil|bitsadmin)") || + regexMatch("log.eventDataServiceName", "(?i)^[a-zA-Z]{4,8}$") || + regexMatch("log.eventDataServiceFileName", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\)") || + regexMatch("log.eventDataServiceFileName", "(?i)\\\\.exe\\\\s+[a-zA-Z0-9+/=]{50,}") || + regexMatch("log.eventDataServiceName", "(?i)(BTOBTO|meterpreter|msf|payload|beacon|cobalt)") || + regexMatch("log.eventDataServiceFileName", "(?i)(comsvcs\\\\.dll|MiniDump|procdump|lsass)") +) +', '2026-02-23 16:18:51.276047', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1039, 'Startup Folder Persistence Detection', 2, 3, 2, 'Persistence', 'T1547.001 - Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder', e'Detects file creation or modification in Windows Startup folders, which execute programs +automatically on user login. Attackers drop malicious scripts, executables, or shortcut files +into Startup folders for persistence. The rule monitors both per-user and system-wide Startup +directories for suspicious file types. + +Next Steps: +1. Identify the file dropped into the Startup folder and analyze its contents +2. Check the parent process that created the file for suspicious origins +3. Verify if the file is a legitimate application shortcut or a malicious payload +4. Remove the suspicious file from the Startup folder +5. Analyze the executable or script for malicious behavior in a sandbox +6. Search for additional persistence mechanisms on the same host +7. Investigate the initial access vector +', '["https://attack.mitre.org/techniques/T1547/001/","https://docs.microsoft.com/en-us/windows/win32/shell/manage-program-startup","https://www.cybereason.com/blog/persistence-techniques-that-persist"]', e'( + equals("log.eventCode", "11") && + equals("log.providerName", "Microsoft-Windows-Sysmon") && + ( + regexMatch("log.eventDataTargetFilename", "(?i)\\\\\\\\Start Menu\\\\\\\\Programs\\\\\\\\Startup\\\\\\\\") || + regexMatch("log.eventDataTargetFilename", "(?i)\\\\\\\\ProgramData\\\\\\\\Microsoft\\\\\\\\Windows\\\\\\\\Start Menu\\\\\\\\Programs\\\\\\\\StartUp\\\\\\\\") + ) && + regexMatch("log.eventDataTargetFilename", "(?i)\\\\.(exe|dll|bat|cmd|vbs|vbe|js|jse|wsf|wsh|ps1|hta|lnk|scr|pif)$") +) || +( + (equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && + ( + (regexMatch("log.eventDataCommandLine", "(?i)copy.*\\\\\\\\Start Menu\\\\\\\\Programs\\\\\\\\Startup") || + regexMatch("log.eventDataCommandLine", "(?i)copy.*\\\\\\\\Start Menu\\\\\\\\Programs\\\\\\\\Startup")) || + (regexMatch("log.eventDataCommandLine", "(?i)move.*\\\\\\\\Start Menu\\\\\\\\Programs\\\\\\\\Startup") || + regexMatch("log.eventDataCommandLine", "(?i)move.*\\\\\\\\Start Menu\\\\\\\\Programs\\\\\\\\Startup")) + ) +) +', '2026-02-23 16:18:52.325125', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1040, 'SMBv1 Usage Detection', 3, 2, 2, 'Lateral Movement', 'T1210 - Exploitation of Remote Services', e'Detects usage of the deprecated and vulnerable SMBv1 protocol which could be exploited for lateral movement or ransomware propagation. SMBv1 is susceptible to numerous security vulnerabilities including EternalBlue and should be disabled in favor of SMBv2/SMBv3. + +Next Steps: +1. Immediately investigate the source system using SMBv1 and identify which service or application is still dependent on this protocol +2. Review network traffic logs to determine if this is internal communication or external access attempts +3. Check for any signs of exploitation attempts or successful compromises on the affected system +4. Identify all systems in the environment that may still have SMBv1 enabled +5. Plan migration to SMBv2/SMBv3 and disable SMBv1 on all systems where possible +6. Monitor for any lateral movement patterns that may indicate ongoing compromise +7. Consider implementing network segmentation to limit exposure if SMBv1 cannot be immediately disabled +', '["https://learn.microsoft.com/en-us/windows-server/storage/file-server/troubleshoot/detect-enable-and-disable-smbv1-v2-v3","https://attack.mitre.org/techniques/T1210/"]', 'equals("log.eventId", "3000") && equals("log.providerName", "Microsoft-Windows-SMBServer") && contains("log.message", "SMB1")', '2026-02-23 16:18:53.458215', true, true, 'origin', null, '[]', '["adversary.host","adversary.ip"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1041, 'Sliver C2 Framework Detection', 3, 3, 2, 'Execution', 'T1059 - Command and Scripting Interpreter', e'Detects Sliver C2 framework execution patterns and artifacts. Sliver is an increasingly popular +open-source C2 framework replacing Cobalt Strike in many threat actor toolkits. The rule monitors +for characteristic Sliver implant patterns including specific PowerShell stager patterns, +named pipe names, and executable naming conventions. + +Next Steps: +1. Identify the Sliver implant type (HTTP, HTTPS, mTLS, DNS, WireGuard) +2. Check for network beaconing to C2 infrastructure +3. Review the process tree for injection and post-exploitation activity +4. Memory scan affected processes for Sliver implant shellcode +5. Isolate the compromised host immediately +6. Block identified C2 domains and IPs at the network perimeter +7. Hunt for Sliver artifacts across all endpoints +8. Review initial access vector (phishing, exploitation, etc.) +', '["https://github.com/BishopFox/sliver","https://www.microsoft.com/en-us/security/blog/2022/08/24/looking-for-the-sliver-lining-hunting-for-emerging-command-and-control-frameworks/","https://www.cybereason.com/blog/sliver-c2-leveraged-by-many-threat-actors"]', e'( + (equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && + ( + regexMatch("log.eventDataCommandLine", "(?i)(sliver|sliverpb|silver-implant)") || + regexMatch("log.eventDataCommandLine", "(?i)(sliver|sliverpb|silver-implant)") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\\\\\\\\\\\\\.\\\\\\\\pipe\\\\\\\\(sliverpb|sliver-)") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\\\\\\\\\\\\\.\\\\\\\\pipe\\\\\\\\(sliverpb|sliver-)") || + regexMatch("log.eventDataNewProcessName", "(?i)(DECISIVE_|MANAGING_|IMPRESSED_|LITERARY_|ENORMOUS_)") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\$s=New-Object\\\\s+IO\\\\.MemoryStream.*FromBase64String.*IO\\\\.Compression\\\\.GzipStream.*IO\\\\.Compression\\\\.CompressionMode.*ReadToEnd") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\$s=New-Object\\\\s+IO\\\\.MemoryStream.*FromBase64String.*IO\\\\.Compression\\\\.GzipStream") + ) +) || +( + (equals("log.eventCode", "4104") || equals("log.eventId", 4104)) && + equals("log.providerName", "Microsoft-Windows-PowerShell") && + ( + regexMatch("log.eventDataScriptBlockText", "(?i)\\\\$s=New-Object\\\\s+IO\\\\.MemoryStream.*IO\\\\.Compression\\\\.GzipStream.*IO\\\\.Compression\\\\.CompressionMode.*ReadToEnd") || + contains("log.eventDataScriptBlockText", "sliverpb") || + regexMatch("log.eventDataScriptBlockText", "(?i)New-Object\\\\s+System\\\\.Net\\\\.Sockets\\\\.TCPClient.*IO\\\\.StreamReader.*IO\\\\.StreamWriter.*GetStream") + ) +) +', '2026-02-23 16:18:54.463056', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1042, 'Silver Ticket Attack Detection', 3, 3, 2, 'Credential Access', 'T1558.002 - Steal or Forge Kerberos Tickets: Silver Ticket', e'Detects Silver Ticket attacks where adversaries forge Kerberos TGS tickets using a service +account\'s NTLM hash, bypassing the KDC entirely. Unlike Golden Tickets, Silver Tickets target +specific services. The rule detects TGS tickets presented without corresponding TGT requests, +and service access events with anomalous Kerberos authentication patterns. + +Next Steps: +1. Identify the targeted service and its associated service account +2. Verify if the service account hash has been compromised via Kerberoasting +3. Reset the targeted service account password immediately +4. Review access logs for the targeted service for unauthorized activity +5. Check for prior Kerberoasting activity targeting the same service SPN +6. Investigate the source host for compromise indicators +7. Implement AES-only encryption for service accounts +8. Enable Kerberos PAC validation on the targeted services +', '["https://attack.mitre.org/techniques/T1558/002/","https://adsecurity.org/?p=2011","https://www.sans.org/blog/kerberos-in-the-crosshairs-golden-tickets-silver-tickets-mitm-and-more/"]', e'equals("log.eventCode", "4769") && +equals("log.channel", "Security") && +( + equals("log.eventDataTicketEncryptionType", "0x17") && + !regexMatch("log.eventDataServiceName", "(?i)(krbtgt|\\\\$$)") && + !oneOf("log.eventDataStatus", ["0x0", "0x6"]) && + exists("log.eventDataIpAddress") +) +', '2026-02-23 16:18:55.551668', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataIpAddress","operator":"filter_term","value":"{{log.eventDataIpAddress}}"}],"or":null,"within":"now-15m","count":5}]', '["lastEvent.log.eventDataIpAddress","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1043, 'SID History Injection Attempt', 3, 3, 1, 'Defense Evasion, Privilege Escalation', 'T1134.005 - Access Token Manipulation: SID-History Injection', e'Detects attempts to add SID History to an account, which can be used for privilege escalation. SID History injection allows attackers to inherit permissions from privileged accounts without being members of privileged groups. Both successful (4765) and failed (4766) attempts are monitored. + +Next Steps: +1. Immediately investigate the target user account and verify if SID History modification was legitimate +2. Check if the user performing the action has proper administrative privileges for this operation +3. Review the source SID being added to understand what permissions are being inherited +4. Examine recent authentication logs for the target account to identify potential unauthorized access +5. Verify Active Directory configuration and check for signs of domain controller compromise +6. Consider resetting the target account password and removing unauthorized SID History entries +7. Review domain administrator accounts and privileged group memberships for anomalies +', '["https://attack.mitre.org/techniques/T1134/005/","https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4765","https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4766","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=4765"]', e'oneOf("log.eventId", ["4765", "4766"]) && +equals("log.channel", "Security") +', '2026-02-23 16:18:56.694349', true, true, 'origin', null, '[]', '["adversary.user","target.host","target.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1044, 'Shadow Credentials Attack Detection', 3, 3, 1, 'Credential Access', 'T1556 - Modify Authentication Process', e'Detects Shadow Credentials attacks where adversaries modify the msDS-KeyCredentialLink attribute +on Active Directory objects to add their own key credentials for passwordless authentication. +This enables attackers to authenticate as the target account without knowing the password, +effectively taking over the account. The rule monitors Event ID 5136 (directory service object +modification) and Event ID 4662 (object access) for msDS-KeyCredentialLink changes. + +Next Steps: +1. Identify the account whose msDS-KeyCredentialLink was modified +2. Check if the modification was done by an authorized admin or service +3. Examine the added key credential for malicious certificates +4. Remove unauthorized key credentials from the affected account +5. Reset the password for the affected account +6. Review who has write access to the msDS-KeyCredentialLink attribute +7. Audit all accounts for unauthorized key credential additions +8. Investigate for related tools like Whisker or pyWhisker +', '["https://attack.mitre.org/techniques/T1556/","https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab","https://github.com/eladshamir/Whisker"]', e'( + equals("log.eventCode", "5136") && + equals("log.channel", "Security") && + contains("log.eventDataAttributeLDAPDisplayName", "msDS-KeyCredentialLink") +) || +( + equals("log.eventCode", "4662") && + equals("log.channel", "Security") && + contains("log.eventDataProperties", "msDS-KeyCredentialLink") && + !regexMatch("log.eventDataSubjectUserName", "(?i)\\\\$$") +) +', '2026-02-23 16:18:57.869062', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1045, 'Suspicious Scheduled Task Persistence', 2, 3, 2, 'Persistence', 'T1053.005 - Scheduled Task/Job: Scheduled Task', e'Detects creation of scheduled tasks from suspicious locations or executing suspicious binaries, +which is one of the most common persistence mechanisms used by attackers. The rule monitors +Event ID 4698 (scheduled task creation) for tasks that reference suspicious paths such as +Temp, Downloads, AppData, or ProgramData directories, or execute known LOLBINs like +PowerShell, certutil, rundll32, mshta, regsvr32, or cmd.exe with suspicious arguments. + +Next Steps: +1. Examine the full TaskContent XML to understand what the scheduled task executes +2. Identify the user account that created the task and verify authorization +3. Check the task execution path for malicious payloads or scripts +4. Review the trigger schedule for persistence timing patterns +5. Remove the suspicious scheduled task immediately +6. Search for additional persistence mechanisms on the same host +7. Investigate the initial access vector that led to task creation +', '["https://attack.mitre.org/techniques/T1053/005/","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4698","https://posts.specterops.io/abstracting-scheduled-task-creation-6d22febc27e7"]', e'equals("log.eventCode", "4698") && +equals("log.channel", "Security") && +( + regexMatch("log.eventDataTaskContent", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Public\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\)") || + regexMatch("log.eventDataTaskContent", "(?i)(powershell|pwsh|cmd\\\\.exe|certutil|mshta|rundll32|regsvr32|cscript|wscript|msiexec|bitsadmin)") || + regexMatch("log.eventDataTaskContent", "(?i)(-enc\\\\s|-encodedcommand|-nop|-w\\\\s+hidden|downloadstring|invoke-expression|iex)") || + regexMatch("log.eventDataTaskContent", "(?i)(http://|https://|ftp://|\\\\\\\\\\\\\\\\[0-9]+\\\\.[0-9]+\\\\.[0-9]+\\\\.[0-9]+\\\\\\\\)") +) && +!regexMatch("log.eventDataSubjectUserName", "(?i)^(SYSTEM|LOCAL SERVICE|NETWORK SERVICE)$") +', '2026-02-23 16:18:59.041587', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1046, 'SAM Database Access Attempt', 3, 3, 1, 'Credential Access', 'T1003.002 - OS Credential Dumping: Security Account Manager', e'Detects attempts to access the Security Account Manager (SAM) database, which contains local user account hashes. This activity may indicate credential dumping attempts by attackers trying to extract password hashes for offline cracking or lateral movement. + +Next Steps: +1. Immediately investigate the user account and process that accessed the SAM database +2. Check for any unusual processes running on the affected system +3. Review recent logon events and privilege escalation activities +4. Examine network connections from the affected host for lateral movement +5. Consider isolating the affected system if malicious activity is confirmed +6. Review security policies for SAM database access permissions +7. Check for presence of credential dumping tools or suspicious files +', '["https://attack.mitre.org/techniques/T1003/002/","https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4661"]', e'equals("log.eventCode", "4663") && +equals("log.channel", "Security") && +( + endsWith("log.eventDataObjectName", "\\\\SAM") || + endsWith("log.eventDataObjectName", "\\\\SECURITY") || + endsWith("log.eventDataObjectName", "\\\\SYSTEM") +) && +oneOf("log.eventDataAccessMask", ["0x20019", "0x1f01ff", "0x40", "0x20", "0x1"]) +', '2026-02-23 16:19:00.316495', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1047, 'Rundll32 Suspicious Abuse Detection', 2, 3, 2, 'Defense Evasion', 'T1218.011 - System Binary Proxy Execution: Rundll32', e'Detects suspicious rundll32.exe usage patterns including loading DLLs from non-system paths, +comsvcs.dll MiniDump for LSASS dumping, JavaScript/VBScript execution, and loading from Temp +or Downloads directories. Rundll32 is a signed Microsoft binary frequently abused for defense +evasion and code execution. + +Next Steps: +1. Examine the DLL path and export function being called +2. Verify the DLL is not loading from a suspicious location (Temp, Downloads, AppData) +3. Check the parent process for delivery mechanism indicators +4. Analyze the loaded DLL in a sandbox environment +5. Review network connections made by the rundll32 process +6. Check for process injection from the rundll32 process +7. Search for the same DLL hash across other endpoints +', '["https://attack.mitre.org/techniques/T1218/011/","https://lolbas-project.github.io/lolbas/Binaries/Rundll32/","https://redcanary.com/threat-detection-report/techniques/rundll32/"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + regexMatch("log.eventDataNewProcessName", "(?i)rundll32\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)rundll32\\\\.exe$") +) && +( + regexMatch("log.eventDataCommandLine", "(?i)comsvcs\\\\.dll.*(MiniDump|#24)") || + regexMatch("log.eventDataCommandLine", "(?i)(javascript:|vbscript:)") || + regexMatch("log.eventDataCommandLine", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\).*\\\\.dll") || + regexMatch("log.eventDataCommandLine", "(?i)shell32\\\\.dll.*ShellExec_RunDLL") || + regexMatch("log.eventDataCommandLine", "(?i)advpack\\\\.dll.*RegisterOCX") || + regexMatch("log.eventDataCommandLine", "(?i)url\\\\.dll.*FileProtocolHandler") || + regexMatch("log.eventDataCommandLine", "(?i)zipfldr\\\\.dll.*RouteTheCall") || + regexMatch("log.eventDataCommandLine", "(?i)comsvcs\\\\.dll.*(MiniDump|#24)") || + regexMatch("log.eventDataCommandLine", "(?i)(javascript:|vbscript:)") || + regexMatch("log.eventDataCommandLine", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\).*\\\\.dll") +) +', '2026-02-23 16:19:01.465752', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1048, 'Registry Run Key Persistence Detection', 2, 3, 2, 'Persistence', 'T1547.001 - Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder', e'Detects modifications to Windows Registry Run and RunOnce keys, which are the most fundamental +Windows persistence mechanism. Attackers add entries to these keys to execute malicious code +every time a user logs on or the system starts. The rule monitors Event ID 4657 (registry value +modifications) for changes to Run/RunOnce keys with suspicious values pointing to unusual +executable paths, scripts, or LOLBINs. + +Next Steps: +1. Examine the registry value data to identify the persistence payload +2. Verify if the modification was made by a legitimate software installer +3. Check the modifying process and user account for compromise indicators +4. Remove the malicious registry entry immediately +5. Analyze the payload binary or script referenced in the registry value +6. Search for additional persistence mechanisms on the same host +7. Review the timeline of events leading to the registry modification +', '["https://attack.mitre.org/techniques/T1547/001/","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4657","https://docs.microsoft.com/en-us/windows/win32/setupapi/run-and-runonce-registry-keys"]', e'equals("log.eventCode", "4657") && +equals("log.channel", "Security") && +regexMatch("log.eventDataObjectName", "(?i)(\\\\\\\\Run\\\\\\\\|\\\\\\\\RunOnce\\\\\\\\|\\\\\\\\RunOnceEx\\\\\\\\|\\\\\\\\RunServices\\\\\\\\|\\\\\\\\RunServicesOnce\\\\\\\\)") && +( + regexMatch("log.eventDataObjectValueName", "(?i)(powershell|cmd|mshta|rundll32|regsvr32|certutil|wscript|cscript|bitsadmin)") || + regexMatch("log.eventDataNewValue", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\)") || + regexMatch("log.eventDataNewValue", "(?i)(powershell|cmd\\\\.exe\\\\s*/c|mshta|rundll32|regsvr32|certutil|wscript|cscript)") || + regexMatch("log.eventDataNewValue", "(?i)(http://|https://|\\\\\\\\\\\\\\\\[0-9]+\\\\.[0-9]+)") || + regexMatch("log.eventDataNewValue", "(?i)(-enc\\\\s|-encodedcommand|-w\\\\s+hidden)") +) +', '2026-02-23 16:19:02.639948', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1049, 'RDP Session Hijacking Detection', 3, 3, 2, 'Lateral Movement', 'T1563.002 - Remote Service Session Hijacking: RDP Hijacking', e'Detects RDP session hijacking using tscon.exe, which allows a user with SYSTEM privileges +to connect to another user\'s RDP session without authentication. This is commonly used +after privilege escalation to gain access to active sessions of other users, potentially +including domain administrators. + +Next Steps: +1. Identify which user\'s session was hijacked via tscon +2. Verify the SYSTEM-level access method used (service, scheduled task, etc.) +3. Check what actions were performed in the hijacked session +4. Review if the target session had access to sensitive resources +5. Force logout the compromised session +6. Investigate how the attacker obtained SYSTEM privileges +7. Review all active RDP sessions on the affected server +', '["https://attack.mitre.org/techniques/T1563/002/","https://medium.com/@youraveragetechnoob/rdp-session-hijacking-55ef3f85feaa","https://www.keysight.com/blogs/tech/nwvs/2022/09/21/rdp-session-hijacking"]', e'(equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && +( + (regexMatch("log.eventDataCommandLine", "(?i)tscon(.exe)?\\\\s+\\\\d+\\\\s+/dest:") || + regexMatch("log.eventDataCommandLine", "(?i)tscon(.exe)?\\\\s+\\\\d+\\\\s+/dest:")) || + (regexMatch("log.eventDataCommandLine", "(?i)query\\\\s+session.*tscon") || + regexMatch("log.eventDataCommandLine", "(?i)query\\\\s+session.*tscon")) || + (regexMatch("log.eventDataNewProcessName", "(?i)\\\\\\\\tscon\\\\.exe$") && + regexMatch("log.eventDataCommandLine", "(?i)\\\\d+\\\\s+/dest:")) +) +', '2026-02-23 16:19:03.780715', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1053, 'Print Spooler Exploitation Detection', 3, 3, 2, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', e'Detects potential PrintNightmare exploitation attempts through suspicious print spooler activity including DLL loading and driver installation. This rule identifies suspicious activity related to the Print Spooler service that could indicate CVE-2021-34527 (PrintNightmare) exploitation attempts. + +Next Steps: +1. Investigate the affected host for unauthorized driver installations +2. Check for recent DLL files placed in the print spooler drivers directory +3. Review print spooler service logs for unusual activity +4. Examine process execution context and parent processes +5. Verify if print driver installations were authorized +6. Check for lateral movement from the affected system +7. Consider isolating the affected host if exploitation is confirmed +', '["https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-34527","https://attack.mitre.org/techniques/T1068/"]', '(equals("log.providerName", "Microsoft-Windows-PrintService") && equals("log.eventId", "316") && contains("log.message", "kernelbase.dll")) || (equals("log.eventDataProcessName", "spoolsv.exe") && contains("log.eventDataTargetFilename", "\\spool\\drivers\\x64\\") && endsWith("log.eventDataTargetFilename", ".dll"))', '2026-02-23 16:19:08.354150', true, true, 'origin', null, '[]', '["lastEvent.log.eventDataTargetFilename","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1054, 'PowerShell Empire Detection', 3, 3, 2, 'Execution', 'T1059.001 - Command and Scripting Interpreter: PowerShell', e'Detects potential PowerShell Empire framework usage based on characteristic command patterns, obfuscation techniques, and encoded payloads commonly used by this post-exploitation framework. PowerShell Empire is a post-exploitation framework that uses PowerShell and Python agents to maintain persistence and execute commands on compromised systems. + +Next Steps: +1. Immediately isolate the affected host to prevent lateral movement +2. Analyze the complete PowerShell script block content for additional IOCs +3. Check for persistence mechanisms (scheduled tasks, registry entries, services) +4. Review network connections from the host for C2 communication +5. Examine process tree and parent processes that spawned PowerShell +6. Search for additional Empire artifacts across the environment +7. Reset credentials for any accounts used on the compromised system +8. Conduct memory analysis to identify injected code or payloads +9. Review recent user activity and file access patterns +10. Update endpoint detection rules based on specific Empire techniques observed +', '["https://attack.mitre.org/techniques/T1059/001/","https://www.powershellempire.com/","https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging"]', e'(equals("log.eventCode", "4104") || equals("log.eventId", 4104)) && +equals("log.providerName", "Microsoft-Windows-PowerShell") && +( + contains("log.eventDataScriptBlockText", "System.Management.Automation.AmsiUtils") || + regexMatch("log.eventDataScriptBlockText", "(?i)(empire|invoke-empire|invoke-psempire)") || + regexMatch("log.eventDataScriptBlockText", "(?i)\\\\[System\\\\.Convert\\\\]::FromBase64String") || + regexMatch("log.eventDataScriptBlockText", "(?i)IEX\\\\s*\\\\(\\\\s*New-Object") || + regexMatch("log.eventDataScriptBlockText", "(?i)-enc\\\\s+[A-Za-z0-9+/=]{100,}") || + regexMatch("log.eventDataScriptBlockText", "(?i)\\\\$DoIt\\\\s*=\\\\s*@") || + regexMatch("log.eventDataScriptBlockText", "(?i)\\\\[System\\\\.Text\\\\.Encoding\\\\]::Unicode\\\\.GetString") || + contains("log.eventDataScriptBlockText", "Invoke-Shellcode") || + contains("log.eventDataScriptBlockText", "Invoke-ReflectivePEInjection") || + contains("log.eventDataScriptBlockText", "Invoke-Mimikatz") +) +', '2026-02-23 16:19:09.561590', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1055, 'PetitPotam NTLM Relay Attack Detection', 3, 3, 2, 'Credential Access', 'T1187 - Forced Authentication', e'Detects PetitPotam NTLM relay attacks that abuse EFS RPC calls to coerce domain controller +authentication. The attack forces a DC to authenticate to an attacker-controlled host, enabling +NTLM relay to Active Directory Certificate Services (AD CS) for domain compromise. The rule +monitors Event ID 5145 (network share access) for IPC$ share access to specific EFS-related +named pipes used by PetitPotam. + +Next Steps: +1. Identify the source IP attempting the EFS pipe access +2. Check if the source is an authorized system or a potential attacker +3. Verify if AD CS is configured and potentially vulnerable to NTLM relay +4. Apply Microsoft patches for PetitPotam (KB5005413) +5. Enable Extended Protection for Authentication on AD CS +6. Disable NTLM authentication where possible +7. Monitor for certificate enrollment from the relayed authentication +8. Restrict access to EFS RPC endpoints via Windows Firewall rules +', '["https://attack.mitre.org/techniques/T1187/","https://github.com/topotam/PetitPotam","https://msrc.microsoft.com/update-guide/vulnerability/ADV210003"]', e'equals("log.eventCode", "5145") && +equals("log.channel", "Security") && +equals("log.eventDataShareName", "\\\\\\\\*\\\\IPC$") && +( + contains("log.eventDataRelativeTargetName", "efsrpc") || + contains("log.eventDataRelativeTargetName", "lsarpc") || + contains("log.eventDataRelativeTargetName", "efsr") || + contains("log.eventDataRelativeTargetName", "samr") +) +', '2026-02-23 16:19:10.693974', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataIpAddress","operator":"filter_term","value":"{{log.eventDataIpAddress}}"}],"or":null,"within":"now-5m","count":3}]', '["lastEvent.log.eventDataIpAddress","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1056, 'Pass-the-Ticket Attack Detection', 3, 3, 2, 'Lateral Movement', 'T1550.003 - Use Alternate Authentication Material: Pass the Ticket', e'Detects Pass-the-Ticket attacks where attackers steal and reuse Kerberos tickets from one +endpoint to authenticate on another. The rule monitors for Kerberos authentication events +(Event ID 4624 LogonType 3 with Kerberos package) combined with anomalous ticket usage +patterns and tools like Rubeus, Mimikatz kerberos::ptt, and Invoke-Mimikatz. + +Next Steps: +1. Identify the source of the Kerberos ticket and the target system +2. Check if the ticket was exported using Mimikatz or Rubeus +3. Verify if the original ticket owner\'s host is compromised +4. Review Kerberos TGS/TGT events from the source IP +5. Force Kerberos ticket renewal by resetting the user\'s password +6. Check for additional lateral movement from the authenticated session +7. Implement Kerberos constrained delegation and armoring +', '["https://attack.mitre.org/techniques/T1550/003/","https://adsecurity.org/?p=2011","https://www.sans.org/blog/kerberos-in-the-crosshairs-golden-tickets-silver-tickets-mitm-and-more/"]', e'( + (equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && + ( + (regexMatch("log.eventDataCommandLine", "(?i)kerberos::ptt") || + regexMatch("log.eventDataCommandLine", "(?i)kerberos::ptt")) || + (regexMatch("log.eventDataCommandLine", "(?i)Rubeus.*ptt") || + regexMatch("log.eventDataCommandLine", "(?i)Rubeus.*ptt")) || + (regexMatch("log.eventDataCommandLine", "(?i)Rubeus.*(asktgt|asktgs|renew|s4u|createnetonly)") || + regexMatch("log.eventDataCommandLine", "(?i)Rubeus.*(asktgt|asktgs|renew|s4u|createnetonly)")) || + (regexMatch("log.eventDataCommandLine", "(?i)Invoke-Mimikatz.*kerberos") || + regexMatch("log.eventDataCommandLine", "(?i)Invoke-Mimikatz.*kerberos")) || + (regexMatch("log.eventDataCommandLine", "(?i)klist.*purge|klist.*tickets") || + regexMatch("log.eventDataCommandLine", "(?i)klist.*purge|klist.*tickets")) + ) +) || +( + equals("log.eventCode", "4768") && + equals("log.channel", "Security") && + !equals("log.eventDataStatus", "0x0") && + oneOf("log.eventDataStatus", ["0x1f", "0x20", "0x25"]) && + exists("log.eventDataIpAddress") +) +', '2026-02-23 16:19:11.792793', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1057, 'Pass-the-Hash Attack Detection', 3, 3, 2, 'Lateral Movement', 'T1550.002 - Use Alternate Authentication Material: Pass the Hash', e'Detects Pass-the-Hash attacks by monitoring for NTLM authentication (Event ID 4624) with +LogonType 9 (NewCredentials) or LogonType 3 (Network) from unusual sources, combined with +the use of Seclogon service. Attackers use stolen NTLM hashes to authenticate without +knowing the plaintext password, commonly through tools like Mimikatz sekurlsa::pth, +Impacket, or CrackMapExec. + +Next Steps: +1. Identify the source IP and user account used for the NTLM authentication +2. Verify if the source host should be authenticating with NTLM to this target +3. Check for prior credential dumping activity on the source host +4. Review if the authentication was followed by lateral movement or data access +5. Reset the compromised account password and any related accounts +6. Implement NTLM restrictions via Group Policy where possible +7. Enable Windows Defender Credential Guard to protect NTLM hashes +', '["https://attack.mitre.org/techniques/T1550/002/","https://www.sans.org/blog/pass-the-hash-attack-detection/","https://stealthbits.com/blog/how-to-detect-pass-the-hash-attacks/"]', e'( + equals("log.eventCode", "4624") && + equals("log.channel", "Security") && + equals("log.eventDataLogonType", "9") && + equals("log.eventDataAuthenticationPackageName", "Negotiate") && + !regexMatch("log.eventDataSubjectUserName", "(?i)^(SYSTEM|LOCAL SERVICE|NETWORK SERVICE|ANONYMOUS LOGON|-|\\\\$)") && + exists("target.user") && + !regexMatch("target.user", "(?i)\\\\$$") +) || +( + equals("log.eventCode", "4624") && + equals("log.channel", "Security") && + equals("log.eventDataLogonType", "3") && + equals("log.eventDataLmPackageName", "NTLM V1") && + exists("log.eventDataIpAddress") && + !equals("log.eventDataIpAddress", "-") && + !equals("log.eventDataIpAddress", "::1") && + !equals("log.eventDataIpAddress", "127.0.0.1") +) +', '2026-02-23 16:19:12.990314', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataIpAddress","operator":"filter_term","value":"{{log.eventDataIpAddress}}"},{"field":"log.eventCode","operator":"filter_term","value":"4624"}],"or":null,"within":"now-30m","count":3}]', '["lastEvent.log.eventDataIpAddress","adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1058, 'NTLM Authentication Downgrade Attack', 3, 3, 1, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', e'Detects NTLM authentication downgrade attacks via registry modifications to LMCompatibilityLevel, +NtlmMinClientSec, and NtlmMinServerSec. Attackers modify these registry values to weaken NTLM +authentication security, enabling credential interception, relay attacks, and offline cracking +of captured NTLM hashes. Downgrading to LM or NTLMv1 authentication makes credential theft +significantly easier. + +Next Steps: +1. Check the new registry value to determine the downgrade severity +2. LMCompatibilityLevel < 3 enables NTLMv1 which is trivially crackable +3. Identify the process and user that made the registry modification +4. Restore the registry values to enforce NTLMv2 (LMCompatibilityLevel = 5) +5. Review Group Policy for conflicting NTLM settings +6. Check for NTLM relay attacks following the downgrade +7. Audit network traffic for NTLMv1 authentication attempts +8. Consider disabling NTLM entirely where possible +', '["https://attack.mitre.org/techniques/T1562/001/","https://www.praetorian.com/blog/ntlm-relaying-attacks/","https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-security-lan-manager-authentication-level"]', e'equals("log.eventCode", "4657") && +equals("log.channel", "Security") && +( + regexMatch("log.eventDataObjectName", "(?i)\\\\\\\\CurrentControlSet\\\\\\\\Control\\\\\\\\Lsa\\\\\\\\LMCompatibilityLevel") || + regexMatch("log.eventDataObjectName", "(?i)\\\\\\\\CurrentControlSet\\\\\\\\Control\\\\\\\\Lsa\\\\\\\\MSV1_0\\\\\\\\NtlmMinClientSec") || + regexMatch("log.eventDataObjectName", "(?i)\\\\\\\\CurrentControlSet\\\\\\\\Control\\\\\\\\Lsa\\\\\\\\MSV1_0\\\\\\\\NtlmMinServerSec") || + regexMatch("log.eventDataObjectName", "(?i)\\\\\\\\CurrentControlSet\\\\\\\\Control\\\\\\\\Lsa\\\\\\\\MSV1_0\\\\\\\\RestrictSendingNTLMTraffic") || + regexMatch("log.eventDataObjectName", "(?i)\\\\\\\\CurrentControlSet\\\\\\\\Control\\\\\\\\Lsa\\\\\\\\MSV1_0\\\\\\\\AuditReceivingNTLMTraffic") || + regexMatch("log.eventDataObjectName", "(?i)\\\\\\\\CurrentControlSet\\\\\\\\Services\\\\\\\\Netlogon\\\\\\\\Parameters\\\\\\\\RequireSignOrSeal") +) && +!regexMatch("log.eventDataSubjectUserName", "(?i)^(SYSTEM|TrustedInstaller)$") +', '2026-02-23 16:19:14.046987', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1059, 'NTDS.dit Extraction Attempt', 3, 3, 1, 'Credential Access', 'T1003.003 - OS Credential Dumping: NTDS', e'Detects attempts to access or copy the Active Directory domain database (NTDS.dit) which contains password hashes for all domain users. This is a critical indicator of credential theft attempts and potential domain compromise. + +Next Steps: +1. Immediately isolate the affected system to prevent further compromise +2. Review all recent activity from the source host and user account +3. Check for signs of lateral movement from this system +4. Verify integrity of domain controllers and examine recent administrative actions +5. Look for evidence of credential harvesting tools (ntdsutil, vssadmin, mimikatz) +6. Review privileged account usage and consider forcing password resets +7. Examine network traffic for data exfiltration attempts +8. Check backup systems and shadow copies for unauthorized access +9. Coordinate with incident response team for full forensic analysis +', '["https://attack.mitre.org/techniques/T1003/003/","https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4663"]', e'oneOf("log.eventCode", ["4663", "4656"]) && +equals("log.channel", "Security") && +( + endsWith("log.eventDataObjectName", "\\\\ntds.dit") || + contains("log.eventDataObjectName", "\\\\NTDS\\\\") || + endsWith("log.eventDataProcessName", "\\\\ntdsutil.exe") || + endsWith("log.eventDataProcessName", "\\\\vssadmin.exe") +) && +!equals("log.eventDataAccessMask", "0x0") +', '2026-02-23 16:19:15.221310', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host","operator":"filter_term","value":"{{origin.host}}"}],"or":null,"within":"now-30m","count":2}]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1060, 'Network Sniffer and Packet Capture Detection', 3, 1, 0, 'Discovery', 'T1040 - Network Sniffing', e'Detects execution of network sniffing and packet capture tools that could be used to +intercept network traffic, capture credentials, or perform man-in-the-middle attacks. +Monitors for known sniffer binaries, raw socket creation, and promiscuous mode indicators. + +Next Steps: +1. Verify if the packet capture tool usage was authorized (IT/security staff) +2. Check which user account executed the tool and validate their role +3. Determine what network interfaces were targeted for capture +4. Review if any sensitive data (credentials, tokens) may have been captured +5. Check for data exfiltration - captured packets being sent to external destinations +6. Look for lateral movement from the host running the sniffer +7. Investigate if the sniffer was installed recently or is part of standard tooling +', '["https://attack.mitre.org/techniques/T1040/","https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4688","https://www.sans.org/reading-room/whitepapers/detection/detecting-network-sniffers-1180"]', e'( + (equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && + ( + regexMatch("log.eventDataNewProcessName", "(?i)(wireshark|tshark|dumpcap|windump|tcpdump|rawcap|netsh\\\\.exe|pktmon\\\\.exe)\\\\.exe$") || + regexMatch("log.eventDataNewProcessName", "(?i)(ettercap|NetworkMiner|Microsoft Network Monitor|nmcap)\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)(wireshark|tshark|dumpcap|windump|tcpdump|rawcap)\\\\.exe$") || + ( + regexMatch("log.eventDataNewProcessName", "(?i)netsh\\\\.exe$") && + regexMatch("log.eventDataCommandLine", "(?i)(trace\\\\s+start|capture\\\\s+start)") + ) || + ( + regexMatch("log.eventDataNewProcessName", "(?i)pktmon\\\\.exe$") && + contains("log.eventDataCommandLine", "start") + ) || + regexMatch("log.eventDataCommandLine", "(?i)(npcap|winpcap|pcap\\\\.dll|raw\\\\s+socket|promiscuous)") || + regexMatch("log.eventDataCommandLine", "(?i)(npcap|winpcap|pcap\\\\.dll|raw\\\\s+socket|promiscuous)") + ) +) || +( + (equals("log.eventCode", "4104") || equals("log.eventId", 4104)) && + equals("log.providerName", "Microsoft-Windows-PowerShell") && + ( + contains("log.eventDataScriptBlockText", "Net.Sockets.Socket") || + contains("log.eventDataScriptBlockText", "SocketType.Raw") || + contains("log.eventDataScriptBlockText", "ProtocolType.IP") || + contains("log.eventDataScriptBlockText", "IOControlCode") || + contains("log.eventDataScriptBlockText", "SIO_RCVALL") || + contains("log.eventDataScriptBlockText", "Invoke-PacketCapture") || + contains("log.eventDataScriptBlockText", "New-NetEventSession") + ) +) || +( + equals("log.eventCode", "7045") && + regexMatch("log.eventDataServiceName", "(?i)(npcap|winpcap|npf|rawcap)") +) +', '2026-02-23 16:19:16.331532', true, true, 'origin', '["adversary.host","adversary.user"]', '[]', null); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1061, 'Named Pipe Impersonation Attack', 3, 3, 2, 'Defense Evasion, Privilege Escalation', 'T1134.001 - Access Token Manipulation: Token Impersonation/Theft', e'Detects potential named pipe impersonation attacks used for privilege escalation. Monitors for suspicious process creation patterns including cmd.exe or powershell.exe with pipe-related commands, and processes creating named pipes with suspicious naming patterns commonly used by attack tools like Meterpreter and Cobalt Strike. + +Next Steps: +1. Verify the legitimacy of the process and command line parameters +2. Check for any privilege escalation activities following this event +3. Examine the process parent-child relationship and execution context +4. Review recent logon events (4672) for the affected host +5. Investigate any unusual network connections or file access patterns +6. Consider isolating the affected system if malicious activity is confirmed +', '["https://bherunda.medium.com/hunting-named-pipe-token-impersonation-abuse-573dcca36ae0","https://attack.mitre.org/techniques/T1134/001/"]', e'equals("log.eventCode", "4688") && +exists("log.eventDataProcessName") && +(contains("log.eventDataProcessName", "cmd.exe") || contains("log.eventDataProcessName", "powershell.exe")) && +exists("log.eventDataProcessCommandLine") && +(contains("log.eventDataProcessCommandLine", "\\\\\\\\.\\\\pipe\\\\") || + (contains("log.eventDataProcessCommandLine", "echo") && contains("log.eventDataProcessCommandLine", "pipe")) || + contains("log.eventDataProcessCommandLine", "CreateNamedPipe") || + contains("log.eventDataProcessCommandLine", "ImpersonateNamedPipeClient")) +', '2026-02-23 16:19:17.435809', true, true, 'origin', null, '[]', '["lastEvent.log.eventDataProcessId","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1062, 'MSIExec Remote Payload Installation', 2, 3, 2, 'Defense Evasion', 'T1218.007 - System Binary Proxy Execution: Msiexec', e'Detects abuse of msiexec.exe for installing MSI packages from remote URLs, executing DLLs, +or running packages with quiet/silent flags to avoid user interaction. Attackers use msiexec +as a proxy to execute malicious code because it is a signed Microsoft binary that can download +and execute payloads from remote locations. + +Next Steps: +1. Examine the MSI package URL or path for malicious content +2. Review the quiet/silent installation flags for evasion intent +3. Check the parent process to determine the delivery mechanism +4. Analyze the MSI package contents in a sandbox +5. Review network connections to the MSI download source +6. Block the identified URL at the proxy/firewall +7. Search for the same MSI package hash across other endpoints +', '["https://attack.mitre.org/techniques/T1218/007/","https://lolbas-project.github.io/lolbas/Binaries/Msiexec/","https://www.trendmicro.com/en_us/research/19/b/msiexec-abuse.html"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + regexMatch("log.eventDataNewProcessName", "(?i)msiexec\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)msiexec\\\\.exe$") +) && +( + regexMatch("log.eventDataCommandLine", "(?i)/i\\\\s+(http://|https://)") || + regexMatch("log.eventDataCommandLine", "(?i)/y\\\\s+") || + regexMatch("log.eventDataCommandLine", "(?i)/z\\\\s+(http://|https://)") || + ( + regexMatch("log.eventDataCommandLine", "(?i)(/q|/quiet|/passive)") && + regexMatch("log.eventDataCommandLine", "(?i)(http://|https://|\\\\\\\\\\\\\\\\)") + ) || + regexMatch("log.eventDataCommandLine", "(?i)/i\\\\s+(http://|https://)") || + regexMatch("log.eventDataCommandLine", "(?i)/y\\\\s+") || + ( + regexMatch("log.eventDataCommandLine", "(?i)(/q|/quiet|/passive)") && + regexMatch("log.eventDataCommandLine", "(?i)(http://|https://|\\\\\\\\\\\\\\\\)") + ) +) +', '2026-02-23 16:19:18.532899', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1063, 'MSHTA Suspicious Execution Detection', 2, 3, 2, 'Defense Evasion', 'T1218.005 - System Binary Proxy Execution: Mshta', e'Detects suspicious mshta.exe execution patterns including remote HTA file execution, inline +VBScript/JavaScript execution, and COM scriptlet execution. MSHTA is a signed Microsoft binary +that executes Microsoft HTML Applications (HTA) and is frequently abused for initial access, +defense evasion, and payload delivery because it bypasses application whitelisting. + +Next Steps: +1. Examine the full command line for remote URLs or inline script content +2. Identify the parent process to determine the initial delivery mechanism +3. Check for downloaded HTA files and analyze their contents +4. Review network connections made by the mshta.exe process +5. Block identified malicious URLs at the proxy/firewall +6. Search for similar mshta executions across other endpoints +7. Check for persistence mechanisms established after mshta execution +', '["https://attack.mitre.org/techniques/T1218/005/","https://lolbas-project.github.io/lolbas/Binaries/Mshta/","https://redcanary.com/threat-detection-report/techniques/mshta/"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + regexMatch("log.eventDataNewProcessName", "(?i)mshta\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)mshta\\\\.exe$") +) && +( + regexMatch("log.eventDataCommandLine", "(?i)(http://|https://|ftp://)") || + regexMatch("log.eventDataCommandLine", "(?i)(vbscript:|javascript:)") || + regexMatch("log.eventDataCommandLine", "(?i)(GetObject|Execute|CreateObject|WScript\\\\.Shell)") || + regexMatch("log.eventDataCommandLine", "(?i)sct:") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\.hta\\\\s") || + regexMatch("log.eventDataCommandLine", "(?i)(http://|https://|ftp://)") || + regexMatch("log.eventDataCommandLine", "(?i)(vbscript:|javascript:)") || + regexMatch("log.eventDataCommandLine", "(?i)(GetObject|Execute|CreateObject|WScript\\\\.Shell)") +) +', '2026-02-23 16:19:19.648137', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1064, 'Mimikatz Tool Usage Detection', 3, 3, 1, 'Credential Access', 'T1003.001 - OS Credential Dumping: LSASS Memory', e'Detects potential Mimikatz credential dumping tool usage through various indicators including characteristic command patterns, LSASS access, and known Mimikatz modules. Mimikatz is a well-known post-exploitation tool used to extract plaintext passwords, hash, PIN code and kerberos tickets from memory. + +Next Steps: +1. Immediately isolate the affected host to prevent lateral movement +2. Review the full command line and process execution details +3. Check for any credential theft or privilege escalation activities +4. Examine recent logon events and account usage patterns +5. Scan for additional persistence mechanisms or backdoors +6. Reset passwords for all potentially compromised accounts +7. Review security logs for signs of lateral movement to other systems +8. Conduct forensic analysis of memory dumps and system artifacts +', '["https://attack.mitre.org/techniques/T1003/001/","https://github.com/gentilkiwi/mimikatz","https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4688"]', e'( + (equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && + ( + regexMatch("log.eventDataNewProcessName", "(?i)mimikatz") || + regexMatch("log.eventDataCommandLine", "(?i)(sekurlsa|kerberos|crypto|vault|lsadump|dpapi)::") || + regexMatch("log.eventDataCommandLine", "(?i)(logonpasswords|pth|golden|silver|ticket)") || + regexMatch("log.eventDataCommandLine", "(?i)privilege::debug") || + contains("log.eventDataCommandLine", "coffee") || + contains("log.eventDataCommandLine", "kirbi") + ) +) || +( + (equals("log.eventCode", "4104") || equals("log.eventId", 4104)) && + equals("log.providerName", "Microsoft-Windows-PowerShell") && + ( + regexMatch("log.eventDataScriptBlockText", "(?i)invoke-mimikatz") || + regexMatch("log.eventDataScriptBlockText", "(?i)mimikatz\\\\.ps1") || + regexMatch("log.eventDataScriptBlockText", "(?i)DumpCreds|DumpCerts") || + contains("log.eventDataScriptBlockText", "Win32_ShadowCopy") + ) +) || +( + equals("log.eventCode", "10") && + regexMatch("log.eventDataTargetImage", "(?i)lsass\\\\.exe") && + oneOf("log.eventDataGrantedAccess", ["0x1010", "0x1038", "0x1418", "0x1438"]) +) +', '2026-02-23 16:19:20.709892', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1065, 'Process Masquerading Detection', 2, 3, 2, 'Defense Evasion', 'T1036.005 - Masquerading: Match Legitimate Name or Location', e'Detects executables masquerading as legitimate Windows system processes but running from +incorrect locations. For example, svchost.exe should only run from C:\\Windows\\System32, +and explorer.exe should only run from C:\\Windows. Malware commonly uses legitimate process +names to avoid detection by analysts and automated tools. + +Next Steps: +1. Identify the actual file path of the masquerading process +2. Compare the file hash against known good versions of the legitimate binary +3. Check the digital signature of the suspicious executable +4. Analyze the executable in a sandbox environment +5. Review the parent process that launched the masquerading binary +6. Kill the suspicious process and quarantine the file +7. Search for other instances of the same file across the environment +', '["https://attack.mitre.org/techniques/T1036/005/","https://www.elastic.co/blog/how-hunt-masquerade-ball","https://redcanary.com/threat-detection-report/techniques/masquerading/"]', e'(equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && +( + ( + regexMatch("log.eventDataNewProcessName", "(?i)\\\\\\\\svchost\\\\.exe$") && + !regexMatch("log.eventDataNewProcessName", "(?i)^C:\\\\\\\\Windows\\\\\\\\(System32|SysWOW64)\\\\\\\\svchost\\\\.exe$") + ) || + ( + regexMatch("log.eventDataNewProcessName", "(?i)\\\\\\\\csrss\\\\.exe$") && + !regexMatch("log.eventDataNewProcessName", "(?i)^C:\\\\\\\\Windows\\\\\\\\(System32|SysWOW64)\\\\\\\\csrss\\\\.exe$") + ) || + ( + regexMatch("log.eventDataNewProcessName", "(?i)\\\\\\\\lsass\\\\.exe$") && + !regexMatch("log.eventDataNewProcessName", "(?i)^C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\lsass\\\\.exe$") + ) || + ( + regexMatch("log.eventDataNewProcessName", "(?i)\\\\\\\\services\\\\.exe$") && + !regexMatch("log.eventDataNewProcessName", "(?i)^C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\services\\\\.exe$") + ) || + ( + regexMatch("log.eventDataNewProcessName", "(?i)\\\\\\\\smss\\\\.exe$") && + !regexMatch("log.eventDataNewProcessName", "(?i)^C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\smss\\\\.exe$") + ) || + ( + regexMatch("log.eventDataNewProcessName", "(?i)\\\\\\\\wininit\\\\.exe$") && + !regexMatch("log.eventDataNewProcessName", "(?i)^C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\wininit\\\\.exe$") + ) || + ( + regexMatch("log.eventDataNewProcessName", "(?i)\\\\\\\\explorer\\\\.exe$") && + !regexMatch("log.eventDataNewProcessName", "(?i)^C:\\\\\\\\Windows\\\\\\\\explorer\\\\.exe$") + ) || + ( + regexMatch("log.eventDataProcessName", "(?i)\\\\\\\\svchost\\\\.exe$") && + !regexMatch("log.eventDataProcessName", "(?i)^C:\\\\\\\\Windows\\\\\\\\(System32|SysWOW64)\\\\\\\\svchost\\\\.exe$") + ) || + ( + regexMatch("log.eventDataProcessName", "(?i)\\\\\\\\lsass\\\\.exe$") && + !regexMatch("log.eventDataProcessName", "(?i)^C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\lsass\\\\.exe$") + ) +) +', '2026-02-23 16:19:21.854705', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1077, 'Event Log Clearing Detection', 3, 3, 2, 'Defense Evasion', 'T1070.001 - Indicator Removal on Host: Clear Windows Event Logs', e'Detects when Windows event logs are cleared, which is often done by attackers to cover their tracks and remove evidence of malicious activities. This rule monitors for Event ID 1102 (Security log cleared), Event ID 104 (System log cleared), and command-line activities using wevtutil or PowerShell cmdlets to clear event logs. + +Next Steps: +1. Identify the user account that performed the log clearing operation +2. Review the timeline of events before the log clearing to identify potential malicious activities +3. Check for any remaining forensic artifacts in other log sources (network logs, endpoint logs, etc.) +4. Investigate if this was authorized maintenance or potential malicious activity +5. Review user privileges and access patterns for the account involved +6. Consider implementing additional logging and monitoring for critical systems +', '["https://attack.mitre.org/techniques/T1070/001/","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=1102"]', e'equals("log.eventId", "1102") || +equals("log.eventCode", "1102") || +(equals("log.eventId", "104") && + contains("log.channel", "System")) || +(equals("log.eventId", "4688") && + ((contains("log.eventDataCommandLine", "wevtutil") && + contains("log.eventDataCommandLine", " cl ")) || + contains("log.eventDataCommandLine", "Clear-EventLog") || + contains("log.eventDataCommandLine", "Remove-EventLog") || + contains("log.eventDataCommandLine", "Limit-EventLog"))) +', '2026-02-23 16:19:35.038887', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1078, 'ETW Patching and Tampering Detection', 3, 3, 2, 'Defense Evasion', 'T1562.006 - Impair Defenses: Indicator Blocking', e'Detects attempts to tamper with Event Tracing for Windows (ETW) to blind security tools. +Attackers patch ETW functions in memory (NtTraceEvent, EtwEventWrite) or use PowerShell +Reflection to disable .NET ETW providers, preventing security tools from receiving telemetry. +This technique is frequently used before executing post-exploitation tools to evade detection. + +Next Steps: +1. Investigate the process that attempted ETW tampering +2. Check what malicious activity followed the ETW patching +3. Review PowerShell script block logs for ETW manipulation code +4. Examine if the .NET ETW provider was targeted (common for AMSI bypass chains) +5. Verify that ETW providers are functioning correctly after remediation +6. Search for additional evasion techniques on the same host +7. Isolate the host and investigate the full attack chain +', '["https://attack.mitre.org/techniques/T1562/006/","https://blog.xpnsec.com/hiding-your-dotnet-etw/","https://www.mdsec.co.uk/2020/03/hiding-your-net-etw/"]', e'( + (equals("log.eventCode", "4104") || equals("log.eventCode", "4103")) && + equals("log.providerName", "Microsoft-Windows-PowerShell") && + ( + contains("log.eventDataScriptBlockText", "EtwEventWrite") || + contains("log.eventDataScriptBlockText", "NtTraceEvent") || + contains("log.eventDataScriptBlockText", "NtTraceControl") || + regexMatch("log.eventDataScriptBlockText", "(?i)\\\\[Reflection\\\\.Assembly\\\\].*ETW") || + regexMatch("log.eventDataScriptBlockText", "(?i)Patch.*ETW|ETW.*Patch") || + contains("log.eventDataScriptBlockText", "EventProvider") || + contains("log.eventDataScriptBlockText", "EtwEventWrite") || + contains("log.eventDataScriptBlockText", "NtTraceEvent") + ) +) || +( + (equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && + ( + regexMatch("log.eventDataCommandLine", "(?i)logman.*stop.*EventLog") || + regexMatch("log.eventDataCommandLine", "(?i)logman.*stop.*EventLog") || + regexMatch("log.eventDataCommandLine", "(?i)auditpol.*/clear") || + regexMatch("log.eventDataCommandLine", "(?i)auditpol.*/clear") || + regexMatch("log.eventDataCommandLine", "(?i)Set-EtwTraceProvider.*0x0") || + regexMatch("log.eventDataCommandLine", "(?i)Set-EtwTraceProvider.*0x0") + ) +) +', '2026-02-23 16:19:36.223267', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1079, 'DPAPI Domain Backup Key Extraction', 3, 3, 1, 'Credential Access', 'T1003.004 - OS Credential Dumping: LSA Secrets', e'Detects extraction of the DPAPI domain backup key from a domain controller. The BCKUPKEY secret +object contains the domain\'s DPAPI backup key which can be used to decrypt any DPAPI-protected +data for all domain users, including passwords, certificates, and other sensitive data. This is +a high-severity credential access technique that indicates an attacker has domain admin access. + +Next Steps: +1. Verify the account accessing the BCKUPKEY object is authorized (should only be DC replication) +2. Check if the source host is a legitimate domain controller +3. Review the user account for signs of compromise +4. Investigate for related DCSync or NTDS.dit extraction attempts +5. Assume all DPAPI-protected secrets are compromised if unauthorized +6. Rotate the DPAPI domain backup key (requires careful planning) +7. Reset credentials for all privileged accounts +', '["https://attack.mitre.org/techniques/T1003/004/","https://www.dsinternals.com/en/retrieving-dpapi-backup-keys-from-active-directory/","https://adsecurity.org/?p=1785"]', e'equals("log.eventCode", "4662") && +equals("log.channel", "Security") && +contains("log.eventDataProperties", "BCKUPKEY") && +contains("log.eventDataObjectServer", "DS") && +!regexMatch("log.eventDataSubjectUserName", "(?i)\\\\$$") +', '2026-02-23 16:19:37.411333', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1080, 'Domain Trust Discovery Detection', 2, 1, 1, 'Discovery', 'T1482 - Domain Trust Discovery', e'Detects domain trust enumeration using nltest, dsquery, Get-ADTrust, and other Active +Directory discovery tools. Attackers enumerate domain trusts to identify additional targets +for lateral movement between trusted domains and forests. This is typically an early +reconnaissance step in Active Directory attacks. + +Next Steps: +1. Identify the user account performing domain trust enumeration +2. Verify if this is authorized security assessment or IT administration +3. Check for other discovery commands from the same user or host +4. Review if the user subsequently accessed resources in trusted domains +5. Correlate with other reconnaissance activities (BloodHound, SharpHound) +6. Monitor for lateral movement into discovered trusted domains +7. Review trust configurations for unnecessary or overly permissive trusts +', '["https://attack.mitre.org/techniques/T1482/","https://adsecurity.org/?p=1588","https://www.harmj0y.net/blog/redteaming/a-guide-to-attacking-domain-trusts/"]', e'(equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && +( + (regexMatch("log.eventDataCommandLine", "(?i)nltest(.exe)?\\\\s+/(domain_trusts|trusted_domains|dclist|dsgetdc)") || + regexMatch("log.eventDataCommandLine", "(?i)nltest(.exe)?\\\\s+/(domain_trusts|trusted_domains|dclist|dsgetdc)")) || + (regexMatch("log.eventDataCommandLine", "(?i)dsquery(.exe)?\\\\s+trust") || + regexMatch("log.eventDataCommandLine", "(?i)dsquery(.exe)?\\\\s+trust")) || + (regexMatch("log.eventDataCommandLine", "(?i)Get-ADTrust|Get-DomainTrust|Get-ForestTrust|Get-NetForestTrust") || + regexMatch("log.eventDataCommandLine", "(?i)Get-ADTrust|Get-DomainTrust|Get-ForestTrust|Get-NetForestTrust")) || + (regexMatch("log.eventDataCommandLine", "(?i)\\\\[System\\\\.DirectoryServices\\\\.ActiveDirectory\\\\.(Domain|Forest)\\\\]") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\[System\\\\.DirectoryServices\\\\.ActiveDirectory\\\\.(Domain|Forest)\\\\]")) || + (regexMatch("log.eventDataCommandLine", "(?i)adfind(.exe)?.*-f.*trustdirection") || + regexMatch("log.eventDataCommandLine", "(?i)adfind(.exe)?.*-f.*trustdirection")) +) +', '2026-02-23 16:19:38.636625', true, true, 'origin', '["adversary.host","adversary.user"]', '[]', null); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1068, 'LSASS Credential Dumping Detection', 3, 3, 1, 'Credential Access', 'T1003.001 - OS Credential Dumping: LSASS Memory', e'Detects multiple LSASS credential dumping techniques beyond Mimikatz, including comsvcs.dll MiniDump, +procdump, nanodump, pypykatz, Task Manager dump, rundll32 with comsvcs.dll, and direct NT API calls. +LSASS holds credentials for all authenticated users and is a primary target for credential theft. +This rule complements the existing Mimikatz detection by covering alternative dumping methods. + +Next Steps: +1. Immediately isolate the affected host to prevent lateral movement +2. Identify the dumping technique used and the resulting dump file location +3. Check if the dump file has been exfiltrated to an external destination +4. Review the process tree to understand how the dumping tool was executed +5. Reset passwords for all accounts that were logged into the compromised system +6. Enable Credential Guard or RunAsPPL to protect LSASS +7. Search for the same dumping technique across other endpoints +8. Investigate the initial compromise vector +', '["https://attack.mitre.org/techniques/T1003/001/","https://www.microsoft.com/en-us/security/blog/2022/10/05/detecting-and-preventing-lsass-credential-dumping-attacks/","https://redcanary.com/threat-detection-report/techniques/lsass-memory/"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + ( + regexMatch("log.eventDataCommandLine", "(?i)comsvcs\\\\.dll[,\\\\s]+(MiniDump|#24)") || + regexMatch("log.eventDataCommandLine", "(?i)comsvcs\\\\.dll[,\\\\s]+(MiniDump|#24)") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)procdump.*-ma\\\\s+(lsass|\\\\d+)") || + regexMatch("log.eventDataCommandLine", "(?i)procdump.*-ma\\\\s+(lsass|\\\\d+)") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)rundll32\\\\.exe.*comsvcs") || + regexMatch("log.eventDataCommandLine", "(?i)rundll32\\\\.exe.*comsvcs") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)(nanodump|handlekatz|physmem2profit|dumpert)") || + regexMatch("log.eventDataCommandLine", "(?i)(nanodump|handlekatz|physmem2profit|dumpert)") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)taskmgr.*lsass") || + regexMatch("log.eventDataCommandLine", "(?i)taskmgr.*lsass") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)(Out-Minidump|Get-Process.*lsass.*\\\\.DumpProcess)") || + regexMatch("log.eventDataCommandLine", "(?i)(Out-Minidump|Get-Process.*lsass.*\\\\.DumpProcess)") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)sqldumper\\\\.exe.*lsass") || + regexMatch("log.eventDataCommandLine", "(?i)sqldumper\\\\.exe.*lsass") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)createdump\\\\.exe.*(-f|--full)") || + regexMatch("log.eventDataCommandLine", "(?i)createdump\\\\.exe.*(-f|--full)") + ) +) +', '2026-02-23 16:19:24.740211', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1069, 'Regsvr32 LOLBIN Abuse Detection', 3, 3, 2, 'Defense Evasion', 'T1218.010 - System Binary Proxy Execution: Regsvr32', e'Detects suspicious regsvr32.exe usage including Squiblydoo attacks (loading remote SCT files), +AppLocker bypass techniques, and execution of DLLs from suspicious locations. Regsvr32 is a +signed Microsoft binary that can be abused to proxy execution of malicious code, bypassing +application whitelisting controls. + +Next Steps: +1. Examine the DLL or SCT file being loaded by regsvr32 +2. Check if a remote URL was used (Squiblydoo attack indicator) +3. Analyze the parent process that launched regsvr32 +4. Review network connections made by the regsvr32 process +5. Check if the loaded DLL is from a suspicious location (Temp, AppData, etc.) +6. Block the remote URL if a network-based attack was used +7. Investigate what payload was delivered through the proxy execution +', '["https://attack.mitre.org/techniques/T1218/010/","https://pentestlab.blog/2017/05/11/applocker-bypass-regsvr32/","https://lolbas-project.github.io/lolbas/Binaries/Regsvr32/"]', e'(equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && +( + regexMatch("log.eventDataNewProcessName", "(?i)regsvr32\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)regsvr32\\\\.exe$") +) && +( + (regexMatch("log.eventDataCommandLine", "(?i)/s.*/u.*/i:") || + regexMatch("log.eventDataCommandLine", "(?i)/s.*/u.*/i:")) || + (regexMatch("log.eventDataCommandLine", "(?i)/i:(http|https|ftp)://") || + regexMatch("log.eventDataCommandLine", "(?i)/i:(http|https|ftp)://")) || + (regexMatch("log.eventDataCommandLine", "(?i)\\\\.sct") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\.sct")) || + (regexMatch("log.eventDataCommandLine", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\)") || + regexMatch("log.eventDataCommandLine", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\)")) || + (regexMatch("log.eventDataCommandLine", "(?i)scrobj\\\\.dll") || + regexMatch("log.eventDataCommandLine", "(?i)scrobj\\\\.dll")) +) +', '2026-02-23 16:19:25.874536', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1070, 'LaZagne Credential Harvester Detection', 3, 2, 1, 'Credential Access', 'T1555 - Credentials from Password Stores', e'Detects LaZagne credential harvesting tool execution. LaZagne is an open-source tool that +retrieves credentials stored on a local computer from 25+ sources including browsers, email +clients, databases, Wi-Fi passwords, Windows credentials, and more. The rule monitors for +both the executable name and characteristic command-line arguments used by LaZagne. + +Next Steps: +1. Immediately isolate the affected host to prevent credential abuse +2. Identify all credential stores that may have been accessed +3. Review the command-line arguments to determine which modules were used +4. Reset passwords for all accounts stored on the compromised system +5. Check browser credential stores, email clients, and Windows vaults +6. Review Wi-Fi profiles for exposed network credentials +7. Investigate the initial access vector and delivery mechanism +8. Search for LaZagne execution across other endpoints +', '["https://attack.mitre.org/techniques/T1555/","https://github.com/AlessandroZ/LaZagne","https://www.sans.org/blog/detecting-lazagne/"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + regexMatch("log.eventDataNewProcessName", "(?i)lazagne\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)lazagne\\\\.exe$") || + regexMatch("log.eventDataCommandLine", "(?i)lazagne\\\\s+(all|browsers|chats|databases|games|git|mails|maven|memory|multimedia|php|svn|sysadmin|wifi|windows)") || + regexMatch("log.eventDataCommandLine", "(?i)lazagne\\\\s+(all|browsers|chats|databases|games|git|mails|maven|memory|multimedia|php|svn|sysadmin|wifi|windows)") || + regexMatch("log.eventDataCommandLine", "(?i)lazagne\\\\.exe") || + regexMatch("log.eventDataCommandLine", "(?i)lazagne\\\\.exe") +) +', '2026-02-23 16:19:26.931122', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1071, 'Keylogger Activity Detection', 3, 2, 0, 'Collection', 'T1056.001 - Input Capture: Keylogging', e'Detects keylogger activity through Windows API hook installation, clipboard monitoring, +keyboard input capture, and known keylogger tool execution. Attackers use keyloggers +to capture user credentials, sensitive data, and communications. + +Next Steps: +1. Immediately isolate the affected host to stop credential capture +2. Identify the source process installing keyboard hooks and its origin +3. Check if the hooking process is a known legitimate application +4. Review what user accounts have been active on the host during the capture period +5. Force password resets for all accounts used on the compromised system +6. Check for data exfiltration - keylog data being sent externally +7. Examine the process tree to find how the keylogger was installed +8. Scan for persistence mechanisms associated with the keylogger +9. Review MFA tokens and session cookies that may have been captured +', '["https://attack.mitre.org/techniques/T1056/001/","https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexw","https://www.sans.org/reading-room/whitepapers/detection/detecting-keyloggers-36062"]', e'( + (equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && + ( + regexMatch("log.eventDataCommandLine", "(?i)(GetAsyncKeyState|GetKeyState|GetKeyboardState|SetWindowsHookEx|SetWindowsHookExW|SetWindowsHookExA)") || + regexMatch("log.eventDataCommandLine", "(?i)(keylog|key.?log|key.?stroke|key.?capture|keystroke.?log)") || + regexMatch("log.eventDataCommandLine", "(?i)(Get-Clipboard|Set-Clipboard|clipboard.?monitor|ClipboardChanged)") || + regexMatch("log.eventDataCommandLine", "(?i)(GetAsyncKeyState|GetKeyState|SetWindowsHookEx)") || + regexMatch("log.eventDataCommandLine", "(?i)(keylog|key.?log|key.?stroke|key.?capture)") + ) +) || +( + (equals("log.eventCode", "4104") || equals("log.eventId", 4104)) && + equals("log.providerName", "Microsoft-Windows-PowerShell") && + ( + contains("log.eventDataScriptBlockText", "GetAsyncKeyState") || + contains("log.eventDataScriptBlockText", "GetKeyboardState") || + contains("log.eventDataScriptBlockText", "SetWindowsHookEx") || + contains("log.eventDataScriptBlockText", "WH_KEYBOARD_LL") || + contains("log.eventDataScriptBlockText", "WH_KEYBOARD") || + contains("log.eventDataScriptBlockText", "MapVirtualKey") || + contains("log.eventDataScriptBlockText", "Get-Keystrokes") || + contains("log.eventDataScriptBlockText", "Invoke-Keylogger") || + ( + contains("log.eventDataScriptBlockText", "user32.dll") && + ( + contains("log.eventDataScriptBlockText", "GetAsyncKeyState") || + contains("log.eventDataScriptBlockText", "GetForegroundWindow") || + contains("log.eventDataScriptBlockText", "GetWindowText") + ) + ) || + ( + contains("log.eventDataScriptBlockText", "DllImport") && + contains("log.eventDataScriptBlockText", "user32") && + contains("log.eventDataScriptBlockText", "KeyState") + ) || + contains("log.eventDataScriptBlockText", "OpenClipboard") || + contains("log.eventDataScriptBlockText", "GetClipboardData") || + contains("log.eventDataScriptBlockText", "GetAsyncKeyState") || + contains("log.eventDataScriptBlockText", "SetWindowsHookEx") || + contains("log.eventDataScriptBlockText", "WH_KEYBOARD_LL") + ) +) || +( + equals("log.eventCode", "8") && + contains("log.eventDataStartModule", "user32.dll") && + contains("log.eventDataStartFunction", "SetWindowsHookEx") +) +', '2026-02-23 16:19:27.976258', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host","operator":"filter_term","value":"{{origin.host}}"}],"or":null,"within":"now-15m","count":2}]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1072, 'Kerberoasting Attack Detection', 3, 2, 1, 'Credential Access', 'T1558.003 - Steal or Forge Kerberos Tickets: Kerberoasting', e'Detects Kerberoasting attacks where adversaries request Kerberos TGS tickets encrypted with RC4 (0x17) for +service accounts in order to crack them offline and obtain plaintext credentials. This is the most common +Active Directory credential theft technique used in real-world compromises. The rule monitors Event ID 4769 +(Kerberos Service Ticket Operations) for RC4 encryption requests while excluding machine accounts (ending in $) +and legitimate system services. + +Next Steps: +1. Identify the requesting user account and verify if this is authorized security testing +2. Check which service account SPNs were targeted for TGS requests +3. Review if the requesting account has been compromised +4. Audit all service accounts with SPNs for weak passwords +5. Consider implementing AES-only Kerberos encryption policies +6. Rotate passwords for targeted service accounts immediately +7. Enable Group Managed Service Accounts (gMSA) where possible +8. Monitor for follow-up lateral movement using obtained credentials +', '["https://attack.mitre.org/techniques/T1558/003/","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4769","https://adsecurity.org/?p=2293"]', e'equals("log.eventCode", "4769") && +equals("log.channel", "Security") && +equals("log.eventDataTicketEncryptionType", "0x17") && +!regexMatch("log.eventDataServiceName", "(?i)\\\\$$") && +!equals("log.eventDataServiceName", "krbtgt") && +!oneOf("log.eventDataTicketOptions", ["0x40810000", "0x40800000", "0x40810010"]) && +exists("log.eventDataServiceName") +', '2026-02-23 16:19:29.093231', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataIpAddress","operator":"filter_term","value":"{{log.eventDataIpAddress}}"}],"or":null,"within":"now-15m","count":3}]', '["lastEvent.log.eventDataIpAddress","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1073, 'Impacket Lateral Movement Detection', 3, 3, 2, 'Lateral Movement', 'T1021.002 - Remote Services: SMB/Windows Admin Shares', e'Detects Impacket framework lateral movement patterns including wmiexec, smbexec, dcomexec, and atexec. +Impacket is the most commonly used lateral movement framework in real-world attacks. The characteristic +pattern involves cmd.exe /Q /c with output redirection to 127.0.0.1 via named pipes or ADMIN$ share, +as well as specific command-line patterns unique to each Impacket module. + +Next Steps: +1. Identify the source of the lateral movement and the compromised credentials used +2. Examine the full command line for data exfiltration or payload delivery +3. Check for Impacket artifacts in the ADMIN$ share or temp directories +4. Review authentication logs for the credential source +5. Isolate affected hosts and block lateral movement paths +6. Reset credentials for all accounts used in the lateral movement +7. Hunt for Impacket usage across all domain-joined systems +8. Review network logs for SMB traffic patterns consistent with Impacket +', '["https://attack.mitre.org/techniques/T1021/002/","https://github.com/fortra/impacket","https://www.13cubed.com/downloads/impacket_exec_commands_cheat_sheet.pdf"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + ( + regexMatch("log.eventDataCommandLine", "(?i)cmd\\\\.exe\\\\s+/Q\\\\s+/c\\\\s+.+\\\\s+1>\\\\s*\\\\\\\\\\\\\\\\127\\\\.0\\\\.0\\\\.1\\\\\\\\") || + regexMatch("log.eventDataCommandLine", "(?i)cmd\\\\.exe\\\\s+/Q\\\\s+/c\\\\s+.+\\\\s+1>\\\\s*\\\\\\\\\\\\\\\\127\\\\.0\\\\.0\\\\.1\\\\\\\\") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)cmd\\\\.exe\\\\s+/Q\\\\s+/c\\\\s+.+\\\\s+2>&1") || + regexMatch("log.eventDataCommandLine", "(?i)cmd\\\\.exe\\\\s+/Q\\\\s+/c\\\\s+.+\\\\s+2>&1") + ) || + ( + regexMatch("log.eventDataParentProcessName", "(?i)wmiprvse\\\\.exe$") && + regexMatch("log.eventDataCommandLine", "(?i)cmd\\\\.exe\\\\s+/Q\\\\s+/c") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)\\\\\\\\__output\\\\s") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\\\\\__output\\\\s") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)cmd\\\\.exe\\\\s+/C\\\\s+.+\\\\\\\\ADMIN\\\\$\\\\\\\\__\\\\d+\\\\.\\\\d+") || + regexMatch("log.eventDataCommandLine", "(?i)cmd\\\\.exe\\\\s+/C\\\\s+.+\\\\\\\\ADMIN\\\\$\\\\\\\\__\\\\d+\\\\.\\\\d+") + ) +) +', '2026-02-23 16:19:30.226586', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1074, 'Image File Execution Options Debugger Persistence', 3, 3, 2, 'Persistence', 'T1546.012 - Event Triggered Execution: Image File Execution Options Injection', e'Detects abuse of Image File Execution Options (IFEO) registry keys to establish persistence +or redirect program execution. Attackers set the Debugger value under IFEO to hijack +execution of legitimate programs (like accessibility tools sethc.exe, utilman.exe, narrator.exe) +and redirect them to cmd.exe or other malicious payloads. This technique is also used for +persistence through GlobalFlag and SilentProcessExit monitoring. + +Next Steps: +1. Check which program\'s IFEO key was modified and what debugger was set +2. Verify if accessibility tool hijacking was used (sethc, utilman, narrator, magnify, osk) +3. Remove the malicious Debugger registry value +4. Check for RDP access if accessibility tools were hijacked (sticky keys attack) +5. Investigate the user account that made the registry modification +6. Search for additional persistence mechanisms +7. Review recent RDP login activity on the affected host +', '["https://attack.mitre.org/techniques/T1546/012/","https://blog.malwarebytes.com/101/2015/12/an-introduction-to-image-file-execution-options/","https://oddvar.moe/2018/04/10/persistence-using-globalflags-in-image-file-execution-options/"]', e'( + equals("log.eventCode", "13") && + equals("log.providerName", "Microsoft-Windows-Sysmon") && + regexMatch("log.eventDataTargetObject", "(?i)\\\\\\\\Image File Execution Options\\\\\\\\.*\\\\\\\\(Debugger|GlobalFlag|MonitorProcess)$") && + !regexMatch("log.eventDataDetails", "(?i)(werfault|drwtsn32|vsjitdebugger|windbg)") +) || +( + (equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && + ( + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+add.*Image File Execution Options.*Debugger") || + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+add.*Image File Execution Options.*Debugger") + ) +) +', '2026-02-23 16:19:31.386534', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1075, 'GPO Tampering Detection', 2, 3, 2, 'Defense Evasion, Privilege Escalation', 'T1484.001 - Domain Policy Modification: Group Policy Modification', e'Detects modifications to Group Policy Objects (GPOs) which could indicate an adversary attempting to escalate privileges, deploy malware across the domain, or establish persistence. GPO tampering is a common technique used by attackers to enforce malicious configurations or deploy payloads to multiple systems simultaneously. + +**Next Steps:** +1. Immediately review the specific GPO that was modified and identify what changes were made +2. Verify if the modification was authorized by checking with the responsible administrator +3. Examine the user account that made the changes for signs of compromise +4. Review recent authentication logs for the user account to identify potential lateral movement +5. Check domain controllers for additional suspicious activities around the same timeframe +6. If unauthorized, immediately revert the GPO changes and investigate the compromise vector +7. Consider temporarily disabling the affected user account pending investigation +', '["https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing","https://attack.mitre.org/techniques/T1484/001/"]', 'equals("log.eventId", "5136") && equals("log.providerName", "Microsoft-Windows-Security-Auditing") && contains("log.eventDataObjectDN", "CN=Policies,CN=System")', '2026-02-23 16:19:32.544738', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1076, 'Golden Ticket Attack Detection', 3, 3, 3, 'Credential Access', 'T1558.001 - Steal or Forge Kerberos Tickets: Golden Ticket', e'Detects Golden Ticket attacks where adversaries forge Kerberos TGTs using the KRBTGT account +hash, granting unlimited domain access. The rule detects anomalous TGT usage patterns including +TGS requests with unusual encryption types, tickets with abnormally long lifetimes, and Kerberos +authentication from non-domain-controller sources for the KRBTGT service. + +Next Steps: +1. Immediately verify if the KRBTGT account password has been compromised +2. Reset the KRBTGT password TWICE to invalidate all existing tickets +3. Identify the source host and investigate for full domain compromise +4. Review all domain admin activity from the suspected timeframe +5. Check for DCSync or NTDS.dit extraction as precursor activities +6. Audit all privileged account access across the domain +7. Consider rebuilding the domain if compromise is confirmed +8. Implement Kerberos armoring and constrained delegation +', '["https://attack.mitre.org/techniques/T1558/001/","https://adsecurity.org/?p=1640","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4769"]', e'( + equals("log.eventCode", "4769") && + equals("log.channel", "Security") && + equals("log.eventDataServiceName", "krbtgt") && + !equals("log.eventDataStatus", "0x0") && + exists("log.eventDataIpAddress") +) || +( + equals("log.eventCode", "4768") && + equals("log.channel", "Security") && + !oneOf("log.eventDataTicketEncryptionType", ["0x12", "0x11"]) && + exists("target.user") && + !regexMatch("target.user", "(?i)\\\\$$") +) || +( + equals("log.eventCode", "4672") && + equals("log.channel", "Security") && + contains("log.eventDataPrivilegeList", "SeTcbPrivilege") && + !regexMatch("log.eventDataSubjectUserName", "(?i)^(SYSTEM|LOCAL SERVICE|NETWORK SERVICE)$") && + !regexMatch("log.eventDataSubjectUserName", "(?i)\\\\$$") +) +', '2026-02-23 16:19:33.705324', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host","operator":"filter_term","value":"{{origin.host}}"}],"or":null,"within":"now-30m","count":3}]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1086, 'Credential Dump via Registry Export', 3, 2, 1, 'Credential Access', 'T1003.004 - OS Credential Dumping: LSA Secrets', e'Detects credential dumping via registry export of SAM, SYSTEM, and SECURITY hives using +reg.exe save commands. Attackers export these hives to extract password hashes offline using +tools like secretsdump.py or samdump2. This technique is commonly used after gaining local +administrator privileges. + +Next Steps: +1. Identify the user account executing the reg save commands +2. Check if SAM, SYSTEM, and SECURITY hives were all exported (indicates deliberate extraction) +3. Search for the output files on disk and check if they were exfiltrated +4. Review for follow-up pass-the-hash or credential reuse activity +5. Reset all local account passwords on the affected system +6. Investigate how the attacker obtained local administrator privileges +7. Check for domain credential exposure if SECURITY hive was exported +', '["https://attack.mitre.org/techniques/T1003/004/","https://www.sans.org/blog/protecting-privileged-domain-accounts-safeguarding-password-hashes/","https://pentestlab.blog/2018/07/04/dumping-domain-password-hashes/"]', e'(equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && +( + (regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+(save|export)\\\\s+[\\"\']?HKLM\\\\\\\\SAM") || + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+(save|export)\\\\s+[\\"\']?HKLM\\\\\\\\SAM")) || + (regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+(save|export)\\\\s+[\\"\']?HKLM\\\\\\\\SYSTEM") || + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+(save|export)\\\\s+[\\"\']?HKLM\\\\\\\\SYSTEM")) || + (regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+(save|export)\\\\s+[\\"\']?HKLM\\\\\\\\SECURITY") || + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+(save|export)\\\\s+[\\"\']?HKLM\\\\\\\\SECURITY")) || + (regexMatch("log.eventDataCommandLine", "(?i)esentutl.*/y.*/d.*ntds\\\\.dit") || + regexMatch("log.eventDataCommandLine", "(?i)esentutl.*/y.*/d.*ntds\\\\.dit")) +) +', '2026-02-23 16:19:45.775914', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1095, 'AS-REP Roasting Attack Detection', 3, 2, 1, 'Credential Access', 'T1558.004 - Steal or Forge Kerberos Tickets: AS-REP Roasting', e'Detects AS-REP Roasting attacks targeting accounts with Kerberos pre-authentication disabled. +Attackers request AS-REP messages encrypted with RC4 (0x17) for accounts that do not require +pre-authentication, enabling offline password cracking. This is a companion technique to Kerberoasting +and targets a different set of vulnerable accounts. + +Next Steps: +1. Identify accounts with pre-authentication disabled and evaluate business justification +2. Enable Kerberos pre-authentication on all identified accounts +3. Verify the requesting source IP is not a known attack tool +4. Reset passwords for targeted accounts using strong, complex passwords +5. Audit Active Directory for accounts with DONT_REQUIRE_PREAUTH flag +6. Monitor for subsequent credential usage from the requesting IP +', '["https://attack.mitre.org/techniques/T1558/004/","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4768","https://blog.harmj0y.net/activedirectory/roasting-as-reps/"]', e'equals("log.eventCode", "4768") && +equals("log.channel", "Security") && +equals("log.eventDataTicketEncryptionType", "0x17") && +equals("log.eventDataPreAuthType", "0") && +!regexMatch("target.user", "(?i)\\\\$$") && +exists("target.user") +', '2026-02-23 16:19:55.865220', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataIpAddress","operator":"filter_term","value":"{{log.eventDataIpAddress}}"}],"or":null,"within":"now-15m","count":3}]', '["lastEvent.log.eventDataIpAddress","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1096, 'AppInit DLLs Persistence Detection', 3, 3, 2, 'Persistence', 'T1546.010 - Event Triggered Execution: AppInit DLLs', e'Detects modifications to the AppInit_DLLs registry key, which causes specified DLLs to be +loaded into every process that loads User32.dll. Attackers use this technique for persistence +and DLL injection, as the malicious DLL will be loaded into virtually every user-mode process. +On modern Windows with Secure Boot, this requires LoadAppInit_DLLs to also be enabled. + +Next Steps: +1. Identify the DLL path being added to the AppInit_DLLs registry value +2. Analyze the referenced DLL for malicious functionality +3. Check if LoadAppInit_DLLs was also enabled (required on modern Windows) +4. Remove the malicious DLL path from the registry value +5. Delete the malicious DLL file from disk +6. Reboot the system to stop the DLL from being loaded into new processes +7. Investigate the initial compromise that led to this persistence +', '["https://attack.mitre.org/techniques/T1546/010/","https://docs.microsoft.com/en-us/windows/win32/dlls/secure-boot-and-appinit-dlls","https://pentestlab.blog/2020/01/07/persistence-appinit-dlls/"]', e'( + equals("log.eventCode", "13") && + equals("log.providerName", "Microsoft-Windows-Sysmon") && + regexMatch("log.eventDataTargetObject", "(?i)\\\\\\\\Windows\\\\\\\\CurrentVersion\\\\\\\\Windows\\\\\\\\(AppInit_DLLs|LoadAppInit_DLLs)$") && + exists("log.eventDataDetails") +) || +( + (equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && + ( + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+add.*AppInit_DLLs") || + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+add.*AppInit_DLLs") + ) +) +', '2026-02-23 16:19:56.893551', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1081, 'DLL Sideloading Detection', 2, 3, 1, 'Defense Evasion', 'T1574.002 - Hijack Execution Flow: DLL Side-Loading', e'Detects DLL sideloading patterns where known vulnerable legitimate applications load malicious +DLLs from non-system paths. Attackers place a malicious DLL alongside a vulnerable application +that loads DLLs from its own directory rather than the system directory. This technique +leverages trusted application signatures to execute malicious code and evade security controls. + +Next Steps: +1. Verify the executable path - legitimate apps should be in Program Files, not user directories +2. Check the DLL loaded alongside the executable for unexpected modifications +3. Compare DLL hashes against known good versions +4. Examine the parent process to understand how the vulnerable app was launched +5. Review file creation timestamps for the executable and DLL pair +6. Analyze the suspicious DLL in a sandbox +7. Search for similar sideloading patterns across other endpoints +', '["https://attack.mitre.org/techniques/T1574/002/","https://www.mandiant.com/resources/blog/dll-side-loading-a-thorn-in-the-side-of-the-anti-virus-industry","https://hijacklibs.net/"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + ( + regexMatch("log.eventDataNewProcessName", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Desktop\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\)") && + regexMatch("log.eventDataNewProcessName", "(?i)(OneDriveStandaloneUpdater|colorcpl|consent|dxcap|eudcedit|eventvwr|isoburn|msconfig|msdt|mstsc|narrator|netplwiz|odbcad32|presentationhost|rstrui|sdclt|sethc|sigverif|utilman|write)\\\\.exe$") + ) || + ( + regexMatch("log.eventDataProcessName", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Desktop\\\\\\\\)") && + regexMatch("log.eventDataProcessName", "(?i)(OneDriveStandaloneUpdater|colorcpl|consent|dxcap|eudcedit|eventvwr|isoburn|msconfig|msdt|mstsc|narrator|netplwiz|odbcad32|presentationhost|rstrui|sdclt|sethc|sigverif|utilman|write)\\\\.exe$") + ) || + ( + regexMatch("log.eventDataNewProcessName", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Desktop\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\)") && + regexMatch("log.eventDataNewProcessName", "(?i)(WerFault|SearchProtocolHost|SearchFilterHost|WmiPrvSE|backgroundTaskHost|RuntimeBroker|smartscreen|tabcal|winsat)\\\\.exe$") + ) +) +', '2026-02-23 16:19:39.810138', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1082, 'DCSync Attack Detection', 3, 3, 1, 'Credential Access', 'T1003.006 - OS Credential Dumping: DCSync', e'Detects DCSync attacks where attackers use directory replication services to retrieve password hashes from domain controllers. +This technique exploits legitimate Active Directory replication functionality to extract credentials without directly accessing the domain controller\'s files. +The rule monitors for specific replication GUIDs associated with credential access operations in Windows Event ID 4662. + +Next Steps: +- Immediately verify the legitimacy of the user account performing the replication operation +- Check if the source host is an authorized domain controller or backup system +- Review recent privilege escalation activities for the identified user account +- Examine network traffic for additional signs of credential harvesting +- Consider resetting passwords for high-privilege accounts if compromise is confirmed +- Review domain controller access logs for unauthorized administrative activities +', '["https://attack.mitre.org/techniques/T1003/006/","https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4662","https://www.elastic.co/guide/en/security/current/potential-credential-access-via-dcsync.html"]', e'equals("log.eventCode", "4662") && +equals("log.channel", "Security") && +equals("log.eventDataObjectServer", "DS") && +( + contains("log.eventDataProperties", "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2") || + contains("log.eventDataProperties", "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2") || + contains("log.eventDataProperties", "89e95b76-444d-4c62-991a-0facbeda640c") || + contains("log.eventDataProperties", "19195a5b-6da0-11d0-afd3-00c04fd930c9") +) && +!regexMatch("log.eventDataSubjectUserName", ".*\\\\$$") && +!contains("origin.host", "DC") +', '2026-02-23 16:19:40.983773', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataSubjectUserName","operator":"filter_term","value":"{{log.eventDataSubjectUserName}}"}],"or":null,"within":"now-1h","count":1}]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1083, 'DCShadow Attack Detection', 3, 3, 2, 'Defense Evasion', 'T1207 - Rogue Domain Controller', e'Detects DCShadow attacks where attackers register a rogue domain controller to push malicious Active Directory changes. This technique allows adversaries to modify Active Directory objects by registering a rogue domain controller and triggering replication, effectively bypassing security controls and detection mechanisms. + +The rule monitors for: +- Computer account modifications with domain controller service principal names +- Access to sensitive Active Directory objects and properties +- Creation of server objects in the domain controller configuration + +Next Steps: +1. Immediately investigate the source host and user account involved in the activity +2. Check if the host is an authorized domain controller in your environment +3. Review recent Active Directory changes and replication logs +4. Examine authentication logs for the affected user account +5. Verify the legitimacy of any recent domain controller promotions +6. Check for signs of compromise on the source system +7. Consider isolating the affected host if unauthorized activity is confirmed +8. Review domain controller security policies and access controls +', '["https://attack.mitre.org/techniques/T1207/","https://www.dcshadow.com/","https://blog.alsid.eu/dcshadow-explained-4510f52fc19d"]', e'( + (equals("log.eventCode", "4742") && + equals("log.channel", "Security") && + contains("log.eventDataServicePrincipalNames", "GC/") && + contains("log.eventDataUserAccountControl", "SERVER_TRUST_ACCOUNT")) || + (equals("log.eventCode", "4662") && + equals("log.channel", "Security") && + equals("log.eventDataObjectType", "{bf967a92-0de6-11d0-a285-00aa003049e2}") && + contains("log.eventDataProperties", "1131f6ac-9c07-11d1-f79f-00c04fc2dcd2")) || + (equals("log.eventCode", "5137") && + equals("log.channel", "Security") && + equals("log.eventDataObjectClass", "server") && + contains("log.eventDataObjectDN", "CN=Servers,CN=")) +) && +!contains("origin.host", "DC") +', '2026-02-23 16:19:42.186234', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host","operator":"filter_term","value":"{{origin.host}}"}],"or":null,"within":"now-2h","count":2}]', '["lastEvent.log.eventDataSubjectUserName","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1084, 'DCOM Lateral Movement Detection', 3, 3, 2, 'Lateral Movement', 'T1021.003 - Remote Services: Distributed Component Object Model', e'Detects potential DCOM lateral movement attempts by monitoring for suspicious process creation with DCOM-related command line parameters. Looks for processes with automation embedding flags and specific DCOM object CLSIDs commonly abused for lateral movement such as ShellWindows and MMC20.Application. + +Next Steps: +1. Investigate the source host and user account that initiated the DCOM activity +2. Review network connections between the source and target systems around the time of the alert +3. Check for additional lateral movement indicators on both source and target systems +4. Examine the specific DCOM objects and CLSIDs being accessed for known malicious usage +5. Verify if the DCOM activity aligns with legitimate business processes or scheduled tasks +6. Look for privilege escalation attempts following the lateral movement +7. Check for any data exfiltration or persistence mechanisms deployed post-compromise +', '["https://medium.com/@cY83rR0H1t/detecting-dcom-lateral-movement-ee2b461a8705","https://attack.mitre.org/techniques/T1021/003/"]', 'equals("log.eventCode", "4688") && exists("log.eventDataProcessCommandLine") && (contains("log.eventDataProcessCommandLine", "/automation -Embedding") || contains("log.eventDataProcessCommandLine", "9BA05972-F6A8-11CF-A442-00A0C90A8F39") || contains("log.eventDataProcessCommandLine", "c08afd90-f2a1-11d1-8455-00a0c91f3880") || contains("log.eventDataProcessCommandLine", "MMC20.Application") || contains("log.eventDataProcessCommandLine", "Document.Application.ShellExecute") || contains("log.eventDataProcessCommandLine", "GetTypeFromCLSID") || contains("log.eventDataProcessCommandLine", "GetTypeFromProgID"))', '2026-02-23 16:19:43.365910', true, true, 'origin', null, '[]', '["lastEvent.log.eventDataProcessName","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1085, 'Credential Manager Access Detection', 3, 2, 1, 'Credential Access', 'T1555.004 - Credentials from Password Stores: Windows Credential Manager', e'Detects access to Windows Credential Manager using vaultcmd.exe, cmdkey.exe, or credential +enumeration via PowerShell. Attackers use these tools to extract stored credentials including +web passwords, Windows credentials, and RDP saved logins. This is a common post-exploitation +technique for credential harvesting. + +Next Steps: +1. Identify the user account and process executing credential manager commands +2. Verify if this is authorized administrative activity or penetration testing +3. Review what credentials are stored in the affected user\'s vault +4. Check for follow-up lateral movement using extracted credentials +5. Reset any credentials that may have been exposed +6. Review if remote desktop saved credentials were compromised +7. Investigate the initial access vector that led to credential access +', '["https://attack.mitre.org/techniques/T1555/004/","https://www.passcape.com/index.php?section=docsys&cmd=details&id=28","https://blog.malwarebytes.com/threat-analysis/2020/12/credential-stealing/"]', e'(equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && +( + (regexMatch("log.eventDataCommandLine", "(?i)vaultcmd(.exe)?\\\\s+/(list|listcreds|listproperties)") || + regexMatch("log.eventDataCommandLine", "(?i)vaultcmd(.exe)?\\\\s+/(list|listcreds|listproperties)")) || + (regexMatch("log.eventDataCommandLine", "(?i)cmdkey(.exe)?\\\\s+/list") || + regexMatch("log.eventDataCommandLine", "(?i)cmdkey(.exe)?\\\\s+/list")) || + (regexMatch("log.eventDataCommandLine", "(?i)(CredentialManager|Windows\\\\s+Vault|VaultSvc)") || + regexMatch("log.eventDataCommandLine", "(?i)(CredentialManager|Windows\\\\s+Vault|VaultSvc)")) || + (regexMatch("log.eventDataCommandLine", "(?i)Get-VaultCredential|Get-CachedGPPPassword") || + regexMatch("log.eventDataCommandLine", "(?i)Get-VaultCredential|Get-CachedGPPPassword")) +) +', '2026-02-23 16:19:44.557510', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1087, 'COM Hijacking Persistence Detection', 2, 3, 1, 'Persistence', 'T1546.015 - Event Triggered Execution: Component Object Model Hijacking', e'Detects COM (Component Object Model) hijacking used for persistence by modifying InProcServer32 +or LocalServer32 registry values. Attackers replace legitimate COM object DLLs with malicious ones +to achieve persistence, as the malicious DLL will be loaded whenever the COM object is instantiated. +This is a stealthy persistence mechanism that is difficult to detect without registry monitoring. + +Next Steps: +1. Examine the modified InProcServer32/LocalServer32 value to identify the malicious DLL +2. Verify if the new DLL path points to a legitimate or suspicious location +3. Check the original COM object registration for comparison +4. Analyze the replacement DLL in a sandbox +5. Identify the CLSID being hijacked and what software uses it +6. Restore the original COM registration +7. Search for similar COM hijacking across other endpoints +', '["https://attack.mitre.org/techniques/T1546/015/","https://pentestlab.blog/2020/05/20/persistence-com-hijacking/","https://bohops.com/2018/08/18/abusing-the-com-registry-structure-clsid-localserver32-inprocserver32/"]', e'equals("log.eventCode", "4657") && +equals("log.channel", "Security") && +regexMatch("log.eventDataObjectName", "(?i)(InProcServer32|LocalServer32)") && +( + regexMatch("log.eventDataNewValue", "(?i)(\\\\\\\\Temp\\\\\\\\|\\\\\\\\Downloads\\\\\\\\|\\\\\\\\AppData\\\\\\\\|\\\\\\\\ProgramData\\\\\\\\|\\\\\\\\Users\\\\\\\\Public\\\\\\\\|\\\\\\\\Desktop\\\\\\\\)") || + regexMatch("log.eventDataNewValue", "(?i)(rundll32|regsvr32|mshta|powershell|cmd\\\\.exe|wscript|cscript)") || + regexMatch("log.eventDataNewValue", "(?i)scrobj\\\\.dll") || + regexMatch("log.eventDataNewValue", "(?i)(http://|https://)") || + !regexMatch("log.eventDataNewValue", "(?i)^(C:\\\\\\\\Windows\\\\\\\\|C:\\\\\\\\Program Files)") +) && +!regexMatch("log.eventDataSubjectUserName", "(?i)^(SYSTEM|TrustedInstaller)$") +', '2026-02-23 16:19:46.863971', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1088, 'Cobalt Strike Process Behavior Detection', 3, 3, 2, 'Execution', 'T1059 - Command and Scripting Interpreter', e'Detects Cobalt Strike beacon process creation patterns that are distinct from normal system behavior. +These include rundll32.exe spawned without command-line arguments, dllhost.exe or runonce.exe spawning +cmd.exe or powershell, and characteristic post-exploitation process injection patterns. Cobalt Strike +is the most widely used commercial adversary simulation tool and is frequently found in real attacks. + +Next Steps: +1. Examine the parent-child process relationships for injection patterns +2. Check for rundll32 with no arguments (classic beacon default) +3. Review named pipe activity on the host for CS pipe names +4. Check for network beaconing behavior from the suspicious process +5. Memory scan the suspicious processes for CS beacon shellcode +6. Isolate the affected host immediately +7. Hunt for lateral movement from this host to other systems +8. Review the C2 infrastructure and block at network perimeter +', '["https://attack.mitre.org/software/S0154/","https://thedfirreport.com/2021/08/29/cobalt-strike-a-defenders-guide/","https://redcanary.com/threat-detection-report/threats/cobalt-strike/"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + ( + regexMatch("log.eventDataNewProcessName", "(?i)rundll32\\\\.exe$") && + ( + !exists("log.eventDataCommandLine") || + regexMatch("log.eventDataCommandLine", "(?i)^\\"?[A-Z]:\\\\\\\\Windows\\\\\\\\(System32|SysWOW64)\\\\\\\\rundll32\\\\.exe\\"?\\\\s*$") + ) + ) || + ( + regexMatch("log.eventDataParentProcessName", "(?i)(dllhost\\\\.exe|runonce\\\\.exe|searchprotocolhost\\\\.exe)$") && + regexMatch("log.eventDataNewProcessName", "(?i)(cmd\\\\.exe|powershell\\\\.exe|pwsh\\\\.exe)$") + ) || + ( + regexMatch("log.eventDataParentProcessName", "(?i)rundll32\\\\.exe$") && + regexMatch("log.eventDataNewProcessName", "(?i)(cmd\\\\.exe|powershell\\\\.exe|pwsh\\\\.exe)$") && + !exists("log.eventDataParentCommandLine") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)(MSSE-\\\\d+-server|status_\\\\d+|postex_\\\\d+|msagent_\\\\d+)") || + regexMatch("log.eventDataCommandLine", "(?i)(MSSE-\\\\d+-server|status_\\\\d+|postex_\\\\d+|msagent_\\\\d+)") + ) || + ( + regexMatch("log.eventDataCommandLine", "(?i)\\\\\\\\\\\\\\\\\\\\.\\\\\\\\.pipe\\\\\\\\(MSSE-|postex_|status_|msagent_)") || + regexMatch("log.eventDataCommandLine", "(?i)\\\\\\\\\\\\\\\\\\\\.\\\\\\\\.pipe\\\\\\\\(MSSE-|postex_|status_|msagent_)") + ) +) +', '2026-02-23 16:19:47.995694', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1089, 'CMSTP UAC Bypass Detection', 2, 3, 1, 'Defense Evasion', 'T1218.003 - System Binary Proxy Execution: CMSTP', e'Detects CMSTP.exe (Microsoft Connection Manager Profile Installer) being used to bypass UAC +and AppLocker restrictions. Attackers use CMSTP with specially crafted .inf files containing +malicious commands in the RunPreSetupCommandsSection to execute arbitrary code with elevated +privileges. This is a well-known UAC bypass technique. + +Next Steps: +1. Examine the .inf file referenced in the command line for malicious content +2. Check the RunPreSetupCommandsSection of the INF file for commands +3. Identify the parent process and delivery mechanism +4. Review if UAC was successfully bypassed +5. Check for post-exploitation activity with elevated privileges +6. Remove the malicious INF file and any created artifacts +7. Search for similar CMSTP abuse across other endpoints +', '["https://attack.mitre.org/techniques/T1218/003/","https://lolbas-project.github.io/lolbas/Binaries/Cmstp/","https://msitpros.com/?p=3960"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + regexMatch("log.eventDataNewProcessName", "(?i)cmstp\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)cmstp\\\\.exe$") +) && +( + regexMatch("log.eventDataCommandLine", "(?i)/s\\\\s+.*\\\\.inf") || + regexMatch("log.eventDataCommandLine", "(?i)/ni\\\\s+/s\\\\s+") || + regexMatch("log.eventDataCommandLine", "(?i)/au\\\\s+") || + regexMatch("log.eventDataCommandLine", "(?i)/s\\\\s+.*\\\\.inf") || + regexMatch("log.eventDataCommandLine", "(?i)/ni\\\\s+/s\\\\s+") || + regexMatch("log.eventDataCommandLine", "(?i)/au\\\\s+") +) +', '2026-02-23 16:19:49.168845', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1090, 'Certutil LOLBIN Abuse Detection', 2, 3, 1, 'Defense Evasion', 'T1105 - Ingress Tool Transfer', e'Detects abuse of certutil.exe as a Living Off The Land Binary (LOLBIN) for downloading files from URLs, +encoding/decoding Base64 payloads, and NTLM coercion. Certutil is a legitimate Windows certificate +utility that is frequently abused by attackers for payload staging and defense evasion because it is +a signed Microsoft binary that bypasses application whitelisting. + +Next Steps: +1. Examine the full command line to identify downloaded URLs or encoded payloads +2. Check the destination file path for downloaded or decoded files +3. Analyze any downloaded files in a sandbox environment +4. Review the parent process to understand how certutil was invoked +5. Check for subsequent execution of downloaded payloads +6. Block the identified download URLs at the proxy/firewall level +7. Search for similar certutil abuse across other endpoints +', '["https://attack.mitre.org/techniques/T1105/","https://attack.mitre.org/techniques/T1140/","https://lolbas-project.github.io/lolbas/Binaries/Certutil/"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + regexMatch("log.eventDataNewProcessName", "(?i)certutil\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)certutil\\\\.exe$") +) && +( + regexMatch("log.eventDataCommandLine", "(?i)(-urlcache|-URL)") || + regexMatch("log.eventDataCommandLine", "(?i)(-encode|-decode)") || + regexMatch("log.eventDataCommandLine", "(?i)(-ping|-generateSSTFromWU)") || + regexMatch("log.eventDataCommandLine", "(?i)(http://|https://|ftp://)") || + regexMatch("log.eventDataCommandLine", "(?i)-verifyctl") || + regexMatch("log.eventDataCommandLine", "(?i)(-urlcache|-URL)") || + regexMatch("log.eventDataCommandLine", "(?i)(-encode|-decode)") || + regexMatch("log.eventDataCommandLine", "(?i)(http://|https://|ftp://)") +) +', '2026-02-23 16:19:50.309708', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1091, 'Certificate Services Abuse Detection', 3, 3, 1, 'Credential Access', 'T1558 - Steal or Forge Kerberos Tickets', e'Detects suspicious certificate requests and issuance that could indicate Golden Certificate attacks or unauthorized certificate generation for persistence. This rule monitors Windows Certificate Services events for potentially malicious certificate operations, particularly those involving machine accounts or anonymous logons that could be leveraged for persistence and privilege escalation. + +Next Steps: +1. Investigate the certificate request details including the requesting user/machine +2. Verify if the certificate request was legitimate and authorized +3. Check for any recent changes to Certificate Authority policies or templates +4. Review Certificate Authority logs for other suspicious certificate issuance +5. Examine the requesting host for signs of compromise +6. Consider revoking any suspicious certificates issued +7. Validate Certificate Authority security configurations and access controls +', '["https://www.splunk.com/en_us/blog/security/breaking-the-chain-defending-against-certificate-services-abuse.html","https://attack.mitre.org/techniques/T1558/"]', '(equals("log.eventId", "4886") || equals("log.eventId", "4887")) && equals("log.providerName", "Microsoft-Windows-Security-Auditing") && (contains("log.eventDataSubjectUserName", "$") || equals("log.eventDataSubjectUserName", "ANONYMOUS LOGON"))', '2026-02-23 16:19:51.501192', true, true, 'origin', null, '[]', '["lastEvent.log.eventDataSubjectUserName","adversary.host"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1092, 'Boot and Logon Script Persistence Detection', 2, 3, 2, 'Persistence', 'T1037.001 - Boot or Logon Initialization Scripts: Logon Script (Windows)', e'Detects modifications to Windows logon script registry keys including UserInitMprLogonScript, +Userinit, and Shell values. Attackers modify these keys to execute malicious scripts or +binaries during user logon, providing persistent access that survives reboots. These keys +are commonly abused because they execute with the logged-on user\'s privileges. + +Next Steps: +1. Examine the registry value data to identify the script or binary being persisted +2. Analyze the referenced script or executable for malicious content +3. Verify the parent process that modified the registry key +4. Restore the original Userinit or Shell values +5. Remove any malicious scripts from the referenced paths +6. Search for additional persistence mechanisms on the same host +7. Investigate the initial compromise vector +', '["https://attack.mitre.org/techniques/T1037/001/","https://www.cybereason.com/blog/persistence-techniques-that-persist","https://pentestlab.blog/2020/01/14/persistence-logon-scripts/"]', e'( + equals("log.eventCode", "13") && + equals("log.providerName", "Microsoft-Windows-Sysmon") && + ( + regexMatch("log.eventDataTargetObject", "(?i)\\\\\\\\Windows\\\\\\\\CurrentVersion\\\\\\\\(on\\\\\\\\(Userinit|Shell)|Policies\\\\\\\\Explorer\\\\\\\\Run)") || + regexMatch("log.eventDataTargetObject", "(?i)UserInitMprLogonScript$") || + regexMatch("log.eventDataTargetObject", "(?i)\\\\\\\\Environment\\\\\\\\UserInitMprLogonScript$") + ) && + !regexMatch("log.eventDataDetails", "(?i)^C:\\\\\\\\Windows\\\\\\\\system32\\\\\\\\userinit\\\\.exe,?\\\\s*$") && + !regexMatch("log.eventDataDetails", "(?i)^explorer\\\\.exe\\\\s*$") +) || +( + (equals("log.eventCode", "4688") || equals("log.eventCode", "1")) && + ( + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+add.*on.*(Userinit|Shell)") || + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+add.*on.*(Userinit|Shell)") || + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+add.*UserInitMprLogonScript") || + regexMatch("log.eventDataCommandLine", "(?i)reg(.exe)?\\\\s+add.*UserInitMprLogonScript") + ) +) +', '2026-02-23 16:19:52.609140', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1093, 'BloodHound Reconnaissance Activity', 3, 2, 1, 'Discovery', 'T1087 - Account Discovery', e'Detects potential BloodHound Active Directory reconnaissance tool usage through LDAP queries, characteristic patterns, and AD enumeration activities. BloodHound is commonly used by attackers to map Active Directory relationships and identify privilege escalation paths. + +Next Steps: +1. Investigate the source host and user account involved in the activity +2. Review network logs for LDAP queries to domain controllers around the same timeframe +3. Check for other reconnaissance tools or suspicious PowerShell activity on the same host +4. Examine Active Directory audit logs for unusual object access patterns +5. Verify if the user account has legitimate reasons for AD enumeration activities +6. Look for signs of lateral movement or privilege escalation following this reconnaissance +7. Consider isolating the affected host if malicious activity is confirmed +', '["https://attack.mitre.org/techniques/T1087/","https://bloodhound.readthedocs.io/","https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662"]', e'( + (equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && + ( + regexMatch("log.eventDataNewProcessName", "(?i)bloodhound") || + regexMatch("log.eventDataNewProcessName", "(?i)sharphound") || + regexMatch("log.eventDataCommandLine", "(?i)(bloodhound|sharphound)") || + regexMatch("log.eventDataCommandLine", "(?i)--CollectionMethod\\\\s+(All|Session|LoggedOn)") || + regexMatch("log.eventDataCommandLine", "(?i)(DCOnly|ComputerOnly|LocalGroup)") + ) +) || +( + (equals("log.eventCode", "4104") || equals("log.eventId", 4104)) && + equals("log.providerName", "Microsoft-Windows-PowerShell") && + ( + regexMatch("log.eventDataScriptBlockText", "(?i)invoke-bloodhound") || + contains("log.eventDataScriptBlockText", "Get-BloodHoundData") || + contains("log.eventDataScriptBlockText", "Get-NetSession") || + contains("log.eventDataScriptBlockText", "Get-NetLoggedOn") || + contains("log.eventDataScriptBlockText", "Get-DomainTrust") + ) +) || +( + equals("log.eventCode", "4662") && + regexMatch("log.eventDataObjectType", "(?i)(bf967aba-0de6-11d0-a285-00aa003049e2|bf967a9c-0de6-11d0-a285-00aa003049e2)") && + oneOf("log.eventDataAccessMask", ["0x100", "0x10000"]) +) || +( + equals("log.eventCode", "5156") && + equals("log.eventDataDestinationPort", "389") && + equals("log.eventDataDirection", "%%14592") +) +', '2026-02-23 16:19:53.756404', true, true, 'origin', '["adversary.host","lastEvent.log.eventDataSubjectUserName"]', '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host","operator":"filter_term","value":"{{origin.host}}"}],"or":null,"within":"now-2h","count":10}]', null); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1094, 'BITSAdmin Abuse Detection', 2, 3, 1, 'Defense Evasion', 'T1197 - BITS Jobs', e'Detects abuse of bitsadmin.exe for downloading files from remote URLs, creating persistent BITS +jobs, and transferring payloads. BITS (Background Intelligent Transfer Service) is a Windows +service commonly abused by attackers for both download and persistence because BITS jobs survive +reboots and can be configured to execute commands upon completion. + +Next Steps: +1. Examine the download URL and destination path in the command line +2. Review the BITS job for any notification command (persistence mechanism) +3. Check the downloaded file for malicious content +4. List all BITS jobs on the system using \'bitsadmin /list /allusers /verbose\' +5. Remove suspicious BITS jobs and quarantine downloaded files +6. Block the download URL at the proxy/firewall level +7. Search for similar BITS abuse across other endpoints +', '["https://attack.mitre.org/techniques/T1197/","https://lolbas-project.github.io/lolbas/Binaries/Bitsadmin/","https://isc.sans.edu/diary/Investigating+Microsoft+BITS+Activity/23281"]', e'(equals("log.eventCode", "4688") || equals("log.eventId", 4688)) && +( + regexMatch("log.eventDataNewProcessName", "(?i)bitsadmin\\\\.exe$") || + regexMatch("log.eventDataProcessName", "(?i)bitsadmin\\\\.exe$") +) && +( + regexMatch("log.eventDataCommandLine", "(?i)(/transfer|/addfile|/resume|/create|/setnotifycmdline|/setnotifyflags)") || + regexMatch("log.eventDataCommandLine", "(?i)(http://|https://|ftp://)") || + regexMatch("log.eventDataCommandLine", "(?i)/SetMinRetryDelay") || + regexMatch("log.eventDataCommandLine", "(?i)(/transfer|/addfile|/resume|/create|/setnotifycmdline)") || + regexMatch("log.eventDataCommandLine", "(?i)(http://|https://|ftp://)") +) +', '2026-02-23 16:19:54.877359', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1097, 'AMSI Bypass Detection', 3, 3, 2, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', e'Detects attempts to bypass the Antimalware Scan Interface (AMSI) through PowerShell commands, DLL hijacking, or memory patching techniques. AMSI bypass is commonly used by attackers to evade detection when executing malicious PowerShell scripts or other code. + +Next Steps: +1. Immediately isolate the affected host to prevent lateral movement +2. Examine the full PowerShell script block text in Event ID 4104 for malicious content +3. Review command line arguments in Event ID 4688 for suspicious PowerShell execution +4. Check for additional indicators of compromise on the host +5. Verify if legitimate administrative tools are being used or if this is malicious activity +6. Review recent file modifications and process execution history +7. Check for persistence mechanisms that may have been installed +8. Consider reimaging the system if compromise is confirmed +', '["https://attack.mitre.org/techniques/T1562/001/","https://docs.microsoft.com/en-us/windows/win32/amsi/antimalware-scan-interface-portal"]', e'(equals("log.eventId", "4104") && + ((contains("log.eventDataScriptBlockText", "[Ref].Assembly.GetType") && + contains("log.eventDataScriptBlockText", "amsi") && + contains("log.eventDataScriptBlockText", "SetValue")) || + contains("log.eventDataScriptBlockText", "AmsiUtils") || + contains("log.eventDataScriptBlockText", "amsiInitFailed") || + contains("log.eventDataScriptBlockText", "Bypass.AMSI") || + contains("log.eventDataScriptBlockText", "AmsiScanBuffer"))) || +(equals("log.eventId", "4688") && + contains("log.eventDataCommandLine", "powershell") && + (contains("log.eventDataCommandLine", "amsi.dll") || + contains("log.eventDataCommandLine", "AmsiScanBuffer") || + contains("log.eventDataCommandLine", "amsiInitFailed"))) +', '2026-02-23 16:19:57.999926', true, true, 'origin', null, '[]', '["adversary.host","adversary.user"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1098, 'AdminSDHolder Abuse Detection', 3, 3, 2, 'Persistence, Privilege Escalation', 'T1098 - Account Manipulation', e'Detects modifications to the AdminSDHolder object which can be used for persistence by granting elevated privileges. The SDProp process propagates these permissions to protected groups every 60 minutes, making this a critical security event. + +Next Steps: +1. Immediately review the user account that performed the modification +2. Check if the modification was authorized and part of legitimate administrative activities +3. Examine the specific permissions that were changed on the AdminSDHolder object +4. Monitor for privilege escalation activities in the next 60 minutes (SDProp cycle) +5. Review all members of protected groups for unauthorized additions +6. Audit recent administrative activities by the same user account +7. Consider temporarily disabling the user account if unauthorized activity is suspected +', '["https://attack.mitre.org/techniques/T1098/","https://adsecurity.org/?p=1906","https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/appendix-c--protected-accounts-and-groups-in-active-directory"]', e'oneOf("log.eventCode", ["4662", "5136", "4670"]) && +equals("log.channel", "Security") && +( + contains("log.eventDataObjectName", "CN=AdminSDHolder,CN=System") || + contains("log.eventDataObjectDN", "CN=AdminSDHolder,CN=System") +) && +( + oneOf("log.eventDataOperationType", ["Object Access", "Write Property"]) || + oneOf("log.eventDataAccessMask", ["0x20000", "0x40000", "0x80000"]) || + regexMatch("log.action", ".*Permissions.*changed.*") +) && +!equals("log.eventDataSubjectUserName", "SYSTEM") +', '2026-02-23 16:19:59.099681', true, true, 'origin', null, '[]', '["lastEvent.log.eventDataObjectName","lastEvent.log.eventDataSubjectUserName"]'); +INSERT INTO public.utm_correlation_rules (id, rule_name, rule_confidentiality, rule_integrity, rule_availability, rule_category, rule_technique, rule_description, rule_references_def, rule_definition_def, rule_last_update, rule_active, system_owner, rule_adversary, rule_deduplicate_by_def, rule_after_events_def, rule_group_by_def) VALUES (1099, 'ADFS Authentication Anomalies', 3, 2, 2, 'Defense Evasion, Persistence, Privilege Escalation, Initial Access', 'T1078 - Valid Accounts', e'Detects anomalous authentication attempts against Active Directory Federation Services (ADFS) including multiple failed attempts that could indicate password spraying or brute force attacks. This rule monitors for authentication failures, token validation failures, and other ADFS security events that may indicate malicious activity. + +Next Steps: +1. Review the source IP address and determine if it\'s from a known/trusted location +2. Check for patterns of failed authentication attempts across multiple users +3. Examine ADFS audit logs for additional context around the authentication failures +4. Verify if the targeted user accounts are valid and active +5. Consider implementing IP-based blocking if malicious activity is confirmed +6. Review ADFS configuration for security hardening opportunities +7. Correlate with other authentication events across the domain +', '["https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/troubleshooting/ad-fs-tshoot-logging","https://attack.mitre.org/techniques/T1078/"]', 'equals("log.providerName", "AD FS") && (equals("log.eventId", "411") || equals("log.eventId", "342") || equals("log.eventId", "516")) && contains("log.message", "token validation failed")', '2026-02-23 16:20:00.102814', true, true, 'origin', null, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataIpAddress","operator":"filter_term","value":"{{log.eventDataIpAddress}}"}],"or":null,"within":"now-10m","count":10}]', '["adversary.ip","target.user"]'); diff --git a/backend/src/main/resources/config/liquibase/data/20260223/utm_group_rules_data_type.sql b/backend/src/main/resources/config/liquibase/data/20260223/utm_group_rules_data_type.sql new file mode 100644 index 000000000..2923b3541 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/data/20260223/utm_group_rules_data_type.sql @@ -0,0 +1,221 @@ +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1015, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1016, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1017, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1018, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1048, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1049, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1050, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1051, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1052, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1053, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1054, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1055, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1056, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1057, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1058, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1059, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1060, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1061, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1062, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1063, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1064, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1065, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1077, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1078, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1079, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1080, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1081, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (879, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (880, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (881, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (882, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (883, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (884, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (885, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (886, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (887, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (888, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (889, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (890, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (891, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (892, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (893, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (894, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (895, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (896, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (897, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (898, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (899, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (900, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (901, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (902, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (903, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (979, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (980, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (981, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (982, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (983, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (984, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (985, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (986, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (987, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (988, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (989, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (990, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (991, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (992, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (993, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (994, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (995, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (996, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (997, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (998, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (999, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1000, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1001, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1002, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1003, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1004, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1005, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1006, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1007, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1008, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1009, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1010, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1011, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1012, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1013, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1014, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1019, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1020, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1021, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1022, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1023, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1024, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1066, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1067, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1068, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1069, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1070, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1071, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1072, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1073, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1074, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1075, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1076, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1082, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1083, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1084, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1085, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1086, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1087, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1088, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1089, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1090, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1091, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1092, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1093, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1094, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1095, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1096, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1097, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1098, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1099, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (904, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (905, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (906, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (907, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (908, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (909, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (910, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (911, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (912, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (913, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (914, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (915, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (916, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (917, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (918, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (919, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (920, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (921, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (922, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (923, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (924, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (925, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (926, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (927, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (928, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (929, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (930, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (931, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (932, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (933, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (934, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (935, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (936, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (937, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (938, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (939, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (940, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (941, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (942, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (943, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (944, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (945, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (946, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (947, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (948, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (949, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (950, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (951, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (952, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (953, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (954, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (955, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (956, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (957, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (958, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (959, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (960, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (961, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (962, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (963, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (964, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (965, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (966, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (967, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (968, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (969, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (970, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (971, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (972, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (973, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (974, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (975, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (976, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (977, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (978, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1025, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1026, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1027, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1028, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1029, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1030, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1031, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1032, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1033, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1034, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1035, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1036, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1037, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1038, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1039, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1040, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1041, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1042, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1043, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1044, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1045, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1046, 1, null); +INSERT INTO public.utm_group_rules_data_type (rule_id, data_type_id, last_update) VALUES (1047, 1, null); From 3a660fd210ee6d32ab965af866e01f39ae450a7d Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 23 Feb 2026 10:35:38 -0600 Subject: [PATCH 051/115] feat(winevent): add updates for winevent correlation rules and filter --- .../20260223001_update_filter_winevent.xml | 2995 +++++++++++++++++ .../resources/config/liquibase/master.xml | 2 + 2 files changed, 2997 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260223001_update_filter_winevent.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260223001_update_filter_winevent.xml b/backend/src/main/resources/config/liquibase/changelog/20260223001_update_filter_winevent.xml new file mode 100644 index 000000000..14d99dc73 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260223001_update_filter_winevent.xml @@ -0,0 +1,2995 @@ + + + + + + + + + + \ 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 b9fbb258a..7a609ec65 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -469,6 +469,8 @@ + + From 58314204d77d6b42c2851d6972352381b320713f Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 23 Feb 2026 11:20:41 -0600 Subject: [PATCH 052/115] feat(winevent): update filter version and rename log fields for improved clarity --- .../20260223001_update_filter_winevent.xml | 505 ++++++++---------- 1 file changed, 222 insertions(+), 283 deletions(-) diff --git a/backend/src/main/resources/config/liquibase/changelog/20260223001_update_filter_winevent.xml b/backend/src/main/resources/config/liquibase/changelog/20260223001_update_filter_winevent.xml index 14d99dc73..3fbc5da66 100644 --- a/backend/src/main/resources/config/liquibase/changelog/20260223001_update_filter_winevent.xml +++ b/backend/src/main/resources/config/liquibase/changelog/20260223001_update_filter_winevent.xml @@ -10,9 +10,9 @@ Date: Mon, 23 Feb 2026 13:22:01 -0600 Subject: [PATCH 053/115] feat(visualizations): update Windows visualizations to align with logstash filter v3.1.0 field transformations --- ...60223003_update_windows_visualizations.xml | 407 ++++++++++++++++++ ...60223004_update_windows_visualizations.xml | 86 ++++ .../resources/config/liquibase/master.xml | 5 + 3 files changed, 498 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260223003_update_windows_visualizations.xml create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260223004_update_windows_visualizations.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260223003_update_windows_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260223003_update_windows_visualizations.xml new file mode 100644 index 000000000..410d2b6b0 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260223003_update_windows_visualizations.xml @@ -0,0 +1,407 @@ + + + + + Update Windows visualizations to match logstash filter v3.1.0 field transformations + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/changelog/20260223004_update_windows_visualizations.xml b/backend/src/main/resources/config/liquibase/changelog/20260223004_update_windows_visualizations.xml new file mode 100644 index 000000000..59f6b2c2e --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260223004_update_windows_visualizations.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + UPDATE utm_visualization + SET aggregation = REPLACE(aggregation::text, '"log.winlog.recordNumber.keyword"', '"log.recordNumber.keyword"')::jsonb + WHERE aggregation::text LIKE '%log.winlog.recordNumber.keyword%' + AND system_owner = true; + + + + + UPDATE utm_visualization + SET aggregation = REPLACE(aggregation::text, '"log.eventDataparam11.keyword"', '"log.eventDataParam11.keyword"')::jsonb + WHERE aggregation::text LIKE '%log.eventDataparam11.keyword%' + AND system_owner = true; + + + + + UPDATE utm_visualization + SET aggregation = REPLACE(aggregation::text, '"log.eventDataparam12.keyword"', '"log.eventDataParam12.keyword"')::jsonb + WHERE aggregation::text LIKE '%log.eventDataparam12.keyword%' + AND system_owner = true; + + + + + UPDATE utm_visualization + SET aggregation = REPLACE(aggregation::text, '"log.eventDataparam13.keyword"', '"log.eventDataParam13.keyword"')::jsonb + WHERE aggregation::text LIKE '%log.eventDataparam13.keyword%' + AND system_owner = true; + + + + + UPDATE utm_visualization + SET aggregation = REPLACE(aggregation::text, '"log.eventDataparam17.keyword"', '"log.eventDataParam17.keyword"')::jsonb + WHERE aggregation::text LIKE '%log.eventDataparam17.keyword%' + AND system_owner = true; + + + + + UPDATE utm_visualization + SET aggregation = REPLACE(aggregation::text, '"log.eventDataparam20.keyword"', '"log.eventDataParam20.keyword"')::jsonb + WHERE aggregation::text LIKE '%log.eventDataparam20.keyword%' + AND system_owner = true; + + + + + UPDATE utm_visualization + SET aggregation = REPLACE(aggregation::text, '"log.eventDataparam3.keyword"', '"log.eventDataParam3.keyword"')::jsonb + WHERE aggregation::text LIKE '%log.eventDataparam3.keyword%' + AND system_owner = true; + + + + + UPDATE utm_visualization + SET aggregation = REPLACE(aggregation::text, '"log.eventDataparam4.keyword"', '"log.eventDataParam4.keyword"')::jsonb + WHERE aggregation::text LIKE '%log.eventDataparam4.keyword%' + AND system_owner = true; + + + + + UPDATE utm_visualization + SET aggregation = REPLACE(aggregation::text, '"log.eventDataparam8.keyword"', '"log.eventDataParam8.keyword"')::jsonb + WHERE aggregation::text LIKE '%log.eventDataparam8.keyword%' + AND system_owner = true; + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 7a609ec65..8603507cc 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -473,4 +473,9 @@ + + + + + From 1ed34c0aa332a5a1b0ef1a69e02bb4be62b735f6 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 23 Feb 2026 10:36:55 -0600 Subject: [PATCH 054/115] feat(import): disable back button during loading and fix spacing in upload error message Signed-off-by: Manuel Abascal --- .../app-rule/components/import-rules/import-rule.component.html | 1 + .../utm/util/utm-file-upload/utm-file-upload.component.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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 0665d74b3..93149ae62 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 @@ -210,6 +210,7 @@
From 137afb1346735613f365344a173d619bd506fdf0 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 23 Feb 2026 14:37:26 -0600 Subject: [PATCH 055/115] feat(idp): enhance metadata URL validation with improved error handling and encryption key checks --- .../idp_provider/IdentityProviderService.java | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/idp_provider/IdentityProviderService.java b/backend/src/main/java/com/park/utmstack/service/idp_provider/IdentityProviderService.java index 19f404b76..db8d38ce0 100644 --- a/backend/src/main/java/com/park/utmstack/service/idp_provider/IdentityProviderService.java +++ b/backend/src/main/java/com/park/utmstack/service/idp_provider/IdentityProviderService.java @@ -1,6 +1,7 @@ package com.park.utmstack.service.idp_provider; +import com.park.utmstack.config.Constants; import com.park.utmstack.domain.idp_provider.IdentityProviderConfig; import com.park.utmstack.repository.idp_provider.IdentityProviderConfigRepository; import com.park.utmstack.service.dto.idp_provider.dto.*; @@ -18,11 +19,11 @@ import java.io.IOException; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URL; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -39,6 +40,13 @@ public List getAllActiveProviders() { public IdentityProviderConfigResponseDto create(IdentityProviderCreateConfigDto dto) { validateMetadataUrl(dto.getMetadataUrl()); + + String encryptionKey = System.getenv(Constants.ENV_ENCRYPTION_KEY); + if (encryptionKey == null || encryptionKey.isBlank()) { + throw new IllegalStateException( + "Environment variable " + Constants.ENV_ENCRYPTION_KEY + " not configured"); + } + IdentityProviderConfig entity = mapper.toEntity(dto); entity.setCreatedAt(LocalDateTime.now()); entity.setUpdatedAt(LocalDateTime.now()); @@ -65,7 +73,12 @@ public IdentityProviderConfigResponseDto update(Long id, IdentityProviderConfigR if(dto instanceof IdentityProviderCreateConfigDto createDto){ if (createDto.getSpPrivateKeyPem() != null) { - String encryptedKey = CipherUtil.encrypt(createDto.getSpPrivateKeyPem(), System.getenv("ENCRYPTION_KEY")); + String encryptionKey = System.getenv(Constants.ENV_ENCRYPTION_KEY); + if (encryptionKey == null || encryptionKey.isBlank()) { + throw new IllegalStateException( + "Environment variable " + Constants.ENV_ENCRYPTION_KEY + " not configured"); + } + String encryptedKey = CipherUtil.encrypt(createDto.getSpPrivateKeyPem(), encryptionKey); existing.setSpPrivateKeyPem(encryptedKey); } if (createDto.getSpCertificatePem() != null) { @@ -117,10 +130,20 @@ private void validateMetadataUrl(String metadataUrl) { int responseCode = connection.getResponseCode(); if (responseCode != 200) { - throw new SamlMetadataUrlInvalidException("Metadata URL is not accessible"); + throw new SamlMetadataUrlInvalidException( + String.format("Metadata URL is not accessible. HTTP Status: %d", responseCode)); } + + connection.disconnect(); + } catch (MalformedURLException e) { + throw new SamlMetadataUrlInvalidException( + "Invalid metadata URL format: " + e.getMessage()); } catch (IOException e) { - throw new SamlMetadataUrlInvalidException("Failed to access metadata URL"); + throw new SamlMetadataUrlInvalidException( + "Failed to access metadata URL: " + e.getMessage()); + } catch (Exception e) { + throw new SamlMetadataUrlInvalidException( + "Unexpected error validating metadata URL: " + e.getMessage()); } } From 4a609e891ee1f3c0c2be0933988d44a098ffcd00 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 23 Feb 2026 14:44:48 -0600 Subject: [PATCH 056/115] feat(idp): refactor encryption key handling with dedicated validation method --- ...amlRelyingPartyRegistrationRepository.java | 21 +++++++--- .../idp_provider/IdentityProviderService.java | 42 +++++++++++-------- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java index d1dd4b13e..4e9c778fd 100644 --- a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java @@ -63,13 +63,24 @@ private void loadProviders(IdentityProviderConfigRepository jpaProviderRepositor } } + /** + * Validates and retrieves the encryption key from environment variables. + * + * @return The validated encryption key + * @throws IllegalStateException if ENCRYPTION_KEY is not configured + */ + private String getValidatedEncryptionKey() { + String encryptionKey = System.getenv(Constants.ENV_ENCRYPTION_KEY); + if (encryptionKey == null || encryptionKey.isBlank()) { + throw new IllegalStateException( + "Environment variable " + Constants.ENV_ENCRYPTION_KEY + " not configured"); + } + return encryptionKey; + } + private RelyingPartyRegistration buildRelyingPartyRegistration(IdentityProviderConfig entity) { try { - String encryptionKey = System.getenv(Constants.ENV_ENCRYPTION_KEY); - if (encryptionKey == null || encryptionKey.isBlank()) { - throw new IllegalStateException( - "Environment variable " + Constants.ENV_ENCRYPTION_KEY + " not configured"); - } + String encryptionKey = getValidatedEncryptionKey(); String decryptedKey = CipherUtil.decrypt(entity.getSpPrivateKeyPem(), encryptionKey); PrivateKey spKey = PemUtils.parsePrivateKey(decryptedKey); diff --git a/backend/src/main/java/com/park/utmstack/service/idp_provider/IdentityProviderService.java b/backend/src/main/java/com/park/utmstack/service/idp_provider/IdentityProviderService.java index db8d38ce0..fa67a2e30 100644 --- a/backend/src/main/java/com/park/utmstack/service/idp_provider/IdentityProviderService.java +++ b/backend/src/main/java/com/park/utmstack/service/idp_provider/IdentityProviderService.java @@ -22,7 +22,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.time.LocalDateTime; -import java.util.List; import java.util.Optional; @Service @@ -33,19 +32,12 @@ public class IdentityProviderService { private final IdentityProviderConfigRepository repository; private final ApplicationEventPublisher publisher; - public List getAllActiveProviders() { - return repository.findAllByActiveTrue(); - } - public IdentityProviderConfigResponseDto create(IdentityProviderCreateConfigDto dto) { validateMetadataUrl(dto.getMetadataUrl()); - String encryptionKey = System.getenv(Constants.ENV_ENCRYPTION_KEY); - if (encryptionKey == null || encryptionKey.isBlank()) { - throw new IllegalStateException( - "Environment variable " + Constants.ENV_ENCRYPTION_KEY + " not configured"); - } + // Validate encryption key before mapper encrypts the private key + getValidatedEncryptionKey(); IdentityProviderConfig entity = mapper.toEntity(dto); entity.setCreatedAt(LocalDateTime.now()); @@ -73,11 +65,7 @@ public IdentityProviderConfigResponseDto update(Long id, IdentityProviderConfigR if(dto instanceof IdentityProviderCreateConfigDto createDto){ if (createDto.getSpPrivateKeyPem() != null) { - String encryptionKey = System.getenv(Constants.ENV_ENCRYPTION_KEY); - if (encryptionKey == null || encryptionKey.isBlank()) { - throw new IllegalStateException( - "Environment variable " + Constants.ENV_ENCRYPTION_KEY + " not configured"); - } + String encryptionKey = getValidatedEncryptionKey(); String encryptedKey = CipherUtil.encrypt(createDto.getSpPrivateKeyPem(), encryptionKey); existing.setSpPrivateKeyPem(encryptedKey); } @@ -116,14 +104,30 @@ public void delete(Long id) { repository.deleteById(id); } + /** + * Validates and retrieves the encryption key from environment variables. + * + * @return The validated encryption key + * @throws IllegalStateException if ENCRYPTION_KEY is not configured + */ + private String getValidatedEncryptionKey() { + String encryptionKey = System.getenv(Constants.ENV_ENCRYPTION_KEY); + if (encryptionKey == null || encryptionKey.isBlank()) { + throw new IllegalStateException( + "Environment variable " + Constants.ENV_ENCRYPTION_KEY + " not configured"); + } + return encryptionKey; + } + private void validateMetadataUrl(String metadataUrl) { if (metadataUrl == null || metadataUrl.trim().isEmpty()) { throw new SamlMetadataUrlInvalidException("Metadata URL is required"); } + HttpURLConnection connection = null; try { URL url = new URL(metadataUrl); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(5000); connection.setReadTimeout(5000); @@ -133,8 +137,6 @@ private void validateMetadataUrl(String metadataUrl) { throw new SamlMetadataUrlInvalidException( String.format("Metadata URL is not accessible. HTTP Status: %d", responseCode)); } - - connection.disconnect(); } catch (MalformedURLException e) { throw new SamlMetadataUrlInvalidException( "Invalid metadata URL format: " + e.getMessage()); @@ -144,6 +146,10 @@ private void validateMetadataUrl(String metadataUrl) { } catch (Exception e) { throw new SamlMetadataUrlInvalidException( "Unexpected error validating metadata URL: " + e.getMessage()); + } finally { + if (connection != null) { + connection.disconnect(); + } } } From 8d45e5b5b7f5dd2a038508f96e821e6edf82cbd4 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 23 Feb 2026 16:34:33 -0600 Subject: [PATCH 057/115] feat(saml): implement SAML metadata fetching and provider loading with timeout handling --- .../config/saml/SamlMetadataFetcher.java | 155 ++++++++++++++++ .../config/saml/SamlProvidersLoader.java | 167 ++++++++++++++++++ .../config/saml/SamlRegistrationBuilder.java | 124 +++++++++++++ ...amlRelyingPartyRegistrationRepository.java | 63 ++----- 4 files changed, 462 insertions(+), 47 deletions(-) create mode 100644 backend/src/main/java/com/park/utmstack/config/saml/SamlMetadataFetcher.java create mode 100644 backend/src/main/java/com/park/utmstack/config/saml/SamlProvidersLoader.java create mode 100644 backend/src/main/java/com/park/utmstack/config/saml/SamlRegistrationBuilder.java diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlMetadataFetcher.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlMetadataFetcher.java new file mode 100644 index 000000000..32d0eec49 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlMetadataFetcher.java @@ -0,0 +1,155 @@ +package com.park.utmstack.config.saml; + +import com.park.utmstack.domain.idp_provider.IdentityProviderConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.util.concurrent.*; + +/** + * Responsible for fetching SAML metadata with timeout handling. + * Separates the concern of async metadata fetching from registration building. + */ +@Slf4j +public class SamlMetadataFetcher { + + private static final Duration METADATA_FETCH_TIMEOUT = Duration.ofSeconds(10); + + /** + * Fetches SAML metadata with timeout protection. + * Returns null if timeout, error, or interruption occurs. + * Logs detailed error information instead of throwing exceptions. + * + * @param entity Provider configuration + * @return RelyingPartyRegistration, or null if fetch fails + */ + public RelyingPartyRegistration fetchMetadataWithTimeout(IdentityProviderConfig entity) { + ExecutorService timeoutExecutor = null; + Future future = null; + + try { + timeoutExecutor = createMetadataFetchExecutor(entity); + + future = CompletableFuture.supplyAsync(() -> { + try { + return RelyingPartyRegistrations + .fromMetadataLocation(entity.getMetadataUrl()) + .registrationId(entity.getName()) + .build(); + } catch (Exception e) { + throw new CompletionException(e); + } + }, timeoutExecutor); + + return future.get(METADATA_FETCH_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + + } catch (TimeoutException e) { + handleTimeoutException(entity, future, e); + return null; + + } catch (ExecutionException e) { + handleExecutionException(entity, e); + return null; + + } catch (InterruptedException e) { + handleInterruptedException(entity, e); + return null; + + } finally { + cleanupExecutor(entity, timeoutExecutor); + } + } + + /** + * Creates an executor for metadata fetching with proper naming and exception handling. + */ + private ExecutorService createMetadataFetchExecutor(IdentityProviderConfig entity) { + return Executors.newSingleThreadExecutor(r -> { + Thread t = new Thread(r); + t.setName("saml-metadata-fetch-" + entity.getName()); + t.setDaemon(true); + t.setUncaughtExceptionHandler((thread, throwable) -> + log.error("Uncaught exception in SAML metadata fetch thread for {}: {}", + entity.getName(), throwable.getMessage(), throwable) + ); + return t; + }); + } + + /** + * Handles timeout exception with detailed logging. + */ + private void handleTimeoutException(IdentityProviderConfig entity, Future future, TimeoutException e) { + if (future != null) { + future.cancel(true); + } + log.error( + "SAML metadata fetch TIMEOUT: Provider='{}', Timeout={}s, MetadataUrl='{}'. " + + "This provider will not be available for SSO until it responds faster or the endpoint is fixed.", + entity.getName(), + METADATA_FETCH_TIMEOUT.getSeconds(), + entity.getMetadataUrl(), + e + ); + } + + /** + * Handles execution exception with root cause extraction. + */ + private void handleExecutionException(IdentityProviderConfig entity, ExecutionException e) { + Throwable rootCause = e.getCause() != null ? e.getCause() : e; + log.error( + "SAML metadata fetch FAILED: Provider='{}'. Root cause: {}. " + + "Error details: {}. This provider will not be available for SSO.", + entity.getName(), + rootCause.getClass().getSimpleName(), + rootCause.getMessage(), + rootCause + ); + } + + /** + * Handles interruption exception. + */ + private void handleInterruptedException(IdentityProviderConfig entity, InterruptedException e) { + Thread.currentThread().interrupt(); + log.error( + "SAML metadata fetch INTERRUPTED: Provider='{}'. " + + "Current thread was interrupted. Thread status restored. " + + "This provider will not be available for SSO.", + entity.getName(), + e + ); + } + + /** + * Safely shuts down the executor and logs any issues. + */ + private void cleanupExecutor(IdentityProviderConfig entity, ExecutorService executor) { + if (executor != null) { + try { + executor.shutdownNow(); + + if (!executor.awaitTermination(2, TimeUnit.SECONDS)) { + log.warn( + "Executor for SAML provider '{}' did not terminate cleanly within 2 seconds. " + + "Potential thread leak detected.", + entity.getName() + ); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error( + "Interrupted while waiting for executor shutdown for SAML provider '{}'. " + + "Thread status restored.", + entity.getName(), + e + ); + } + } + } +} + diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlProvidersLoader.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlProvidersLoader.java new file mode 100644 index 000000000..58dbdac75 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlProvidersLoader.java @@ -0,0 +1,167 @@ +package com.park.utmstack.config.saml; + +import com.park.utmstack.domain.idp_provider.IdentityProviderConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.*; + +/** + * Responsible for loading multiple SAML providers concurrently. + * Separates the concern of concurrent loading and resource management. + */ +@Slf4j +public class SamlProvidersLoader { + + private static final Duration GLOBAL_LOADING_TIMEOUT = Duration.ofSeconds(30); + private static final int MAX_CONCURRENT_LOADS = 5; + + private final SamlRegistrationBuilder registrationBuilder; + + public SamlProvidersLoader(SamlRegistrationBuilder registrationBuilder) { + this.registrationBuilder = registrationBuilder; + } + + /** + * Loads multiple providers concurrently with global timeout. + * Returns a map of loaded registrations (some may be missing if they failed). + * + * @param activeProviders List of provider configurations to load + * @return Map of provider type -> registration (only successful loads) + */ + public Map loadProvidersAsync( + List activeProviders) { + + if (activeProviders.isEmpty()) { + log.info("No active SAML providers found"); + return new ConcurrentHashMap<>(); + } + + log.info("Starting async load of {} SAML provider(s)...", activeProviders.size()); + + ExecutorService executor = null; + try { + executor = createExecutor(activeProviders.size()); + final ExecutorService finalExecutor = executor; + Map registrations = new ConcurrentHashMap<>(); + + List> futures = activeProviders.stream() + .map(entity -> loadProviderAsync(entity, registrations, finalExecutor)) + .toList(); + + waitForAllLoads(futures); + logLoadingResults(activeProviders.size(), registrations.size()); + + return registrations; + + } finally { + shutdownExecutorGracefully(executor); + } + } + + /** + * Creates a thread pool executor for concurrent provider loading. + */ + private ExecutorService createExecutor(int providerCount) { + int poolSize = Math.min(MAX_CONCURRENT_LOADS, providerCount); + return Executors.newFixedThreadPool( + poolSize, + r -> { + Thread t = new Thread(r); + t.setName("saml-provider-loader"); + t.setDaemon(true); + t.setUncaughtExceptionHandler((thread, throwable) -> + log.error("Uncaught exception in provider loading thread: {}", + throwable.getMessage(), throwable) + ); + return t; + } + ); + } + + /** + * Loads a single provider asynchronously. + */ + private CompletableFuture loadProviderAsync( + IdentityProviderConfig entity, + Map registrations, + ExecutorService executor) { + + return CompletableFuture.runAsync(() -> { + try { + RelyingPartyRegistration registration = registrationBuilder.buildRegistration(entity); + + if (registration != null) { + registrations.put(entity.getProviderType().name().toLowerCase(), registration); + log.info("Successfully loaded SAML provider: {} (type: {})", + entity.getName(), entity.getProviderType()); + } else { + log.warn("SAML provider '{}' (type: {}) skipped - unable to load registration. " + + "SSO will not be available for this provider type.", + entity.getName(), entity.getProviderType()); + } + } catch (Exception e) { + log.error("Unexpected error loading SAML provider '{}': {}", + entity.getName(), e.getMessage(), e); + } + }, executor); + } + + /** + * Waits for all provider loads to complete with global timeout. + */ + private void waitForAllLoads(List> futures) { + try { + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .get(GLOBAL_LOADING_TIMEOUT.getSeconds(), TimeUnit.SECONDS); + } catch (TimeoutException e) { + log.warn("Provider loading exceeded global timeout of {}s", + GLOBAL_LOADING_TIMEOUT.getSeconds()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("Provider loading was interrupted"); + } catch (ExecutionException e) { + log.error("Error during provider loading: {}", e.getMessage()); + } + } + + /** + * Logs summary of loading results. + */ + private void logLoadingResults(int totalProviders, int loadedCount) { + int failedCount = totalProviders - loadedCount; + log.info("SAML provider loading completed: {} loaded, {} failed, {} total", + loadedCount, failedCount, totalProviders); + } + + /** + * Safely shuts down the executor with proper timeout handling. + */ + private void shutdownExecutorGracefully(ExecutorService executor) { + if (executor == null) { + return; + } + + try { + executor.shutdown(); + if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { + log.warn("Executor did not terminate in time, forcing shutdown"); + List droppedTasks = executor.shutdownNow(); + if (!droppedTasks.isEmpty()) { + log.warn("Dropped {} pending tasks during forced shutdown", droppedTasks.size()); + } + + if (!executor.awaitTermination(2, TimeUnit.SECONDS)) { + log.error("Executor did not terminate even after forced shutdown - potential thread leak"); + } + } + } catch (InterruptedException e) { + log.error("Interrupted while waiting for executor termination"); + executor.shutdownNow(); + Thread.currentThread().interrupt(); + } + } +} + diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlRegistrationBuilder.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlRegistrationBuilder.java new file mode 100644 index 000000000..07766c1b7 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlRegistrationBuilder.java @@ -0,0 +1,124 @@ +package com.park.utmstack.config.saml; + +import com.park.utmstack.domain.idp_provider.IdentityProviderConfig; +import com.park.utmstack.util.CipherUtil; +import com.park.utmstack.util.saml.PemUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; + +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +/** + * Responsible for building SAML registration objects. + * Separates credential handling and registration building logic. + */ +@Slf4j +public class SamlRegistrationBuilder { + + private final String encryptionKey; + private final SamlMetadataFetcher metadataFetcher; + + public SamlRegistrationBuilder(String encryptionKey, SamlMetadataFetcher metadataFetcher) { + this.encryptionKey = encryptionKey; + this.metadataFetcher = metadataFetcher; + } + + /** + * Builds a complete SAML registration with timeout protection and error handling. + * Returns null if any step fails. + * + * @param entity Provider configuration + * @return RelyingPartyRegistration, or null if build fails + */ + public RelyingPartyRegistration buildRegistration(IdentityProviderConfig entity) { + try { + // Step 1: Fetch metadata with timeout + RelyingPartyRegistration baseRegistration = metadataFetcher.fetchMetadataWithTimeout(entity); + if (baseRegistration == null) { + log.debug("Skipping provider '{}' - metadata fetch failed", entity.getName()); + return null; + } + + // Step 2: Load and validate credentials + PrivateKey spKey = loadAndDecryptPrivateKey(entity); + if (spKey == null) { + return null; + } + + X509Certificate spCert = loadCertificate(entity); + if (spCert == null) { + return null; + } + + // Step 3: Build final registration with credentials + return buildWithCredentials(baseRegistration, entity, spKey, spCert); + + } catch (Exception e) { + log.error("Unexpected error building SAML registration for provider '{}': {}", + entity.getName(), e.getMessage(), e); + return null; + } + } + + /** + * Loads, decrypts and validates the SP private key. + * Returns null if decryption or parsing fails. + */ + private PrivateKey loadAndDecryptPrivateKey(IdentityProviderConfig entity) { + try { + String decryptedKey = CipherUtil.decrypt(entity.getSpPrivateKeyPem(), this.encryptionKey); + return PemUtils.parsePrivateKey(decryptedKey); + } catch (Exception e) { + log.error("Failed to load/decrypt SP private key for provider '{}': {}", + entity.getName(), e.getMessage(), e); + return null; + } + } + + /** + * Loads and validates the SP certificate. + * Returns null if parsing fails. + */ + private X509Certificate loadCertificate(IdentityProviderConfig entity) { + try { + return PemUtils.parseCertificate(entity.getSpCertificatePem()); + } catch (Exception e) { + log.error("Failed to load SP certificate for provider '{}': {}", + entity.getName(), e.getMessage(), e); + return null; + } + } + + /** + * Configures the registration with SP credentials and custom settings. + */ + private RelyingPartyRegistration buildWithCredentials( + RelyingPartyRegistration baseRegistration, + IdentityProviderConfig entity, + PrivateKey spKey, + X509Certificate spCert) { + + try { + // Note: RelyingPartyRegistration from metadata is already built + // We need to configure it with our SP credentials + // For now, return the base registration built from metadata + // Additional configuration can be done via Spring Security configuration + return RelyingPartyRegistrations + .fromMetadataLocation(entity.getMetadataUrl()) + .registrationId(entity.getProviderType().name().toLowerCase()) + .assertionConsumerServiceLocation(entity.getSpAcsUrl()) + .signingX509Credentials(c -> { + c.add(Saml2X509Credential.signing(spKey, spCert)); + }) + .build(); + } catch (Exception e) { + log.error("Failed to configure SAML registration for provider '{}': {}", + entity.getName(), e.getMessage(), e); + return null; + } + } +} + diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java index 4e9c778fd..6be98303c 100644 --- a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java @@ -3,18 +3,10 @@ import com.park.utmstack.config.Constants; import com.park.utmstack.domain.idp_provider.IdentityProviderConfig; import com.park.utmstack.repository.idp_provider.IdentityProviderConfigRepository; -import com.park.utmstack.util.CipherUtil; -import com.park.utmstack.util.exceptions.ApiException; -import com.park.utmstack.util.saml.PemUtils; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -23,8 +15,16 @@ public class SamlRelyingPartyRegistrationRepository implements RelyingPartyRegistrationRepository { private final Map registrations = new ConcurrentHashMap<>(); + private final SamlProvidersLoader providersLoader; public SamlRelyingPartyRegistrationRepository(IdentityProviderConfigRepository jpaProviderRepository) { + + String encryptionKey = getValidatedEncryptionKey(); + SamlMetadataFetcher metadataFetcher = new SamlMetadataFetcher(); + SamlRegistrationBuilder registrationBuilder = new SamlRegistrationBuilder(encryptionKey, metadataFetcher); + this.providersLoader = new SamlProvidersLoader(registrationBuilder); + + // Load providers on initialization loadProviders(jpaProviderRepository); } @@ -38,28 +38,18 @@ public void reloadProviders(IdentityProviderConfigRepository jpaProviderReposito loadProviders(jpaProviderRepository); } + /** + * Loads SAML providers using the specialized loader. + * Delegates all async loading logic to SamlProvidersLoader. + */ private void loadProviders(IdentityProviderConfigRepository jpaProviderRepository) { try { List activeProviders = jpaProviderRepository.findAllByActiveTrue(); - - if (activeProviders.isEmpty()) { - return; - } - - activeProviders.forEach(entity -> { - try { - RelyingPartyRegistration registration = buildRelyingPartyRegistration(entity); - registrations.put(entity.getProviderType().name().toLowerCase(), registration); - log.info("Loaded SAML provider: {} (type: {})", entity.getName(), entity.getProviderType()); - } catch (Exception e) { - log.error("Failed to load SAML provider: {}", entity.getName(), e); - } - }); - - log.info("Successfully loaded {} SAML provider(s)", registrations.size()); + Map loadedRegistrations = + providersLoader.loadProvidersAsync(activeProviders); + registrations.putAll(loadedRegistrations); } catch (Exception e) { - log.error("Failed to load SAML providers: {}", e.getMessage(), e); - throw new ApiException(String.format("Failed to load SAML providers: %s", e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR); + log.error("Error during SAML provider loading: {}", e.getMessage(), e); } } @@ -78,25 +68,4 @@ private String getValidatedEncryptionKey() { return encryptionKey; } - private RelyingPartyRegistration buildRelyingPartyRegistration(IdentityProviderConfig entity) { - try { - String encryptionKey = getValidatedEncryptionKey(); - - String decryptedKey = CipherUtil.decrypt(entity.getSpPrivateKeyPem(), encryptionKey); - PrivateKey spKey = PemUtils.parsePrivateKey(decryptedKey); - X509Certificate spCert = PemUtils.parseCertificate(entity.getSpCertificatePem()); - - return RelyingPartyRegistrations - .fromMetadataLocation(entity.getMetadataUrl()) - .registrationId(entity.getName()) - .entityId(entity.getSpEntityId()) - .assertionConsumerServiceLocation(entity.getSpAcsUrl()) - .signingX509Credentials(c -> c.add(Saml2X509Credential.signing(spKey, spCert))) - .build(); - } catch (Exception e) { - log.error("Failed to build SAML registration for provider: {}", entity.getName(), e); - throw new ApiException(String.format("Failed to build SAML registration for provider: %s", entity.getName()), HttpStatus.INTERNAL_SERVER_ERROR); - } - } - } \ No newline at end of file From c6d062923606bdaf9d8ad76f68b8eddba016601f Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 24 Feb 2026 07:12:33 -0600 Subject: [PATCH 058/115] feat(saml): enhance SAML2 login success handling with improved user not found logging and provider reloading --- ...amlRelyingPartyRegistrationRepository.java | 32 +++++++++++++------ .../saml/Saml2LoginSuccessHandler.java | 12 +++---- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java index 6be98303c..50fe2305e 100644 --- a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java @@ -14,7 +14,7 @@ @Slf4j public class SamlRelyingPartyRegistrationRepository implements RelyingPartyRegistrationRepository { - private final Map registrations = new ConcurrentHashMap<>(); + private volatile Map registrations = new ConcurrentHashMap<>(); private final SamlProvidersLoader providersLoader; public SamlRelyingPartyRegistrationRepository(IdentityProviderConfigRepository jpaProviderRepository) { @@ -24,7 +24,6 @@ public SamlRelyingPartyRegistrationRepository(IdentityProviderConfigRepository j SamlRegistrationBuilder registrationBuilder = new SamlRegistrationBuilder(encryptionKey, metadataFetcher); this.providersLoader = new SamlProvidersLoader(registrationBuilder); - // Load providers on initialization loadProviders(jpaProviderRepository); } @@ -34,22 +33,30 @@ public RelyingPartyRegistration findByRegistrationId(String registrationId) { } public void reloadProviders(IdentityProviderConfigRepository jpaProviderRepository) { - registrations.clear(); - loadProviders(jpaProviderRepository); + try { + registrations = loadActiveProviders(jpaProviderRepository); + log.info("SAML providers reloaded successfully: {} providers loaded", registrations.size()); + } catch (Exception e) { + log.error("Failed to reload SAML providers - keeping previous configuration", e); + } } /** * Loads SAML providers using the specialized loader. * Delegates all async loading logic to SamlProvidersLoader. + * App will start without providers if loading fails. */ private void loadProviders(IdentityProviderConfigRepository jpaProviderRepository) { try { - List activeProviders = jpaProviderRepository.findAllByActiveTrue(); - Map loadedRegistrations = - providersLoader.loadProvidersAsync(activeProviders); - registrations.putAll(loadedRegistrations); + registrations = loadActiveProviders(jpaProviderRepository); + if (registrations.isEmpty()) { + log.warn("No active SAML2 providers found. SAML2 authentication will not be available."); + } else { + log.info("Successfully loaded {} SAML2 provider(s) on startup", registrations.size()); + } } catch (Exception e) { - log.error("Error during SAML provider loading: {}", e.getMessage(), e); + log.error("Error during SAML provider loading - app will start without SAML2 authentication: {}", + e.getMessage(), e); } } @@ -68,4 +75,11 @@ private String getValidatedEncryptionKey() { return encryptionKey; } + private Map loadActiveProviders(IdentityProviderConfigRepository repo) { + List activeProviders = repo.findAllByActiveTrue(); + Map loaded = providersLoader.loadProvidersAsync(activeProviders); + return new ConcurrentHashMap<>(loaded); + } + + } \ No newline at end of file diff --git a/backend/src/main/java/com/park/utmstack/security/saml/Saml2LoginSuccessHandler.java b/backend/src/main/java/com/park/utmstack/security/saml/Saml2LoginSuccessHandler.java index 536418db3..ec3e2057f 100644 --- a/backend/src/main/java/com/park/utmstack/security/saml/Saml2LoginSuccessHandler.java +++ b/backend/src/main/java/com/park/utmstack/security/saml/Saml2LoginSuccessHandler.java @@ -12,7 +12,6 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; -import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.web.util.UriComponentsBuilder; @@ -22,9 +21,6 @@ import java.net.URI; import java.util.Collection; import java.util.Objects; -import java.util.Optional; - -import static com.park.utmstack.config.Constants.FRONT_BASE_URL; /** * Success handler for SAML2 login. @@ -38,7 +34,6 @@ public class Saml2LoginSuccessHandler implements AuthenticationSuccessHandler { private final TokenProvider tokenProvider; private final UserRepository userRepository; - private final Saml2LoginFailureHandler failureHandler; @Override @@ -48,14 +43,16 @@ public void onAuthenticationSuccess(HttpServletRequest request, String scheme = Objects.requireNonNullElse(request.getHeader("X-Forwarded-Proto"), request.getScheme()); String host = Objects.requireNonNullElse(request.getHeader("Host"), request.getServerName()); - String frontBaseUrl = scheme + "://" + host; Saml2AuthenticatedPrincipal samlUser = (Saml2AuthenticatedPrincipal) authentication.getPrincipal(); String username = samlUser.getName(); User user = userRepository.findOneByLogin(username) - .orElseThrow(() -> new BadCredentialsException("The provided credentials do not match any active user account.")); + .orElseThrow(() -> { + log.warn("SAML2 authentication successful for '{}' but user not found in local database", username); + return new BadCredentialsException("User not provisioned in local system"); + }); Collection authorities = Objects.requireNonNull(user.getAuthorities()) .stream() @@ -75,6 +72,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, .build() .toUri(); + log.info("SAML2 login successful for user: {}", username); response.sendRedirect(redirectUri.toString()); } } From b1b41f3ec9f817819d92411ec771326b7fe109f7 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 24 Feb 2026 07:26:41 -0600 Subject: [PATCH 059/115] feat(saml): update host retrieval in SAML2 login success handler to use X-Forwarded-Host header --- .../park/utmstack/security/saml/Saml2LoginSuccessHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/park/utmstack/security/saml/Saml2LoginSuccessHandler.java b/backend/src/main/java/com/park/utmstack/security/saml/Saml2LoginSuccessHandler.java index ec3e2057f..27aabe8f5 100644 --- a/backend/src/main/java/com/park/utmstack/security/saml/Saml2LoginSuccessHandler.java +++ b/backend/src/main/java/com/park/utmstack/security/saml/Saml2LoginSuccessHandler.java @@ -42,7 +42,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, Authentication authentication) throws IOException { String scheme = Objects.requireNonNullElse(request.getHeader("X-Forwarded-Proto"), request.getScheme()); - String host = Objects.requireNonNullElse(request.getHeader("Host"), request.getServerName()); + String host = Objects.requireNonNullElse(request.getHeader("X-Forwarded-Host"), request.getServerName()); String frontBaseUrl = scheme + "://" + host; Saml2AuthenticatedPrincipal samlUser = (Saml2AuthenticatedPrincipal) authentication.getPrincipal(); From f70359484dcdf03c611be83205b81834765fe68e Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 24 Feb 2026 10:44:06 -0600 Subject: [PATCH 060/115] feat(saml): refactor SAML metadata fetching to improve error handling and registration building --- .../config/SecurityConfiguration.java | 3 +- .../config/saml/SamlMetadataFetcher.java | 168 ++++-------------- .../config/saml/SamlProvidersLoader.java | 167 ----------------- .../config/saml/SamlRegistrationBuilder.java | 124 ------------- ...amlRelyingPartyRegistrationRepository.java | 35 +++- 5 files changed, 64 insertions(+), 433 deletions(-) delete mode 100644 backend/src/main/java/com/park/utmstack/config/saml/SamlProvidersLoader.java delete mode 100644 backend/src/main/java/com/park/utmstack/config/saml/SamlRegistrationBuilder.java diff --git a/backend/src/main/java/com/park/utmstack/config/SecurityConfiguration.java b/backend/src/main/java/com/park/utmstack/config/SecurityConfiguration.java index 9252976f4..9478be6b8 100644 --- a/backend/src/main/java/com/park/utmstack/config/SecurityConfiguration.java +++ b/backend/src/main/java/com/park/utmstack/config/SecurityConfiguration.java @@ -128,8 +128,7 @@ public void configure(HttpSecurity http) throws Exception { .and() .saml2Login() .successHandler(new Saml2LoginSuccessHandler(tokenProvider, - userRepository, - saml2LoginFailureHandler())) + userRepository)) .failureHandler(new Saml2LoginFailureHandler()) .and() .apply(securityConfigurerAdapterForJwt()) diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlMetadataFetcher.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlMetadataFetcher.java index 32d0eec49..9264ed181 100644 --- a/backend/src/main/java/com/park/utmstack/config/saml/SamlMetadataFetcher.java +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlMetadataFetcher.java @@ -2,154 +2,56 @@ import com.park.utmstack.domain.idp_provider.IdentityProviderConfig; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.saml2.core.Saml2X509Credential; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; -import org.springframework.stereotype.Component; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; import java.time.Duration; import java.util.concurrent.*; -/** - * Responsible for fetching SAML metadata with timeout handling. - * Separates the concern of async metadata fetching from registration building. - */ @Slf4j public class SamlMetadataFetcher { - private static final Duration METADATA_FETCH_TIMEOUT = Duration.ofSeconds(10); - - /** - * Fetches SAML metadata with timeout protection. - * Returns null if timeout, error, or interruption occurs. - * Logs detailed error information instead of throwing exceptions. - * - * @param entity Provider configuration - * @return RelyingPartyRegistration, or null if fetch fails - */ - public RelyingPartyRegistration fetchMetadataWithTimeout(IdentityProviderConfig entity) { - ExecutorService timeoutExecutor = null; - Future future = null; + private static final Duration TIMEOUT = Duration.ofSeconds(10); + + private final ExecutorService executor = Executors.newFixedThreadPool(5, r -> { + Thread t = new Thread(r); + t.setName("saml-metadata-fetch"); + t.setDaemon(true); + return t; + }); + + public RelyingPartyRegistration fetch(IdentityProviderConfig entity, + PrivateKey spKey, + X509Certificate spCert) { + + CompletableFuture future = + CompletableFuture.supplyAsync(() -> { + try { + return RelyingPartyRegistrations + .fromMetadataLocation(entity.getMetadataUrl()) + .registrationId(entity.getName()) + .entityId(entity.getSpEntityId()) + .assertionConsumerServiceLocation(entity.getSpAcsUrl()) + .signingX509Credentials(c -> c.add(Saml2X509Credential.signing(spKey, spCert))) + .build(); + } catch (Exception e) { + throw new CompletionException(e); + } + }, executor); try { - timeoutExecutor = createMetadataFetchExecutor(entity); - - future = CompletableFuture.supplyAsync(() -> { - try { - return RelyingPartyRegistrations - .fromMetadataLocation(entity.getMetadataUrl()) - .registrationId(entity.getName()) - .build(); - } catch (Exception e) { - throw new CompletionException(e); - } - }, timeoutExecutor); - - return future.get(METADATA_FETCH_TIMEOUT.getSeconds(), TimeUnit.SECONDS); - - } catch (TimeoutException e) { - handleTimeoutException(entity, future, e); - return null; - - } catch (ExecutionException e) { - handleExecutionException(entity, e); - return null; - - } catch (InterruptedException e) { - handleInterruptedException(entity, e); - return null; - - } finally { - cleanupExecutor(entity, timeoutExecutor); - } - } + return future.get(TIMEOUT.getSeconds(), TimeUnit.SECONDS); - /** - * Creates an executor for metadata fetching with proper naming and exception handling. - */ - private ExecutorService createMetadataFetchExecutor(IdentityProviderConfig entity) { - return Executors.newSingleThreadExecutor(r -> { - Thread t = new Thread(r); - t.setName("saml-metadata-fetch-" + entity.getName()); - t.setDaemon(true); - t.setUncaughtExceptionHandler((thread, throwable) -> - log.error("Uncaught exception in SAML metadata fetch thread for {}: {}", - entity.getName(), throwable.getMessage(), throwable) - ); - return t; - }); - } - - /** - * Handles timeout exception with detailed logging. - */ - private void handleTimeoutException(IdentityProviderConfig entity, Future future, TimeoutException e) { - if (future != null) { + } catch (Exception e) { future.cancel(true); + log.error("Metadata fetch failed for provider '{}': {}", entity.getName(), e.getMessage()); + return null; } - log.error( - "SAML metadata fetch TIMEOUT: Provider='{}', Timeout={}s, MetadataUrl='{}'. " + - "This provider will not be available for SSO until it responds faster or the endpoint is fixed.", - entity.getName(), - METADATA_FETCH_TIMEOUT.getSeconds(), - entity.getMetadataUrl(), - e - ); - } - - /** - * Handles execution exception with root cause extraction. - */ - private void handleExecutionException(IdentityProviderConfig entity, ExecutionException e) { - Throwable rootCause = e.getCause() != null ? e.getCause() : e; - log.error( - "SAML metadata fetch FAILED: Provider='{}'. Root cause: {}. " + - "Error details: {}. This provider will not be available for SSO.", - entity.getName(), - rootCause.getClass().getSimpleName(), - rootCause.getMessage(), - rootCause - ); - } - - /** - * Handles interruption exception. - */ - private void handleInterruptedException(IdentityProviderConfig entity, InterruptedException e) { - Thread.currentThread().interrupt(); - log.error( - "SAML metadata fetch INTERRUPTED: Provider='{}'. " + - "Current thread was interrupted. Thread status restored. " + - "This provider will not be available for SSO.", - entity.getName(), - e - ); } +} - /** - * Safely shuts down the executor and logs any issues. - */ - private void cleanupExecutor(IdentityProviderConfig entity, ExecutorService executor) { - if (executor != null) { - try { - executor.shutdownNow(); - if (!executor.awaitTermination(2, TimeUnit.SECONDS)) { - log.warn( - "Executor for SAML provider '{}' did not terminate cleanly within 2 seconds. " + - "Potential thread leak detected.", - entity.getName() - ); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.error( - "Interrupted while waiting for executor shutdown for SAML provider '{}'. " + - "Thread status restored.", - entity.getName(), - e - ); - } - } - } -} diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlProvidersLoader.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlProvidersLoader.java deleted file mode 100644 index 58dbdac75..000000000 --- a/backend/src/main/java/com/park/utmstack/config/saml/SamlProvidersLoader.java +++ /dev/null @@ -1,167 +0,0 @@ -package com.park.utmstack.config.saml; - -import com.park.utmstack.domain.idp_provider.IdentityProviderConfig; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; - -import java.time.Duration; -import java.util.*; -import java.util.concurrent.*; - -/** - * Responsible for loading multiple SAML providers concurrently. - * Separates the concern of concurrent loading and resource management. - */ -@Slf4j -public class SamlProvidersLoader { - - private static final Duration GLOBAL_LOADING_TIMEOUT = Duration.ofSeconds(30); - private static final int MAX_CONCURRENT_LOADS = 5; - - private final SamlRegistrationBuilder registrationBuilder; - - public SamlProvidersLoader(SamlRegistrationBuilder registrationBuilder) { - this.registrationBuilder = registrationBuilder; - } - - /** - * Loads multiple providers concurrently with global timeout. - * Returns a map of loaded registrations (some may be missing if they failed). - * - * @param activeProviders List of provider configurations to load - * @return Map of provider type -> registration (only successful loads) - */ - public Map loadProvidersAsync( - List activeProviders) { - - if (activeProviders.isEmpty()) { - log.info("No active SAML providers found"); - return new ConcurrentHashMap<>(); - } - - log.info("Starting async load of {} SAML provider(s)...", activeProviders.size()); - - ExecutorService executor = null; - try { - executor = createExecutor(activeProviders.size()); - final ExecutorService finalExecutor = executor; - Map registrations = new ConcurrentHashMap<>(); - - List> futures = activeProviders.stream() - .map(entity -> loadProviderAsync(entity, registrations, finalExecutor)) - .toList(); - - waitForAllLoads(futures); - logLoadingResults(activeProviders.size(), registrations.size()); - - return registrations; - - } finally { - shutdownExecutorGracefully(executor); - } - } - - /** - * Creates a thread pool executor for concurrent provider loading. - */ - private ExecutorService createExecutor(int providerCount) { - int poolSize = Math.min(MAX_CONCURRENT_LOADS, providerCount); - return Executors.newFixedThreadPool( - poolSize, - r -> { - Thread t = new Thread(r); - t.setName("saml-provider-loader"); - t.setDaemon(true); - t.setUncaughtExceptionHandler((thread, throwable) -> - log.error("Uncaught exception in provider loading thread: {}", - throwable.getMessage(), throwable) - ); - return t; - } - ); - } - - /** - * Loads a single provider asynchronously. - */ - private CompletableFuture loadProviderAsync( - IdentityProviderConfig entity, - Map registrations, - ExecutorService executor) { - - return CompletableFuture.runAsync(() -> { - try { - RelyingPartyRegistration registration = registrationBuilder.buildRegistration(entity); - - if (registration != null) { - registrations.put(entity.getProviderType().name().toLowerCase(), registration); - log.info("Successfully loaded SAML provider: {} (type: {})", - entity.getName(), entity.getProviderType()); - } else { - log.warn("SAML provider '{}' (type: {}) skipped - unable to load registration. " + - "SSO will not be available for this provider type.", - entity.getName(), entity.getProviderType()); - } - } catch (Exception e) { - log.error("Unexpected error loading SAML provider '{}': {}", - entity.getName(), e.getMessage(), e); - } - }, executor); - } - - /** - * Waits for all provider loads to complete with global timeout. - */ - private void waitForAllLoads(List> futures) { - try { - CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) - .get(GLOBAL_LOADING_TIMEOUT.getSeconds(), TimeUnit.SECONDS); - } catch (TimeoutException e) { - log.warn("Provider loading exceeded global timeout of {}s", - GLOBAL_LOADING_TIMEOUT.getSeconds()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.error("Provider loading was interrupted"); - } catch (ExecutionException e) { - log.error("Error during provider loading: {}", e.getMessage()); - } - } - - /** - * Logs summary of loading results. - */ - private void logLoadingResults(int totalProviders, int loadedCount) { - int failedCount = totalProviders - loadedCount; - log.info("SAML provider loading completed: {} loaded, {} failed, {} total", - loadedCount, failedCount, totalProviders); - } - - /** - * Safely shuts down the executor with proper timeout handling. - */ - private void shutdownExecutorGracefully(ExecutorService executor) { - if (executor == null) { - return; - } - - try { - executor.shutdown(); - if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { - log.warn("Executor did not terminate in time, forcing shutdown"); - List droppedTasks = executor.shutdownNow(); - if (!droppedTasks.isEmpty()) { - log.warn("Dropped {} pending tasks during forced shutdown", droppedTasks.size()); - } - - if (!executor.awaitTermination(2, TimeUnit.SECONDS)) { - log.error("Executor did not terminate even after forced shutdown - potential thread leak"); - } - } - } catch (InterruptedException e) { - log.error("Interrupted while waiting for executor termination"); - executor.shutdownNow(); - Thread.currentThread().interrupt(); - } - } -} - diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlRegistrationBuilder.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlRegistrationBuilder.java deleted file mode 100644 index 07766c1b7..000000000 --- a/backend/src/main/java/com/park/utmstack/config/saml/SamlRegistrationBuilder.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.park.utmstack.config.saml; - -import com.park.utmstack.domain.idp_provider.IdentityProviderConfig; -import com.park.utmstack.util.CipherUtil; -import com.park.utmstack.util.saml.PemUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.saml2.core.Saml2X509Credential; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations; - -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -/** - * Responsible for building SAML registration objects. - * Separates credential handling and registration building logic. - */ -@Slf4j -public class SamlRegistrationBuilder { - - private final String encryptionKey; - private final SamlMetadataFetcher metadataFetcher; - - public SamlRegistrationBuilder(String encryptionKey, SamlMetadataFetcher metadataFetcher) { - this.encryptionKey = encryptionKey; - this.metadataFetcher = metadataFetcher; - } - - /** - * Builds a complete SAML registration with timeout protection and error handling. - * Returns null if any step fails. - * - * @param entity Provider configuration - * @return RelyingPartyRegistration, or null if build fails - */ - public RelyingPartyRegistration buildRegistration(IdentityProviderConfig entity) { - try { - // Step 1: Fetch metadata with timeout - RelyingPartyRegistration baseRegistration = metadataFetcher.fetchMetadataWithTimeout(entity); - if (baseRegistration == null) { - log.debug("Skipping provider '{}' - metadata fetch failed", entity.getName()); - return null; - } - - // Step 2: Load and validate credentials - PrivateKey spKey = loadAndDecryptPrivateKey(entity); - if (spKey == null) { - return null; - } - - X509Certificate spCert = loadCertificate(entity); - if (spCert == null) { - return null; - } - - // Step 3: Build final registration with credentials - return buildWithCredentials(baseRegistration, entity, spKey, spCert); - - } catch (Exception e) { - log.error("Unexpected error building SAML registration for provider '{}': {}", - entity.getName(), e.getMessage(), e); - return null; - } - } - - /** - * Loads, decrypts and validates the SP private key. - * Returns null if decryption or parsing fails. - */ - private PrivateKey loadAndDecryptPrivateKey(IdentityProviderConfig entity) { - try { - String decryptedKey = CipherUtil.decrypt(entity.getSpPrivateKeyPem(), this.encryptionKey); - return PemUtils.parsePrivateKey(decryptedKey); - } catch (Exception e) { - log.error("Failed to load/decrypt SP private key for provider '{}': {}", - entity.getName(), e.getMessage(), e); - return null; - } - } - - /** - * Loads and validates the SP certificate. - * Returns null if parsing fails. - */ - private X509Certificate loadCertificate(IdentityProviderConfig entity) { - try { - return PemUtils.parseCertificate(entity.getSpCertificatePem()); - } catch (Exception e) { - log.error("Failed to load SP certificate for provider '{}': {}", - entity.getName(), e.getMessage(), e); - return null; - } - } - - /** - * Configures the registration with SP credentials and custom settings. - */ - private RelyingPartyRegistration buildWithCredentials( - RelyingPartyRegistration baseRegistration, - IdentityProviderConfig entity, - PrivateKey spKey, - X509Certificate spCert) { - - try { - // Note: RelyingPartyRegistration from metadata is already built - // We need to configure it with our SP credentials - // For now, return the base registration built from metadata - // Additional configuration can be done via Spring Security configuration - return RelyingPartyRegistrations - .fromMetadataLocation(entity.getMetadataUrl()) - .registrationId(entity.getProviderType().name().toLowerCase()) - .assertionConsumerServiceLocation(entity.getSpAcsUrl()) - .signingX509Credentials(c -> { - c.add(Saml2X509Credential.signing(spKey, spCert)); - }) - .build(); - } catch (Exception e) { - log.error("Failed to configure SAML registration for provider '{}': {}", - entity.getName(), e.getMessage(), e); - return null; - } - } -} - diff --git a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java index 50fe2305e..d466998af 100644 --- a/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java +++ b/backend/src/main/java/com/park/utmstack/config/saml/SamlRelyingPartyRegistrationRepository.java @@ -3,10 +3,14 @@ import com.park.utmstack.config.Constants; import com.park.utmstack.domain.idp_provider.IdentityProviderConfig; import com.park.utmstack.repository.idp_provider.IdentityProviderConfigRepository; +import com.park.utmstack.util.CipherUtil; +import com.park.utmstack.util.saml.PemUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -15,14 +19,13 @@ public class SamlRelyingPartyRegistrationRepository implements RelyingPartyRegistrationRepository { private volatile Map registrations = new ConcurrentHashMap<>(); - private final SamlProvidersLoader providersLoader; + private final SamlMetadataFetcher fetcher; + private final String encryptionKey; public SamlRelyingPartyRegistrationRepository(IdentityProviderConfigRepository jpaProviderRepository) { - String encryptionKey = getValidatedEncryptionKey(); - SamlMetadataFetcher metadataFetcher = new SamlMetadataFetcher(); - SamlRegistrationBuilder registrationBuilder = new SamlRegistrationBuilder(encryptionKey, metadataFetcher); - this.providersLoader = new SamlProvidersLoader(registrationBuilder); + encryptionKey = getValidatedEncryptionKey(); + fetcher = new SamlMetadataFetcher(); loadProviders(jpaProviderRepository); } @@ -76,9 +79,27 @@ private String getValidatedEncryptionKey() { } private Map loadActiveProviders(IdentityProviderConfigRepository repo) { + + Map map = new ConcurrentHashMap<>(); + List activeProviders = repo.findAllByActiveTrue(); - Map loaded = providersLoader.loadProvidersAsync(activeProviders); - return new ConcurrentHashMap<>(loaded); + + activeProviders.forEach(entity -> { + + PrivateKey spKey = PemUtils.parsePrivateKey(CipherUtil.decrypt( + entity.getSpPrivateKeyPem(), + encryptionKey)); + + X509Certificate spCert = PemUtils.parseCertificate(entity.getSpCertificatePem()); + + RelyingPartyRegistration reg = fetcher.fetch(entity, spKey, spCert); + + if (reg != null) { + map.put(entity.getProviderType().name().toLowerCase(), reg); + } + }); + + return map; } From 007d5b5d12227970cb70adc3bd38e35b6b163b35 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 24 Feb 2026 11:22:46 -0600 Subject: [PATCH 061/115] feat(platforms): enhance platform creation with additional Linux ARM64 support and update Windows service paths Signed-off-by: Manuel Abascal --- .../guide-syslog/guide-syslog.component.ts | 9 -- .../app/app-module/guides/shared/constant.ts | 110 ++++++++++-------- 2 files changed, 63 insertions(+), 56 deletions(-) diff --git a/frontend/src/app/app-module/guides/guide-syslog/guide-syslog.component.ts b/frontend/src/app/app-module/guides/guide-syslog/guide-syslog.component.ts index 0f855f2d7..6f032090a 100644 --- a/frontend/src/app/app-module/guides/guide-syslog/guide-syslog.component.ts +++ b/frontend/src/app/app-module/guides/guide-syslog/guide-syslog.component.ts @@ -16,15 +16,6 @@ export class GuideSyslogComponent implements OnInit { @Input() moduleEnum: UtmModulesEnum; @Input() dataType: string; module = UtmModulesEnum; - moduleImages: SyslogModuleImages[] = [ - {module: UtmModulesEnum.FORTIGATE, img: 'fortigate.png'}, - {module: UtmModulesEnum.UFW, img: 'ufw.png'}, - {module: UtmModulesEnum.MIKROTIK, img: 'mikrotik.png'}, - {module: UtmModulesEnum.PALO_ALTO, img: 'paloalto.png'}, - {module: UtmModulesEnum.SONIC_WALL, img: 'sonicwall.png'}, - {module: UtmModulesEnum.DECEPTIVE_BYTES, img: 'deceptivebytes.png'}, - {module: UtmModulesEnum.SOPHOS_XG, img: 'sophosxg.png'} - ]; syslogPorts: SyslogModulePorts[] = [ {module: UtmModulesEnum.FORTIGATE, port: '7005 TCP'}, diff --git a/frontend/src/app/app-module/guides/shared/constant.ts b/frontend/src/app/app-module/guides/shared/constant.ts index 468dd47fc..96c839d23 100644 --- a/frontend/src/app/app-module/guides/shared/constant.ts +++ b/frontend/src/app/app-module/guides/shared/constant.ts @@ -22,54 +22,69 @@ function createPlatform( path?: string, restart?: string, extraCommands?: string[]): Platform { - return { id, name, command, shell, path, restart, extraCommands }; + return {id, name, command, shell, path, restart, extraCommands}; } export const createPlatforms = ( - windowsCommandAMD64: string, - windowsCommandARM64: string, - linuxCommand: string, - windowsPath?: string, - windowsRestart?: string, - linuxPath?: string, - linuxRestart?: string): Platform[] => [ - createPlatform( - 1, - 'WINDOWS (AMD64)', - windowsCommandAMD64, - WINDOWS_SHELL, - windowsPath, - windowsRestart,[ - 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service.exe" ' + - '-ArgumentList \'load-tls-certs\', \'[YOUR_CERT_PATH]\', \'[YOUR_KEY_PATH]\' ' + - '-NoNewWindow -Wait' - ] - ), - createPlatform( - 2, - 'WINDOWS (ARM64)', - windowsCommandARM64, - WINDOWS_SHELL, - windowsPath, - windowsRestart, - [ - 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_arm64.exe" ' + - '-ArgumentList \'load-tls-certs\', \'[YOUR_CERT_PATH]\', \'[YOUR_KEY_PATH]\' ' + - '-NoNewWindow -Wait' - ] - ), - createPlatform( - 3, - 'LINUX', - linuxCommand, - LINUX_SHELL, - linuxPath, - linuxRestart, - [ - `sudo bash -c "/opt/utmstack-linux-agent/utmstack_agent_service load-tls-certs [YOUR_CERT_PATH] [YOUR_KEY_PATH]"` - ] - ) -]; + windowsCommandAMD64: string, + windowsCommandARM64: string, + linuxCommandAMD64: string, + linuxCommandARM64: string, + windowsPath?: string, + windowsRestart?: string, + linuxPath?: string, + linuxRestart?: string): Platform[] => [ + + createPlatform( + 1, + 'WINDOWS (AMD64)', + windowsCommandAMD64, + WINDOWS_SHELL, + windowsPath, + windowsRestart, [ + 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_amd64.exe" ' + + '-ArgumentList \'load-tls-certs\', \'[YOUR_CERT_PATH]\', \'[YOUR_KEY_PATH]\' ' + + '-NoNewWindow -Wait' + ] + ), + + createPlatform( + 2, + 'WINDOWS (ARM64)', + windowsCommandARM64, + WINDOWS_SHELL, + windowsPath, + windowsRestart, + [ + 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_arm64.exe" ' + + '-ArgumentList \'load-tls-certs\', \'[YOUR_CERT_PATH]\', \'[YOUR_KEY_PATH]\' ' + + '-NoNewWindow -Wait' + ] + ), + createPlatform( + 3, + 'LINUX (AMD64)', + linuxCommandAMD64, + LINUX_SHELL, + linuxPath, + linuxRestart, + [ + `sudo bash -c "/opt/utmstack-linux-agent/utmstack_agent_service load-tls-certs [YOUR_CERT_PATH] [YOUR_KEY_PATH]"` + ] + ), + createPlatform( + 3, + 'LINUX (ARM64)', + linuxCommandARM64, + LINUX_SHELL, + linuxPath, + linuxRestart, + [ + `sudo bash -c "/opt/utmstack-linux-agent/utmstack_agent_service load-tls-certs [YOUR_CERT_PATH] [YOUR_KEY_PATH]"` + ] + ) + ] +; export const createFileBeatsPlatforms = ( windowsCommand: string, @@ -97,9 +112,10 @@ export const createFileBeatsPlatforms = ( ]; export const PLATFORMS = createPlatforms( - 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service.exe" -ArgumentList \'ACTION\', \'AGENT_NAME\', \'PROTOCOL\', \'TLS\' -NoNewWindow -Wait\n', + 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_amd64.exe " -ArgumentList \'ACTION\', \'AGENT_NAME\', \'PROTOCOL\', \'TLS\' -NoNewWindow -Wait\n', 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_arm64.exe" -ArgumentList \'ACTION\', \'AGENT_NAME\', \'PROTOCOL\, \'TLS\' -NoNewWindow -Wait\n', - 'sudo bash -c "/opt/utmstack-linux-agent/utmstack_agent_service ACTION AGENT_NAME PROTOCOL TLS"' + 'sudo bash -c "/opt/utmstack-linux-agent/utmstack_agent_service_amd64 ACTION AGENT_NAME PROTOCOL TLS"', + 'sudo bash -c "/opt/utmstack-linux-agent/utmstack_agent_service_arm64 ACTION AGENT_NAME PROTOCOL TLS"' ); export const FILEBEAT_PLATFORMS = createFileBeatsPlatforms( From 82f7acef3e8a7b618b6ab528fcf503f5cf97e144 Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Tue, 24 Feb 2026 21:01:17 +0000 Subject: [PATCH 062/115] fix(config): detect filter and rule deletions by tracking active row counts hasChanges only checked MAX(timestamp) increases, missing deletions where the timestamp didn't advance. Now also compares COUNT of active rows so deactivations and hard deletes trigger config file regeneration. Co-Authored-By: Claude Opus 4.6 --- plugins/config/main.go | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/plugins/config/main.go b/plugins/config/main.go index 3db45fca5..5d596d59f 100644 --- a/plugins/config/main.go +++ b/plugins/config/main.go @@ -75,9 +75,13 @@ type ExpressionBackend struct { type ConfigState struct { AssetsLastUpdate time.Time + AssetsCount int RulesLastUpdate time.Time + RulesCount int FiltersLastUpdate time.Time + FiltersCount int PatternsLastUpdate time.Time + PatternsCount int } func (b *ExpressionBackend) ToExpression() Expression { @@ -431,26 +435,35 @@ func hasChanges(db *sql.DB, state *ConfigState) (bool, ConfigState, error) { changed := false queries := []struct { - query string - target *time.Time - old time.Time + timestampQuery string + countQuery string + targetTime *time.Time + targetCount *int + oldTime time.Time + oldCount int }{ - {"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}, + {"SELECT MAX(last_update) FROM utm_tenant_config", "SELECT COUNT(*) FROM utm_tenant_config", &newState.AssetsLastUpdate, &newState.AssetsCount, state.AssetsLastUpdate, state.AssetsCount}, + {"SELECT MAX(rule_last_update) FROM utm_correlation_rules", "SELECT COUNT(*) FROM utm_correlation_rules WHERE rule_active = true", &newState.RulesLastUpdate, &newState.RulesCount, state.RulesLastUpdate, state.RulesCount}, + {"SELECT MAX(updated_at) FROM utm_logstash_filter", "SELECT COUNT(*) FROM utm_logstash_filter WHERE is_active = true", &newState.FiltersLastUpdate, &newState.FiltersCount, state.FiltersLastUpdate, state.FiltersCount}, + {"SELECT MAX(last_update) FROM utm_regex_pattern", "SELECT COUNT(*) FROM utm_regex_pattern", &newState.PatternsLastUpdate, &newState.PatternsCount, state.PatternsLastUpdate, state.PatternsCount}, } for _, q := range queries { var lastUpdate sql.NullTime - err := db.QueryRow(q.query).Scan(&lastUpdate) + err := db.QueryRow(q.timestampQuery).Scan(&lastUpdate) if err != nil { return false, newState, err } if lastUpdate.Valid { - *q.target = lastUpdate.Time + *q.targetTime = lastUpdate.Time } - if (*q.target).After(q.old) { + + err = db.QueryRow(q.countQuery).Scan(q.targetCount) + if err != nil { + return false, newState, err + } + + if (*q.targetTime).After(q.oldTime) || *q.targetCount != q.oldCount { changed = true } } From d570851c52c86b4e2fc83094acb4f86bbe09f300 Mon Sep 17 00:00:00 2001 From: Yadian Llada Lopez Date: Tue, 24 Feb 2026 16:06:55 -0500 Subject: [PATCH 063/115] Update go-sdk dependency across multiple plugins --- plugins/alerts/go.mod | 2 +- plugins/alerts/go.sum | 4 ++-- plugins/aws/go.mod | 2 +- plugins/aws/go.sum | 4 ++-- plugins/azure/go.mod | 2 +- plugins/azure/go.sum | 4 ++-- plugins/bitdefender/go.mod | 2 +- plugins/bitdefender/go.sum | 4 ++-- plugins/config/go.mod | 2 +- plugins/config/go.sum | 4 ++-- plugins/crowdstrike/go.mod | 2 +- plugins/crowdstrike/go.sum | 4 ++-- plugins/events/go.mod | 2 +- plugins/events/go.sum | 4 ++-- plugins/feeds/go.mod | 2 +- plugins/feeds/go.sum | 4 ++-- plugins/gcp/go.mod | 2 +- plugins/gcp/go.sum | 4 ++-- plugins/geolocation/go.mod | 2 +- plugins/geolocation/go.sum | 25 +++++++++++++++++++++++++ plugins/inputs/go.mod | 2 +- plugins/inputs/go.sum | 4 ++-- plugins/modules-config/go.mod | 2 +- plugins/modules-config/go.sum | 4 ++-- plugins/o365/go.mod | 2 +- plugins/o365/go.sum | 4 ++-- plugins/sophos/go.mod | 2 +- plugins/sophos/go.sum | 4 ++-- plugins/stats/go.mod | 2 +- plugins/stats/go.sum | 4 ++-- utmstack-collector/go.mod | 2 +- utmstack-collector/go.sum | 4 ++-- 32 files changed, 71 insertions(+), 46 deletions(-) diff --git a/plugins/alerts/go.mod b/plugins/alerts/go.mod index ec1fd100d..37e0a8559 100644 --- a/plugins/alerts/go.mod +++ b/plugins/alerts/go.mod @@ -3,7 +3,7 @@ module github.com/utmstack/UTMStack/plugins/alerts go 1.25.5 require ( - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 github.com/tidwall/gjson v1.18.0 google.golang.org/protobuf v1.36.11 ) diff --git a/plugins/alerts/go.sum b/plugins/alerts/go.sum index 1abc5a7c4..fb814cb43 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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/aws/go.mod b/plugins/aws/go.mod index 3e66b6e26..f76bb3dae 100644 --- a/plugins/aws/go.mod +++ b/plugins/aws/go.mod @@ -7,7 +7,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.32.8 github.com/aws/aws-sdk-go-v2/credentials v1.19.8 github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 ) require ( diff --git a/plugins/aws/go.sum b/plugins/aws/go.sum index f967d7975..24f0c5b67 100644 --- a/plugins/aws/go.sum +++ b/plugins/aws/go.sum @@ -122,8 +122,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/azure/go.mod b/plugins/azure/go.mod index 55ed03781..7af3ef831 100644 --- a/plugins/azure/go.mod +++ b/plugins/azure/go.mod @@ -7,7 +7,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs/v2 v2.0.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) diff --git a/plugins/azure/go.sum b/plugins/azure/go.sum index 88b1689df..8041018ed 100644 --- a/plugins/azure/go.sum +++ b/plugins/azure/go.sum @@ -122,8 +122,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/bitdefender/go.mod b/plugins/bitdefender/go.mod index d251061a0..f23ff74f3 100644 --- a/plugins/bitdefender/go.mod +++ b/plugins/bitdefender/go.mod @@ -5,7 +5,7 @@ go 1.25.5 require ( github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) diff --git a/plugins/bitdefender/go.sum b/plugins/bitdefender/go.sum index 2eb91827c..f5cf22703 100644 --- a/plugins/bitdefender/go.sum +++ b/plugins/bitdefender/go.sum @@ -92,8 +92,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/config/go.mod b/plugins/config/go.mod index 959755c23..b94d8e1dd 100644 --- a/plugins/config/go.mod +++ b/plugins/config/go.mod @@ -4,7 +4,7 @@ go 1.25.5 require ( github.com/lib/pq v1.11.2 - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/yaml v1.6.0 ) diff --git a/plugins/config/go.sum b/plugins/config/go.sum index 6f922c27f..106bc7804 100644 --- a/plugins/config/go.sum +++ b/plugins/config/go.sum @@ -90,8 +90,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/crowdstrike/go.mod b/plugins/crowdstrike/go.mod index 3dbaf59a6..81a249a21 100644 --- a/plugins/crowdstrike/go.mod +++ b/plugins/crowdstrike/go.mod @@ -5,7 +5,7 @@ go 1.25.5 require ( github.com/crowdstrike/gofalcon v0.19.0 github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) diff --git a/plugins/crowdstrike/go.sum b/plugins/crowdstrike/go.sum index 494a0d183..0ef3a0f64 100644 --- a/plugins/crowdstrike/go.sum +++ b/plugins/crowdstrike/go.sum @@ -149,8 +149,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/events/go.mod b/plugins/events/go.mod index 01104171c..b5edeb8c1 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.14 + github.com/threatwinds/go-sdk v1.1.15 github.com/tidwall/gjson v1.18.0 ) diff --git a/plugins/events/go.sum b/plugins/events/go.sum index 1abc5a7c4..fb814cb43 100644 --- a/plugins/events/go.sum +++ b/plugins/events/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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/feeds/go.mod b/plugins/feeds/go.mod index f8fa84d58..96c2044a9 100644 --- a/plugins/feeds/go.mod +++ b/plugins/feeds/go.mod @@ -5,7 +5,7 @@ go 1.25.5 require ( github.com/AtlasInsideCorp/AtlasInsideAES v1.0.0 github.com/opensearch-project/opensearch-go/v2 v2.3.0 - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 golang.org/x/sync v0.19.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/plugins/feeds/go.sum b/plugins/feeds/go.sum index f8fdd174d..a8c1262b1 100644 --- a/plugins/feeds/go.sum +++ b/plugins/feeds/go.sum @@ -110,8 +110,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/gcp/go.mod b/plugins/gcp/go.mod index ca74803b1..f95db4dc9 100644 --- a/plugins/gcp/go.mod +++ b/plugins/gcp/go.mod @@ -5,7 +5,7 @@ go 1.25.5 require ( cloud.google.com/go/pubsub v1.50.1 github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 google.golang.org/api v0.267.0 google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 diff --git a/plugins/gcp/go.sum b/plugins/gcp/go.sum index 71153b223..02a528ce1 100644 --- a/plugins/gcp/go.sum +++ b/plugins/gcp/go.sum @@ -158,8 +158,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/geolocation/go.mod b/plugins/geolocation/go.mod index 2b49180db..8cc022fe6 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.14 + github.com/threatwinds/go-sdk v1.1.15 github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 ) diff --git a/plugins/geolocation/go.sum b/plugins/geolocation/go.sum index a37652182..ad1333a84 100644 --- a/plugins/geolocation/go.sum +++ b/plugins/geolocation/go.sum @@ -90,6 +90,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/threatwinds/go-sdk v1.1.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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= @@ -111,14 +113,19 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -127,25 +134,43 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y= +golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= +golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= 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-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/api v0.0.0-20260223185530-2f722ef697dc h1:ULD+ToGXUIU6Pkzr1ARxdyvwfHbelw+agoFDRbLg4TU= +google.golang.org/genproto/googleapis/api v0.0.0-20260223185530-2f722ef697dc/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng= google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260223185530-2f722ef697dc h1:51Wupg8spF+5FC6D+iMKbOddFjMckETnNnEiZ+HX37s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260223185530-2f722ef697dc/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= 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/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugins/inputs/go.mod b/plugins/inputs/go.mod index 906942c47..f9f500cce 100644 --- a/plugins/inputs/go.mod +++ b/plugins/inputs/go.mod @@ -5,7 +5,7 @@ go 1.25.5 require ( github.com/gin-gonic/gin v1.11.0 github.com/google/uuid v1.6.0 - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) diff --git a/plugins/inputs/go.sum b/plugins/inputs/go.sum index 7402b79de..72d534d36 100644 --- a/plugins/inputs/go.sum +++ b/plugins/inputs/go.sum @@ -90,8 +90,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/modules-config/go.mod b/plugins/modules-config/go.mod index 5f06af9bb..c733f16ba 100644 --- a/plugins/modules-config/go.mod +++ b/plugins/modules-config/go.mod @@ -12,7 +12,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 github.com/crowdstrike/gofalcon v0.19.0 github.com/gin-gonic/gin v1.11.0 - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 google.golang.org/api v0.267.0 google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 diff --git a/plugins/modules-config/go.sum b/plugins/modules-config/go.sum index 48bf6b99f..529be11de 100644 --- a/plugins/modules-config/go.sum +++ b/plugins/modules-config/go.sum @@ -280,8 +280,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/o365/go.mod b/plugins/o365/go.mod index 9fc336715..53c619ae0 100644 --- a/plugins/o365/go.mod +++ b/plugins/o365/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.14 + github.com/threatwinds/go-sdk v1.1.15 google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) diff --git a/plugins/o365/go.sum b/plugins/o365/go.sum index 7402b79de..72d534d36 100644 --- a/plugins/o365/go.sum +++ b/plugins/o365/go.sum @@ -90,8 +90,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/sophos/go.mod b/plugins/sophos/go.mod index 6bd956d58..4dfeb6264 100644 --- a/plugins/sophos/go.mod +++ b/plugins/sophos/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.14 + github.com/threatwinds/go-sdk v1.1.15 google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 ) diff --git a/plugins/sophos/go.sum b/plugins/sophos/go.sum index 7402b79de..72d534d36 100644 --- a/plugins/sophos/go.sum +++ b/plugins/sophos/go.sum @@ -90,8 +90,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/stats/go.mod b/plugins/stats/go.mod index 8f5733dc0..18ca43b23 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.14 + github.com/threatwinds/go-sdk v1.1.15 google.golang.org/protobuf v1.36.11 ) diff --git a/plugins/stats/go.sum b/plugins/stats/go.sum index 1abc5a7c4..fb814cb43 100644 --- a/plugins/stats/go.sum +++ b/plugins/stats/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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= 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/utmstack-collector/go.mod b/utmstack-collector/go.mod index 166d9165b..1260dd667 100644 --- a/utmstack-collector/go.mod +++ b/utmstack-collector/go.mod @@ -9,7 +9,7 @@ require ( github.com/glebarez/sqlite v1.11.0 github.com/google/uuid v1.6.0 github.com/kardianos/service v1.2.4 - github.com/threatwinds/go-sdk v1.1.14 + github.com/threatwinds/go-sdk v1.1.15 github.com/threatwinds/logger v1.2.3 google.golang.org/grpc v1.79.1 google.golang.org/protobuf v1.36.11 diff --git a/utmstack-collector/go.sum b/utmstack-collector/go.sum index ba290d189..51823e3b5 100644 --- a/utmstack-collector/go.sum +++ b/utmstack-collector/go.sum @@ -161,8 +161,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.14 h1:9XqqGPZvDHHuJ/XkfMsDl3fe7Adfi1fMh/PpQFkUkJU= -github.com/threatwinds/go-sdk v1.1.14/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= +github.com/threatwinds/go-sdk v1.1.15 h1:LvyaNT78y0whlq9ioR0aRKcRCxt0gX0s90X9z1fF5c4= +github.com/threatwinds/go-sdk v1.1.15/go.mod h1:Kfu26gkSZDpNNkPvuQbTAW3dWIQ66pVIrNYW1YBG3Kg= github.com/threatwinds/logger v1.2.3 h1:V2SVAXzbq+/huCvIWOfqzMTH+WBHJxankyBgVG2hy1Y= github.com/threatwinds/logger v1.2.3/go.mod h1:N+bJKvF4FQNJZLfQpVYWpr6D8iEAFnAQfHYqH5iR1TI= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= From 7e4a0c0d9b471a769391392205f50d1b72b55541 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 24 Feb 2026 15:10:17 -0600 Subject: [PATCH 064/115] feat(logstash): integrate Monaco Editor for YAML filter definition and enhance form styling Signed-off-by: Manuel Abascal --- .../logstash-filter-create.component.html | 96 +++++++------- .../logstash-filter-create.component.scss | 117 ++++++++++++++++-- .../logstash-filter-create.component.ts | 54 +++++++- frontend/src/app/logstash/logstash.module.ts | 27 ++-- 4 files changed, 223 insertions(+), 71 deletions(-) diff --git a/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.html b/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.html index acc79bedf..9b27fc43a 100644 --- a/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.html +++ b/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.html @@ -1,4 +1,4 @@ -
+
Read the logs filter documentation at
-
+
-
- - -
-
-
- - - -
- Data type is required. +
+ + +
+
+
+ + + +
+ Data type is required. +
-
-
- - -
-
- - -
+
+ +
+ + +
+
+ +
+ + +
diff --git a/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.scss b/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.scss index 98651c800..406f9663b 100644 --- a/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.scss +++ b/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.scss @@ -1,21 +1,112 @@ -:host ::ng-deep json-editor, -:host ::ng-deep json-editor .jsoneditor, -:host ::ng-deep json-editor > div, -:host ::ng-deep json-editor jsoneditor-outer { - height: 90% !important; - border: none !important; +:host { + display: block; + height: auto; } +.template-editor { + height: 800px!important; +} + +.logstash-filter-wrapper { + display: flex; + flex-direction: column; + width: 100%; + height: auto; + padding: 0.5rem; + gap: 1rem; +} + +.logstash-filter-form { + display: flex; + flex-direction: column; + flex: 1; + gap: 1rem; +} + +.yaml-editor-group { + display: flex; + flex-direction: column; + flex: 1; + min-height: 600px; + margin: 0 !important; + + label { + margin-bottom: 0.5rem; + font-weight: 500; + } +} + +.yaml-editor-container { + border: 1px solid #d9d9d9; + border-radius: 4px; + overflow: hidden; + height: 600px; + width: 100%; + flex: 1; + + ::ng-deep { + .monaco-editor { + width: 100% !important; + height: 100% !important; + } + + .monaco-editor-background { + background-color: #f5f5f5 !important; + } + + .overflow-guard { + width: 100% !important; + height: 100% !important; + } + + .monaco-scrollable-element { + width: 100% !important; + height: 100% !important; + } -::ng-deep { - .jsoneditor { - .jsoneditor-menu { - background-color: #232f3e !important; - border-bottom: 0 solid transparent !important; + .monaco-editor.readonly { + background-color: #f0f0f0 !important; + opacity: 0.8 !important; + } + + .monaco-editor.readonly .monaco-editor-background { + background-color: #f0f0f0 !important; + } + + // Syntax highlighting colors for YAML + .mtk5 { + color: #0277bd !important; // Keys - Blue + font-weight: 600 !important; + } + + .mtk1 { + color: #ff6b6b !important; // Values - Red } } +} + +.form-actions { + flex-shrink: 0; + margin-top: 1rem !important; +} - .pico-modal-header { - background-color: #232f3e !important; +@media screen and (max-height: 1000px) { + .yaml-editor-group { + min-height: 500px; + } + + .yaml-editor-container { + height: 500px; + } +} + +@media screen and (min-height: 1200px) { + .yaml-editor-group { + min-height: 700px; + } + + .yaml-editor-container { + height: 700px; } } + diff --git a/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.ts b/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.ts index 31b54d600..0d2529494 100644 --- a/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.ts +++ b/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.ts @@ -1,4 +1,4 @@ -import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {Component, EventEmitter, Input, OnInit, Output, OnChanges, SimpleChanges, ViewChild, AfterViewInit} from '@angular/core'; import {Observable} from 'rxjs'; import {DataType} from '../../../rule-management/models/rule.model'; import {DataTypeService} from '../../../rule-management/services/data-type.service'; @@ -12,13 +12,15 @@ import {UtmPipeline} from '../../shared/types/logstash-stats.type'; templateUrl: './logstash-filter-create.component.html', styleUrls: ['./logstash-filter-create.component.scss'] }) -export class LogstashFilterCreateComponent implements OnInit { +export class LogstashFilterCreateComponent implements OnInit, OnChanges { @Output() filterEditClose = new EventEmitter(); @Output() close = new EventEmitter(); @Input() filter: LogstashFilterType; @Input() pipeline: UtmPipeline; @Input() dataType: DataType; @Input() disable = false; + @ViewChild('editorContainer') editorContainer: any; + types$: Observable; daTypeRequest: {page: number, size: number} = { page: -1, @@ -29,6 +31,24 @@ export class LogstashFilterCreateComponent implements OnInit { show = true; error = false; loadingDataTypes = false; + private editorInstance: any; + + editorOptions: any = { + theme: 'vs-light', + language: 'yaml', + automaticLayout: true, + fontSize: 13, + fontFamily: 'Courier New, Monaco, Menlo, monospace', + lineNumbers: 'on', + wordWrap: 'on', + minimap: { enabled: false }, + scrollBeyondLastLine: false, + formatOnPaste: true, + formatOnType: true, + tabSize: 2, + insertSpaces: true, + readOnly: false + }; constructor(private logstashFilterService: LogstashService, private utmToastService: UtmToastService, @@ -42,7 +62,33 @@ export class LogstashFilterCreateComponent implements OnInit { this.types$ = this.dataTypeService.type$; this.loadDataTypes(); + this.updateEditorOptions(); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes && changes['disable']) { + this.updateEditorOptions(); + } + } + + private forceEditorLayout(): void { + try { + if (this.editorContainer) { + const editorInstance = (this.editorContainer as any).editor; + if (editorInstance && editorInstance.layout) { + editorInstance.layout(); + } + } + } catch (e) { + console.warn('Error al forzar layout del editor Monaco:', e); + } + } + private updateEditorOptions(): void { + this.editorOptions = { + ...this.editorOptions, + readOnly: this.disable + }; } @@ -94,4 +140,8 @@ export class LogstashFilterCreateComponent implements OnInit { trackByFn(type: DataType) { return type.id; } + + onEditorChange(value: string): void { + this.filter.logstashFilter = value; + } } diff --git a/frontend/src/app/logstash/logstash.module.ts b/frontend/src/app/logstash/logstash.module.ts index 23ba9bf8e..6b74f42e3 100644 --- a/frontend/src/app/logstash/logstash.module.ts +++ b/frontend/src/app/logstash/logstash.module.ts @@ -2,29 +2,30 @@ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {NgbModule} from '@ng-bootstrap/ng-bootstrap'; +import {NgSelectModule} from '@ng-select/ng-select'; import {InlineSVGModule} from 'ng-inline-svg'; +import {MonacoEditorModule} from 'ngx-monaco-editor'; import {AlertManagementSharedModule} from '../data-management/alert-management/shared/alert-management-shared.module'; import {UtmSharedModule} from '../shared/utm-shared.module'; import {LogstashFilterCreateComponent} from './logstash-filters/logstash-filter-create/logstash-filter-create.component'; import {LogstashFiltersComponent} from './logstash-filters/logstash-filters.component'; import {LogstashPipelinesComponent} from './logstash-pipelines/logstash-pipelines.component'; import {LogstashRoutingModule} from './logstash-routing.module'; -import {NgSelectModule} from '@ng-select/ng-select'; -import {DataTypeService} from '../rule-management/services/data-type.service'; @NgModule({ declarations: [LogstashFiltersComponent, LogstashFilterCreateComponent, LogstashPipelinesComponent], - imports: [ - CommonModule, - UtmSharedModule, - FormsModule, - NgbModule, - LogstashRoutingModule, - InlineSVGModule, - AlertManagementSharedModule, - NgSelectModule, - ReactiveFormsModule - ], + imports: [ + CommonModule, + UtmSharedModule, + FormsModule, + NgbModule, + LogstashRoutingModule, + InlineSVGModule, + AlertManagementSharedModule, + NgSelectModule, + ReactiveFormsModule, + MonacoEditorModule + ], entryComponents: [LogstashFilterCreateComponent] }) export class LogstashModule { From 251899500b02ed7c0c0b7d1ac43fb00160dcd898 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 25 Feb 2026 08:01:54 -0600 Subject: [PATCH 065/115] feat: update filter card interaction to improve usability Signed-off-by: Manuel Abascal --- .../logstash/logstash-filters/logstash-filters.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/logstash/logstash-filters/logstash-filters.component.html b/frontend/src/app/logstash/logstash-filters/logstash-filters.component.html index f2e37d578..aef7de0ea 100644 --- a/frontend/src/app/logstash/logstash-filters/logstash-filters.component.html +++ b/frontend/src/app/logstash/logstash-filters/logstash-filters.component.html @@ -20,9 +20,9 @@
Pipeline filters
-
+
-
+
Date: Wed, 25 Feb 2026 08:51:05 -0600 Subject: [PATCH 066/115] feat(rule-view): integrate Monaco Editor for YAML editing and enhance styling Signed-off-by: Manuel Abascal --- .../logstash-filter-create.component.scss | 19 +++-- .../see-rule/rule-view.component.html | 14 +++- .../see-rule/rule-view.component.scss | 81 +++++++++++++------ .../see-rule/rule-view.component.ts | 31 ++++--- .../rule-management/rule-management.module.ts | 4 +- 5 files changed, 105 insertions(+), 44 deletions(-) diff --git a/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.scss b/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.scss index 406f9663b..070803090 100644 --- a/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.scss +++ b/frontend/src/app/logstash/logstash-filters/logstash-filter-create/logstash-filter-create.component.scss @@ -73,14 +73,23 @@ background-color: #f0f0f0 !important; } - // Syntax highlighting colors for YAML - .mtk5 { - color: #0277bd !important; // Keys - Blue + .mtk7 { + color: #2d7a1a !important; + font-weight: 600 !important; + } + + .mtk22 { + color: #0055cc !important; font-weight: 600 !important; } - .mtk1 { - color: #ff6b6b !important; // Values - Red + .mtk1, + .mtk5 { + color: #666666 !important; + } + + .mtk3 { + color: #c26e00 !important; } } } 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 3780f27e1..e091c1b4d 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 @@ -16,6 +16,16 @@
-
-

+
+ +
+ + 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 2025c4be1..f57c8e216 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 @@ -35,29 +35,6 @@ app-utm-modal-header { } } -.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; @@ -110,3 +87,61 @@ app-utm-modal-header { } } +.rule-view-container { + border: 1px solid #d9d9d9; + border-radius: 4px; + overflow: hidden; + height: 600px; + width: 100%; + flex: 1; + + ::ng-deep { + .monaco-editor { + width: 100% !important; + height: 100% !important; + } + + .monaco-editor-background { + background-color: #f5f5f5 !important; + } + + .overflow-guard { + width: 100% !important; + height: 100% !important; + } + + .monaco-scrollable-element { + width: 100% !important; + height: 100% !important; + } + + .monaco-editor.readonly { + background-color: #f0f0f0 !important; + opacity: 0.8 !important; + } + + .monaco-editor.readonly .monaco-editor-background { + background-color: #f0f0f0 !important; + } + + .mtk7 { + color: #2d7a1a !important; + font-weight: 600 !important; + } + + .mtk22 { + color: #0055cc !important; + font-weight: 600 !important; + } + + .mtk1, + .mtk5 { + color: #666666 !important; + } + + .mtk3 { + color: #c26e00 !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 d9c0696ba..39bd08cc5 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 @@ -8,14 +8,27 @@ import {Rule} from '../../../models/rule.model'; templateUrl: './rule-view.component.html', styleUrls: ['./rule-view.component.scss'], }) -export class RuleViewComponent implements OnInit { +export class RuleViewComponent { @Input() rowDocument: Rule; copied = false; - - ngOnInit() { - console.log('Rule received:', this.rowDocument); - } + editorOptions: any = { + theme: 'vs-light', + language: 'yaml', + automaticLayout: true, + fontSize: 13, + lineHeight: 24, + fontFamily: 'Courier New, Monaco, Menlo, monospace', + lineNumbers: 'on', + wordWrap: 'on', + minimap: { enabled: false }, + scrollBeyondLastLine: false, + formatOnPaste: true, + formatOnType: true, + tabSize: 2, + insertSpaces: true, + readOnly: true + }; get yamlString(): string { try { @@ -33,14 +46,6 @@ export class RuleViewComponent implements OnInit { } } - 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; diff --git a/frontend/src/app/rule-management/rule-management.module.ts b/frontend/src/app/rule-management/rule-management.module.ts index 6d4b76f2b..f3b5fe362 100644 --- a/frontend/src/app/rule-management/rule-management.module.ts +++ b/frontend/src/app/rule-management/rule-management.module.ts @@ -46,6 +46,7 @@ import {RuleService} from './services/rule.service'; import {RulesResolverService} from './services/rules.resolver.service'; import {GenericFilterComponent} from './share/generic-filter/generic-filter.component'; import {RuleDetailComponent} from "./app-rule/components/rule-list/components/rule-detail/rule-detail.component"; +import {MonacoEditorModule} from "ngx-monaco-editor"; @NgModule({ @@ -87,7 +88,8 @@ import {RuleDetailComponent} from "./app-rule/components/rule-list/components/ru NgSelectModule, InfiniteScrollModule, FileBrowserModule, - FormsModule + FormsModule, + MonacoEditorModule ], providers: [ RuleService, From f9e89930dcf03b03717d57fd8989efaa632f0f22 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 25 Feb 2026 09:21:17 -0600 Subject: [PATCH 067/115] feat: update Windows service paths for UTMStack agent Signed-off-by: Manuel Abascal --- .../src/app/app-module/guides/shared/constant.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/app-module/guides/shared/constant.ts b/frontend/src/app/app-module/guides/shared/constant.ts index 96c839d23..5a0a93036 100644 --- a/frontend/src/app/app-module/guides/shared/constant.ts +++ b/frontend/src/app/app-module/guides/shared/constant.ts @@ -42,7 +42,7 @@ export const createPlatforms = ( WINDOWS_SHELL, windowsPath, windowsRestart, [ - 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_amd64.exe" ' + + 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_windows_amd64.exe" ' + '-ArgumentList \'load-tls-certs\', \'[YOUR_CERT_PATH]\', \'[YOUR_KEY_PATH]\' ' + '-NoNewWindow -Wait' ] @@ -56,7 +56,7 @@ export const createPlatforms = ( windowsPath, windowsRestart, [ - 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_arm64.exe" ' + + 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_windows_arm64.exe" ' + '-ArgumentList \'load-tls-certs\', \'[YOUR_CERT_PATH]\', \'[YOUR_KEY_PATH]\' ' + '-NoNewWindow -Wait' ] @@ -112,10 +112,10 @@ export const createFileBeatsPlatforms = ( ]; export const PLATFORMS = createPlatforms( - 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_amd64.exe " -ArgumentList \'ACTION\', \'AGENT_NAME\', \'PROTOCOL\', \'TLS\' -NoNewWindow -Wait\n', - 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_arm64.exe" -ArgumentList \'ACTION\', \'AGENT_NAME\', \'PROTOCOL\, \'TLS\' -NoNewWindow -Wait\n', - 'sudo bash -c "/opt/utmstack-linux-agent/utmstack_agent_service_amd64 ACTION AGENT_NAME PROTOCOL TLS"', - 'sudo bash -c "/opt/utmstack-linux-agent/utmstack_agent_service_arm64 ACTION AGENT_NAME PROTOCOL TLS"' + 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_windows_amd64.exe " -ArgumentList \'ACTION\', \'AGENT_NAME\', \'PROTOCOL\', \'TLS\' -NoNewWindow -Wait\n', + 'Start-Process "C:\\Program Files\\UTMStack\\UTMStack Agent\\utmstack_agent_service_windows_arm64.exe" -ArgumentList \'ACTION\', \'AGENT_NAME\', \'PROTOCOL\, \'TLS\' -NoNewWindow -Wait\n', + 'sudo bash -c "/opt/utmstack-linux-agent/utmstack_agent_service_linux_amd64 ACTION AGENT_NAME PROTOCOL TLS"', + 'sudo bash -c "/opt/utmstack-linux-agent/utmstack_agent_service_linux_arm64 ACTION AGENT_NAME PROTOCOL TLS"' ); export const FILEBEAT_PLATFORMS = createFileBeatsPlatforms( From 4b8e4d7cc8041b90feddb07d0d5a2104046cb868 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 25 Feb 2026 10:46:39 -0600 Subject: [PATCH 068/115] feat(db): add unique constraint on asset_name in utm_tenant_config table --- ...0260225001_add_unique_constraint_asset_name.xml | 14 ++++++++++++++ .../src/main/resources/config/liquibase/master.xml | 2 ++ 2 files changed, 16 insertions(+) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260225001_add_unique_constraint_asset_name.xml diff --git a/backend/src/main/resources/config/liquibase/changelog/20260225001_add_unique_constraint_asset_name.xml b/backend/src/main/resources/config/liquibase/changelog/20260225001_add_unique_constraint_asset_name.xml new file mode 100644 index 000000000..cadac062f --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260225001_add_unique_constraint_asset_name.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 8603507cc..d37fad4e8 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -477,5 +477,7 @@ + + From be06aadebea70ea89608a151348ae102d03dd8b2 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 25 Feb 2026 13:42:44 -0600 Subject: [PATCH 069/115] feat(tenant-config): add findByAssetName method to retrieve UtmTenantConfig by asset name --- .../correlation/config/UtmTenantConfigRepository.java | 5 +++++ .../correlation/config/UtmTenantConfigService.java | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/backend/src/main/java/com/park/utmstack/repository/correlation/config/UtmTenantConfigRepository.java b/backend/src/main/java/com/park/utmstack/repository/correlation/config/UtmTenantConfigRepository.java index 34fe0e922..4719e200f 100644 --- a/backend/src/main/java/com/park/utmstack/repository/correlation/config/UtmTenantConfigRepository.java +++ b/backend/src/main/java/com/park/utmstack/repository/correlation/config/UtmTenantConfigRepository.java @@ -9,10 +9,15 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import java.util.Optional; + public interface UtmTenantConfigRepository extends JpaRepository, JpaSpecificationExecutor { + @Query(value = "SELECT t FROM UtmTenantConfig t WHERE" + "(:search IS NULL OR ((t.assetName LIKE :search OR lower(t.assetName) LIKE lower(:search))))") Page searchByFilters(@Param("search") String search, Pageable pageable); + + Optional findByAssetName(String assetName); } diff --git a/backend/src/main/java/com/park/utmstack/service/correlation/config/UtmTenantConfigService.java b/backend/src/main/java/com/park/utmstack/service/correlation/config/UtmTenantConfigService.java index 3f752c079..f80ca56ce 100644 --- a/backend/src/main/java/com/park/utmstack/service/correlation/config/UtmTenantConfigService.java +++ b/backend/src/main/java/com/park/utmstack/service/correlation/config/UtmTenantConfigService.java @@ -118,6 +118,15 @@ public Optional findOne(Long id) { } } + public Optional findByAssetName(String assetName) { + final String ctx = CLASSNAME + ".findByAssetName"; + try { + return utmTenantConfigRepository.findByAssetName(assetName); + } catch (Exception e) { + throw new RuntimeException(ctx + ": " + e.getMessage()); + } + } + /** * Get all UtmTenantConfig. * From 89d7e8cda29ebde4350410f20656147b51ef4d60 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 25 Feb 2026 14:23:39 -0600 Subject: [PATCH 070/115] feat(data-input-status): add methods to retrieve data input status by source and build sources list from tenant config --- .../UtmDataInputStatusRepository.java | 2 + .../service/UtmDataInputStatusService.java | 78 ++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/repository/datainput_ingestion/UtmDataInputStatusRepository.java b/backend/src/main/java/com/park/utmstack/repository/datainput_ingestion/UtmDataInputStatusRepository.java index 47be50866..a167dde27 100644 --- a/backend/src/main/java/com/park/utmstack/repository/datainput_ingestion/UtmDataInputStatusRepository.java +++ b/backend/src/main/java/com/park/utmstack/repository/datainput_ingestion/UtmDataInputStatusRepository.java @@ -65,4 +65,6 @@ and lower(trim(ds.dataType)) != lower(trim(:dataType)) @Query("SELECT s FROM UtmDataInputStatus s WHERE s.source = :ip OR s.source = :hostname") List findByIpOrHostname(@Param("ip") String ip, @Param("hostname") String hostname); + + Optional findBySourceIsIn(List sources); } diff --git a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java index 7bbd975f6..fa9559d7c 100644 --- a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java +++ b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java @@ -1,6 +1,7 @@ package com.park.utmstack.service; import com.park.utmstack.config.Constants; +import com.park.utmstack.domain.correlation.config.UtmTenantConfig; import com.park.utmstack.domain.datainput_ingestion.UtmDataInputStatus; import com.park.utmstack.domain.UtmServerModule; import com.park.utmstack.domain.application_events.enums.ApplicationEventType; @@ -17,6 +18,7 @@ import com.park.utmstack.repository.correlation.config.UtmDataTypesRepository; import com.park.utmstack.repository.network_scan.UtmNetworkScanRepository; import com.park.utmstack.service.application_events.ApplicationEventService; +import com.park.utmstack.service.correlation.config.UtmTenantConfigService; import com.park.utmstack.service.elasticsearch.ElasticsearchService; import com.park.utmstack.service.elasticsearch.SearchUtil; import com.park.utmstack.service.logstash_pipeline.response.statistic.StatisticDocument; @@ -24,6 +26,7 @@ import com.park.utmstack.service.network_scan.UtmNetworkScanService; import com.park.utmstack.util.enums.AlertSeverityEnum; import com.park.utmstack.util.enums.AlertStatus; +import com.park.utmstack.util.exceptions.ApiException; import lombok.RequiredArgsConstructor; import org.apache.http.conn.util.InetAddressUtils; import org.opensearch.client.json.JsonData; @@ -34,6 +37,7 @@ import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -68,6 +72,7 @@ public class UtmDataInputStatusService { private final UtmDataTypesRepository dataTypesRepository; private final UtmNetworkScanRepository networkScanRepository; private final UtmDataInputStatusCheckpointRepository checkpointRepository; + private final UtmTenantConfigService utmTenantConfigService; /** @@ -174,7 +179,7 @@ private void checkDataInputStatus(List inputs, String server } } - @Scheduled(fixedDelay = 15000, initialDelay = 30000) + @Scheduled(fixedDelay = 1000, initialDelay = 2000) public void syncDataInputStatus() { final String ctx = CLASSNAME + ".syncDataInputStatus"; @@ -193,7 +198,7 @@ public void syncDataInputStatus() { latestStats.forEach((key, stat) -> { try { String dataType = stat.getDataType(); - String dataSource = stat.getDataSource(); + String dataSource = getDataSource(stat.getDataSource()); long timestamp = Instant.parse(stat.getTimestamp()).getEpochSecond(); String compositeKey = dataType + "-" + dataSource; @@ -210,7 +215,9 @@ public void syncDataInputStatus() { .median(86400L) .build(); changed = true; + } else if (status.getTimestamp() != timestamp) { + status.setSource(dataSource); status.setTimestamp(timestamp); changed = true; } @@ -497,4 +504,71 @@ private Map getLatestStatisticsByDataSource() { return result; } + private String getDataSource(String assetName) { + final String ctx = CLASSNAME + ".getDataSource"; + + Optional tenantConfig = this.utmTenantConfigService.findByAssetName(assetName); + + if (tenantConfig.isEmpty()) { + return assetName; + } + + List sources = buildSourcesList(tenantConfig.get()); + + if (CollectionUtils.isEmpty(sources)) { + return assetName; + } + + Optional dataInputStatus = this.findDataInputBySource(sources); + + return dataInputStatus + .map(UtmDataInputStatus::getSource) + .orElse(assetName); + } + + /** + * Builds a combined list of hostnames and IPs from tenant configuration + * + * @param tenantConfig the tenant configuration + * @return combined list of sources, or empty list if none available + */ + private List buildSourcesList(UtmTenantConfig tenantConfig) { + List sources = new ArrayList<>(); + + if (!CollectionUtils.isEmpty(tenantConfig.getAssetHostnameList())) { + sources.addAll(tenantConfig.getAssetHostnameList()); + } + + if (!CollectionUtils.isEmpty(tenantConfig.getAssetIpList())) { + sources.addAll(tenantConfig.getAssetIpList()); + } + + return sources; + } + + /** + * Finds a data input status by searching in the provided list of sources. + * Returns the first available source from the database. + * + * @param sources list of source hostnames/IPs to search for + * @return Optional containing the data input status if found + */ + public Optional findDataInputBySource(List sources) { + final String ctx = CLASSNAME + ".findDataInputBySource"; + + if (CollectionUtils.isEmpty(sources)) { + return Optional.empty(); + } + + try { + return this.dataInputStatusRepository.findBySourceIsIn(sources); + + } catch (Exception ex) { + log.error("{}: Error finding data input status by source {} - {}", + ctx, sources, ex.getMessage(), ex); + return Optional.empty(); + } + } + + } From 57f38e6a1fc31502e718b2c8ebaf710e53462935 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 25 Feb 2026 14:25:43 -0600 Subject: [PATCH 071/115] feat(data-input-status): add methods to retrieve data input status by source and build sources list from tenant config --- .../com/park/utmstack/service/UtmDataInputStatusService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java index fa9559d7c..5e7f3c904 100644 --- a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java +++ b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java @@ -179,7 +179,7 @@ private void checkDataInputStatus(List inputs, String server } } - @Scheduled(fixedDelay = 1000, initialDelay = 2000) + @Scheduled(fixedDelay = 15000, initialDelay = 30000) public void syncDataInputStatus() { final String ctx = CLASSNAME + ".syncDataInputStatus"; From 49b3d6c2eea61729bc1733a6c31da533dde713ef Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 25 Feb 2026 15:33:42 -0600 Subject: [PATCH 072/115] feat(data-input-status): add methods to retrieve data input status by source and build sources list from tenant config --- .../service/UtmDataInputStatusService.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java index 5e7f3c904..64d9781df 100644 --- a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java +++ b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java @@ -179,7 +179,7 @@ private void checkDataInputStatus(List inputs, String server } } - @Scheduled(fixedDelay = 15000, initialDelay = 30000) + @Scheduled(fixedDelay = 1000, initialDelay = 2000) public void syncDataInputStatus() { final String ctx = CLASSNAME + ".syncDataInputStatus"; @@ -198,10 +198,11 @@ public void syncDataInputStatus() { latestStats.forEach((key, stat) -> { try { String dataType = stat.getDataType(); - String dataSource = getDataSource(stat.getDataSource()); + String assetName = this.getDataSource(stat.getDataSource()); + long timestamp = Instant.parse(stat.getTimestamp()).getEpochSecond(); - String compositeKey = dataType + "-" + dataSource; + String compositeKey = dataType + "-" + assetName; UtmDataInputStatus status = existing.get(compositeKey); boolean changed = false; @@ -210,14 +211,18 @@ public void syncDataInputStatus() { status = UtmDataInputStatus.builder() .id(compositeKey) .dataType(dataType) - .source(dataSource) + .source(assetName) .timestamp(timestamp) .median(86400L) .build(); changed = true; } else if (status.getTimestamp() != timestamp) { - status.setSource(dataSource); + + if (!assetName.equals(stat.getDataSource())) { + status.setSource(stat.getDataSource()); + } + status.setTimestamp(timestamp); changed = true; } From bc6f3f560cb45e648e94b34f2f03855dfb88ba00 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 26 Feb 2026 08:04:43 -0600 Subject: [PATCH 073/115] feat(data-input-status): add methods to retrieve data input status by source and build sources list from tenant config --- .../com/park/utmstack/service/UtmDataInputStatusService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java index 64d9781df..46f69bd0e 100644 --- a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java +++ b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java @@ -179,7 +179,7 @@ private void checkDataInputStatus(List inputs, String server } } - @Scheduled(fixedDelay = 1000, initialDelay = 2000) + @Scheduled(fixedDelay = 15000, initialDelay = 30000) public void syncDataInputStatus() { final String ctx = CLASSNAME + ".syncDataInputStatus"; From f8a8aff65cb5e7714a8bb860c6375280532c76dc Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 26 Feb 2026 11:29:12 -0600 Subject: [PATCH 074/115] feat(data-input-status): add alias column and update logic for data input status --- .../UtmDataInputStatus.java | 4 ++ .../service/UtmDataInputStatusService.java | 51 +++++++++++++------ ...0226001_add_alias_to_data_input_status.xml | 21 ++++++++ .../resources/config/liquibase/master.xml | 2 + 4 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 backend/src/main/resources/config/liquibase/changelog/20260226001_add_alias_to_data_input_status.xml diff --git a/backend/src/main/java/com/park/utmstack/domain/datainput_ingestion/UtmDataInputStatus.java b/backend/src/main/java/com/park/utmstack/domain/datainput_ingestion/UtmDataInputStatus.java index 8a4dd7046..19a94835e 100644 --- a/backend/src/main/java/com/park/utmstack/domain/datainput_ingestion/UtmDataInputStatus.java +++ b/backend/src/main/java/com/park/utmstack/domain/datainput_ingestion/UtmDataInputStatus.java @@ -43,6 +43,10 @@ public class UtmDataInputStatus implements Serializable { @Column(name = "median") private Long median; + @Size(max = 500) + @Column(name = "alias", length = 500, nullable = true) + private String alias; + /** * Define if a source is down or up. * Null is returned when the calculation could not be done. diff --git a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java index 46f69bd0e..5f44ea0b0 100644 --- a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java +++ b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java @@ -26,7 +26,6 @@ import com.park.utmstack.service.network_scan.UtmNetworkScanService; import com.park.utmstack.util.enums.AlertSeverityEnum; import com.park.utmstack.util.enums.AlertStatus; -import com.park.utmstack.util.exceptions.ApiException; import lombok.RequiredArgsConstructor; import org.apache.http.conn.util.InetAddressUtils; import org.opensearch.client.json.JsonData; @@ -37,7 +36,6 @@ import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpStatus; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -198,11 +196,14 @@ public void syncDataInputStatus() { latestStats.forEach((key, stat) -> { try { String dataType = stat.getDataType(); - String assetName = this.getDataSource(stat.getDataSource()); + String statName = stat.getDataSource(); + String sourceWithAlias = this.getSourceName(statName); + String resolvedAlias = sourceWithAlias != null ? statName : null; + String source = sourceWithAlias == null ? statName : sourceWithAlias; long timestamp = Instant.parse(stat.getTimestamp()).getEpochSecond(); - String compositeKey = dataType + "-" + assetName; + String compositeKey = dataType + "-" + source; UtmDataInputStatus status = existing.get(compositeKey); boolean changed = false; @@ -211,19 +212,16 @@ public void syncDataInputStatus() { status = UtmDataInputStatus.builder() .id(compositeKey) .dataType(dataType) - .source(assetName) + .source(source) + .alias(resolvedAlias) .timestamp(timestamp) .median(86400L) .build(); changed = true; - } else if (status.getTimestamp() != timestamp) { - - if (!assetName.equals(stat.getDataSource())) { - status.setSource(stat.getDataSource()); - } - + } else if (status.getTimestamp() != timestamp || !Objects.equals(status.getAlias(), resolvedAlias)) { status.setTimestamp(timestamp); + status.setAlias(resolvedAlias); changed = true; } @@ -280,6 +278,7 @@ public void synchronizeSourcesToAssets() { } Map sourcesWithStatus = extractSourcesWithUpDownStatus(sources); + Map sourcesWithAlias = extractSourcesWithAlias(sources); List keys = new ArrayList<>(sourcesWithStatus.keySet()); List assets = networkScanRepository.findByAssetIpInOrAssetNameIn(keys, keys); @@ -293,12 +292,23 @@ public void synchronizeSourcesToAssets() { for (Map.Entry entry : sourcesWithStatus.entrySet()) { String key = entry.getKey(); + String alias = sourcesWithAlias.get(key); Boolean alive = entry.getValue(); + UtmNetworkScan asset = assetsByKey.get(key); + if (asset == null && StringUtils.hasText(alias)) { + asset = assetsByKey.get(alias); + } + if (asset != null) { if (asset.getUpdateLevel() == null || asset.getUpdateLevel().equals(UpdateLevel.DATASOURCE) || asset.getUpdateLevel().equals(UpdateLevel.AGENT)) { + + if (StringUtils.hasText(alias) && !alias.equals(asset.getAssetAlias())) { + asset.assetAliases(alias); + } + asset.assetAlive(alive) .updateLevel(UpdateLevel.DATASOURCE) .assetStatus(AssetStatus.CHECK) @@ -332,6 +342,17 @@ public void synchronizeSourcesToAssets() { } } + private Map extractSourcesWithAlias(List sources) { + Map alias = new HashMap<>(); + + sources.forEach(src -> { + if (StringUtils.hasText(src.getAlias())) { + alias.put(src.getSource(), src.getAlias()); + } + }); + return alias; + } + private Map extractSourcesWithUpDownStatus(List sources) { Map upDown = new HashMap<>(); sources.forEach(src -> { @@ -509,26 +530,26 @@ private Map getLatestStatisticsByDataSource() { return result; } - private String getDataSource(String assetName) { + private String getSourceName(String assetName) { final String ctx = CLASSNAME + ".getDataSource"; Optional tenantConfig = this.utmTenantConfigService.findByAssetName(assetName); if (tenantConfig.isEmpty()) { - return assetName; + return null; } List sources = buildSourcesList(tenantConfig.get()); if (CollectionUtils.isEmpty(sources)) { - return assetName; + return null; } Optional dataInputStatus = this.findDataInputBySource(sources); return dataInputStatus .map(UtmDataInputStatus::getSource) - .orElse(assetName); + .orElse(null); } /** diff --git a/backend/src/main/resources/config/liquibase/changelog/20260226001_add_alias_to_data_input_status.xml b/backend/src/main/resources/config/liquibase/changelog/20260226001_add_alias_to_data_input_status.xml new file mode 100644 index 000000000..da6b01924 --- /dev/null +++ b/backend/src/main/resources/config/liquibase/changelog/20260226001_add_alias_to_data_input_status.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index d37fad4e8..21a9c36ab 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -479,5 +479,7 @@ + + From 8876d2f3985d76efa63943fa9a156ff6a4d78bd4 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 26 Feb 2026 12:59:52 -0600 Subject: [PATCH 075/115] feat: remove alert from addTag function in fields-selector component Signed-off-by: Manuel Abascal --- .../components/fields-selector/fields-selector.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/app/rule-management/app-rule/components/fields-selector/fields-selector.component.ts b/frontend/src/app/rule-management/app-rule/components/fields-selector/fields-selector.component.ts index 2e532a690..e671f6322 100644 --- a/frontend/src/app/rule-management/app-rule/components/fields-selector/fields-selector.component.ts +++ b/frontend/src/app/rule-management/app-rule/components/fields-selector/fields-selector.component.ts @@ -39,7 +39,6 @@ export class FieldsSelectorComponent implements OnInit { } addTag(name: string) { - alert(name) return { name }; } } From 5158938448abcc0525174d3ba12a53ce93fb7669 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 27 Feb 2026 09:43:18 -0600 Subject: [PATCH 076/115] fix: deprecate enable parameter in TFA section Signed-off-by: Manuel Abascal --- .../app-config-sections/app-config-sections.component.html | 2 +- .../app-config-sections/app-config-sections.component.ts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/shared/components/utm/config/app-config-sections/app-config-sections.component.html b/frontend/src/app/shared/components/utm/config/app-config-sections/app-config-sections.component.html index 2ed339dbc..bc3288290 100644 --- a/frontend/src/app/shared/components/utm/config/app-config-sections/app-config-sections.component.html +++ b/frontend/src/app/shared/components/utm/config/app-config-sections/app-config-sections.component.html @@ -28,7 +28,7 @@
{{section.section | titlecase}}
class="mr-2" *ngIf="section.shortName === sectionType[sectionType.EMAIL]"> - c.confParamShort !== 'utmstack.tfa.enable'); + if (this.isOnline && this.section.id === this.sectionType.INSTANCE_REGISTRATION) { return this.configs.filter( c => c.confParamShort !== 'utmstack.instance.auth'); } else { @@ -328,10 +331,6 @@ export class AppConfigSectionsComponent implements OnInit, OnDestroy { } } - isEnabledTfa(): boolean { - return this.configs.some(conf => conf.confParamShort === 'utmstack.tfa.enable' && conf.confParamValue === 'true'); - } - serializeConfigValue(conf: SectionConfigParamType): string { if (conf.confParamDatatype === ConfigDataTypeEnum.Cron) { return JSON.stringify(conf.confParamValue); From 2219cfa96188c3f727ddaac41f417bc70654325d Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 27 Feb 2026 10:05:15 -0600 Subject: [PATCH 077/115] feat(.gitignore): add .env file to ignore list --- backend/.gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/.gitignore b/backend/.gitignore index 518edd788..95d319e15 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -144,3 +144,9 @@ Desktop.ini ###################### .eslintcache init-utmstack-backend.bat + +###################### +# ENV +###################### + +.env/ From 7e28b0a3a880e60e6d059b895951bc2c74c2a5d8 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 2 Mar 2026 07:34:04 -0600 Subject: [PATCH 078/115] fix: update winevent correlation rules --- ...001_update_winevent_correlation_rules.xml} | 6 +- .../utm_correlation_rules.sql | 2108 ++++++++--------- .../utm_group_rules_data_type.sql | 242 +- .../resources/config/liquibase/master.xml | 2 +- 4 files changed, 1179 insertions(+), 1179 deletions(-) rename backend/src/main/resources/config/liquibase/changelog/{20260223002_update_winevent_correlation_rules.xml => 20260302001_update_winevent_correlation_rules.xml} (85%) rename backend/src/main/resources/config/liquibase/data/{20260223 => 20260302}/utm_correlation_rules.sql (88%) rename backend/src/main/resources/config/liquibase/data/{20260223 => 20260302}/utm_group_rules_data_type.sql (99%) diff --git a/backend/src/main/resources/config/liquibase/changelog/20260223002_update_winevent_correlation_rules.xml b/backend/src/main/resources/config/liquibase/changelog/20260302001_update_winevent_correlation_rules.xml similarity index 85% rename from backend/src/main/resources/config/liquibase/changelog/20260223002_update_winevent_correlation_rules.xml rename to backend/src/main/resources/config/liquibase/changelog/20260302001_update_winevent_correlation_rules.xml index 77917533f..9a56a4ac3 100644 --- a/backend/src/main/resources/config/liquibase/changelog/20260223002_update_winevent_correlation_rules.xml +++ b/backend/src/main/resources/config/liquibase/changelog/20260302001_update_winevent_correlation_rules.xml @@ -4,7 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"> - +
diff --git a/frontend/src/app/app-module/conf/int-generic-group-config/int-generic-group-config.component.ts b/frontend/src/app/app-module/conf/int-generic-group-config/int-generic-group-config.component.ts index 399158384..fca5fc706 100644 --- a/frontend/src/app/app-module/conf/int-generic-group-config/int-generic-group-config.component.ts +++ b/frontend/src/app/app-module/conf/int-generic-group-config/int-generic-group-config.component.ts @@ -1,12 +1,11 @@ import {ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges} from '@angular/core'; import {Subject} from 'rxjs'; -import {debounceTime, distinctUntilChanged, finalize, map, takeUntil, tap} from 'rxjs/operators'; +import {debounceTime, finalize, map, takeUntil, tap} from 'rxjs/operators'; import {ModalService} from '../../../core/modal/modal.service'; import {UtmToastService} from '../../../shared/alert/utm-toast.service'; import { ModalConfirmationComponent } from '../../../shared/components/utm/util/modal-confirmation/modal-confirmation.component'; -import {EncryptService} from '../../../shared/services/util/encrypt.service'; import {ModuleChangeStatusBehavior} from '../../shared/behavior/module-change-status.behavior'; import {IntCreateGroupComponent} from '../../shared/components/int-create-group/int-create-group.component'; import {GroupTypeEnum} from '../../shared/enum/group-type.enum'; @@ -46,10 +45,10 @@ export class IntGenericGroupConfigComponent implements OnInit, OnChanges, OnDest destroy$ = new Subject(); uniqueConfigNameConstrain = false; invalidDomainOrIp = false; + savingConfigs = new Map(); constructor(private utmModuleGroupService: UtmModuleGroupService, private toast: UtmToastService, - private encryptService: EncryptService, private utmModuleGroupConfService: UtmModuleGroupConfService, private modalService: ModalService, private moduleChangeStatusBehavior: ModuleChangeStatusBehavior, @@ -189,7 +188,7 @@ export class IntGenericGroupConfigComponent implements OnInit, OnChanges, OnDest } saveConfig(group: UtmModuleGroupType) { - this.savingConfig = true; + this.savingConfigs.set(group.id, true); const configs = this.changes.keys.filter(change => change.groupId === group.id); this.utmModuleGroupConfService.update({ @@ -197,7 +196,7 @@ export class IntGenericGroupConfigComponent implements OnInit, OnChanges, OnDest keys: configs }).pipe( finalize(() => { - this.savingConfig = false; + this.savingConfigs.set(group.id, false); this.cdr.detectChanges(); }) ).subscribe({ @@ -440,6 +439,7 @@ export class IntGenericGroupConfigComponent implements OnInit, OnChanges, OnDest this.addChange(integrationConfig); } + ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); From 313e44681148ab66aba1e375867c3dd7e94204af Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Mar 2026 08:12:44 -0600 Subject: [PATCH 106/115] fix(asset-sync): adjust scheduling parameters for data synchronization --- .../service/network_scan/AssetSynchronizationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java index 6e66bcf74..becd8125e 100644 --- a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java +++ b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java @@ -39,7 +39,7 @@ public class AssetSynchronizationService { private final UtmTenantConfigService tenantConfigService; @Transactional - @Scheduled(fixedDelay = 30000, initialDelay = 60000) + @Scheduled(fixedDelay = 60000, initialDelay = 120000) public void syncDataInputsAndAssets() { String correlationId = UUID.randomUUID().toString().substring(0, 8); From a51df297e9901added0ffec5101143cea2281f23 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Mar 2026 16:58:12 -0600 Subject: [PATCH 107/115] feat: add logging to ElasticsearchService for better error tracking and index existence checks --- .../elasticsearch/ElasticsearchService.java | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/elasticsearch/ElasticsearchService.java b/backend/src/main/java/com/park/utmstack/service/elasticsearch/ElasticsearchService.java index d70b28f45..0b4e6605c 100644 --- a/backend/src/main/java/com/park/utmstack/service/elasticsearch/ElasticsearchService.java +++ b/backend/src/main/java/com/park/utmstack/service/elasticsearch/ElasticsearchService.java @@ -20,6 +20,7 @@ import com.utmstack.opensearch_connector.types.IndexSort; import com.utmstack.opensearch_connector.types.SearchSqlResponse; import com.utmstack.opensearch_connector.types.SqlQueryRequest; +import lombok.extern.slf4j.Slf4j; import org.opensearch.client.opensearch._types.SortOrder; import org.opensearch.client.opensearch._types.query_dsl.Query; import org.opensearch.client.opensearch.cat.indices.IndicesRecord; @@ -47,6 +48,7 @@ * @author Leonardo M. López */ @Service +@Slf4j public class ElasticsearchService { private static final String CLASSNAME = "ElasticsearchService"; private final Logger log = LoggerFactory.getLogger(ElasticsearchService.class); @@ -78,7 +80,7 @@ public List getFieldValues(String keyword, String indexPattern) { final String ctx = CLASSNAME + ".getFieldValues"; try { return new ArrayList<>(client.getClient().getFieldValues(keyword, indexPattern, - null, 10000, TermOrder.Count, SortOrder.Desc).keySet()); + null, 10000, TermOrder.Count, SortOrder.Desc).keySet()); } catch (Exception e) { throw new RuntimeException(ctx + ": " + e.getLocalizedMessage()); } @@ -98,7 +100,7 @@ public Map getFieldValuesWithCount(String field, String index, Lis final String ctx = CLASSNAME + ".getFieldValuesWithCount"; try { return client.getClient().getFieldValues(field, index, SearchUtil.toQuery(filters), top, - orderByCount ? TermOrder.Count : TermOrder.Key, sortAsc ? SortOrder.Asc : SortOrder.Desc); + orderByCount ? TermOrder.Count : TermOrder.Key, sortAsc ? SortOrder.Asc : SortOrder.Desc); } catch (Exception e) { throw new RuntimeException(ctx + ": " + e.getLocalizedMessage()); } @@ -143,15 +145,17 @@ public IndexResponse index(String index, T document) { public List getIndexProperties(String indexPattern) { final String ctx = CLASSNAME + ".getIndexProperties"; - if (!indexExist(indexPattern)) - throw new OpenSearchIndexNotFoundException(ctx + ": Index [" + indexPattern + "] not found"); + if (!indexExist(indexPattern)) { + log.info("{} Index pattern {} does not exist", ctx, indexPattern); + return Collections.emptyList(); + } try { Map properties = client.getClient().getIndexProperties(indexPattern); if (CollectionUtils.isEmpty(properties)) return Collections.emptyList(); return properties.entrySet() - .stream().map(e -> new IndexPropertyType(e.getKey(), e.getValue())).collect(Collectors.toList()); + .stream().map(e -> new IndexPropertyType(e.getKey(), e.getValue())).collect(Collectors.toList()); } catch (Exception e) { throw new RuntimeException(ctx + ": " + e.getMessage()); } @@ -167,7 +171,7 @@ public List getIndexProperties(String indexPattern) { * @throws UtmElasticsearchException In case of any error */ public Page getAllIndexes(boolean includeSystemIndex, String pattern, Pageable pageable) throws - UtmElasticsearchException { + UtmElasticsearchException { final String ctx = CLASSNAME + ".getAllIndexes"; try { Assert.notNull(pageable, "Argument pageable can't be null"); @@ -179,7 +183,7 @@ public Page getAllIndexes(boolean includeSystemIndex, String patt if (!includeSystemIndex) indices = indices.stream().filter(index -> !index.index().startsWith(".")) - .collect(Collectors.toList()); + .collect(Collectors.toList()); PagedListHolder pageDefinition = new PagedListHolder<>(); pageDefinition.setSource(indices); @@ -198,7 +202,7 @@ private IndexSort from(Sort sort) { return IndexSort.unSorted(); IndexSort.Builder sortBuilder = IndexSort.builder(); sort.forEach(order -> sortBuilder.with(IndexSortableProperty.fromJsonValue(order.getProperty()), - order.getDirection().isAscending() ? SortOrder.Asc : SortOrder.Desc)); + order.getDirection().isAscending() ? SortOrder.Asc : SortOrder.Desc)); return sortBuilder.build(); } catch (Exception e) { throw new RuntimeException(ctx + ": " + e.getLocalizedMessage()); @@ -239,14 +243,14 @@ public void preventSystemCrashBySpace() { return; UtmSpaceNotificationControl notificationControl = spaceNotificationControlService.findById(1L) - .orElse(new UtmSpaceNotificationControl()); + .orElse(new UtmSpaceNotificationControl()); if (Objects.isNull(notificationControl.getId())) notificationControl.setId(1L); Instant now = LocalDateTime.now().toInstant(ZoneOffset.UTC); if (Objects.isNull(notificationControl.getNextNotification()) || - now.isAfter(notificationControl.getNextNotification())) { + now.isAfter(notificationControl.getNextNotification())) { mailService.sendLowSpaceEmail(admins, clusterStatus); notificationControl.setNextNotification(now.plus(24, ChronoUnit.HOURS)); spaceNotificationControlService.save(notificationControl); @@ -266,7 +270,7 @@ private void deleteOldestIndices() { final String ctx = CLASSNAME + ".deleteOldestIndices"; try { List indices = client.getClient().getIndices(Constants.SYS_INDEX_PATTERN.get(SystemIndexPattern.LOGS), IndexSort.builder() - .with(IndexSortableProperty.CreationDate, SortOrder.Asc).build()); + .with(IndexSortableProperty.CreationDate, SortOrder.Asc).build()); // If no index that match with log-* was found then te function is terminated if (CollectionUtils.isEmpty(indices)) @@ -278,10 +282,10 @@ private void deleteOldestIndices() { // Delete oldest indices deleteIndex(Collections.singletonList(index.index())); eventService.createEvent(String.format("Index %1$s was deleted to avoid system crash by space:\n" + - "Creation Date: %2$s\n" + - "Docs Count: %3$s\n" + - "Size: %4$s", - index.index(), index.creationDateString(), index.docsCount(), index.storeSize()), ApplicationEventType.INFO); + "Creation Date: %2$s\n" + + "Docs Count: %3$s\n" + + "Size: %4$s", + index.index(), index.creationDateString(), index.docsCount(), index.storeSize()), ApplicationEventType.INFO); } catch (Exception e) { String msg = String.format("%1$s: Fail to delete index: %2$s with message: %3$s", ctx, index.index(), e.getMessage()); eventService.createEvent(msg, ApplicationEventType.WARNING); From 33d6d53a32480f8c0bcf2a715f58f225e30166f7 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Mar 2026 18:18:47 -0600 Subject: [PATCH 108/115] feat: improve source activity fetching and error handling in SourceActivityProvider --- .../network_scan/SourceActivityProvider.java | 94 +++++++++++++------ 1 file changed, 67 insertions(+), 27 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/network_scan/SourceActivityProvider.java b/backend/src/main/java/com/park/utmstack/service/network_scan/SourceActivityProvider.java index 4c9f89941..32d980398 100644 --- a/backend/src/main/java/com/park/utmstack/service/network_scan/SourceActivityProvider.java +++ b/backend/src/main/java/com/park/utmstack/service/network_scan/SourceActivityProvider.java @@ -10,7 +10,6 @@ import com.park.utmstack.service.logstash_pipeline.response.statistic.StatisticDocument; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.opensearch.client.json.JsonData; import org.opensearch.client.opensearch._types.SortOrder; import org.opensearch.client.opensearch.core.SearchRequest; import org.opensearch.client.opensearch.core.SearchResponse; @@ -37,7 +36,6 @@ public class SourceActivityProvider { public Map fetchLatestSourceActivity() { UtmDataInputStatusCheckpoint checkpoint = getOrCreateCheckpoint(); - String fromTimestamp = checkpoint.getLastProcessedTimestamp() .minus(OVERLAP_SECONDS, ChronoUnit.SECONDS) .toString(); @@ -51,12 +49,15 @@ public Map fetchLatestSourceActivity() { Map activityMap = extractLatestHits(response); if (!activityMap.isEmpty()) { + log.debug("Fetched {} active sources from statistics index", activityMap.size()); updateCheckpoint(checkpoint, activityMap); + } else { + log.debug("No new source activity found since checkpoint: {}", checkpoint.getLastProcessedTimestamp()); } return activityMap; } catch (Exception e) { - log.error("Error consultando telemetría en Elasticsearch: {}", e.getMessage()); + log.error("Error fetching telemetry from Elasticsearch: {}", e.getMessage(), e); return Collections.emptyMap(); } } @@ -70,45 +71,84 @@ private SearchRequest buildActivityQuery(String fromTimestamp) { return SearchRequest.of(s -> s .index(Constants.STATISTICS_INDEX_PATTERN) .query(SearchUtil.toQuery(filters)) - .collapse(c -> c - .field("dataSource.keyword") - .innerHits(ih -> ih - .name("latest") - .size(1) - .sort(sort -> sort.field(f -> f.field("@timestamp").order(SortOrder.Desc))) + .aggregations("by_source", agg -> agg + .terms(t -> t + .field("dataSource.keyword") + .size(10000) + ) + .aggregations("latest_doc", latestAgg -> latestAgg + .topHits(th -> th + .size(1) + .sort(sort -> sort.field(f -> f.field("@timestamp").order(SortOrder.Desc))) + ) ) ) - .size(10000) + .size(0) // We don't need the main hits, only aggregations ); } private Map extractLatestHits(SearchResponse response) { Map results = new HashMap<>(); - response.hits().hits().forEach(hit -> { - if (hit.innerHits() != null && hit.innerHits().containsKey("latest")) { - var innerHits = hit.innerHits().get("latest").hits().hits(); - if (!innerHits.isEmpty()) { - JsonData json = innerHits.get(0).source(); - if (json != null) { - StatisticDocument doc = json.to(StatisticDocument.class); - results.put(doc.getDataSource(), doc); + try { + var aggregations = response.aggregations(); + if (aggregations == null || aggregations.get("by_source") == null) { + log.warn("No aggregation results found in response"); + return results; + } + + var bySourceAgg = aggregations.get("by_source").sterms(); + if (bySourceAgg == null || bySourceAgg.buckets().array().isEmpty()) { + log.debug("No data source buckets found in aggregation"); + return results; + } + + bySourceAgg.buckets().array().forEach(bucket -> { + String dataSource = bucket.key(); + + var latestDocsAgg = bucket.aggregations().get("latest_doc"); + if (latestDocsAgg != null) { + var topHits = latestDocsAgg.topHits(); + if (topHits != null && !topHits.hits().hits().isEmpty()) { + var hit = topHits.hits().hits().get(0); + if (hit.source() != null) { + StatisticDocument doc = hit.source().to(StatisticDocument.class); + if (doc != null) { + results.put(dataSource, doc); + log.debug("Extracted latest activity for source: {} at {}", dataSource, doc.getTimestamp()); + } + } } } - } - }); - return results; + }); + + return results; + } catch (Exception e) { + log.error("Error extracting latest hits from aggregation response: {}", e.getMessage(), e); + return results; + } } private void updateCheckpoint(UtmDataInputStatusCheckpoint checkpoint, Map activityMap) { activityMap.values().stream() - .map(doc -> Instant.parse(doc.getTimestamp())) + .map(doc -> { + try { + return Instant.parse(doc.getTimestamp()); + } catch (Exception e) { + log.warn("Failed to parse timestamp '{}' for document: {}", doc.getTimestamp(), e.getMessage()); + return null; + } + }) + .filter(java.util.Objects::nonNull) .max(Instant::compareTo) - .ifPresent(latest -> { - checkpoint.setLastProcessedTimestamp(latest); - checkpointRepository.save(checkpoint); - log.debug("Checkpoint actualizado a: {}", latest); - }); + .ifPresentOrElse( + latest -> { + checkpoint.setLastProcessedTimestamp(latest); + checkpointRepository.save(checkpoint); + log.info("Checkpoint updated to: {}", latest); + }, + () -> log.debug("No valid timestamps found to update checkpoint") + ); } private UtmDataInputStatusCheckpoint getOrCreateCheckpoint() { From 1f48e3cef5a2073fc8799ec130f22426a836ec7e Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Mar 2026 19:12:12 -0600 Subject: [PATCH 109/115] feat: refactor asset synchronization logic and enhance data source aggregation --- .../AssetSynchronizationService.java | 134 ++++++++++-------- .../network_scan/SourceActivityProvider.java | 59 +++++--- 2 files changed, 109 insertions(+), 84 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java index becd8125e..65b80985d 100644 --- a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java +++ b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java @@ -41,52 +41,43 @@ public class AssetSynchronizationService { @Transactional @Scheduled(fixedDelay = 60000, initialDelay = 120000) public void syncDataInputsAndAssets() { - String correlationId = UUID.randomUUID().toString().substring(0, 8); log.info("[{}] Starting unified asset synchronization cycle", correlationId); try { - Map agentsMap = loadAgents(); Map statsMap = sourceActivityProvider.fetchLatestSourceActivity(); - if (statsMap.isEmpty()) { log.debug("[{}] No new activity detected in data sources", correlationId); return; } - List sourcesKeys = new ArrayList<>(statsMap.keySet()); - - List dataInputStatus = dataInputStatusService.findDataInputStatus(); - - Map currentDataInputStatusMap = dataInputStatus - .stream() - .collect(Collectors.toMap(UtmDataInputStatus::getId, f -> f)); - - Map currentAssetsMap = - networkScanRepository.findByAssetIpInOrAssetNameIn(sourcesKeys, sourcesKeys) - .stream() - .collect(Collectors.toMap(UtmNetworkScan::getAssetName, f -> f)); + Map agentsMap = loadAgents(); + Map statusMap = buildDataInputStatusMap(); + Map assetsMap = buildNetworkAssetsMap(new ArrayList<>(statsMap.keySet())); List statusToSave = new ArrayList<>(); List assetsToSave = new ArrayList<>(); - Map> statsBySource = statsMap.values() - .stream() - .collect(Collectors.groupingBy(StatisticDocument::getDataSource)); + for (String sourceName : statsMap.keySet()) { - statsBySource.forEach((sourceName, stats) -> { + StatisticDocument stat = statsMap.get(sourceName); + UtmDataInputStatus status = processDataInputStatus(stat, statusMap); + statusToSave.add(status); - for (StatisticDocument stat : stats) { - processDataInputStatus(stat, currentDataInputStatusMap, statusToSave); - } + // Update network asset + UtmNetworkScan asset = processNetworkAsset(sourceName, agentsMap, assetsMap, statusMap); + assetsToSave.add(asset); + } - processNetworkAsset(sourceName, agentsMap, currentAssetsMap, currentDataInputStatusMap, assetsToSave); - }); + if (!statusToSave.isEmpty()) { + dataInputStatusRepository.saveAll(statusToSave); + } - if (!statusToSave.isEmpty()) dataInputStatusRepository.saveAll(statusToSave); - if (!assetsToSave.isEmpty()) networkScanRepository.saveAll(assetsToSave); + if (!assetsToSave.isEmpty()) { + networkScanRepository.saveAll(assetsToSave); + } - log.info("[{}] Asset synchronization cycle completed successfully - {} data input status updated, {} assets synced", + log.info("[{}] Asset synchronization cycle completed - {} status updated, {} assets synced", correlationId, statusToSave.size(), assetsToSave.size()); } catch (Exception e) { @@ -94,60 +85,79 @@ public void syncDataInputsAndAssets() { } } - private void processDataInputStatus(StatisticDocument stat, - Map currentStatusMap, - List statusToSave) { + private Map buildDataInputStatusMap() { + return dataInputStatusService.findDataInputStatus() + .stream() + .collect(Collectors.toMap(UtmDataInputStatus::getId, Function.identity())); + } + + private Map buildNetworkAssetsMap(List sourcesKeys) { + return networkScanRepository.findByAssetIpInOrAssetNameIn(sourcesKeys, sourcesKeys) + .stream() + .collect(Collectors.toMap(UtmNetworkScan::getAssetName, Function.identity(), (a1, a2) -> a1)); + } + private UtmDataInputStatus processDataInputStatus(StatisticDocument stat, + Map statusMap) { String statusId = stat.getDataType() + "-" + stat.getDataSource(); long statTimestamp = Instant.parse(stat.getTimestamp()).getEpochSecond(); - UtmDataInputStatus status = currentStatusMap.getOrDefault(statusId, - UtmDataInputStatus.builder() - .id(statusId) - .dataType(stat.getDataType()) - .timestamp(statTimestamp) - .source(stat.getDataSource()) - .median(86400L) - .build()); + UtmDataInputStatus status = statusMap.getOrDefault(statusId, createNewDataInputStatus(statusId, stat, statTimestamp)); - boolean isExisting = status.getId() != null; - - if (isExisting && status.getTimestamp() != statTimestamp) { + if (status.getTimestamp() != statTimestamp) { status.setTimestamp(statTimestamp); } - statusToSave.add(status); + return status; } - private void processNetworkAsset(String sourceName, - Map agentsMap, - Map currentAssetsMap, - Map currentDataInputStatusMap, - List assetsToSave) { + private UtmDataInputStatus createNewDataInputStatus(String id, StatisticDocument stat, long timestamp) { + return UtmDataInputStatus.builder() + .id(id) + .dataType(stat.getDataType()) + .timestamp(timestamp) + .source(stat.getDataSource()) + .median(86400L) + .build(); + } - boolean hasAlias = false; - boolean isAlive = currentDataInputStatusMap.values().stream() + private UtmNetworkScan processNetworkAsset(String sourceName, + Map agentsMap, + Map assetsMap, + Map statusMap) { + boolean isAlive = isDataSourceAlive(sourceName, statusMap); + UtmNetworkScan asset = resolveAsset(sourceName, assetsMap); + boolean isExisting = asset != null && asset.getId() != null; + + if (asset == null) { + asset = new UtmNetworkScan(sourceName, isAlive); + } + + enrichAssetWithData(asset, sourceName, agentsMap, isAlive, isExisting); + return asset; + } + + private boolean isDataSourceAlive(String sourceName, Map statusMap) { + return statusMap.values().stream() .filter(status -> status.getSource().equalsIgnoreCase(sourceName)) .anyMatch(s -> !s.isDown()); + } - UtmNetworkScan asset = currentAssetsMap.get(sourceName); - String resolvedAssetName = null; + private UtmNetworkScan resolveAsset(String sourceName, Map assetsMap) { + UtmNetworkScan asset = assetsMap.get(sourceName); if (asset == null) { asset = resolveAssetNameFromTenantConfig(sourceName); - hasAlias = asset != null; } - boolean isExisting = asset != null && asset.getId() != null; - - if (asset == null) { - asset = new UtmNetworkScan(sourceName, isAlive); - } else { - if (hasAlias) { - asset.assetName(sourceName); - } - } + return asset; + } + private void enrichAssetWithData(UtmNetworkScan asset, + String sourceName, + Map agentsMap, + boolean isAlive, + boolean isExisting) { asset.assetAlive(isAlive) .updateLevel(UpdateLevel.DATASOURCE) .modifiedAt(LocalDateTime.now().toInstant(ZoneOffset.UTC)); @@ -166,8 +176,6 @@ private void processNetworkAsset(String sourceName, } else { asset.setIsAgent(false); } - - assetsToSave.add(asset); } private UtmNetworkScan resolveAssetNameFromTenantConfig(String sourceName) { diff --git a/backend/src/main/java/com/park/utmstack/service/network_scan/SourceActivityProvider.java b/backend/src/main/java/com/park/utmstack/service/network_scan/SourceActivityProvider.java index 32d980398..36d1d56f9 100644 --- a/backend/src/main/java/com/park/utmstack/service/network_scan/SourceActivityProvider.java +++ b/backend/src/main/java/com/park/utmstack/service/network_scan/SourceActivityProvider.java @@ -76,14 +76,21 @@ private SearchRequest buildActivityQuery(String fromTimestamp) { .field("dataSource.keyword") .size(10000) ) - .aggregations("latest_doc", latestAgg -> latestAgg - .topHits(th -> th - .size(1) - .sort(sort -> sort.field(f -> f.field("@timestamp").order(SortOrder.Desc))) + .aggregations("by_type", typeAgg -> typeAgg + .terms(t -> t + .field("dataType.keyword") + .size(10000) + ) + // Get the latest document for each dataSource + dataType combination + .aggregations("latest_doc", latestAgg -> latestAgg + .topHits(th -> th + .size(1) + .sort(sort -> sort.field(f -> f.field("@timestamp").order(SortOrder.Desc))) + ) ) ) ) - .size(0) // We don't need the main hits, only aggregations + .size(0) ); } @@ -103,23 +110,33 @@ private Map extractLatestHits(SearchResponse { - String dataSource = bucket.key(); - - var latestDocsAgg = bucket.aggregations().get("latest_doc"); - if (latestDocsAgg != null) { - var topHits = latestDocsAgg.topHits(); - if (topHits != null && !topHits.hits().hits().isEmpty()) { - var hit = topHits.hits().hits().get(0); - if (hit.source() != null) { - StatisticDocument doc = hit.source().to(StatisticDocument.class); - if (doc != null) { - results.put(dataSource, doc); - log.debug("Extracted latest activity for source: {} at {}", dataSource, doc.getTimestamp()); + bySourceAgg.buckets().array().forEach(sourceBucket -> { + String dataSource = sourceBucket.key(); + + var byTypeAgg = sourceBucket.aggregations().get("by_type").sterms(); + if (byTypeAgg == null || byTypeAgg.buckets().array().isEmpty()) { + log.debug("No data type buckets found for source: {}", dataSource); + return; + } + + byTypeAgg.buckets().array().forEach(typeBucket -> { + String dataType = typeBucket.key(); + + var latestDocsAgg = typeBucket.aggregations().get("latest_doc"); + if (latestDocsAgg != null) { + var topHits = latestDocsAgg.topHits(); + if (topHits != null && !topHits.hits().hits().isEmpty()) { + var hit = topHits.hits().hits().get(0); + if (hit.source() != null) { + StatisticDocument doc = hit.source().to(StatisticDocument.class); + if (doc != null) { + String compositeKey = dataSource + "|" + dataType; + results.put(compositeKey, doc); + } } } } - } + }); }); return results; @@ -135,7 +152,7 @@ private void updateCheckpoint(UtmDataInputStatusCheckpoint checkpoint, Map { checkpoint.setLastProcessedTimestamp(latest); checkpointRepository.save(checkpoint); - log.info("Checkpoint updated to: {}", latest); + log.info("Checkpoint updated to: {} ({} active sources)", latest, activityMap.size()); }, () -> log.debug("No valid timestamps found to update checkpoint") ); From b9f3bf0154a91f59b9e5e1e448d7894e44009b1b Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 4 Mar 2026 20:50:51 -0600 Subject: [PATCH 110/115] feat: refactor asset synchronization logic and enhance data source aggregation --- .../service/network_scan/AssetSynchronizationService.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java index 65b80985d..90b387e2e 100644 --- a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java +++ b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java @@ -53,7 +53,11 @@ public void syncDataInputsAndAssets() { Map agentsMap = loadAgents(); Map statusMap = buildDataInputStatusMap(); - Map assetsMap = buildNetworkAssetsMap(new ArrayList<>(statsMap.keySet())); + Map assetsMap = buildNetworkAssetsMap( + new ArrayList<>(statsMap.values() + .stream() + .map(StatisticDocument::getDataSource) + .collect(Collectors.toSet()))); List statusToSave = new ArrayList<>(); List assetsToSave = new ArrayList<>(); @@ -65,7 +69,7 @@ public void syncDataInputsAndAssets() { statusToSave.add(status); // Update network asset - UtmNetworkScan asset = processNetworkAsset(sourceName, agentsMap, assetsMap, statusMap); + UtmNetworkScan asset = processNetworkAsset(stat.getDataSource(), agentsMap, assetsMap, statusMap); assetsToSave.add(asset); } From 18d820391425c24cecbe1d9a4ecddb6442e0b67e Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 5 Mar 2026 13:30:55 +0300 Subject: [PATCH 111/115] feat(filters): enhance fortinet field mapping --- filters/fortinet/fortinet.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/filters/fortinet/fortinet.yml b/filters/fortinet/fortinet.yml index 26f6e5a35..aedde27a9 100644 --- a/filters/fortinet/fortinet.yml +++ b/filters/fortinet/fortinet.yml @@ -108,6 +108,22 @@ pipeline: from: - log.src_port to: origin.port + - rename: + from: + - log.rcvdbyte + to: origin.bytesReceived + - rename: + from: + - log.rcvdpkt + to: origin.packagesReceived + - rename: + from: + - log.sentbyte + to: origin.bytesSent + - rename: + from: + - log.sentpkt + to: origin.packagesSent # Removing unused caracters - trim: @@ -1086,4 +1102,5 @@ pipeline: # Removing unused fields - delete: fields: - - log.kvMessage \ No newline at end of file + - log.kvMessage + - log.proto \ No newline at end of file From 6a9ec8146b299e957d9829e4038ee59e899eba90 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 5 Mar 2026 08:47:19 -0600 Subject: [PATCH 112/115] fix: adjust infinite scroll throttle for improved performance Signed-off-by: Manuel Abascal --- .../asset-generic-filter.component.html | 6 +----- .../asset-generic-filter/asset-generic-filter.component.ts | 2 ++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/assets-discover/shared/components/filters/asset-generic-filter/asset-generic-filter.component.html b/frontend/src/app/assets-discover/shared/components/filters/asset-generic-filter/asset-generic-filter.component.html index e37d85a4c..33b44a149 100644 --- a/frontend/src/app/assets-discover/shared/components/filters/asset-generic-filter/asset-generic-filter.component.html +++ b/frontend/src/app/assets-discover/shared/components/filters/asset-generic-filter/asset-generic-filter.component.html @@ -5,16 +5,12 @@
-
- -
diff --git a/frontend/src/app/assets-discover/shared/components/filters/asset-generic-filter/asset-generic-filter.component.ts b/frontend/src/app/assets-discover/shared/components/filters/asset-generic-filter/asset-generic-filter.component.ts index a83f7b326..59157aa0f 100644 --- a/frontend/src/app/assets-discover/shared/components/filters/asset-generic-filter/asset-generic-filter.component.ts +++ b/frontend/src/app/assets-discover/shared/components/filters/asset-generic-filter/asset-generic-filter.component.ts @@ -43,10 +43,12 @@ export class AssetGenericFilterComponent implements OnInit, AfterViewInit { } ngOnInit() { + this.requestParams = { ...this.requestParams, prop: this.fieldFilter.field }; + this.fieldValues$ = this.assetGenericFilterService.onRefresh$ .pipe( filter(filterData => !!filterData && filterData.refresh && filterData.fieldFilter === this.fieldFilter.field), From 31855c4aeb878c526653d77245eacf773edc615e Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 5 Mar 2026 10:56:33 -0600 Subject: [PATCH 113/115] feat: enhance asset processing by separating network asset name and IP mapping --- .../AssetSynchronizationService.java | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java index 90b387e2e..9886e3d4f 100644 --- a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java +++ b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java @@ -53,7 +53,14 @@ public void syncDataInputsAndAssets() { Map agentsMap = loadAgents(); Map statusMap = buildDataInputStatusMap(); - Map assetsMap = buildNetworkAssetsMap( + + Map assetsNameMap = buildNetworkAssetsNameMap( + new ArrayList<>(statsMap.values() + .stream() + .map(StatisticDocument::getDataSource) + .collect(Collectors.toSet()))); + + Map assetsNameIpMap = buildNetworkAssetsIpMap( new ArrayList<>(statsMap.values() .stream() .map(StatisticDocument::getDataSource) @@ -68,8 +75,7 @@ public void syncDataInputsAndAssets() { UtmDataInputStatus status = processDataInputStatus(stat, statusMap); statusToSave.add(status); - // Update network asset - UtmNetworkScan asset = processNetworkAsset(stat.getDataSource(), agentsMap, assetsMap, statusMap); + UtmNetworkScan asset = processNetworkAsset(stat.getDataSource(), agentsMap, assetsNameMap, assetsNameIpMap, statusMap); assetsToSave.add(asset); } @@ -95,12 +101,18 @@ private Map buildDataInputStatusMap() { .collect(Collectors.toMap(UtmDataInputStatus::getId, Function.identity())); } - private Map buildNetworkAssetsMap(List sourcesKeys) { + private Map buildNetworkAssetsNameMap(List sourcesKeys) { return networkScanRepository.findByAssetIpInOrAssetNameIn(sourcesKeys, sourcesKeys) .stream() .collect(Collectors.toMap(UtmNetworkScan::getAssetName, Function.identity(), (a1, a2) -> a1)); } + private Map buildNetworkAssetsIpMap(List sourcesKeys) { + return networkScanRepository.findByAssetIpInOrAssetNameIn(sourcesKeys, sourcesKeys) + .stream() + .collect(Collectors.toMap(UtmNetworkScan::getAssetIp, Function.identity(), (a1, a2) -> a1)); + } + private UtmDataInputStatus processDataInputStatus(StatisticDocument stat, Map statusMap) { String statusId = stat.getDataType() + "-" + stat.getDataSource(); @@ -127,10 +139,11 @@ private UtmDataInputStatus createNewDataInputStatus(String id, StatisticDocument private UtmNetworkScan processNetworkAsset(String sourceName, Map agentsMap, - Map assetsMap, + Map assetsNameMap, + Map assetsIpMap, Map statusMap) { boolean isAlive = isDataSourceAlive(sourceName, statusMap); - UtmNetworkScan asset = resolveAsset(sourceName, assetsMap); + UtmNetworkScan asset = resolveAsset(sourceName, assetsNameMap, assetsIpMap); boolean isExisting = asset != null && asset.getId() != null; if (asset == null) { @@ -147,8 +160,12 @@ private boolean isDataSourceAlive(String sourceName, Map !s.isDown()); } - private UtmNetworkScan resolveAsset(String sourceName, Map assetsMap) { - UtmNetworkScan asset = assetsMap.get(sourceName); + private UtmNetworkScan resolveAsset(String sourceName, Map assetsNameMap, Map assetsIpMap) { + UtmNetworkScan asset = assetsNameMap.get(sourceName); + + if (asset == null) { + asset = assetsIpMap.get(sourceName); + } if (asset == null) { asset = resolveAssetNameFromTenantConfig(sourceName); From 627338226dea2d30cd80b6226d5180ce0a548cb2 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 5 Mar 2026 11:17:13 -0600 Subject: [PATCH 114/115] feat: enhance asset processing by separating network asset name and IP mapping --- .../service/network_scan/AssetSynchronizationService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java index 9886e3d4f..4dd6f6adf 100644 --- a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java +++ b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java @@ -160,7 +160,9 @@ private boolean isDataSourceAlive(String sourceName, Map !s.isDown()); } - private UtmNetworkScan resolveAsset(String sourceName, Map assetsNameMap, Map assetsIpMap) { + private UtmNetworkScan resolveAsset(String sourceName, + Map assetsNameMap, + Map assetsIpMap) { UtmNetworkScan asset = assetsNameMap.get(sourceName); if (asset == null) { From a49e40eb0b914be9af441f818eca567cab279d55 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 5 Mar 2026 11:20:57 -0600 Subject: [PATCH 115/115] feat: enhance asset processing by separating network asset name and IP mapping --- .../service/network_scan/AssetSynchronizationService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java index 4dd6f6adf..7736314c3 100644 --- a/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java +++ b/backend/src/main/java/com/park/utmstack/service/network_scan/AssetSynchronizationService.java @@ -154,7 +154,8 @@ private UtmNetworkScan processNetworkAsset(String sourceName, return asset; } - private boolean isDataSourceAlive(String sourceName, Map statusMap) { + private boolean isDataSourceAlive(String sourceName, + Map statusMap) { return statusMap.values().stream() .filter(status -> status.getSource().equalsIgnoreCase(sourceName)) .anyMatch(s -> !s.isDown());