From ac40f7e4380a90479bad531b0c83254272c1830b Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 5 Sep 2025 11:31:08 -0500 Subject: [PATCH 001/422] feat(logging): add LoggingContextFilter for enhanced request logging with MDC --- .../utmstack/config/LoggingContextFilter.java | 41 +++++++++++++++++++ backend/src/main/resources/logback-spring.xml | 13 ++++++ 2 files changed, 54 insertions(+) create mode 100644 backend/src/main/java/com/park/utmstack/config/LoggingContextFilter.java diff --git a/backend/src/main/java/com/park/utmstack/config/LoggingContextFilter.java b/backend/src/main/java/com/park/utmstack/config/LoggingContextFilter.java new file mode 100644 index 000000000..8dd2e4e87 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/config/LoggingContextFilter.java @@ -0,0 +1,41 @@ +package com.park.utmstack.config; + +import org.slf4j.MDC; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.*; +import javax.servlet.http.*; +import java.io.IOException; +import java.util.Optional; +import java.util.UUID; + +@Component +public class LoggingContextFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { + + try { + String username = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) + .map(Authentication::getName) + .orElse("anonymous"); + + MDC.put("username", username); + MDC.put("requestId", UUID.randomUUID().toString()); + MDC.put("path", request.getRequestURI()); + MDC.put("method", request.getMethod()); + MDC.put("remoteAddr", request.getRemoteAddr()); + + filterChain.doFilter(request, response); + } finally { + MDC.clear(); + } + } +} + diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml index 4aa548af3..bcf89e606 100644 --- a/backend/src/main/resources/logback-spring.xml +++ b/backend/src/main/resources/logback-spring.xml @@ -2,6 +2,19 @@ + + + {"app":"utmstack-backend"} + username + requestId + path + + + + + + + From a62ff15730073c33dac923be8e6a88952858ab00 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Sat, 6 Sep 2025 12:45:36 -0500 Subject: [PATCH 002/422] feat(logging): introduce LogContextBuilder for enhanced MDC logging context management --- backend/pom.xml | 3 ++ .../utmstack/loggin/LogContextBuilder.java | 45 +++++++++++++++++++ .../LoggingContextFilter.java | 18 +++++--- 3 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java rename backend/src/main/java/com/park/utmstack/{config => loggin}/LoggingContextFilter.java (66%) diff --git a/backend/pom.xml b/backend/pom.xml index abb29ba28..87bf8a432 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -376,6 +376,9 @@ 3.0.5 + + + diff --git a/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java b/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java new file mode 100644 index 000000000..b40932cca --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java @@ -0,0 +1,45 @@ +package com.park.utmstack.loggin; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.MDC; + +import java.util.*; +import java.util.stream.Collectors; + +public class LogContextBuilder { + + private static final ObjectMapper mapper = new ObjectMapper(); + + public static void init(String traceCode, Map args, Throwable exception) { + MDC.put("code", traceCode != null ? traceCode : UUID.randomUUID().toString().replace("-", "")); + MDC.put("args", serializeArgs(args)); + MDC.put("trace", serializeStackTrace(exception)); + } + + public static void clear() { + MDC.remove("code"); + MDC.remove("args"); + MDC.remove("trace"); + } + + private static String serializeArgs(Map args) { + try { + return args != null ? mapper.writeValueAsString(args) : "{}"; + } catch (Exception e) { + return "{\"error\":\"Failed to serialize args\"}"; + } + } + + private static String serializeStackTrace(Throwable ex) { + if (ex == null) return "[]"; + List traceList = Arrays.stream(ex.getStackTrace()) + .map(ste -> ste.getClassName() + "." + ste.getMethodName() + " " + ste.getLineNumber()) + .collect(Collectors.toList()); + try { + return mapper.writeValueAsString(traceList); + } catch (Exception e) { + return "[\"Failed to serialize stack trace\"]"; + } + } +} + diff --git a/backend/src/main/java/com/park/utmstack/config/LoggingContextFilter.java b/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java similarity index 66% rename from backend/src/main/java/com/park/utmstack/config/LoggingContextFilter.java rename to backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java index 8dd2e4e87..681b694bd 100644 --- a/backend/src/main/java/com/park/utmstack/config/LoggingContextFilter.java +++ b/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java @@ -1,5 +1,6 @@ -package com.park.utmstack.config; +package com.park.utmstack.loggin; +import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.MDC; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -9,6 +10,8 @@ import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -26,11 +29,14 @@ protected void doFilterInternal(HttpServletRequest request, .map(Authentication::getName) .orElse("anonymous"); - MDC.put("username", username); - MDC.put("requestId", UUID.randomUUID().toString()); - MDC.put("path", request.getRequestURI()); - MDC.put("method", request.getMethod()); - MDC.put("remoteAddr", request.getRemoteAddr()); + Map args = new HashMap<>(); + args.put("username", username); + args.put("requestId", UUID.randomUUID().toString()); + args.put("path", request.getRequestURI()); + args.put("method", request.getMethod()); + args.put("remoteAddr", request.getRemoteAddr()); + + MDC.put("args", new ObjectMapper().writeValueAsString(args)); filterChain.doFilter(request, response); } finally { From 53905529c54306146692f469bb7c228b359f40cd Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 11 Sep 2025 17:25:00 -0400 Subject: [PATCH 003/422] Update log writing with correct use of 'catcher' in the AWS plugin. --- plugins/aws/config/config.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/aws/config/config.go b/plugins/aws/config/config.go index 2d0470900..4694b7d21 100644 --- a/plugins/aws/config/config.go +++ b/plugins/aws/config/config.go @@ -2,8 +2,6 @@ package config import ( "context" - "fmt" - "log" "strings" sync "sync" "time" @@ -52,7 +50,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") + catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) time.Sleep(reconnectDelay) continue } @@ -133,7 +131,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - log.Printf("Received configuration update: %v", message.Config) + catcher.Info("Received configuration update", map[string]any{"config": message.Config}) cnf = message.Config } } From 5709e11b7856148fccae10915336be478d0647be Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 11 Sep 2025 17:32:21 -0400 Subject: [PATCH 004/422] Update log writing with correct use of 'catcher' in the azure plugin. --- plugins/azure/config/config.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/azure/config/config.go b/plugins/azure/config/config.go index 15a7bd078..2d42cc7e0 100644 --- a/plugins/azure/config/config.go +++ b/plugins/azure/config/config.go @@ -2,8 +2,6 @@ package config import ( "context" - "fmt" - "log" "strings" sync "sync" "time" @@ -52,7 +50,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") + catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) time.Sleep(reconnectDelay) continue } @@ -133,7 +131,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - log.Printf("Received configuration update: %v", message.Config) + catcher.Info("Received configuration update", map[string]any{"config": message.Config}) cnf = message.Config } } From b9e2c77562ab5cfab082af8b47910b6780496db6 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 11 Sep 2025 18:36:51 -0400 Subject: [PATCH 005/422] Update log writing with correct use of 'catcher' in the bitdefender plugin. --- plugins/bitdefender/config/config.go | 8 ++-- plugins/bitdefender/server/certs.go | 13 ++++-- plugins/bitdefender/server/server.go | 69 ++++++++++++++++++---------- 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/plugins/bitdefender/config/config.go b/plugins/bitdefender/config/config.go index 214b5cdff..3a9d6aa0f 100644 --- a/plugins/bitdefender/config/config.go +++ b/plugins/bitdefender/config/config.go @@ -3,8 +3,6 @@ package config import ( "context" "encoding/json" - "fmt" - "log" "net/http" "strings" sync "sync" @@ -65,7 +63,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") + catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) time.Sleep(reconnectDelay) continue } @@ -146,7 +144,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - log.Printf("Received configuration update: %v", message.Config) + catcher.Info("Received configuration update", map[string]any{"config": message.Config}) cnf = message.Config go processConfigurations(cnf) } @@ -216,7 +214,7 @@ func apiPush(config BDGZModuleConfig, operation string) error { for i := 0; i < 5; i++ { response, err := fn(config) if err != nil { - _ = catcher.Error(fmt.Sprintf("%v", err), err, map[string]any{}) + _ = catcher.Error("operation failed", err, map[string]any{}) time.Sleep(1 * time.Minute) continue } diff --git a/plugins/bitdefender/server/certs.go b/plugins/bitdefender/server/certs.go index 6dc270bfa..1bc61ba05 100644 --- a/plugins/bitdefender/server/certs.go +++ b/plugins/bitdefender/server/certs.go @@ -2,7 +2,6 @@ package server import ( "crypto/tls" - "fmt" "os" "time" @@ -31,18 +30,24 @@ func loadCerts() (tls.Certificate, error) { certsFolder, err := utils.MkdirJoin(certsFolderConfig) if err != nil { - return tls.Certificate{}, fmt.Errorf("cannot create certificates directory: %v", err) + return tls.Certificate{}, catcher.Error("cannot create certificates directory", err, map[string]any{ + "certsFolder": certsFolderConfig, + }) } certPath := certsFolder.FileJoin("utm.crt") keyPath := certsFolder.FileJoin("utm.key") if _, err := os.Stat(certPath); os.IsNotExist(err) { - return tls.Certificate{}, fmt.Errorf("certificate file does not exist: %s", certPath) + return tls.Certificate{}, catcher.Error("certificate file does not exist", err, map[string]any{ + "certPath": certPath, + }) } if _, err := os.Stat(keyPath); os.IsNotExist(err) { - return tls.Certificate{}, fmt.Errorf("key file does not exist: %s", keyPath) + return tls.Certificate{}, catcher.Error("key file does not exist", err, map[string]any{ + "keyPath": keyPath, + }) } return tls.LoadX509KeyPair(certPath, keyPath) diff --git a/plugins/bitdefender/server/server.go b/plugins/bitdefender/server/server.go index cfcc5ea07..858d84f7f 100644 --- a/plugins/bitdefender/server/server.go +++ b/plugins/bitdefender/server/server.go @@ -25,14 +25,13 @@ func GetLogs() http.HandlerFunc { if conf.ModuleActive { if r.Header.Get("authorization") == "" { - message := "401 Missing Authorization Header" - _ = catcher.Error("missing authorization header", nil, map[string]any{}) - j, _ := json.Marshal(message) - w.WriteHeader(http.StatusUnauthorized) - _, err := w.Write(j) - if err != nil { - _ = catcher.Error("cannot write response", err, nil) - } + _ = catcher.Error("missing authorization header", nil, map[string]any{ + "status": http.StatusUnauthorized, + "endpoint": r.URL.Path, + "method": r.Method, + "remoteAddr": r.RemoteAddr, + }) + http.Error(w, "Missing Authorization Header", http.StatusUnauthorized) return } @@ -44,35 +43,46 @@ func GetLogs() http.HandlerFunc { } } if !isAuth { - message := "401 Invalid Authentication Credentials" - _ = catcher.Error("invalid authentication credentials", nil, map[string]any{}) - j, _ := json.Marshal(message) - w.WriteHeader(http.StatusUnauthorized) - _, err := w.Write(j) - if err != nil { - _ = catcher.Error("cannot write response", err, nil) - } + _ = catcher.Error("invalid authentication credentials", nil, map[string]any{ + "status": http.StatusUnauthorized, + "endpoint": r.URL.Path, + "method": r.Method, + "remoteAddr": r.RemoteAddr, + }) + http.Error(w, "Invalid Authentication Credentials", http.StatusUnauthorized) return } var newBody schema.BodyEvents err := json.NewDecoder(r.Body).Decode(&newBody) if err != nil { - _ = catcher.Error("error decoding body", err, map[string]any{}) + _ = catcher.Error("error decoding request body", err, map[string]any{ + "status": http.StatusBadRequest, + "endpoint": r.URL.Path, + "method": r.Method, + "remoteAddr": r.RemoteAddr, + }) + http.Error(w, "Invalid request body", http.StatusBadRequest) return } events := newBody.Events CreateMessage(conf, events) - j, _ := json.Marshal("HTTP 200 OK") w.WriteHeader(http.StatusOK) - _, err = w.Write(j) - if err != nil { - _ = catcher.Error("cannot write response", err, nil) + if _, err := w.Write([]byte("OK")); err != nil { + _ = catcher.Error("cannot write response", err, map[string]any{ + "status": http.StatusOK, + }) } } else { - _ = catcher.Error("bitdefender module disabled", nil, map[string]any{}) + _ = catcher.Error("bitdefender module disabled", nil, map[string]any{ + "status": http.StatusServiceUnavailable, + "endpoint": r.URL.Path, + "method": r.Method, + "remoteAddr": r.RemoteAddr, + }) + http.Error(w, "Service Unavailable", http.StatusServiceUnavailable) } } } @@ -81,8 +91,21 @@ func StartServer() { r := mux.NewRouter().StrictSlash(false) r.HandleFunc("/api", GetLogs()).Methods("POST") r.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) { + catcher.Info("health check requested", map[string]any{ + "endpoint": r.URL.Path, + "method": r.Method, + "remoteAddr": r.RemoteAddr, + "userAgent": r.UserAgent(), + }) + w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("Server is up and running")) + if _, err := w.Write([]byte("Server is up and running")); err != nil { + _ = catcher.Error("cannot write health check response", err, map[string]any{ + "status": http.StatusOK, + "endpoint": r.URL.Path, + "remoteAddr": r.RemoteAddr, + }) + } }).Methods("GET") loadedCerts, err := loadCerts() From d08c4dc4c9dfced83114e8f60844c2946e7d40f4 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 11 Sep 2025 18:49:12 -0400 Subject: [PATCH 006/422] Update log writing with correct use of 'catcher' in the config plugin. --- plugins/config/main.go | 58 +++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/plugins/config/main.go b/plugins/config/main.go index 7d677fa5e..9029cf116 100644 --- a/plugins/config/main.go +++ b/plugins/config/main.go @@ -388,7 +388,7 @@ func connect() (*sql.DB, error) { func getPatterns(db *sql.DB) (map[string]string, error) { rows, err := db.Query("SELECT pattern_id, pattern_definition FROM utm_regex_pattern") if err != nil { - return nil, fmt.Errorf("failed to get patterns: %v", err) + return nil, catcher.Error("failed to get patterns", err, map[string]any{}) } defer func() { _ = rows.Close() }() @@ -401,7 +401,7 @@ func getPatterns(db *sql.DB) (map[string]string, error) { err = rows.Scan(&name, &pattern) if err != nil { - return nil, fmt.Errorf("failed to scan row: %v", err) + return nil, catcher.Error("failed to scan row", err, map[string]any{}) } patterns[name] = pattern @@ -413,7 +413,7 @@ func getPatterns(db *sql.DB) (map[string]string, error) { func getFilters(db *sql.DB) ([]Filter, error) { rows, err := db.Query("SELECT id, filter_name, logstash_filter FROM utm_logstash_filter WHERE is_active = true") if err != nil { - return nil, fmt.Errorf("failed to get filters: %v", err) + return nil, catcher.Error("failed to get filters", err, map[string]any{}) } defer func() { _ = rows.Close() }() @@ -429,7 +429,7 @@ func getFilters(db *sql.DB) ([]Filter, error) { err = rows.Scan(&id, &name, &body) if err != nil { - return nil, fmt.Errorf("failed to scan row: %v", err) + return nil, catcher.Error("failed to scan row", err, map[string]any{}) } filter := Filter{} @@ -447,7 +447,7 @@ func getFilters(db *sql.DB) ([]Filter, error) { func getAssets(db *sql.DB) ([]Asset, error) { rows, err := db.Query("SELECT id,asset_name,asset_hostname_list_def,asset_ip_list_def,asset_confidentiality,asset_integrity,asset_availability,last_update FROM utm_tenant_config") if err != nil { - return nil, fmt.Errorf("failed to get assets: %v", err) + return nil, catcher.Error("failed to get assets", err, map[string]any{}) } defer func() { _ = rows.Close() }() @@ -469,7 +469,7 @@ func getAssets(db *sql.DB) ([]Asset, error) { err = rows.Scan(&id, &name, &hostnames, &ips, &confidentiality, &integrity, &availability, &lastUpdate) if err != nil { - return nil, fmt.Errorf("failed to scan row: %v", err) + return nil, catcher.Error("failed to scan row", err, map[string]any{}) } asset := Asset{} @@ -485,7 +485,7 @@ func getAssets(db *sql.DB) ([]Asset, error) { func getRules(db *sql.DB) ([]Rule, error) { rows, err := db.Query("SELECT id,rule_name,rule_confidentiality,rule_integrity,rule_availability,rule_category,rule_technique,rule_description,rule_references_def,rule_definition_def,rule_adversary,rule_deduplicate_by_def,rule_after_events_def FROM utm_correlation_rules WHERE rule_active = true") if err != nil { - return nil, fmt.Errorf("failed to get rules: %v", err) + return nil, catcher.Error("failed to get rules", err, map[string]any{}) } defer func() { _ = rows.Close() }() @@ -512,7 +512,7 @@ func getRules(db *sql.DB) ([]Rule, error) { err = rows.Scan(&id, &ruleName, &confidentiality, &integrity, &availability, &category, &technique, &description, &references, &where, &adversary, &deduplicate, &after) if err != nil { - return nil, fmt.Errorf("failed to scan row: %v", err) + return nil, catcher.Error("failed to scan row", err, map[string]any{}) } rule := Rule{} @@ -533,7 +533,7 @@ func getRules(db *sql.DB) ([]Rule, error) { func getRuleDataTypes(db *sql.DB, ruleId int64) ([]string, error) { rows, err := db.Query("SELECT data_type_id FROM utm_group_rules_data_type WHERE rule_id = $1", ruleId) if err != nil { - return nil, fmt.Errorf("failed to get data types: %v", err) + return nil, catcher.Error("failed to get data types", err, map[string]any{}) } defer func() { _ = rows.Close() }() @@ -548,14 +548,14 @@ func getRuleDataTypes(db *sql.DB, ruleId int64) ([]string, error) { err = rows.Scan(&dataTypeId) if err != nil { - return nil, fmt.Errorf("failed to scan row: %v", err) + return nil, catcher.Error("failed to scan row", err, map[string]any{}) } row := db.QueryRow("SELECT data_type FROM utm_data_types WHERE id = $1", dataTypeId) err := row.Scan(&dataType) if err != nil { - return nil, fmt.Errorf("failed to scan row: %v", err) + return nil, catcher.Error("failed to scan row", err, map[string]any{}) } dataTypes = append(dataTypes, utils.CastString(dataType)) @@ -580,7 +580,7 @@ func listFiles(folder string) ([]string, error) { }) if err != nil { - return nil, fmt.Errorf("failed to list files: %v", err) + return nil, catcher.Error("failed to list files", err, nil) } return files, nil @@ -610,7 +610,7 @@ func cleanUpFilters(filters []Filter) error { if !keep { err := os.Remove(file) if err != nil { - return fmt.Errorf("failed to remove file: %v", err) + return catcher.Error("failed to remove file", err, map[string]any{}) } } } @@ -642,7 +642,7 @@ func cleanUpRules(rules []Rule) error { if !keep { err := os.Remove(file) if err != nil { - return fmt.Errorf("failed to remove file: %v", err) + return catcher.Error("failed to remove file", err, map[string]any{}) } } } @@ -659,17 +659,17 @@ func writeFilters(filters []Filter) error { file, err := os.Create(filtersFolder.FileJoin(fmt.Sprintf("%d.yaml", filter.Id))) if err != nil { - return fmt.Errorf("failed to create file: %v", err) + return catcher.Error("failed to create file", err, map[string]any{}) } _, err = file.WriteString(filter.Filter) if err != nil { - return fmt.Errorf("failed to write to file: %v", err) + return catcher.Error("failed to write to file", err, map[string]any{}) } err = file.Close() if err != nil { - return fmt.Errorf("failed to close file: %v", err) + return catcher.Error("failed to close file", err, map[string]any{}) } } @@ -684,7 +684,7 @@ func writeTenant(tenant Tenant) error { file, err := os.Create(pipelineFolder.FileJoin("tenants.yaml")) if err != nil { - return fmt.Errorf("failed to create file: %v", err) + return catcher.Error("failed to create file", err, map[string]any{}) } sdkTenant := plugins.Tenant(tenant) @@ -695,17 +695,17 @@ func writeTenant(tenant Tenant) error { bTenants, err := k8syaml.Marshal(tenants) if err != nil { - return fmt.Errorf("failed to marshal tenant: %v", err) + return catcher.Error("failed to marshal tenant", err, map[string]any{}) } _, err = file.Write(bTenants) if err != nil { - return fmt.Errorf("failed to write to file: %v", err) + return catcher.Error("failed to write to file", err, map[string]any{}) } err = file.Close() if err != nil { - return fmt.Errorf("failed to close file: %v", err) + return catcher.Error("failed to close file", err, map[string]any{}) } return nil @@ -720,22 +720,22 @@ func writeRules(rules []Rule) error { file, err := os.Create(filePath.FileJoin(fmt.Sprintf("%d.yaml", rule.Id))) if err != nil { - return fmt.Errorf("failed to create file: %v", err) + return catcher.Error("failed to create file", err, map[string]any{}) } bRule, err := yaml.Marshal([]Rule{rule}) if err != nil { - return fmt.Errorf("failed to marshal rule: %v", err) + return catcher.Error("failed to marshal rule", err, map[string]any{}) } _, err = file.Write(bRule) if err != nil { - return fmt.Errorf("failed to write to file: %v", err) + return catcher.Error("failed to write to file", err, map[string]any{}) } err = file.Close() if err != nil { - return fmt.Errorf("failed to close file: %v", err) + return catcher.Error("failed to close file", err, map[string]any{}) } } @@ -749,7 +749,7 @@ func writePatterns(patterns map[string]string) error { } file, err := os.Create(filePath.FileJoin("patterns.yaml")) if err != nil { - return fmt.Errorf("failed to create file: %v", err) + return catcher.Error("failed to create file", err, map[string]any{}) } config := plugins.Config{ @@ -758,17 +758,17 @@ func writePatterns(patterns map[string]string) error { bPatterns, err := k8syaml.Marshal(config) if err != nil { - return fmt.Errorf("failed to marshal patterns: %v", err) + return catcher.Error("failed to marshal patterns", err, map[string]any{}) } _, err = file.Write(bPatterns) if err != nil { - return fmt.Errorf("failed to write to file: %v", err) + return catcher.Error("failed to write to file", err, map[string]any{}) } err = file.Close() if err != nil { - return fmt.Errorf("failed to close file: %v", err) + return catcher.Error("failed to close file", err, map[string]any{}) } return nil From 4349637ef43edc006ed93fcf09cee5b3fce44354 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 11 Sep 2025 18:49:32 -0400 Subject: [PATCH 007/422] Update log writing with correct use of 'catcher' in the gcp plugin. --- plugins/gcp/config/config.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/gcp/config/config.go b/plugins/gcp/config/config.go index d7b9fa2fd..5217999ee 100644 --- a/plugins/gcp/config/config.go +++ b/plugins/gcp/config/config.go @@ -2,8 +2,6 @@ package config import ( "context" - "fmt" - "log" "strings" sync "sync" "time" @@ -52,7 +50,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") + catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) time.Sleep(reconnectDelay) continue } @@ -133,7 +131,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - log.Printf("Received configuration update: %v", message.Config) + catcher.Info("Received configuration update", map[string]any{"config": message.Config}) cnf = message.Config } } From 6ec652c50323fb89512b6172bd3f1551dfd2d72e Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Thu, 11 Sep 2025 19:37:18 -0400 Subject: [PATCH 008/422] Update log writing with correct use of 'catcher' in the input plugin. --- plugins/inputs/main.go | 7 +++---- plugins/inputs/middlewares.go | 7 ++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/inputs/main.go b/plugins/inputs/main.go index af9b38988..9c0080f0f 100644 --- a/plugins/inputs/main.go +++ b/plugins/inputs/main.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" "runtime" "time" @@ -75,18 +74,18 @@ func loadCerts() (string, string, error) { certsFolder, err := utils.MkdirJoin(certsFolderPath) if err != nil { - return "", "", fmt.Errorf("cannot create certificates directory: %v", err) + return "", "", catcher.Error("cannot create certificates directory", err, map[string]any{"path": certsFolderPath}) } certPath := certsFolder.FileJoin(utmCertFileName) keyPath := certsFolder.FileJoin(utmCertFileKey) if _, err := os.Stat(certPath); os.IsNotExist(err) { - return "", "", fmt.Errorf("certificate file does not exist: %s", certPath) + return "", "", catcher.Error("certificate file does not exist", err, map[string]any{"path": certPath}) } if _, err := os.Stat(keyPath); os.IsNotExist(err) { - return "", "", fmt.Errorf("key file does not exist: %s", keyPath) + return "", "", catcher.Error("key file does not exist", err, map[string]any{"path": keyPath}) } return certPath, keyPath, nil diff --git a/plugins/inputs/middlewares.go b/plugins/inputs/middlewares.go index 0142e1961..b869c4436 100644 --- a/plugins/inputs/middlewares.go +++ b/plugins/inputs/middlewares.go @@ -7,6 +7,10 @@ import ( "crypto/sha256" "errors" "fmt" + "io" + "strconv" + "strings" + "github.com/gin-gonic/gin" "github.com/threatwinds/go-sdk/catcher" "github.com/threatwinds/go-sdk/plugins" @@ -14,9 +18,6 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" - "io" - "strconv" - "strings" ) type Middlewares struct { From 44e674b13c65089ea33ca98a31780d3f073bba8c Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Mon, 15 Sep 2025 11:46:01 -0400 Subject: [PATCH 009/422] Update log writing with correct use of 'catcher' in the modules-config plugin. --- plugins/modules-config/config/config.go | 7 ++- plugins/modules-config/handlers.go | 3 +- plugins/modules-config/validations/aws.go | 14 ++--- plugins/modules-config/validations/azure.go | 22 ++++---- plugins/modules-config/validations/bdgz.go | 53 +++++++++---------- plugins/modules-config/validations/gcp.go | 9 ++-- plugins/modules-config/validations/o365.go | 24 +++++---- plugins/modules-config/validations/socai.go | 28 +++++++--- plugins/modules-config/validations/sophos.go | 38 ++++++++----- .../modules-config/validations/validate.go | 3 +- 10 files changed, 114 insertions(+), 87 deletions(-) diff --git a/plugins/modules-config/config/config.go b/plugins/modules-config/config/config.go index 28780d84b..de8e00038 100644 --- a/plugins/modules-config/config/config.go +++ b/plugins/modules-config/config/config.go @@ -129,7 +129,7 @@ func (s *ConfigServer) NotifyUpdate(moduleName string, section *ConfigurationSec case "SOPHOS": pluginType = PluginType_SOPHOS default: - _ = catcher.Error("unknown module name", fmt.Errorf("module: %s", moduleName), nil) + _ = catcher.Error("unknown module name", nil, map[string]any{"module": moduleName}) return } @@ -179,7 +179,10 @@ func (s *ConfigServer) SyncConfigs(backend string, internalKey string) { break } - fmt.Printf("Error fetching configuration for %s: %v, status code: %d. Retrying...\n", name, err, status) + _ = catcher.Error("error fetching configuration", err, map[string]any{ + "module": name, + "status": status, + }) time.Sleep(5 * time.Second) } } diff --git a/plugins/modules-config/handlers.go b/plugins/modules-config/handlers.go index 54dd5604e..a7925de26 100644 --- a/plugins/modules-config/handlers.go +++ b/plugins/modules-config/handlers.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "net" "net/http" @@ -71,7 +70,7 @@ func UpdateModuleConfig(c *gin.Context) { if len(body) != 0 { config.GetConfigServer().NotifyUpdate(moduleName, &body[0]) } else { - fmt.Println("Received empty configuration body, no updates made") + catcher.Info("Received empty configuration body, no updates made", nil) } c.JSON(http.StatusOK, gin.H{"status": "Module configuration updated successfully"}) diff --git a/plugins/modules-config/validations/aws.go b/plugins/modules-config/validations/aws.go index 4ddb008bb..8d4f3c351 100644 --- a/plugins/modules-config/validations/aws.go +++ b/plugins/modules-config/validations/aws.go @@ -2,11 +2,11 @@ package validations import ( "context" - "fmt" awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -14,7 +14,7 @@ func ValidateAwsConfig(config *config.ModuleGroup) error { var regionName, accessKey, secretAccessKey string if config == nil { - return fmt.Errorf("AWS_IAM_USER configuration is nil") + return catcher.Error("AWS_IAM_USER configuration is nil", nil, nil) } for _, cnf := range config.ModuleGroupConfigurations { @@ -29,13 +29,13 @@ func ValidateAwsConfig(config *config.ModuleGroup) error { } if regionName == "" { - return fmt.Errorf("Default Region is required in AWS_IAM_USER configuration") + return catcher.Error("Default Region is required in AWS_IAM_USER configuration", nil, nil) } if accessKey == "" { - return fmt.Errorf("Access Key is required in AWS_IAM_USER configuration") + return catcher.Error("Access Key is required in AWS_IAM_USER configuration", nil, nil) } if secretAccessKey == "" { - return fmt.Errorf("Secret Key is required in AWS_IAM_USER configuration") + return catcher.Error("Secret Key is required in AWS_IAM_USER configuration", nil, nil) } cfg, err := awsconfig.LoadDefaultConfig(context.TODO(), @@ -47,14 +47,14 @@ func ValidateAwsConfig(config *config.ModuleGroup) error { )), ) if err != nil { - return fmt.Errorf("failed to load AWS configuration: %w", err) + return catcher.Error("failed to load AWS configuration", err, nil) } stsClient := sts.NewFromConfig(cfg) _, err = stsClient.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{}) if err != nil { - return fmt.Errorf("AWS credentials validation failed: %w", err) + return catcher.Error("AWS credentials validation failed", err, nil) } return nil diff --git a/plugins/modules-config/validations/azure.go b/plugins/modules-config/validations/azure.go index 120d622d4..17b5f9c4e 100644 --- a/plugins/modules-config/validations/azure.go +++ b/plugins/modules-config/validations/azure.go @@ -2,12 +2,12 @@ package validations import ( "context" - "fmt" "strings" "time" "github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs/v2" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -15,7 +15,7 @@ func ValidateAzureConfig(config *config.ModuleGroup) error { var eventHubConnection, consumerGroup, storageContainer, storageConnection string if config == nil { - return fmt.Errorf("AZURE configuration is nil") + return catcher.Error("AZURE configuration is nil", nil, nil) } for _, cnf := range config.ModuleGroupConfigurations { @@ -32,28 +32,28 @@ func ValidateAzureConfig(config *config.ModuleGroup) error { } if eventHubConnection == "" { - return fmt.Errorf("eventHubConnection is required in AZURE configuration") + return catcher.Error("eventHubConnection is required in AZURE configuration", nil, nil) } if consumerGroup == "" { - return fmt.Errorf("consumerGroup is required in AZURE configuration") + return catcher.Error("consumerGroup is required in AZURE configuration", nil, nil) } if storageContainer == "" { - return fmt.Errorf("storageContainer is required in AZURE configuration") + return catcher.Error("storageContainer is required in AZURE configuration", nil, nil) } if storageConnection == "" { - return fmt.Errorf("storageConnection is required in AZURE configuration") + return catcher.Error("storageConnection is required in AZURE configuration", nil, nil) } eventHubParts := strings.Split(eventHubConnection, ";EntityPath=") if len(eventHubParts) != 2 { - return fmt.Errorf("invalid Event Hub connection string format: missing EntityPath") + return catcher.Error("invalid Event Hub connection string format: missing EntityPath", nil, nil) } eventHubConnectionBase := eventHubParts[0] eventHubName := eventHubParts[1] consumerClient, err := azeventhubs.NewConsumerClientFromConnectionString(eventHubConnectionBase, eventHubName, consumerGroup, nil) if err != nil { - return fmt.Errorf("failed to create Event Hub consumer client: %w", err) + return catcher.Error("failed to create Event Hub consumer client", err, nil) } defer consumerClient.Close(context.Background()) @@ -62,12 +62,12 @@ func ValidateAzureConfig(config *config.ModuleGroup) error { _, err = consumerClient.GetEventHubProperties(ctx, nil) if err != nil { - return fmt.Errorf("Event Hub connection validation failed: %w", err) + return catcher.Error("Event Hub connection validation failed", err, nil) } blobClient, err := azblob.NewClientFromConnectionString(storageConnection, nil) if err != nil { - return fmt.Errorf("failed to create Storage client: %w", err) + return catcher.Error("failed to create Storage client", err, nil) } containerClient := blobClient.ServiceClient().NewContainerClient(storageContainer) @@ -76,7 +76,7 @@ func ValidateAzureConfig(config *config.ModuleGroup) error { _, err = containerClient.GetProperties(ctx2, nil) if err != nil { - return fmt.Errorf("Storage container validation failed: %w", err) + return catcher.Error("Storage container validation failed", err, nil) } return nil diff --git a/plugins/modules-config/validations/bdgz.go b/plugins/modules-config/validations/bdgz.go index 9b28e54b5..ca8a0e449 100644 --- a/plugins/modules-config/validations/bdgz.go +++ b/plugins/modules-config/validations/bdgz.go @@ -4,12 +4,12 @@ import ( "bytes" "encoding/base64" "encoding/json" - "fmt" "io" "net/http" "strings" "time" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -28,7 +28,7 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { var connectionKey, accessUrl, masterIp, companiesIDs string if config == nil { - return fmt.Errorf("Bitdefender configuration is nil") + return catcher.Error("Bitdefender configuration is nil", nil, nil) } for _, cnf := range config.ModuleGroupConfigurations { @@ -45,20 +45,20 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { } if connectionKey == "" { - return fmt.Errorf("Connection Key is required in Bitdefender configuration") + return catcher.Error("Connection Key is required in Bitdefender configuration", nil, nil) } if accessUrl == "" { - return fmt.Errorf("Access URL is required in Bitdefender configuration") + return catcher.Error("Access URL is required in Bitdefender configuration", nil, nil) } if masterIp == "" { - return fmt.Errorf("Master IP is required in Bitdefender configuration") + return catcher.Error("Master IP is required in Bitdefender configuration", nil, nil) } if companiesIDs == "" { - return fmt.Errorf("Companies IDs is required in Bitdefender configuration") + return catcher.Error("Companies IDs is required in Bitdefender configuration", nil, nil) } if !strings.HasPrefix(accessUrl, "http://") && !strings.HasPrefix(accessUrl, "https://") { - return fmt.Errorf("Access URL must start with http:// or https://") + return catcher.Error("Access URL must start with http:// or https://", nil, nil) } authCode := generateAuthCode(connectionKey) @@ -67,17 +67,17 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { JsonRPC: "2.0", Method: "getPushEventSettings", ID: "1", - Params: map[string]interface{}{}, + Params: map[string]any{}, } body, err := json.Marshal(testRequest) if err != nil { - return fmt.Errorf("failed to create test request: %w", err) + return catcher.Error("failed to create test request", err, nil) } req, err := http.NewRequest("POST", accessUrl+endpointPush, bytes.NewBuffer(body)) if err != nil { - return fmt.Errorf("failed to create HTTP request: %w", err) + return catcher.Error("failed to create HTTP request", err, nil) } req.Header.Add("Content-Type", "application/json") @@ -89,25 +89,20 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { resp, err := client.Do(req) if err != nil { - return fmt.Errorf("Bitdefender API request failed: %w", err) + return catcher.Error("Bitdefender API request failed", err, nil) } defer resp.Body.Close() // Read response body bodyBytes, _ := io.ReadAll(resp.Body) - // Log response for debugging (you can remove this later) - if len(bodyBytes) > 0 { - fmt.Printf("Response body: %s\n", string(bodyBytes)) - } - - var respBody map[string]interface{} + var respBody map[string]any if err := json.Unmarshal(bodyBytes, &respBody); err == nil { if errorField, ok := respBody["error"]; ok { - if errorMap, ok := errorField.(map[string]interface{}); ok { + if errorMap, ok := errorField.(map[string]any); ok { if code, ok := errorMap["code"].(float64); ok { details := "" - if dataMap, ok := errorMap["data"].(map[string]interface{}); ok { + if dataMap, ok := errorMap["data"].(map[string]any); ok { if d, ok := dataMap["details"].(string); ok { details = d } @@ -118,7 +113,7 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { } if code == -32001 || code == -32002 { - return fmt.Errorf("Bitdefender authentication failed: invalid Connection Key") + return catcher.Error("Bitdefender authentication failed: invalid Connection Key", nil, nil) } if message, ok := errorMap["message"].(string); ok { @@ -127,40 +122,40 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { strings.Contains(combinedError, "authentication") || strings.Contains(combinedError, "invalid api key") || strings.Contains(combinedError, "access denied") { - return fmt.Errorf("Bitdefender authentication failed: %s", message) + return catcher.Error("Bitdefender authentication failed", nil, map[string]any{"error": message}) } } } if message, ok := errorMap["message"].(string); ok { - return fmt.Errorf("Bitdefender API error: %s", message) + return catcher.Error("Bitdefender API error", nil, map[string]any{"error": message}) } } - return fmt.Errorf("Bitdefender API error: %v", errorField) + return catcher.Error("Bitdefender API error", nil, map[string]any{"error": errorField}) } if _, hasResult := respBody["result"]; !hasResult && resp.StatusCode == 200 { if _, hasId := respBody["id"]; !hasId { - return fmt.Errorf("Invalid response format from Bitdefender API") + return catcher.Error("Invalid response format from Bitdefender API", nil, nil) } } } else if resp.StatusCode == 200 { - return fmt.Errorf("Invalid JSON response from Bitdefender API") + return catcher.Error("Invalid JSON response from Bitdefender API", nil, nil) } if resp.StatusCode == 401 || resp.StatusCode == 403 { - return fmt.Errorf("Bitdefender authentication failed: invalid Connection Key") + return catcher.Error("Bitdefender authentication failed: invalid Connection Key", nil, nil) } if resp.StatusCode == 404 { - return fmt.Errorf("Bitdefender API endpoint not found. Please check the Access URL") + return catcher.Error("Bitdefender API endpoint not found. Please check the Access URL", nil, nil) } if resp.StatusCode >= 400 { - return fmt.Errorf("Bitdefender API returned error status: %d", resp.StatusCode) + return catcher.Error("Bitdefender API returned error status", nil, map[string]any{"status_code": resp.StatusCode}) } if resp.StatusCode != 200 { - return fmt.Errorf("Unexpected response status: %d", resp.StatusCode) + return catcher.Error("Unexpected response status", nil, map[string]any{"status_code": resp.StatusCode}) } return nil diff --git a/plugins/modules-config/validations/gcp.go b/plugins/modules-config/validations/gcp.go index 4890304e1..ba8f4c2c4 100644 --- a/plugins/modules-config/validations/gcp.go +++ b/plugins/modules-config/validations/gcp.go @@ -6,6 +6,7 @@ import ( "time" "cloud.google.com/go/pubsub" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" "google.golang.org/api/option" ) @@ -14,7 +15,7 @@ func ValidateGcpConfig(config *config.ModuleGroup) error { var jsonKey, projectID, subscriptionID string if config == nil { - return fmt.Errorf("GCP configuration is nil") + return catcher.Error("GCP configuration is nil", nil, nil) } for _, cnf := range config.ModuleGroupConfigurations { @@ -29,13 +30,13 @@ func ValidateGcpConfig(config *config.ModuleGroup) error { } if jsonKey == "" { - return fmt.Errorf("JSON Key is required in GCP configuration") + return catcher.Error("JSON Key is required in GCP configuration", nil, nil) } if projectID == "" { - return fmt.Errorf("Project ID is required in GCP configuration") + return catcher.Error("Project ID is required in GCP configuration", nil, nil) } if subscriptionID == "" { - return fmt.Errorf("Subscription ID is required in GCP configuration") + return catcher.Error("Subscription ID is required in GCP configuration", nil, nil) } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) diff --git a/plugins/modules-config/validations/o365.go b/plugins/modules-config/validations/o365.go index d8e092a9c..34f4edc89 100644 --- a/plugins/modules-config/validations/o365.go +++ b/plugins/modules-config/validations/o365.go @@ -9,6 +9,7 @@ import ( "net/url" "time" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -32,7 +33,7 @@ func ValidateO365Config(config *config.ModuleGroup) error { var clientId, clientSecret, tenantId string if config == nil { - return fmt.Errorf("O365 configuration is nil") + return catcher.Error("O365 configuration is nil", nil, nil) } for _, cnf := range config.ModuleGroupConfigurations { @@ -47,13 +48,13 @@ func ValidateO365Config(config *config.ModuleGroup) error { } if clientId == "" { - return fmt.Errorf("Client ID is required in O365 configuration") + return catcher.Error("Client ID is required in O365 configuration", nil, nil) } if clientSecret == "" { - return fmt.Errorf("Client Secret is required in O365 configuration") + return catcher.Error("Client Secret is required in O365 configuration", nil, nil) } if tenantId == "" { - return fmt.Errorf("Tenant ID is required in O365 configuration") + return catcher.Error("Tenant ID is required in O365 configuration", nil, nil) } // Validate credentials by attempting to get an access token @@ -71,33 +72,36 @@ func ValidateO365Config(config *config.ModuleGroup) error { req, err := http.NewRequest(http.MethodPost, requestUrl, bytes.NewBufferString(data.Encode())) if err != nil { - return fmt.Errorf("failed to create request: %w", err) + return catcher.Error("failed to create request", err, nil) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") resp, err := client.Do(req) if err != nil { - return fmt.Errorf("O365 authentication request failed: %w", err) + return catcher.Error("O365 authentication request failed", err, nil) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("failed to read response: %w", err) + return catcher.Error("failed to read response", err, nil) } var loginResp MicrosoftLoginResponse if err := json.Unmarshal(body, &loginResp); err != nil { - return fmt.Errorf("failed to parse response: %w", err) + return catcher.Error("failed to parse response", err, nil) } if loginResp.Error != "" { - return fmt.Errorf("O365 authentication failed: %s - %s", loginResp.Error, loginResp.ErrorDesc) + return catcher.Error("O365 authentication failed", nil, map[string]any{ + "error": loginResp.Error, + "error_description": loginResp.ErrorDesc, + }) } if loginResp.AccessToken == "" { - return fmt.Errorf("O365 authentication failed: no access token received") + return catcher.Error("O365 authentication failed: no access token received", nil, nil) } return nil diff --git a/plugins/modules-config/validations/socai.go b/plugins/modules-config/validations/socai.go index 352640dcc..c9bdfb61f 100644 --- a/plugins/modules-config/validations/socai.go +++ b/plugins/modules-config/validations/socai.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" + "github.com/threatwinds/go-sdk/catcher" "github.com/threatwinds/go-sdk/utils" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -12,7 +13,7 @@ func ValidateSOCAIConfig(config *config.ModuleGroup) error { var apiKey, provider string if config == nil { - return fmt.Errorf("SOC_AI configuration is nil") + return catcher.Error("SOC_AI configuration is nil", nil, nil) } for _, cnf := range config.ModuleGroupConfigurations { @@ -25,10 +26,10 @@ func ValidateSOCAIConfig(config *config.ModuleGroup) error { } if apiKey == "" { - return fmt.Errorf("API Key is required in SOC_AI configuration") + return catcher.Error("API Key is required in SOC_AI configuration", nil, nil) } if provider == "" { - return fmt.Errorf("Provider is required in SOC_AI configuration") + return catcher.Error("Provider is required in SOC_AI configuration", nil, nil) } else if provider != "openai" { return nil } @@ -42,13 +43,26 @@ func ValidateSOCAIConfig(config *config.ModuleGroup) error { response, status, err := utils.DoReq[map[string]any](url, nil, "GET", headers) if err != nil || status != http.StatusOK { if status == http.StatusRequestTimeout { - return fmt.Errorf("SOC_AI connection timed out") + return catcher.Error("SOC_AI connection timed out", err, map[string]any{ + "status_code": status, + "response": response, + }) } if status == http.StatusUnauthorized { - return fmt.Errorf("SOC_AI API Key is invalid") + return catcher.Error("SOC_AI API Key is invalid", err, map[string]any{ + "status_code": status, + "response": response, + }) } - fmt.Printf("Error validating SOC_AI connection: %v, status code: %d, response: %v\n", err, status, response) - return fmt.Errorf("SOC_AI API Key is invalid") + catcher.Info("Error validating SOC_AI connection", map[string]any{ + "error": err, + "status_code": status, + "response": response, + }) + return catcher.Error("SOC_AI API Key is invalid", err, map[string]any{ + "status_code": status, + "response": response, + }) } return nil diff --git a/plugins/modules-config/validations/sophos.go b/plugins/modules-config/validations/sophos.go index 01c778420..e35b928e4 100644 --- a/plugins/modules-config/validations/sophos.go +++ b/plugins/modules-config/validations/sophos.go @@ -3,12 +3,12 @@ package validations import ( "bytes" "encoding/json" - "fmt" "io" "net/http" "net/url" "time" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -21,7 +21,7 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { var clientID, clientSecret string if config == nil { - return fmt.Errorf("Sophos configuration is nil") + return catcher.Error("Sophos configuration is nil", nil, nil) } for _, cnf := range config.ModuleGroupConfigurations { @@ -34,10 +34,10 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { } if clientID == "" { - return fmt.Errorf("Client ID is required in Sophos configuration") + return catcher.Error("Client ID is required in Sophos configuration", nil, nil) } if clientSecret == "" { - return fmt.Errorf("Client Secret is required in Sophos configuration") + return catcher.Error("Client Secret is required in Sophos configuration", nil, nil) } data := url.Values{} @@ -48,7 +48,7 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { req, err := http.NewRequest(http.MethodPost, sophosAuthURL, bytes.NewBufferString(data.Encode())) if err != nil { - return fmt.Errorf("failed to create request: %w", err) + return catcher.Error("failed to create request", err, nil) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") @@ -59,18 +59,18 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { resp, err := client.Do(req) if err != nil { - return fmt.Errorf("Sophos authentication request failed: %w", err) + return catcher.Error("Sophos authentication request failed", err, nil) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("failed to read response: %w", err) + return catcher.Error("failed to read response", err, nil) } var response map[string]interface{} if err := json.Unmarshal(body, &response); err != nil { - return fmt.Errorf("failed to parse response: %w", err) + return catcher.Error("failed to parse response", err, nil) } if resp.StatusCode != http.StatusOK { @@ -80,21 +80,29 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { message = msg } if errorCode == "oauth.invalid_client_secret" { - return fmt.Errorf("Sophos authentication failed: Invalid Client Secret") + return catcher.Error("Sophos authentication failed: Invalid Client Secret", nil, nil) } if errorCode == "oauth.invalid_client_id" { - return fmt.Errorf("Sophos authentication failed: Invalid Client ID") + return catcher.Error("Sophos authentication failed: Invalid Client ID", nil, nil) } - return fmt.Errorf("Sophos authentication failed: %v - %s", errorCode, message) + return catcher.Error("Sophos authentication failed", nil, map[string]any{ + "error_code": errorCode, + "message": message, + }) } if errorCode, hasError := response["error"]; hasError { errorDesc := "" if desc, ok := response["error_description"].(string); ok { errorDesc = desc } - return fmt.Errorf("Sophos authentication failed: %v - %s", errorCode, errorDesc) + return catcher.Error("Sophos authentication failed", nil, map[string]any{ + "error_code": errorCode, + "message": errorDesc, + }) } - return fmt.Errorf("Sophos authentication failed with status %d", resp.StatusCode) + return catcher.Error("Sophos authentication failed", nil, map[string]any{ + "status_code": resp.StatusCode, + }) } accessToken, ok := response["access_token"].(string) @@ -103,7 +111,9 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { for k := range response { fields = append(fields, k) } - return fmt.Errorf("Sophos authentication failed: no access token received. Response fields: %v", fields) + return catcher.Error("Sophos authentication failed", nil, map[string]any{ + "response_fields": fields, + }) } return nil diff --git a/plugins/modules-config/validations/validate.go b/plugins/modules-config/validations/validate.go index ad5f85b92..aa3c71d6b 100644 --- a/plugins/modules-config/validations/validate.go +++ b/plugins/modules-config/validations/validate.go @@ -3,6 +3,7 @@ package validations import ( "fmt" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -37,7 +38,7 @@ func ValidateModuleConfig(moduleName string, config *config.ModuleGroup) error { // return fmt.Errorf("%v", err) // } default: - return fmt.Errorf("unsupported module: %s", moduleName) + return catcher.Error("unsupported module", nil, map[string]any{"module": moduleName}) } return nil From 6ceca24388393871b0b2aaeee138e76518889079 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Mon, 15 Sep 2025 11:51:33 -0400 Subject: [PATCH 010/422] Update log writing with correct use of 'catcher' in the o365 plugin. --- plugins/o365/config/config.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/o365/config/config.go b/plugins/o365/config/config.go index 18f266909..3b997bbac 100644 --- a/plugins/o365/config/config.go +++ b/plugins/o365/config/config.go @@ -2,8 +2,6 @@ package config import ( "context" - "fmt" - "log" "strings" sync "sync" "time" @@ -52,7 +50,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") + catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) time.Sleep(reconnectDelay) continue } @@ -133,7 +131,8 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - log.Printf("Received configuration update: %v", message.Config) + catcher.Info("Received configuration update", map[string]any{"config": message.Config}) + mu.Lock() cnf = message.Config } } From 7a9c14ca988e59c30458abaa29fa4fd6b489f448 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Mon, 15 Sep 2025 11:55:38 -0400 Subject: [PATCH 011/422] Update log writing with correct use of 'catcher' in the sophos plugin. --- plugins/sophos/config/config.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/sophos/config/config.go b/plugins/sophos/config/config.go index da3a0dbfa..98e677867 100644 --- a/plugins/sophos/config/config.go +++ b/plugins/sophos/config/config.go @@ -2,8 +2,6 @@ package config import ( "context" - "fmt" - "log" "strings" sync "sync" "time" @@ -52,7 +50,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") + catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) time.Sleep(reconnectDelay) continue } @@ -133,7 +131,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - log.Printf("Received configuration update: %v", message.Config) + catcher.Info("Recived configuration update", map[string]any{"config": message.Config}) cnf = message.Config } } From 1d96d7b1471c7391f1b1f57a9da787563ee47923 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Mon, 15 Sep 2025 12:49:46 -0400 Subject: [PATCH 012/422] Update log writing with correct use of 'catcher' in the soc-ai plugin. --- plugins/soc-ai/config/config.go | 11 ++---- plugins/soc-ai/correlation/correlation.go | 7 ++-- plugins/soc-ai/elastic.go | 9 +++-- plugins/soc-ai/elastic/alerts.go | 8 ++-- plugins/soc-ai/elastic/incidents.go | 21 ++++++----- plugins/soc-ai/elastic/index.go | 17 +++++---- plugins/soc-ai/elastic/transf.go | 3 +- plugins/soc-ai/llm.go | 31 ++++++++------- plugins/soc-ai/main.go | 13 +++---- plugins/soc-ai/queue.go | 46 +++++++++++++++++------ plugins/soc-ai/utils/convert.go | 7 ++-- plugins/soc-ai/utils/env.go | 7 ++-- plugins/soc-ai/utils/files.go | 7 ++-- plugins/soc-ai/utils/logger.go | 31 --------------- 14 files changed, 108 insertions(+), 110 deletions(-) delete mode 100644 plugins/soc-ai/utils/logger.go diff --git a/plugins/soc-ai/config/config.go b/plugins/soc-ai/config/config.go index 8b995b101..dd8b06779 100644 --- a/plugins/soc-ai/config/config.go +++ b/plugins/soc-ai/config/config.go @@ -2,15 +2,12 @@ package config import ( "context" - "fmt" - "log" "strings" sync "sync" "time" "github.com/threatwinds/go-sdk/catcher" "github.com/threatwinds/go-sdk/plugins" - "github.com/utmstack/UTMStack/plugins/soc-ai/utils" "google.golang.org/grpc" codes "google.golang.org/grpc/codes" "google.golang.org/grpc/connectivity" @@ -70,7 +67,7 @@ func StartConfigurationSystem() { configMutex.Unlock() if config.Backend == "" || config.InternalKey == "" || config.Opensearch == "" || config.ModulesConfigHost == "" { - fmt.Println("Backend, Internal key, Opensearch or Modules Config Host is not set, skipping UTMStack plugin execution") + catcher.Info("Backend, Internal key, Opensearch or Modules Config Host is not set, skipping UTMStack plugin execution", nil) time.Sleep(reconnectDelay) continue } @@ -151,7 +148,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - log.Printf("Received configuration update: %v", message.Config) + catcher.Info("Received configuration update", map[string]any{"config": message.Config}) updateConfigFromGRPC(message.Config) } } @@ -167,7 +164,7 @@ func updateConfigFromGRPC(grpcConf *ConfigurationSection) { defer configMutex.Unlock() if grpcConf == nil { - utils.Logger.LogF(100, "Received nil configuration from gRPC") + catcher.Info("Received nil configuration from gRPC", nil) return } @@ -195,7 +192,7 @@ func updateConfigFromGRPC(grpcConf *ConfigurationSection) { case "utmstack.socai.custom.url": customURL = c.ConfValue default: - utils.Logger.LogF(100, "Unknown configuration key: %s", c.ConfKey) + catcher.Info("Unknown configuration key", map[string]any{"key": c.ConfKey}) } } diff --git a/plugins/soc-ai/correlation/correlation.go b/plugins/soc-ai/correlation/correlation.go index 1e9536bce..f9ebd47fa 100644 --- a/plugins/soc-ai/correlation/correlation.go +++ b/plugins/soc-ai/correlation/correlation.go @@ -6,6 +6,7 @@ import ( "sort" "strings" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/elastic" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" @@ -14,7 +15,7 @@ import ( func GetCorrelationContext(alert schema.AlertFields) (string, error) { relatedAlerts, err := findRelatedAlerts(alert) if err != nil { - return "", fmt.Errorf("error finding related alerts: %v", err) + return "", catcher.Error("error finding related alerts", err, nil) } if len(relatedAlerts.RelatedAlerts) > 0 { @@ -30,12 +31,12 @@ func findRelatedAlerts(current schema.AlertFields) (schema.AlertCorrelation, err result, err := elastic.ElasticSearch(config.ALERT_INDEX_PATTERN, "name", current.Name) if err != nil { - return correlation, fmt.Errorf("error getting historical alerts: %v", err) + return correlation, catcher.Error("error getting historical alerts", err, nil) } var alerts []schema.AlertFields if err := json.Unmarshal(result, &alerts); err != nil { - return correlation, fmt.Errorf("error unmarshalling alerts: %v", err) + return correlation, catcher.Error("error unmarshalling alerts", err, nil) } for _, hist := range alerts { diff --git a/plugins/soc-ai/elastic.go b/plugins/soc-ai/elastic.go index 2b5f27837..1f656a228 100644 --- a/plugins/soc-ai/elastic.go +++ b/plugins/soc-ai/elastic.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "strings" "github.com/threatwinds/go-sdk/catcher" @@ -19,14 +18,16 @@ func processAlertToElastic(alert *schema.AlertFields) error { if strings.Contains(err.Error(), "index_not_found_exception") || strings.Contains(err.Error(), "no such index") { if createErr := elastic.CreateIndexIfNotExist(config.SOC_AI_INDEX); createErr != nil { - return fmt.Errorf("error updating alert in elastic: %v (failed to create index: %v)", err, createErr) + return catcher.Error("error updating alert in elastic", err, map[string]any{ + "failed_to_create_index": createErr, + }) } if retryErr := elastic.ElasticQuery(config.SOC_AI_INDEX, resp, "update"); retryErr != nil { - return fmt.Errorf("error updating alert in elastic after index creation: %v", retryErr) + return catcher.Error("error updating alert in elastic after index creation", retryErr, nil) } } else { - return fmt.Errorf("error updating alert in elastic: %v", err) + return catcher.Error("error updating alert in elastic", err, nil) } } diff --git a/plugins/soc-ai/elastic/alerts.go b/plugins/soc-ai/elastic/alerts.go index d9343fef4..eed538add 100644 --- a/plugins/soc-ai/elastic/alerts.go +++ b/plugins/soc-ai/elastic/alerts.go @@ -2,9 +2,9 @@ package elastic import ( "encoding/json" - "fmt" "net/http" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" "github.com/utmstack/UTMStack/plugins/soc-ai/utils" @@ -20,15 +20,15 @@ func ChangeAlertStatus(id string, status int, dataSource string, observations st body := schema.ChangeAlertStatus{AlertIDs: []string{id}, Status: status, DataSource: dataSource, StatusObservation: observations} bodyBytes, err := json.Marshal(body) if err != nil { - return fmt.Errorf("error marshalling body: %v", err) + return catcher.Error("error marshalling body", err, nil) } resp, statusCode, err := utils.DoReq(url, bodyBytes, "POST", headers, config.HTTP_TIMEOUT) if err != nil || statusCode != http.StatusOK { - return fmt.Errorf("error while doing request: %v, status: %d, response: %v", err, statusCode, string(resp)) + return catcher.Error("error while doing request", err, map[string]any{"status": statusCode, "response": string(resp)}) } - utils.Logger.LogF(100, "Alert %s status changed successfully", id) + catcher.Info("Alert status changed successfully", map[string]any{"alert_id": id}) return nil } diff --git a/plugins/soc-ai/elastic/incidents.go b/plugins/soc-ai/elastic/incidents.go index 8e8515f3a..f722b5ca4 100644 --- a/plugins/soc-ai/elastic/incidents.go +++ b/plugins/soc-ai/elastic/incidents.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" "github.com/utmstack/UTMStack/plugins/soc-ai/utils" @@ -14,7 +15,7 @@ import ( func CreateNewIncident(alertDetails *schema.AlertFields) error { if alertDetails == nil { - return fmt.Errorf("CreateNewIncident: alertDetails is nil") + return catcher.Error("CreateNewIncident: alertDetails is nil", nil, nil) } url := config.GetConfig().Backend + config.API_INCIDENT_ENDPOINT @@ -38,22 +39,22 @@ func CreateNewIncident(alertDetails *schema.AlertFields) error { bodyBytes, err := json.Marshal(body) if err != nil { - return fmt.Errorf("error marshalling body: %v", err) + return catcher.Error("error marshalling body", err, nil) } resp, statusCode, err := utils.DoReq(url, bodyBytes, "POST", headers, config.HTTP_TIMEOUT) if err != nil || statusCode != http.StatusOK { - return fmt.Errorf("error while doing request: %v, status: %d, response: %v", err, statusCode, string(resp)) + return catcher.Error("error while doing request", err, map[string]any{"status": statusCode, "response": string(resp)}) } - utils.Logger.LogF(100, "Incident %s created successfully", body.IncidentName) + catcher.Info("Incident created successfully", map[string]any{"incident_name": body.IncidentName}) return nil } func AddAlertToIncident(incidentId int, alertDetails *schema.AlertFields) error { if alertDetails == nil { - return fmt.Errorf("AddAlertToIncident: alertDetails is nil") + return catcher.Error("AddAlertToIncident: alertDetails is nil", nil, nil) } url := config.GetConfig().Backend + config.API_INCIDENT_ADD_NEW_ALERT_ENDPOINT @@ -74,15 +75,15 @@ func AddAlertToIncident(incidentId int, alertDetails *schema.AlertFields) error bodyBytes, err := json.Marshal(body) if err != nil { - return fmt.Errorf("error marshalling body: %v", err) + return catcher.Error("error marshalling body", err, nil) } resp, statusCode, err := utils.DoReq(url, bodyBytes, "POST", headers, config.HTTP_TIMEOUT) if err != nil || (statusCode != http.StatusOK && statusCode != http.StatusCreated) { - return fmt.Errorf("error while doing request: %v, status: %d, response: %v", err, statusCode, string(resp)) + return catcher.Error("error while doing request", err, map[string]any{"status": statusCode, "response": string(resp)}) } - utils.Logger.LogF(100, "Alert %s added to incident %d successfully", alertDetails.ID, incidentId) + catcher.Info("Alert added to incident successfully", map[string]any{"alert_id": alertDetails.ID, "incident_id": incidentId}) return nil } @@ -101,13 +102,13 @@ func GetIncidentsByPattern(pattern string) ([]schema.IncidentResp, error) { resp, statusCode, err := utils.DoReq(url, nil, "GET", headers, config.HTTP_TIMEOUT) if err != nil || statusCode != http.StatusOK { - return nil, fmt.Errorf("error while doing request: %v, status: %d, response: %v", err, statusCode, string(resp)) + return nil, catcher.Error("error while doing request", err, map[string]any{"status": statusCode, "response": string(resp)}) } var incidents []schema.IncidentResp err = json.Unmarshal(resp, &incidents) if err != nil { - return nil, fmt.Errorf("error while unmarshalling response: %v", err) + return nil, catcher.Error("error while unmarshalling response", err, nil) } return incidents, nil diff --git a/plugins/soc-ai/elastic/index.go b/plugins/soc-ai/elastic/index.go index 2e6974d08..f47dbae36 100644 --- a/plugins/soc-ai/elastic/index.go +++ b/plugins/soc-ai/elastic/index.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" "github.com/utmstack/UTMStack/plugins/soc-ai/utils" @@ -34,7 +35,7 @@ func ElasticQuery(index string, query interface{}, op string) error { method = "POST" } default: - return fmt.Errorf("unsupported operation: %s", op) + return catcher.Error("unsupported operation", nil, map[string]any{"operation": op}) } headers := map[string]string{ "Content-Type": "application/json", @@ -42,12 +43,12 @@ func ElasticQuery(index string, query interface{}, op string) error { queryBytes, err := json.Marshal(query) if err != nil { - return fmt.Errorf("error marshalling query: %v", err) + return catcher.Error("error marshalling query", err, nil) } resp, statusCode, err := utils.DoReq(url, queryBytes, method, headers, config.HTTP_TIMEOUT) if err != nil || (statusCode != http.StatusOK && statusCode != http.StatusCreated) { - return fmt.Errorf("error while doing request: %v, status: %d, response: %v", err, statusCode, string(resp)) + return catcher.Error("error while doing request", err, map[string]any{"status": statusCode, "response": string(resp)}) } return nil @@ -64,12 +65,12 @@ func ElasticSearch(index, field, value string) ([]byte, error) { body := schema.SearchDetailsRequest{{Field: field, Operator: "IS", Value: value}} bodyBytes, err := json.Marshal(body) if err != nil { - return nil, fmt.Errorf("error marshalling body: %v", err) + return nil, catcher.Error("error marshalling body", err, nil) } resp, statusCode, err := utils.DoReq(url, bodyBytes, "POST", headers, config.HTTP_TIMEOUT) if err != nil || statusCode != http.StatusOK { - return nil, fmt.Errorf("error while doing request for get Alert Details: %v: %s", err, string(resp)) + return nil, catcher.Error("error while doing request for get Alert Details", err, map[string]any{"status": statusCode, "response": string(resp)}) } return resp, nil @@ -93,15 +94,15 @@ func IndexStatus(id, status, op string) error { if strings.Contains(err.Error(), "index_not_found_exception") || strings.Contains(err.Error(), "no such index") { // Try to create the index first if createErr := CreateIndexIfNotExist(config.SOC_AI_INDEX); createErr != nil { - return fmt.Errorf("error creating document in elastic: %v (failed to create index: %v)", err, createErr) + return catcher.Error("error creating document in elastic", err, map[string]any{"message": createErr}) } // Retry the create operation if retryErr := ElasticQuery(config.SOC_AI_INDEX, doc, op); retryErr != nil { - return fmt.Errorf("error creating document in elastic after index creation: %v", retryErr) + return catcher.Error("error creating document in elastic after index creation", retryErr, nil) } } else { - return fmt.Errorf("error creating document in elastic: %v", err) + return catcher.Error("error creating document in elastic", err, nil) } } return nil diff --git a/plugins/soc-ai/elastic/transf.go b/plugins/soc-ai/elastic/transf.go index 7a00547d0..b498147c6 100644 --- a/plugins/soc-ai/elastic/transf.go +++ b/plugins/soc-ai/elastic/transf.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" ) @@ -87,7 +88,7 @@ func buildScriptString(alert schema.GPTAlertResponse) (string, error) { source += fmt.Sprintf("ctx._source['%s'] = params.%s; ", jsonFieldName, jsonFieldName) } default: - return "", fmt.Errorf("unsupported type: %v", reflect.TypeOf(fieldValue).Kind()) + return "", catcher.Error("unsupported type", nil, map[string]any{"kind": reflect.TypeOf(fieldValue).Kind()}) } } diff --git a/plugins/soc-ai/llm.go b/plugins/soc-ai/llm.go index 35a60ea3f..b5de6e745 100644 --- a/plugins/soc-ai/llm.go +++ b/plugins/soc-ai/llm.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/correlation" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" @@ -18,11 +19,11 @@ func sendRequestToLLM(alert *schema.AlertFields) error { content := config.LLM_INSTRUCTION if alert == nil { - return fmt.Errorf("sendRequestToOpenAI: alert is nil") + return catcher.Error("sendRequestToOpenAI: alert is nil", nil, nil) } correlationContext, err := correlation.GetCorrelationContext(*alert) if err != nil { - return fmt.Errorf("error getting correlation context: %v", err) + return catcher.Error("error getting correlation context", err, nil) } if correlationContext != "" { content = fmt.Sprintf("%s%s", content, correlationContext) @@ -30,7 +31,7 @@ func sendRequestToLLM(alert *schema.AlertFields) error { jsonContent, err := json.Marshal(alert) if err != nil { - return fmt.Errorf("error marshalling alert: %v", err) + return catcher.Error("error marshalling alert", err, nil) } req := schema.GPTRequest{ @@ -47,11 +48,11 @@ func sendRequestToLLM(alert *schema.AlertFields) error { }, } - utils.Logger.LogF(100, "Sending request to LLM: %v", req) + catcher.Info("Sending request to LLM", map[string]any{"request": req}) requestJson, err := json.Marshal(req) if err != nil { - return fmt.Errorf("error marshalling request: %v", err) + return catcher.Error("error marshalling request", err, nil) } headers := map[string]string{ @@ -65,13 +66,13 @@ func sendRequestToLLM(alert *schema.AlertFields) error { if err == nil && len(response.Choices) > 0 { err = processLLMResponse(alert, response.Choices[0].Message.Content) if err != nil { - return fmt.Errorf("error processing LLM response: %v", err) + return catcher.Error("error processing LLM response", err, nil) } return nil } if status == 401 { - return fmt.Errorf("invalid api-key") + return catcher.Error("invalid api-key", nil, nil) } lastErr = fmt.Errorf("attempt %d failed: %v (status: %d)", attempt, err, status) @@ -80,21 +81,25 @@ func sendRequestToLLM(alert *schema.AlertFields) error { } } - utils.Logger.LogF(500, "LLM appears to be DOWN - all %d attempts failed for alert %s. Provider: %s, URL: %s, Last error: %v", - maxRetries, alert.ID, config.GetConfig().Provider, config.GetConfig().Url, lastErr) + catcher.Error("LLM appears to be DOWN", lastErr, map[string]any{ + "attempts": maxRetries, + "alert": alert.ID, + "provider": config.GetConfig().Provider, + "url": config.GetConfig().Url, + }) - return fmt.Errorf("all attempts to call LLM failed: %v", lastErr) + return catcher.Error("all attempts to call LLM failed", lastErr, map[string]any{}) } func processLLMResponse(alert *schema.AlertFields, response string) error { response, err := clearJson(response) if err != nil { - return fmt.Errorf("error clearing json: %v", err) + return catcher.Error("error clearing json", err, nil) } alertResponse, err := utils.ConvertFromJsonToStruct[schema.GPTAlertResponse](response) if err != nil { - return fmt.Errorf("error converting json to struct: %v", err) + return catcher.Error("error converting json to struct", err, nil) } nextSteps := []string{} @@ -115,7 +120,7 @@ func clearJson(s string) (string, error) { end := strings.LastIndex(s, "}") if start == -1 || end == -1 { - return "", fmt.Errorf("no valid json found in gpt response") + return "", catcher.Error("no valid json found in gpt response", nil, nil) } return s[start : end+1], nil diff --git a/plugins/soc-ai/main.go b/plugins/soc-ai/main.go index f6ff75ea7..44386b5e8 100644 --- a/plugins/soc-ai/main.go +++ b/plugins/soc-ai/main.go @@ -10,7 +10,6 @@ import ( "github.com/threatwinds/go-sdk/plugins" twutil "github.com/threatwinds/go-sdk/utils" "github.com/utmstack/UTMStack/plugins/soc-ai/config" - "github.com/utmstack/UTMStack/plugins/soc-ai/utils" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" ) @@ -20,8 +19,6 @@ type socAiServer struct { } func main() { - utils.Logger.Info("Starting soc-ai plugin...") - go config.StartConfigurationSystem() time.Sleep(2 * time.Second) @@ -36,7 +33,7 @@ func main() { for retry := 0; retry < maxRetries; retry++ { socketsFolder, err = twutil.MkdirJoin(plugins.WorkDir, "sockets") if err == nil { - utils.Logger.LogF(100, "Socket directory %s created", socketsFolder) + catcher.Info("socket directory created", map[string]any{"directory": socketsFolder}) break } @@ -66,7 +63,7 @@ func main() { for retry := 0; retry < maxRetries; retry++ { unixAddress, err = net.ResolveUnixAddr("unix", socketFile) if err == nil { - utils.Logger.LogF(100, "Socket file %s created", socketFile) + catcher.Info("Socket file created", map[string]any{"socketFile": socketFile}) break } @@ -93,7 +90,7 @@ func main() { for retry := 0; retry < maxRetries; retry++ { listener, err = net.ListenUnix("unix", unixAddress) if err == nil { - utils.Logger.LogF(100, "Listening on %s", socketFile) + catcher.Info("Listening on unix socket", map[string]any{"socketFile": socketFile}) break } @@ -154,12 +151,12 @@ func (p *socAiServer) Correlate(_ context.Context, // Check if the module is active before processing the alert if config.GetConfig() == nil || !config.GetConfig().ModuleActive { - utils.Logger.LogF(100, "SOC-AI module is disabled, skipping alert: %s", alert.Id) + catcher.Info("SOC-AI module is disabled, skipping alert", map[string]any{"alert": alert.Id}) return &emptypb.Empty{}, nil } if !EnqueueAlert(alert) { - utils.Logger.LogF(300, "Alert %s was dropped due to full queue", alert.Id) + catcher.Info("Alert was dropped due to full queue", map[string]any{"alert": alert.Id}) return &emptypb.Empty{}, nil } diff --git a/plugins/soc-ai/queue.go b/plugins/soc-ai/queue.go index cf330db89..91c5c28a4 100644 --- a/plugins/soc-ai/queue.go +++ b/plugins/soc-ai/queue.go @@ -66,12 +66,15 @@ func InitializeQueue() { go alertQueue.metricsLogger() - utils.Logger.LogF(100, "Alert queue initialized with %d workers and queue size %d", DefaultWorkerCount, DefaultQueueSize) + catcher.Info("Alert queue initialized", map[string]any{ + "workers": DefaultWorkerCount, + "queueSize": DefaultQueueSize, + }) } func EnqueueAlert(alert *plugins.Alert) bool { if alertQueue == nil { - utils.Logger.LogF(500, "Alert queue not initialized") + catcher.Error("Alert queue not initialized", nil, nil) return false } @@ -85,7 +88,7 @@ func EnqueueAlert(alert *plugins.Alert) bool { atomic.AddInt64(&alertQueue.queueSize, 1) // Reset consecutive drops counter on successful enqueue atomic.StoreInt64(&alertQueue.consecutiveDrops, 0) - utils.Logger.LogF(100, "Alert %s enqueued for processing", alert.Id) + catcher.Info("Alert enqueued for processing", map[string]any{"alert": alert.Id}) return true case <-time.After(QueueFullTimeout): atomic.AddInt64(&alertQueue.droppedCount, 1) @@ -103,10 +106,19 @@ func EnqueueAlert(alert *plugins.Alert) bool { "consecutive_drops": consecutiveDrops, }).Error(), }) - utils.Logger.ErrorF("QUEUE FULL - Alert %s DROPPED! Queue size: %d/%d, Total dropped: %d, Consecutive: %d.", - alert.Id, currentQueueSize, DefaultQueueSize, totalDropped, consecutiveDrops) + catcher.Error("Alert dropped due to full queue", nil, map[string]any{ + "alert": alert.Id, + "queue_size": currentQueueSize, + "max_queue_size": DefaultQueueSize, + "total_dropped": totalDropped, + "consecutive_drops": consecutiveDrops, + }) - elastic.RegisterError(fmt.Sprintf("Alert dropped - Queue FULL (%d/%d)", currentQueueSize, DefaultQueueSize), alert.Id) + catcher.Error("Alert dropped - Queue FULL", nil, map[string]any{ + "alert": alert.Id, + "queue_size": currentQueueSize, + "max_queue_size": DefaultQueueSize, + }) alertQueue.lastDropAlert = time.Now() return false } @@ -134,7 +146,10 @@ func (aq *AlertQueue) processAlert(workerID int, item *AlertQueueItem) { startTime := time.Now() alert := cleanAlerts(alertToAlertFields(item.Alert)) - utils.Logger.LogF(100, "Worker %d processing alert: %s", workerID, alert.ID) + catcher.Info("Processing alert", map[string]any{ + "alert": alert.ID, + "workerID": workerID, + }) defer func() { if r := recover(); r != nil { @@ -149,7 +164,7 @@ func (aq *AlertQueue) processAlert(workerID int, item *AlertQueueItem) { }() if config.GetConfig() == nil || !config.GetConfig().ModuleActive { - utils.Logger.LogF(100, "SOC-AI module is disabled, skipping alert: %s", alert.ID) + catcher.Info("SOC-AI module is disabled, skipping alert", map[string]any{"alert": alert.ID}) atomic.AddInt64(&aq.processedCount, 1) return } @@ -181,8 +196,12 @@ func (aq *AlertQueue) processAlert(workerID int, item *AlertQueueItem) { duration := time.Since(startTime) queueTime := startTime.Sub(item.Timestamp) - utils.Logger.LogF(100, "Worker %d completed alert %s in %v (queue time: %v)", - workerID, alert.ID, duration, queueTime) + catcher.Info("Alert processed successfully", map[string]any{ + "alert": alert.ID, + "workerID": workerID, + "duration": duration.String(), + "queue_time": queueTime.String(), + }) } func (aq *AlertQueue) metricsLogger() { @@ -199,8 +218,11 @@ func (aq *AlertQueue) metricsLogger() { errors := atomic.LoadInt64(&aq.errorCount) queueSize := atomic.LoadInt64(&aq.queueSize) - utils.Logger.LogF(200, "Queue metrics - Processed: %d, Dropped: %d, Errors: %d, Current queue size: %d", - processed, dropped, errors, queueSize) + catcher.Info("Queue metrics", map[string]any{ + "processed": processed, + "dropped": dropped, + "errors": errors, + "queue_size": queueSize}) } } } diff --git a/plugins/soc-ai/utils/convert.go b/plugins/soc-ai/utils/convert.go index caf07fc16..89df9ff72 100644 --- a/plugins/soc-ai/utils/convert.go +++ b/plugins/soc-ai/utils/convert.go @@ -2,13 +2,14 @@ package utils import ( "encoding/json" - "fmt" + + "github.com/threatwinds/go-sdk/catcher" ) func ConvertFromStructToJsonString(alert interface{}) (string, error) { bytes, err := json.Marshal(alert) if err != nil { - return "", fmt.Errorf("error marshalling alert: %v", err) + return "", catcher.Error("error marshalling alert", err, nil) } return string(bytes), nil @@ -18,7 +19,7 @@ func ConvertFromJsonToStruct[responseType any](jsonString string) (responseType, var response responseType err := json.Unmarshal([]byte(jsonString), &response) if err != nil { - return *new(responseType), fmt.Errorf("error unmarshalling GPT response: %v", err) + return *new(responseType), catcher.Error("error unmarshalling GPT response", err, nil) } return response, nil diff --git a/plugins/soc-ai/utils/env.go b/plugins/soc-ai/utils/env.go index 99979fcfd..89b3149ac 100644 --- a/plugins/soc-ai/utils/env.go +++ b/plugins/soc-ai/utils/env.go @@ -1,21 +1,22 @@ package utils import ( - "log" "os" + + "github.com/threatwinds/go-sdk/catcher" ) func Getenv(key string, isMandatory bool) string { value, defined := os.LookupEnv(key) if !defined { if isMandatory { - log.Fatalf("Error loading environment variable: %s: environment variable does not exist\n", key) + catcher.Error("Error loading environment variable", nil, map[string]any{"key": key}) } else { return "" } } if (value == "" || value == " ") && isMandatory { - log.Fatalf("Error loading environment variable: %s: empty environment variable\n", key) + catcher.Error("Error loading environment variable", nil, map[string]any{"key": key}) } return value } diff --git a/plugins/soc-ai/utils/files.go b/plugins/soc-ai/utils/files.go index d483f886d..8fc4fb304 100644 --- a/plugins/soc-ai/utils/files.go +++ b/plugins/soc-ai/utils/files.go @@ -1,9 +1,10 @@ package utils import ( - "fmt" "os" "path/filepath" + + "github.com/threatwinds/go-sdk/catcher" ) func GetMyPath() (string, error) { @@ -19,10 +20,10 @@ func GetMyPath() (string, error) { func CreatePathIfNotExist(path string) error { if _, err := os.Stat(path); os.IsNotExist(err) { if err := os.Mkdir(path, 0755); err != nil { - return fmt.Errorf("error creating path: %v", err) + return catcher.Error("error creating path", err, map[string]any{"path": path}) } } else if err != nil { - return fmt.Errorf("error checking path: %v", err) + return catcher.Error("error checking path", err, map[string]any{"path": path}) } return nil } diff --git a/plugins/soc-ai/utils/logger.go b/plugins/soc-ai/utils/logger.go deleted file mode 100644 index 4332e7f5a..000000000 --- a/plugins/soc-ai/utils/logger.go +++ /dev/null @@ -1,31 +0,0 @@ -package utils - -import ( - "log" - "os" - "strconv" - - "github.com/threatwinds/logger" -) - -var Logger *logger.Logger - -func init() { - lenv := os.Getenv("LOG_LEVEL") - var level int - var err error - - if lenv != "" && lenv != " " { - level, err = strconv.Atoi(lenv) - if err != nil { - log.Fatalln(err) - } - } else { - level = 200 - } - - Logger = logger.NewLogger(&logger.Config{ - Format: "text", - Level: level, - }) -} From 6b2604773f652ee1466ffaad970ffa51ed21c5d6 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 15 Sep 2025 14:49:00 -0500 Subject: [PATCH 013/422] feat(alert): refactor AlertType and related classes for improved structure and clarity --- .../aop/logging/AlertLoggingAspect.java | 2 +- .../UtmAlertResponseRule.java | 4 + .../UtmAlertResponseRuleExecution.java | 84 +--- .../domain/reports/types/IncidentType.java | 2 +- .../domain/shared_types/AlertType.java | 424 ------------------ .../domain/shared_types/Geolocation.java | 37 ++ .../domain/shared_types/alert/AlertType.java | 129 ++++++ .../shared_types/alert/ComplianceValues.java | 14 + .../domain/shared_types/alert/DiskInfo.java | 15 + .../domain/shared_types/alert/Event.java | 43 ++ .../domain/shared_types/alert/Impact.java | 15 + .../shared_types/alert/IncidentDetail.java | 15 + .../domain/shared_types/alert/Side.java | 118 +++++ ...mAlertResponseRuleExecutionRepository.java | 2 +- .../park/utmstack/service/MailService.java | 2 +- .../utmstack/service/UtmAlertService.java | 2 +- .../service/UtmDataInputStatusService.java | 2 +- ...UtmAlertResponseActionTemplateService.java | 43 -- .../UtmAlertResponseRuleService.java | 4 +- .../service/impl/UtmAlertServiceImpl.java | 2 +- .../service/incident/UtmIncidentService.java | 2 +- .../service/reports/CustomReportService.java | 14 +- .../customs/threatActivityForAlerts.html | 2 +- 23 files changed, 413 insertions(+), 564 deletions(-) delete mode 100644 backend/src/main/java/com/park/utmstack/domain/shared_types/AlertType.java create mode 100644 backend/src/main/java/com/park/utmstack/domain/shared_types/Geolocation.java create mode 100644 backend/src/main/java/com/park/utmstack/domain/shared_types/alert/AlertType.java create mode 100644 backend/src/main/java/com/park/utmstack/domain/shared_types/alert/ComplianceValues.java create mode 100644 backend/src/main/java/com/park/utmstack/domain/shared_types/alert/DiskInfo.java create mode 100644 backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Event.java create mode 100644 backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Impact.java create mode 100644 backend/src/main/java/com/park/utmstack/domain/shared_types/alert/IncidentDetail.java create mode 100644 backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Side.java diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/AlertLoggingAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/AlertLoggingAspect.java index aac5188e4..86bf16d7a 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/AlertLoggingAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/AlertLoggingAspect.java @@ -5,7 +5,7 @@ import com.park.utmstack.domain.chart_builder.types.query.FilterType; import com.park.utmstack.domain.chart_builder.types.query.OperatorType; import com.park.utmstack.domain.index_pattern.enums.SystemIndexPattern; -import com.park.utmstack.domain.shared_types.AlertType; +import com.park.utmstack.domain.shared_types.alert.AlertType; import com.park.utmstack.security.SecurityUtils; import com.park.utmstack.service.UtmAlertLogService; import com.park.utmstack.service.elasticsearch.ElasticsearchService; diff --git a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java index a1bdb042a..7b0a675d7 100644 --- a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java +++ b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java @@ -62,6 +62,10 @@ public class UtmAlertResponseRule implements Serializable { @Column(name = "last_modified_date") private Instant lastModifiedDate; + @OneToMany(mappedBy = "rule", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) + List utmAlertResponseRuleExecutions; + + @ManyToMany @JoinTable( name = "utm_alert_response_rule_template", diff --git a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRuleExecution.java b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRuleExecution.java index acdd815cc..f6def7306 100644 --- a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRuleExecution.java +++ b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRuleExecution.java @@ -3,6 +3,7 @@ import com.park.utmstack.domain.alert_response_rule.enums.RuleExecutionStatus; import com.park.utmstack.domain.alert_response_rule.enums.RuleNonExecutionCause; +import lombok.Data; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -14,6 +15,7 @@ @Entity @Table(name = "utm_alert_response_rule_execution") +@Data @EntityListeners(AuditingEntityListener.class) public class UtmAlertResponseRuleExecution implements Serializable { @@ -61,84 +63,8 @@ public class UtmAlertResponseRuleExecution implements Serializable { @Column(name = "execution_retries") private Integer executionRetries = 0; + @ManyToOne + @JoinColumn(name = "rule_id", referencedColumnName = "id", insertable = false, updatable = false) + UtmAlertResponseRule rule; - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getRuleId() { - return ruleId; - } - - public void setRuleId(Long ruleId) { - this.ruleId = ruleId; - } - - public String getAlertId() { - return alertId; - } - - public void setAlertId(String alertId) { - this.alertId = alertId; - } - - public String getCommand() { - return command; - } - - public void setCommand(String command) { - this.command = command; - } - - public String getCommandResult() { - return commandResult; - } - - public void setCommandResult(String commandResult) { - this.commandResult = commandResult; - } - - public String getAgent() { - return agent; - } - - public void setAgent(String agent) { - this.agent = agent; - } - - public Instant getExecutionDate() { - return executionDate; - } - - public void setExecutionDate(Instant executionDate) { - this.executionDate = executionDate; - } - - public RuleExecutionStatus getExecutionStatus() { - return executionStatus; - } - - public void setExecutionStatus(RuleExecutionStatus executionStatus) { - this.executionStatus = executionStatus; - } - - public RuleNonExecutionCause getNonExecutionCause() { - return nonExecutionCause; - } - - public void setNonExecutionCause(RuleNonExecutionCause nonExecutionCause) { - this.nonExecutionCause = nonExecutionCause; - } - - public Integer getExecutionRetries() { - return executionRetries; - } - - public void setExecutionRetries(Integer executionRetries) { - this.executionRetries = executionRetries; - } } diff --git a/backend/src/main/java/com/park/utmstack/domain/reports/types/IncidentType.java b/backend/src/main/java/com/park/utmstack/domain/reports/types/IncidentType.java index c54a2dfe8..5519d1999 100644 --- a/backend/src/main/java/com/park/utmstack/domain/reports/types/IncidentType.java +++ b/backend/src/main/java/com/park/utmstack/domain/reports/types/IncidentType.java @@ -1,7 +1,7 @@ package com.park.utmstack.domain.reports.types; import com.park.utmstack.domain.incident_response.UtmIncidentJob; -import com.park.utmstack.domain.shared_types.AlertType; +import com.park.utmstack.domain.shared_types.alert.AlertType; import java.util.List; diff --git a/backend/src/main/java/com/park/utmstack/domain/shared_types/AlertType.java b/backend/src/main/java/com/park/utmstack/domain/shared_types/AlertType.java deleted file mode 100644 index fa9b328f0..000000000 --- a/backend/src/main/java/com/park/utmstack/domain/shared_types/AlertType.java +++ /dev/null @@ -1,424 +0,0 @@ -package com.park.utmstack.domain.shared_types; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.park.utmstack.util.enums.AlertStatus; -import org.springframework.util.StringUtils; - -import java.time.Instant; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Locale; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -public class AlertType { - @JsonProperty("@timestamp") - private String timestamp; - private String id; - private Integer status; - private AlertStatus statusLabel; - private String statusObservation; - private Boolean isIncident; - private IncidentDetail incidentDetail; - private String name; - private String category; - private Integer severity; - private String severityLabel; - private String protocol; - private String description; - private String solution; - private String tactic; - private List reference; - private String dataType; - private String dataSource; - private Host source; - private Host destination; - private List logs; - private List tags; - private String notes; - @JsonProperty("TagRulesApplied") - private List tagRulesApplied; - - public String getTimestamp() { - return timestamp; - } - - public Instant getTimestampAsInstant() { - if (StringUtils.hasText(timestamp)) - return Instant.parse(timestamp); - return null; - } - - public String getTimestampFormatted() { - try { - if (!StringUtils.hasText(timestamp)) - return null; - return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withLocale(Locale.getDefault()).withZone( - ZoneId.systemDefault()).format(Instant.parse(timestamp)); - } catch (Exception e) { - return null; - } - } - - public void setTimestamp(String timestamp) { - this.timestamp = timestamp; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public Integer getStatus() { - return status; - } - - public void setStatus(Integer status) { - this.status = status; - } - - public AlertStatus getStatusLabel() { - return statusLabel; - } - - public void setStatusLabel(AlertStatus statusLabel) { - this.statusLabel = statusLabel; - } - - public String getStatusObservation() { - return statusObservation; - } - - public void setStatusObservation(String statusObservation) { - this.statusObservation = statusObservation; - } - - public Boolean getIncident() { - return isIncident != null && isIncident; - } - - public void setIncident(Boolean incident) { - isIncident = incident; - } - - public IncidentDetail getIncidentDetail() { - return incidentDetail; - } - - public void setIncidentDetail(IncidentDetail incidentDetail) { - this.incidentDetail = incidentDetail; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getCategory() { - return category; - } - - public void setCategory(String category) { - this.category = category; - } - - public Integer getSeverity() { - return severity; - } - - public void setSeverity(Integer severity) { - this.severity = severity; - } - - public String getSeverityLabel() { - return severityLabel; - } - - public void setSeverityLabel(String severityLabel) { - this.severityLabel = severityLabel; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getSolution() { - return solution; - } - - public void setSolution(String solution) { - this.solution = solution; - } - - public String getTactic() { - return tactic; - } - - public void setTactic(String tactic) { - this.tactic = tactic; - } - - public List getReference() { - return reference; - } - - public void setReference(List reference) { - this.reference = reference; - } - - public String getDataType() { - return dataType; - } - - public void setDataType(String dataType) { - this.dataType = dataType; - } - - public String getDataSource() { - return dataSource; - } - - public void setDataSource(String dataSource) { - this.dataSource = dataSource; - } - - public Host getSource() { - return source; - } - - public void setSource(Host source) { - this.source = source; - } - - public Host getDestination() { - return destination; - } - - public void setDestination(Host destination) { - this.destination = destination; - } - - public List getLogs() { - return logs; - } - - public void setLogs(List logs) { - this.logs = logs; - } - - public List getTags() { - return tags; - } - - public void setTags(List tags) { - this.tags = tags; - } - - public String getNotes() { - return notes; - } - - public void setNotes(String notes) { - this.notes = notes; - } - - public List getTagRulesApplied() { - return tagRulesApplied; - } - - public void setTagRulesApplied(List tagRulesApplied) { - this.tagRulesApplied = tagRulesApplied; - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Host { - private String user; - private String host; - private String ip; - private Integer port; - private String country; - private String countryCode; - private String city; - private Float[] coordinates; - private Integer accuracyRadius; - private Integer asn; - private String aso; - private Boolean isSatelliteProvider; - private Boolean isAnonymousProxy; - - public String getUser() { - return user; - } - - public void setUser(String user) { - this.user = user; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public Integer getPort() { - return port; - } - - public void setPort(Integer port) { - this.port = port; - } - - public String getCountry() { - return country; - } - - public void setCountry(String country) { - this.country = country; - } - - public String getCountryCode() { - return countryCode; - } - - public void setCountryCode(String countryCode) { - this.countryCode = countryCode; - } - - public String getCity() { - return city; - } - - public void setCity(String city) { - this.city = city; - } - - public Float[] getCoordinates() { - return coordinates; - } - - public void setCoordinates(Float[] coordinates) { - this.coordinates = coordinates; - } - - public Integer getAccuracyRadius() { - return accuracyRadius; - } - - public void setAccuracyRadius(Integer accuracyRadius) { - this.accuracyRadius = accuracyRadius; - } - - public Integer getAsn() { - return asn; - } - - public void setAsn(Integer asn) { - this.asn = asn; - } - - public String getAso() { - return aso; - } - - public void setAso(String aso) { - this.aso = aso; - } - - public Boolean getSatelliteProvider() { - return isSatelliteProvider; - } - - public void setSatelliteProvider(Boolean satelliteProvider) { - isSatelliteProvider = satelliteProvider; - } - - public Boolean getAnonymousProxy() { - return isAnonymousProxy; - } - - public void setAnonymousProxy(Boolean anonymousProxy) { - isAnonymousProxy = anonymousProxy; - } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class IncidentDetail { - private String createdBy; - private String incidentName; - - private Integer incidentId; - private String creationDate; - private String source; - - public String getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(String createdBy) { - this.createdBy = createdBy; - } - - public String getIncidentName() { - return incidentName; - } - - public void setIncidentName(String incidentName) { - this.incidentName = incidentName; - } - - public Integer getIncidentId() { - return incidentId; - } - - public void setIncidentId(Integer incidentId) { - this.incidentId = incidentId; - } - - public String getCreationDate() { - return creationDate; - } - - public void setCreationDate(String creationDate) { - this.creationDate = creationDate; - } - - public String getSource() { - return source; - } - - public void setSource(String source) { - this.source = source; - } - } -} diff --git a/backend/src/main/java/com/park/utmstack/domain/shared_types/Geolocation.java b/backend/src/main/java/com/park/utmstack/domain/shared_types/Geolocation.java new file mode 100644 index 000000000..3f9e9c900 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/domain/shared_types/Geolocation.java @@ -0,0 +1,37 @@ +package com.park.utmstack.domain.shared_types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Geolocation { + + @JsonProperty("country") + private String country; + + @JsonProperty("city") + private String city; + + @JsonProperty("latitude") + private Double latitude; + + @JsonProperty("longitude") + private Double longitude; + + @JsonProperty("asn") + private Long asn; + + @JsonProperty("aso") + private String aso; + + @JsonProperty("countryCode") + private String countryCode; + + @JsonProperty("accuracy") + private Integer accuracy; +} + diff --git a/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/AlertType.java b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/AlertType.java new file mode 100644 index 000000000..63c4de951 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/AlertType.java @@ -0,0 +1,129 @@ +package com.park.utmstack.domain.shared_types.alert; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.park.utmstack.util.enums.AlertStatus; +import lombok.Getter; +import lombok.Setter; +import org.springframework.util.StringUtils; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Locale; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +@Setter +public class AlertType { + @JsonProperty("@timestamp") + private String timestamp; + + @JsonProperty("id") + private String id; + + @JsonProperty("parentId") + private String parentId; + + @JsonProperty("status") + private Integer status; + + @JsonProperty("statusLabel") + private AlertStatus statusLabel; + + @JsonProperty("statusObservation") + private String statusObservation; + + @JsonProperty("isIncident") + private Boolean isIncident; + + @JsonProperty("incidentDetail") + private IncidentDetail incidentDetail; + + @JsonProperty("name") + private String name; + + @JsonProperty("category") + private String category; + + @JsonProperty("severity") + private Integer severity; + + @JsonProperty("severityLabel") + private String severityLabel; + + @JsonProperty("description") + private String description; + + @JsonProperty("solution") + private String solution; + + @JsonProperty("technique") + private String technique; + + @JsonProperty("reference") + private List reference; + + @JsonProperty("dataType") + private String dataType; + + @JsonProperty("impact") + private Impact impact; + + @JsonProperty("impactScore") + private Integer impactScore; + + @JsonProperty("dataSource") + private String dataSource; + + @JsonProperty("adversary") + private Side adversary; + + @JsonProperty("target") + private Side target; + + @JsonProperty("events") + private List events; + + @JsonProperty("lastEvent") + private Event lastEvent; + + @JsonProperty("tags") + private List tags; + + @JsonProperty("notes") + private String notes; + + @JsonProperty("tagRulesApplied") + private List tagRulesApplied; + + @JsonProperty("deduplicatedBy") + private List deduplicatedBy; + + @JsonProperty("logs") + private List logs; + + public Instant getTimestampAsInstant() { + if (StringUtils.hasText(timestamp)) + return Instant.parse(timestamp); + return null; + } + + public String getTimestampFormatted() { + try { + if (!StringUtils.hasText(timestamp)) + return null; + return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withLocale(Locale.getDefault()).withZone( + ZoneId.systemDefault()).format(Instant.parse(timestamp)); + } catch (Exception e) { + return null; + } + } + + public Boolean getIncident() { + return isIncident != null && isIncident; + } +} diff --git a/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/ComplianceValues.java b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/ComplianceValues.java new file mode 100644 index 000000000..b1abcc609 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/ComplianceValues.java @@ -0,0 +1,14 @@ +package com.park.utmstack.domain.shared_types.alert; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import java.util.List; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ComplianceValues { + private List values; +} + diff --git a/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/DiskInfo.java b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/DiskInfo.java new file mode 100644 index 000000000..4815410ff --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/DiskInfo.java @@ -0,0 +1,15 @@ +package com.park.utmstack.domain.shared_types.alert; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class DiskInfo { + private String name; + private Long totalSpace; + private Integer usedPercent; +} + diff --git a/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Event.java b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Event.java new file mode 100644 index 000000000..2c28cb807 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Event.java @@ -0,0 +1,43 @@ +package com.park.utmstack.domain.shared_types.alert; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Event { + + private String id; + + @JsonProperty("@timestamp") + private String timestamp; + + private String deviceTime; + private String dataType; + private String dataSource; + private String tenantId; + private String tenantName; + private String raw; + + private Map log; + + private Side target; + private Side origin; + + private String protocol; + private String connectionStatus; + private Integer statusCode; + private String actionResult; + private String action; + private String severity; + + private List errors; + private Map compliance; +} + diff --git a/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Impact.java b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Impact.java new file mode 100644 index 000000000..9d34a4105 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Impact.java @@ -0,0 +1,15 @@ +package com.park.utmstack.domain.shared_types.alert; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Impact { + private Integer confidentiality; + private Integer integrity; + private Integer availability; +} + diff --git a/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/IncidentDetail.java b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/IncidentDetail.java new file mode 100644 index 000000000..380996951 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/IncidentDetail.java @@ -0,0 +1,15 @@ +package com.park.utmstack.domain.shared_types.alert; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class IncidentDetail { + private String createdBy; + private String observation; + private String creationDate; + private String source; +} \ No newline at end of file diff --git a/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Side.java b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Side.java new file mode 100644 index 000000000..59c376451 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Side.java @@ -0,0 +1,118 @@ +package com.park.utmstack.domain.shared_types.alert; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.park.utmstack.domain.shared_types.Geolocation; +import lombok.Data; +import java.util.List; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class Side { + + // Network traffic attributes + private Double bytesSent; + private Double bytesReceived; + private Long packagesSent; + private Long packagesReceived; + + // Network identification attributes + private String ip; + private String host; + private String user; + private String group; + private Integer port; + private String domain; + private String mac; + private Geolocation geolocation; + private String url; + private String cidr; + + // Certificate and fingerprint attributes + private String certificateFingerprint; + private String ja3Fingerprint; + private String jarmFingerprint; + private String sshBanner; + private String sshFingerprint; + + // Web attributes + private String cookie; + private String jabberId; + + // Email attributes + private String email; + private String dkim; + private String dkimSignature; + private String emailAddress; + private String emailBody; + private String emailDisplayName; + private String emailSubject; + private String emailThreadIndex; + private String emailXMailer; + + // WHOIS attributes + private String whoisRegistrant; + private String whoisRegistrar; + + // Process-related attributes + private String process; + private String processState; + private String command; + private String windowsScheduledTask; + private String windowsServiceDisplayName; + private String windowsServiceName; + + // File-related attributes + private String file; + private String path; + private String filename; + private String sizeInBytes; + private String mimeType; + + // Hash-related attributes + private String hash; + private String authentihash; + private String cdhash; + private String md5; + private String sha1; + private String sha224; + private String sha256; + private String sha384; + private String sha3224; + private String sha3256; + private String sha3384; + private String sha3512; + private String sha512; + private String sha512224; + private String sha512256; + private String hex; + private String base64; + + // System-related attributes + private String operatingSystem; + private String chromeExtension; + private String mobileAppId; + + // Vulnerability-related attributes + private String cpe; + private String cve; + + // Malware-related attributes + private String malware; + private String malwareFamily; + private String malwareType; + + // Key-related attributes + private String pgpPrivateKey; + private String pgpPublicKey; + + // Resources attributes + private Long connections; + private Integer usedCpuPercent; + private Integer usedMemPercent; + private Integer totalCpuUnits; + private Long totalMem; + private List disks; +} + diff --git a/backend/src/main/java/com/park/utmstack/repository/alert_response_rule/UtmAlertResponseRuleExecutionRepository.java b/backend/src/main/java/com/park/utmstack/repository/alert_response_rule/UtmAlertResponseRuleExecutionRepository.java index ec6b68eec..485a707ea 100644 --- a/backend/src/main/java/com/park/utmstack/repository/alert_response_rule/UtmAlertResponseRuleExecutionRepository.java +++ b/backend/src/main/java/com/park/utmstack/repository/alert_response_rule/UtmAlertResponseRuleExecutionRepository.java @@ -12,6 +12,6 @@ @SuppressWarnings("unused") @Repository public interface UtmAlertResponseRuleExecutionRepository extends JpaRepository, JpaSpecificationExecutor { - List findAllByExecutionStatus(RuleExecutionStatus status); + List findAllRuleByExecutionStatusAndRule_RuleActiveTrue(RuleExecutionStatus status); } diff --git a/backend/src/main/java/com/park/utmstack/service/MailService.java b/backend/src/main/java/com/park/utmstack/service/MailService.java index 1ad02efff..e273f4675 100644 --- a/backend/src/main/java/com/park/utmstack/service/MailService.java +++ b/backend/src/main/java/com/park/utmstack/service/MailService.java @@ -5,7 +5,7 @@ import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.domain.incident.UtmIncident; import com.park.utmstack.domain.mail_sender.MailConfig; -import com.park.utmstack.domain.shared_types.AlertType; +import com.park.utmstack.domain.shared_types.alert.AlertType; import com.park.utmstack.domain.shared_types.LogType; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.mail_sender.BaseMailSender; diff --git a/backend/src/main/java/com/park/utmstack/service/UtmAlertService.java b/backend/src/main/java/com/park/utmstack/service/UtmAlertService.java index 3f322b126..1c3fcf361 100644 --- a/backend/src/main/java/com/park/utmstack/service/UtmAlertService.java +++ b/backend/src/main/java/com/park/utmstack/service/UtmAlertService.java @@ -1,7 +1,7 @@ package com.park.utmstack.service; import com.park.utmstack.domain.chart_builder.types.query.FilterType; -import com.park.utmstack.domain.shared_types.AlertType; +import com.park.utmstack.domain.shared_types.alert.AlertType; import com.park.utmstack.domain.shared_types.static_dashboard.CardType; import com.park.utmstack.util.exceptions.DashboardOverviewException; import com.park.utmstack.util.exceptions.ElasticsearchIndexDocumentUpdateException; 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 c1ffb3d94..4aa7d19eb 100644 --- a/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java +++ b/backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java @@ -10,7 +10,7 @@ import com.park.utmstack.domain.network_scan.UtmNetworkScan; import com.park.utmstack.domain.network_scan.enums.AssetStatus; import com.park.utmstack.domain.network_scan.enums.UpdateLevel; -import com.park.utmstack.domain.shared_types.AlertType; +import com.park.utmstack.domain.shared_types.alert.AlertType; import com.park.utmstack.repository.UtmDataInputStatusRepository; import com.park.utmstack.repository.correlation.config.UtmDataTypesRepository; import com.park.utmstack.repository.network_scan.UtmNetworkScanRepository; diff --git a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseActionTemplateService.java b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseActionTemplateService.java index ee8b6a192..c28a81de2 100644 --- a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseActionTemplateService.java +++ b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseActionTemplateService.java @@ -1,55 +1,12 @@ package com.park.utmstack.service.alert_response_rule; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import com.jayway.jsonpath.Criteria; -import com.jayway.jsonpath.Filter; -import com.jayway.jsonpath.Predicate; -import com.park.utmstack.config.Constants; import com.park.utmstack.domain.alert_response_rule.UtmAlertResponseActionTemplate; -import com.park.utmstack.domain.alert_response_rule.UtmAlertResponseRule; -import com.park.utmstack.domain.alert_response_rule.UtmAlertResponseRuleExecution; -import com.park.utmstack.domain.alert_response_rule.UtmAlertResponseRuleHistory; -import com.park.utmstack.domain.alert_response_rule.enums.RuleExecutionStatus; -import com.park.utmstack.domain.alert_response_rule.enums.RuleNonExecutionCause; -import com.park.utmstack.domain.application_events.enums.ApplicationEventType; -import com.park.utmstack.domain.chart_builder.types.query.FilterType; -import com.park.utmstack.domain.chart_builder.types.query.OperatorType; -import com.park.utmstack.domain.compliance.UtmComplianceStandardSection; -import com.park.utmstack.domain.shared_types.AlertType; import com.park.utmstack.repository.alert_response_rule.UtmAlertResponseActionTemplateRepository; -import com.park.utmstack.repository.alert_response_rule.UtmAlertResponseRuleExecutionRepository; -import com.park.utmstack.repository.alert_response_rule.UtmAlertResponseRuleHistoryRepository; -import com.park.utmstack.repository.alert_response_rule.UtmAlertResponseRuleRepository; -import com.park.utmstack.repository.network_scan.UtmNetworkScanRepository; import com.park.utmstack.service.UtmStackService; -import com.park.utmstack.service.agent_manager.AgentService; -import com.park.utmstack.service.application_events.ApplicationEventService; -import com.park.utmstack.service.dto.UtmAlertResponseActionTemplateDTO; -import com.park.utmstack.service.dto.UtmAlertResponseRuleDTO; -import com.park.utmstack.service.dto.agent_manager.AgentDTO; -import com.park.utmstack.service.dto.agent_manager.AgentStatusEnum; -import com.park.utmstack.service.grpc.CommandResult; -import com.park.utmstack.service.incident_response.UtmIncidentVariableService; -import com.park.utmstack.service.incident_response.grpc_impl.IncidentResponseCommandService; -import com.park.utmstack.util.UtilJson; -import com.park.utmstack.util.exceptions.UtmNotImplementedException; -import io.grpc.stub.StreamObserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; @Service @Transactional diff --git a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java index 8a982abfe..8d5a6b1ce 100644 --- a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java +++ b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java @@ -15,7 +15,7 @@ import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.domain.chart_builder.types.query.FilterType; import com.park.utmstack.domain.chart_builder.types.query.OperatorType; -import com.park.utmstack.domain.shared_types.AlertType; +import com.park.utmstack.domain.shared_types.alert.AlertType; import com.park.utmstack.repository.alert_response_rule.UtmAlertResponseActionTemplateRepository; import com.park.utmstack.repository.alert_response_rule.UtmAlertResponseRuleExecutionRepository; import com.park.utmstack.repository.alert_response_rule.UtmAlertResponseRuleHistoryRepository; @@ -292,7 +292,7 @@ private String buildCommand(String rawCommand, String alertJson) { public void executeRuleCommands() { final String ctx = CLASSNAME + ".executeRuleCommands"; try { - List cmds = alertResponseRuleExecutionRepository.findAllByExecutionStatus(RuleExecutionStatus.PENDING); + List cmds = alertResponseRuleExecutionRepository.findAllRuleByExecutionStatusAndRule_RuleActiveTrue(RuleExecutionStatus.PENDING); if (CollectionUtils.isEmpty(cmds)) return; diff --git a/backend/src/main/java/com/park/utmstack/service/impl/UtmAlertServiceImpl.java b/backend/src/main/java/com/park/utmstack/service/impl/UtmAlertServiceImpl.java index 64596c91c..7c6208557 100644 --- a/backend/src/main/java/com/park/utmstack/service/impl/UtmAlertServiceImpl.java +++ b/backend/src/main/java/com/park/utmstack/service/impl/UtmAlertServiceImpl.java @@ -8,7 +8,7 @@ import com.park.utmstack.domain.chart_builder.types.query.FilterType; import com.park.utmstack.domain.chart_builder.types.query.OperatorType; import com.park.utmstack.domain.index_pattern.enums.SystemIndexPattern; -import com.park.utmstack.domain.shared_types.AlertType; +import com.park.utmstack.domain.shared_types.alert.AlertType; import com.park.utmstack.domain.shared_types.LogType; import com.park.utmstack.domain.shared_types.static_dashboard.CardType; import com.park.utmstack.repository.UtmAlertLastRepository; diff --git a/backend/src/main/java/com/park/utmstack/service/incident/UtmIncidentService.java b/backend/src/main/java/com/park/utmstack/service/incident/UtmIncidentService.java index 03546f259..b56146a8a 100644 --- a/backend/src/main/java/com/park/utmstack/service/incident/UtmIncidentService.java +++ b/backend/src/main/java/com/park/utmstack/service/incident/UtmIncidentService.java @@ -6,7 +6,7 @@ import com.park.utmstack.domain.incident.UtmIncidentAlert; import com.park.utmstack.domain.incident.enums.IncidentHistoryActionEnum; import com.park.utmstack.domain.incident.enums.IncidentStatusEnum; -import com.park.utmstack.domain.shared_types.AlertType; +import com.park.utmstack.domain.shared_types.alert.AlertType; import com.park.utmstack.repository.incident.UtmIncidentRepository; import com.park.utmstack.service.MailService; import com.park.utmstack.service.UserService; diff --git a/backend/src/main/java/com/park/utmstack/service/reports/CustomReportService.java b/backend/src/main/java/com/park/utmstack/service/reports/CustomReportService.java index 865a8c075..044d96bfa 100644 --- a/backend/src/main/java/com/park/utmstack/service/reports/CustomReportService.java +++ b/backend/src/main/java/com/park/utmstack/service/reports/CustomReportService.java @@ -6,7 +6,7 @@ import com.park.utmstack.domain.index_pattern.enums.SystemIndexPattern; import com.park.utmstack.domain.network_scan.NetworkScanFilter; import com.park.utmstack.domain.reports.types.IncidentType; -import com.park.utmstack.domain.shared_types.AlertType; +import com.park.utmstack.domain.shared_types.alert.AlertType; import com.park.utmstack.domain.shared_types.enums.ImageShortName; import com.park.utmstack.service.UtmImagesService; import com.park.utmstack.service.elasticsearch.ElasticsearchService; @@ -121,13 +121,13 @@ public Optional buildThreatActivityForIncidents(Instant f String src = "", dest = ""; - if (!Objects.isNull(incident.getSource())) - src = StringUtils.hasText(incident.getSource().getHost()) - ? incident.getSource().getHost() : incident.getSource().getIp(); + if (!Objects.isNull(incident.getAdversary())) + src = StringUtils.hasText(incident.getAdversary().getHost()) + ? incident.getAdversary().getHost() : incident.getAdversary().getIp(); - if (!Objects.isNull(incident.getDestination())) - dest = StringUtils.hasText(incident.getDestination().getHost()) - ? incident.getDestination().getHost() : incident.getDestination().getIp(); + if (!Objects.isNull(incident.getTarget())) + dest = StringUtils.hasText(incident.getTarget().getHost()) + ? incident.getTarget().getHost() : incident.getTarget().getIp(); if (StringUtils.hasText(src)) incidentType.setSrcResponses(incidentJobService.findAllByAgent(src)); diff --git a/backend/src/main/resources/templates/reports/customs/threatActivityForAlerts.html b/backend/src/main/resources/templates/reports/customs/threatActivityForAlerts.html index d280bc8bf..778a6d2df 100644 --- a/backend/src/main/resources/templates/reports/customs/threatActivityForAlerts.html +++ b/backend/src/main/resources/templates/reports/customs/threatActivityForAlerts.html @@ -14,7 +14,7 @@

STACK

- +
From c478194d13027d97b35a9833516cb924c6c7f697 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Mon, 15 Sep 2025 18:28:27 -0400 Subject: [PATCH 014/422] Update log writing with correct use of 'catcher' in the agent-manager. --- agent-manager/agent/agent_imp.go | 31 ++++++++++++++-------------- agent-manager/agent/collector_imp.go | 29 ++++++++++++++------------ agent-manager/agent/lastseen_imp.go | 9 ++++---- agent-manager/agent/parser.go | 5 +++-- agent-manager/agent/utmgrpc.go | 19 ++++++++++------- agent-manager/go.mod | 1 + agent-manager/go.sum | 2 ++ agent-manager/main.go | 10 ++++----- agent-manager/updates/updates.go | 12 ++++++----- agent-manager/utils/logger.go | 31 ---------------------------- 10 files changed, 66 insertions(+), 83 deletions(-) delete mode 100644 agent-manager/utils/logger.go diff --git a/agent-manager/agent/agent_imp.go b/agent-manager/agent/agent_imp.go index 47f5728a1..8ad486046 100644 --- a/agent-manager/agent/agent_imp.go +++ b/agent-manager/agent/agent_imp.go @@ -9,6 +9,7 @@ import ( "time" "github.com/google/uuid" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/database" "github.com/utmstack/UTMStack/agent-manager/models" "github.com/utmstack/UTMStack/agent-manager/utils" @@ -82,7 +83,7 @@ func (s *AgentService) RegisterAgent(ctx context.Context, req *AgentRequest) (*A Key: oldAgent.AgentKey, }, nil } else { - utils.ALogger.ErrorF("agent with hostname %s already exists", agent.Hostname) + catcher.Error("agent already exists", nil, map[string]any{"hostname": agent.Hostname}) return nil, status.Errorf(codes.AlreadyExists, "hostname has already been registered") } } @@ -91,7 +92,7 @@ func (s *AgentService) RegisterAgent(ctx context.Context, req *AgentRequest) (*A agent.AgentKey = key err = s.DBConnection.Create(agent) if err != nil { - utils.ALogger.ErrorF("failed to create agent: %v", err) + catcher.Error("failed to create agent", err, map[string]any{}) return nil, status.Error(codes.Internal, fmt.Sprintf("failed to create agent: %v", err)) } @@ -105,7 +106,7 @@ func (s *AgentService) RegisterAgent(ctx context.Context, req *AgentRequest) (*A LastPing: time.Now(), } - utils.ALogger.Info("Agent %s with id %d registered correctly", agent.Hostname, agent.ID) + catcher.Info("agent registered", map[string]any{"id": agent.ID, "hostname": agent.Hostname}) return &AuthResponse{ Id: uint32(agent.ID), Key: key, @@ -125,7 +126,7 @@ func (s *AgentService) UpdateAgent(ctx context.Context, req *AgentRequest) (*Aut agent := &models.Agent{} err = s.DBConnection.GetFirst(agent, "id = ?", idInt) if err != nil { - utils.ALogger.ErrorF("failed to fetch agent: %v", err) + catcher.Error("failed to fetch agent", err, map[string]any{}) return nil, status.Errorf(codes.NotFound, "agent not found") } @@ -156,7 +157,7 @@ func (s *AgentService) UpdateAgent(ctx context.Context, req *AgentRequest) (*Aut err = s.DBConnection.Upsert(&agent, "id = ?", nil, idInt) if err != nil { - utils.ALogger.ErrorF("failed to update agent: %v", err) + catcher.Error("failed to update agent", err, map[string]any{}) return nil, status.Errorf(codes.Internal, "failed to update agent: %v", err) } @@ -180,18 +181,18 @@ func (s *AgentService) DeleteAgent(ctx context.Context, req *DeleteRequest) (*Au err = s.DBConnection.Upsert(&models.Agent{}, "id = ?", map[string]interface{}{"deleted_by": req.DeletedBy}, id) if err != nil { - utils.ALogger.ErrorF("unable to update delete_by field in agent: %v", err) + catcher.Error("unable to update delete_by field in agent", err, map[string]any{}) } err = s.DBConnection.Delete(&models.AgentCommand{}, "agent_id = ?", false, uint(idInt)) if err != nil { - utils.ALogger.ErrorF("unable to delete agent commands: %v", err) + catcher.Error("unable to delete agent commands", err, map[string]any{}) return &AuthResponse{}, status.Error(codes.Internal, fmt.Sprintf("unable to delete agent commands: %v", err.Error())) } err = s.DBConnection.Delete(&models.Agent{}, "id = ?", false, id) if err != nil { - utils.ALogger.ErrorF("unable to delete agent: %v", err) + catcher.Error("unable to delete agent", err, map[string]any{}) return &AuthResponse{}, status.Error(codes.Internal, fmt.Sprintf("unable to delete agent: %v", err.Error())) } @@ -203,7 +204,7 @@ func (s *AgentService) DeleteAgent(ctx context.Context, req *DeleteRequest) (*Au delete(s.AgentStreamMap, uint(idInt)) s.AgentStreamMutex.Unlock() - utils.ALogger.Info("Agent with key %s deleted by %s", key, req.DeletedBy) + catcher.Info("agent deleted", map[string]any{"agent_id": key, "deleted_by": req.DeletedBy}) return &AuthResponse{ Id: uint32(idInt), @@ -218,7 +219,7 @@ func (s *AgentService) ListAgents(ctx context.Context, req *ListRequest) (*ListA agents := []models.Agent{} total, err := s.DBConnection.GetByPagination(&agents, page, filter, "", false) if err != nil { - utils.ALogger.ErrorF("failed to fetch agents: %v", err) + catcher.Error("failed to fetch agents", err, map[string]any{}) return nil, status.Errorf(codes.Internal, "failed to fetch agents: %v", err) } @@ -266,7 +267,7 @@ func (s *AgentService) AgentStream(stream AgentService_AgentStreamServer) error switch msg := in.StreamMessage.(type) { case *BidirectionalStream_Result: - utils.ALogger.Info("Received command result from agent %s: %s", msg.Result.AgentId, msg.Result.Result) + catcher.Info("Received command result", map[string]any{"agent_id": msg.Result.AgentId, "result": msg.Result.Result}) cmdID := msg.Result.GetCmdId() s.CommandResultChannelM.Lock() @@ -278,7 +279,7 @@ func (s *AgentService) AgentStream(stream AgentService_AgentStreamServer) error ExecutedAt: msg.Result.ExecutedAt, } } else { - utils.ALogger.ErrorF("failed to find result channel for CmdID: %s", cmdID) + catcher.Error("failed to find result channel for CmdID", nil, map[string]any{"cmd_id": cmdID}) } s.CommandResultChannelM.Unlock() } @@ -324,7 +325,7 @@ func (s *AgentService) ProcessCommand(stream PanelService_ProcessCommandServer) histCommand := createHistoryCommand(cmd, cmdID, uint(streamId)) err = s.DBConnection.Create(&histCommand) if err != nil { - utils.ALogger.ErrorF("unable to create a new command history") + catcher.Error("unable to create a new command history", err, map[string]any{}) } err = agentStream.Send(&BidirectionalStream{ @@ -348,7 +349,7 @@ func (s *AgentService) ProcessCommand(stream PanelService_ProcessCommandServer) cmd.AgentId, cmdID, ) if err != nil { - utils.ALogger.ErrorF("failed to update command status: %v", err) + catcher.Error("failed to update command status", err, map[string]any{}) } err = stream.Send(result) @@ -369,7 +370,7 @@ func (s *AgentService) ListAgentCommands(ctx context.Context, req *ListRequest) commands := []models.AgentCommand{} total, err := s.DBConnection.GetByPagination(&commands, page, filter, "", false) if err != nil { - utils.ALogger.ErrorF("failed to fetch agent commands: %v", err) + catcher.Error("failed to fetch agent commands", err, map[string]any{}) return nil, status.Errorf(codes.Internal, "failed to fetch agent commands: %v", err) } diff --git a/agent-manager/agent/collector_imp.go b/agent-manager/agent/collector_imp.go index 17b5434e0..8034fb046 100644 --- a/agent-manager/agent/collector_imp.go +++ b/agent-manager/agent/collector_imp.go @@ -4,11 +4,13 @@ import ( context "context" "fmt" "io" + "os" "strconv" sync "sync" "time" "github.com/google/uuid" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/config" "github.com/utmstack/UTMStack/agent-manager/database" "github.com/utmstack/UTMStack/agent-manager/models" @@ -61,7 +63,8 @@ func InitCollectorService() { collectors := []models.Collector{} _, err := CollectorServ.DBConnection.GetAll(&collectors, "") if err != nil { - utils.ALogger.Fatal("failed to fetch collectors: %v", err) + catcher.Error("failed to fetch collectors", err, nil) + os.Exit(1) } for _, c := range collectors { CollectorServ.CacheCollectorKey[c.ID] = c.CollectorKey @@ -76,7 +79,7 @@ func InitCollectorService() { moduleConfig := &types.ConfigurationSection{} moduleConfig, err = client.GetUTMConfig(moduleType) if err != nil { - utils.ALogger.ErrorF("failed to get module config: %v", err) + catcher.Error("failed to get module config", err, nil) time.Sleep(5 * time.Second) continue external } @@ -86,7 +89,7 @@ func InitCollectorService() { var idInt int idInt, err = strconv.Atoi(group.CollectorID) if err != nil { - utils.ALogger.ErrorF("invalid collector ID: %v", err) + catcher.Error("invalid collector ID", err, nil) continue } @@ -128,7 +131,7 @@ func (s *CollectorService) RegisterCollector(ctx context.Context, req *RegisterR Key: oldCollector.CollectorKey, }, nil } else { - utils.ALogger.ErrorF("collector %s(%s) with id %d already registered with different IP", oldCollector.Hostname, oldCollector.Module, oldCollector.ID) + catcher.Error("collector already registered with different IP", nil, map[string]any{"hostname": oldCollector.Hostname, "module": oldCollector.Module, "id": oldCollector.ID}) return nil, status.Errorf(codes.AlreadyExists, "hostname has already been registered") } } @@ -137,7 +140,7 @@ func (s *CollectorService) RegisterCollector(ctx context.Context, req *RegisterR collector.CollectorKey = key err = s.DBConnection.Create(collector) if err != nil { - utils.ALogger.ErrorF("failed to create collector: %v", err) + catcher.Error("failed to create collector", err, map[string]any{}) return nil, status.Error(codes.Internal, fmt.Sprintf("failed to create collector: %v", err)) } @@ -151,7 +154,7 @@ func (s *CollectorService) RegisterCollector(ctx context.Context, req *RegisterR LastPing: time.Now(), } - utils.ALogger.Info("Collector %s(%s) with id %d registered correctly", collector.Hostname, collector.Module, collector.ID) + catcher.Info("collector registered", map[string]any{"hostname": collector.Hostname, "module": collector.Module, "id": collector.ID}) return &AuthResponse{ Id: uint32(collector.ID), Key: key, @@ -170,12 +173,12 @@ func (s *CollectorService) DeleteCollector(ctx context.Context, req *DeleteReque err = s.DBConnection.Upsert(&models.Collector{}, "id = ?", map[string]interface{}{"deleted_by": req.DeletedBy}, id) if err != nil { - utils.ALogger.ErrorF("unable to delete collector: %v", err) + catcher.Error("unable to delete collector", err, map[string]any{}) } err = s.DBConnection.Delete(&models.Collector{}, "id = ?", false, id) if err != nil { - utils.ALogger.ErrorF("unable to delete collector: %v", err) + catcher.Error("unable to delete collector", err, map[string]any{}) return nil, status.Error(codes.Internal, fmt.Sprintf("unable to delete collector: %v", err.Error())) } @@ -187,7 +190,7 @@ func (s *CollectorService) DeleteCollector(ctx context.Context, req *DeleteReque delete(s.CollectorStreamMap, uint(idInt)) s.CollectorStreamMutex.Unlock() - utils.ALogger.Info("Collector with key %s deleted by %s", key, req.DeletedBy) + catcher.Info("collector deleted", map[string]any{"collector_id": key, "deleted_by": req.DeletedBy}) return &AuthResponse{ Id: uint32(idInt), Key: key, @@ -201,7 +204,7 @@ func (s *CollectorService) ListCollector(ctx context.Context, req *ListRequest) collectors := []models.Collector{} total, err := s.DBConnection.GetByPagination(&collectors, page, filter, "", false) if err != nil { - utils.ALogger.ErrorF("failed to fetch collectors: %v", err) + catcher.Error("failed to fetch collectors", err, map[string]any{}) return nil, status.Errorf(codes.Internal, "failed to fetch collectors: %v", err) } return convertModelToCollectorResponse(collectors, total), nil @@ -211,7 +214,7 @@ func (s *CollectorService) ProcessPendingConfigs() { for configs := range s.CollectorPendigConfigChan { collectorID, err := strconv.Atoi(configs.CollectorId) if err != nil { - utils.ALogger.ErrorF("invalid collector ID: %v", err) + catcher.Error("invalid collector ID", err, map[string]any{}) continue } @@ -228,7 +231,7 @@ func (s *CollectorService) ProcessPendingConfigs() { }, }) if err != nil { - utils.ALogger.ErrorF("failed to send config to collector: %v", err) + catcher.Error("failed to send config to collector", err, map[string]any{}) } } } @@ -273,7 +276,7 @@ func (s *CollectorService) CollectorStream(stream CollectorService_CollectorStre switch msg := in.StreamMessage.(type) { case *CollectorMessages_Result: - utils.ALogger.Info("Received Knowlodge: %s", msg.Result.RequestId) + catcher.Info("Received Knowledge", map[string]any{"request_id": msg.Result.RequestId}) case *CollectorMessages_Config: // Not implemented diff --git a/agent-manager/agent/lastseen_imp.go b/agent-manager/agent/lastseen_imp.go index dace7d91e..a200a2f43 100644 --- a/agent-manager/agent/lastseen_imp.go +++ b/agent-manager/agent/lastseen_imp.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/database" "github.com/utmstack/UTMStack/agent-manager/models" "github.com/utmstack/UTMStack/agent-manager/utils" @@ -50,7 +51,7 @@ func (s *LastSeenService) InitPingSync() { for { _, err := s.DBConnection.GetAll(&pings, "") if err != nil { - utils.ALogger.ErrorF("failed to get LastSeen items: %v", err) + catcher.Error("failed to get LastSeen items", err, nil) time.Sleep(5 * time.Second) continue } @@ -77,7 +78,7 @@ func (s *LastSeenService) processPings() { } } - utils.ALogger.Info("processPings goroutine ended") + catcher.Info("processPings goroutine ended", nil) } func (s *LastSeenService) flushLastSeenToDB() { @@ -106,7 +107,7 @@ func (s *LastSeenService) flushLastSeenToDB() { // Database operations dbOpsCount := len(pings) - + if dbOpsCount == 0 { continue } @@ -130,7 +131,7 @@ func (s *LastSeenService) flushLastSeenToDB() { for ping := range pingChan { err := s.DBConnection.Upsert(&ping, "connector_id = ?", nil, ping.ConnectorID) if err != nil { - utils.ALogger.ErrorF("failed to save LastSeen item for connector %d: %v", ping.ConnectorID, err) + catcher.Error("failed to save LastSeen item", err, map[string]any{"connector_id": ping.ConnectorID}) select { case errorChan <- err: default: diff --git a/agent-manager/agent/parser.go b/agent-manager/agent/parser.go index 5e6bb388e..d79fbe5f0 100644 --- a/agent-manager/agent/parser.go +++ b/agent-manager/agent/parser.go @@ -4,6 +4,7 @@ import ( "regexp" "strconv" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/config" "github.com/utmstack/UTMStack/agent-manager/models" "github.com/utmstack/UTMStack/agent-manager/utils" @@ -39,7 +40,7 @@ func createHistoryCommand(cmd *UtmCommand, cmdID string, agentId uint) *models.A func parseAgentToProto(agent models.Agent) *Agent { agentStatus, lastSeen, err := LastSeenServ.GetLastSeenStatus(agent.ID, "agent") if err != nil { - utils.ALogger.ErrorF("failed to get last seen status for agent %d: %v", agent.ID, err) + catcher.Error("failed to get last seen status for agent", err, map[string]any{"agent_id": agent.ID}) } agentResult := &Agent{ Id: uint32(agent.ID), @@ -107,7 +108,7 @@ func replaceSecretValues(input string) string { func modelToProtoCollector(model models.Collector) *Collector { collectorStatus, lastSeen, err := LastSeenServ.GetLastSeenStatus(model.ID, "collector") if err != nil { - utils.ALogger.ErrorF("failed to get last seen status for collector %d: %v", model.ID, err) + catcher.Error("failed to get last seen status for collector", err, map[string]any{"collector_id": model.ID}) } return &Collector{ Id: int32(model.ID), diff --git a/agent-manager/agent/utmgrpc.go b/agent-manager/agent/utmgrpc.go index d26688805..ec8003e90 100644 --- a/agent-manager/agent/utmgrpc.go +++ b/agent-manager/agent/utmgrpc.go @@ -3,9 +3,10 @@ package agent import ( "crypto/tls" "net" + "os" + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/config" - "github.com/utmstack/UTMStack/agent-manager/utils" grpc "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/health" @@ -15,7 +16,8 @@ import ( func InitGrpcServer() { err := InitAgentService() if err != nil { - utils.ALogger.Fatal("failed to init agent service: %v", err) + catcher.Error("failed to init agent service", err, nil) + os.Exit(1) } go InitCollectorService() @@ -27,14 +29,14 @@ func InitGrpcServer() { func StartGrpcServer() { listener, err := net.Listen("tcp", "0.0.0.0:50051") if err != nil { - utils.ALogger.Fatal("failed to listen: %v", err) - return + catcher.Error("failed to listen", err, nil) + os.Exit(1) } loadedCert, err := tls.LoadX509KeyPair(config.CertPath, config.CertKeyPath) if err != nil { - utils.ALogger.Fatal("failed to load TLS credentials: %v", err) - return + catcher.Error("failed to load TLS credentials", err, nil) + os.Exit(1) } transportCredentials := credentials.NewTLS(&tls.Config{ @@ -57,8 +59,9 @@ func StartGrpcServer() { grpc_health_v1.RegisterHealthServer(grpcServer, healthServer) healthServer.SetServingStatus("", grpc_health_v1.HealthCheckResponse_SERVING) - utils.ALogger.Info("Starting gRPC server on 0.0.0.0:50051") + catcher.Info("Starting gRPC server on 0.0.0.0:50051", nil) if err := grpcServer.Serve(listener); err != nil { - utils.ALogger.Fatal("failed to serve: %v", err) + catcher.Error("failed to serve", err, nil) + os.Exit(1) } } diff --git a/agent-manager/go.mod b/agent-manager/go.mod index b06dfaa5f..f3038aa2b 100644 --- a/agent-manager/go.mod +++ b/agent-manager/go.mod @@ -40,6 +40,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/threatwinds/go-sdk v1.0.45 github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.0 // indirect golang.org/x/arch v0.19.0 // indirect diff --git a/agent-manager/go.sum b/agent-manager/go.sum index 0f9fd6ccf..f7155f938 100644 --- a/agent-manager/go.sum +++ b/agent-manager/go.sum @@ -90,6 +90,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO 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/go-sdk v1.0.45 h1:KZ3s3HviNRrOkg5EqjFnoauANFFzTqjNFyshPLY2SoI= +github.com/threatwinds/go-sdk v1.0.45/go.mod h1:tcWn6r6vqID/W/nL3UKfc5NafA3V/cSkiLvfJnwB58c= github.com/threatwinds/logger v1.2.2 h1:sVuT8yhbecPqP4tT8EwHfp1czNC6e1wdkE1ihNnuBdA= github.com/threatwinds/logger v1.2.2/go.mod h1:Amq0QI1y7fkTpnBUgeGVu2Z/C4u4ys2pNLUOuj3UAAU= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= diff --git a/agent-manager/main.go b/agent-manager/main.go index 0e1e39ba3..bd5e34617 100644 --- a/agent-manager/main.go +++ b/agent-manager/main.go @@ -1,19 +1,19 @@ package main import ( + "os" + + "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/agent" "github.com/utmstack/UTMStack/agent-manager/database" "github.com/utmstack/UTMStack/agent-manager/updates" - "github.com/utmstack/UTMStack/agent-manager/utils" ) func main() { - utils.InitLogger() - utils.ALogger.Info("Starting Agent Manager v1.0.0 ...") - err := database.MigrateDatabase() if err != nil { - utils.ALogger.Fatal("failed to migrate database: %v", err) + _ = catcher.Error("failed to migrate database", err, map[string]any{}) + os.Exit(1) } go updates.InitUpdatesManager() diff --git a/agent-manager/updates/updates.go b/agent-manager/updates/updates.go index e23c76581..e7b030b95 100644 --- a/agent-manager/updates/updates.go +++ b/agent-manager/updates/updates.go @@ -3,12 +3,13 @@ package updates import ( "crypto/tls" "net/http" + "os" "github.com/gin-contrib/gzip" + "github.com/threatwinds/go-sdk/catcher" "github.com/gin-gonic/gin" "github.com/utmstack/UTMStack/agent-manager/config" - "github.com/utmstack/UTMStack/agent-manager/utils" ) func InitUpdatesManager() { @@ -16,7 +17,7 @@ func InitUpdatesManager() { } func ServeDependencies() { - utils.ALogger.LogF(100, "Serving dependencies from %s", config.UpdatesDependenciesFolder) + catcher.Info("Serving dependencies", map[string]any{"config": config.UpdatesDependenciesFolder}) gin.SetMode(gin.ReleaseMode) r := gin.New() @@ -32,7 +33,8 @@ func ServeDependencies() { loadedCert, err := tls.LoadX509KeyPair(config.CertPath, config.CertKeyPath) if err != nil { - utils.ALogger.Fatal("failed to load TLS credentials: %v", err) + catcher.Error("failed to load TLS credentials", err, map[string]any{}) + os.Exit(1) } tlsConfig := &tls.Config{ @@ -53,9 +55,9 @@ func ServeDependencies() { TLSConfig: tlsConfig, } - utils.ALogger.Info("Starting HTTP server on port 8080") + catcher.Info("Starting HTTP server on port 8080", map[string]any{}) if err := server.ListenAndServeTLS("", ""); err != nil { - utils.ALogger.ErrorF("error starting HTTP server: %v", err) + catcher.Error("error starting HTTP server", err, map[string]any{}) return } } diff --git a/agent-manager/utils/logger.go b/agent-manager/utils/logger.go deleted file mode 100644 index 70c32fd8f..000000000 --- a/agent-manager/utils/logger.go +++ /dev/null @@ -1,31 +0,0 @@ -package utils - -import ( - "os" - "strconv" - "sync" - - "github.com/threatwinds/logger" -) - -var ( - ALogger *logger.Logger - loggerOnceInstance sync.Once -) - -func InitLogger() { - logLevel := os.Getenv("LOG_LEVEL") - logLevelInt := 200 - if logLevel != "" { - logL, err := strconv.Atoi(logLevel) - if err == nil { - logLevelInt = logL - } - } - - loggerOnceInstance.Do(func() { - ALogger = logger.NewLogger( - &logger.Config{Format: "text", Level: logLevelInt, Output: "stdout"}, - ) - }) -} From 189f2d32d36a1fc9b11e960251f6cb5088fdff85 Mon Sep 17 00:00:00 2001 From: JocLRojas Date: Tue, 16 Sep 2025 11:20:05 -0400 Subject: [PATCH 015/422] Revert "Merge branch 'backlog/mdc-request-context-logging' into release/v11" This reverts commit f5744418d154dfbbec7a724f8bf93d2ad3e9208d, reversing changes made to 6b2604773f652ee1466ffaad970ffa51ed21c5d6. --- agent-manager/agent/agent_imp.go | 31 ++++----- agent-manager/agent/collector_imp.go | 29 ++++---- agent-manager/agent/lastseen_imp.go | 9 ++- agent-manager/agent/parser.go | 5 +- agent-manager/agent/utmgrpc.go | 19 +++-- agent-manager/go.mod | 1 - agent-manager/go.sum | 2 - agent-manager/main.go | 10 +-- agent-manager/updates/updates.go | 12 ++-- agent-manager/utils/logger.go | 31 +++++++++ backend/pom.xml | 3 - .../utmstack/loggin/LogContextBuilder.java | 45 ------------ .../utmstack/loggin/LoggingContextFilter.java | 47 ------------- backend/src/main/resources/logback-spring.xml | 13 ---- plugins/aws/config/config.go | 6 +- plugins/azure/config/config.go | 6 +- plugins/bitdefender/config/config.go | 8 ++- plugins/bitdefender/server/certs.go | 13 ++-- plugins/bitdefender/server/server.go | 69 +++++++------------ plugins/config/main.go | 58 ++++++++-------- plugins/gcp/config/config.go | 6 +- plugins/inputs/main.go | 7 +- plugins/inputs/middlewares.go | 7 +- plugins/modules-config/config/config.go | 7 +- plugins/modules-config/handlers.go | 3 +- plugins/modules-config/validations/aws.go | 14 ++-- plugins/modules-config/validations/azure.go | 22 +++--- plugins/modules-config/validations/bdgz.go | 53 +++++++------- plugins/modules-config/validations/gcp.go | 9 ++- plugins/modules-config/validations/o365.go | 24 +++---- plugins/modules-config/validations/socai.go | 28 ++------ plugins/modules-config/validations/sophos.go | 38 ++++------ .../modules-config/validations/validate.go | 3 +- plugins/o365/config/config.go | 7 +- plugins/soc-ai/config/config.go | 11 +-- plugins/soc-ai/correlation/correlation.go | 7 +- plugins/soc-ai/elastic.go | 9 ++- plugins/soc-ai/elastic/alerts.go | 8 +-- plugins/soc-ai/elastic/incidents.go | 21 +++--- plugins/soc-ai/elastic/index.go | 17 +++-- plugins/soc-ai/elastic/transf.go | 3 +- plugins/soc-ai/llm.go | 31 ++++----- plugins/soc-ai/main.go | 13 ++-- plugins/soc-ai/queue.go | 46 ++++--------- plugins/soc-ai/utils/convert.go | 7 +- plugins/soc-ai/utils/env.go | 7 +- plugins/soc-ai/utils/files.go | 7 +- plugins/soc-ai/utils/logger.go | 31 +++++++++ plugins/sophos/config/config.go | 6 +- 49 files changed, 368 insertions(+), 501 deletions(-) create mode 100644 agent-manager/utils/logger.go delete mode 100644 backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java delete mode 100644 backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java create mode 100644 plugins/soc-ai/utils/logger.go diff --git a/agent-manager/agent/agent_imp.go b/agent-manager/agent/agent_imp.go index 8ad486046..47f5728a1 100644 --- a/agent-manager/agent/agent_imp.go +++ b/agent-manager/agent/agent_imp.go @@ -9,7 +9,6 @@ import ( "time" "github.com/google/uuid" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/database" "github.com/utmstack/UTMStack/agent-manager/models" "github.com/utmstack/UTMStack/agent-manager/utils" @@ -83,7 +82,7 @@ func (s *AgentService) RegisterAgent(ctx context.Context, req *AgentRequest) (*A Key: oldAgent.AgentKey, }, nil } else { - catcher.Error("agent already exists", nil, map[string]any{"hostname": agent.Hostname}) + utils.ALogger.ErrorF("agent with hostname %s already exists", agent.Hostname) return nil, status.Errorf(codes.AlreadyExists, "hostname has already been registered") } } @@ -92,7 +91,7 @@ func (s *AgentService) RegisterAgent(ctx context.Context, req *AgentRequest) (*A agent.AgentKey = key err = s.DBConnection.Create(agent) if err != nil { - catcher.Error("failed to create agent", err, map[string]any{}) + utils.ALogger.ErrorF("failed to create agent: %v", err) return nil, status.Error(codes.Internal, fmt.Sprintf("failed to create agent: %v", err)) } @@ -106,7 +105,7 @@ func (s *AgentService) RegisterAgent(ctx context.Context, req *AgentRequest) (*A LastPing: time.Now(), } - catcher.Info("agent registered", map[string]any{"id": agent.ID, "hostname": agent.Hostname}) + utils.ALogger.Info("Agent %s with id %d registered correctly", agent.Hostname, agent.ID) return &AuthResponse{ Id: uint32(agent.ID), Key: key, @@ -126,7 +125,7 @@ func (s *AgentService) UpdateAgent(ctx context.Context, req *AgentRequest) (*Aut agent := &models.Agent{} err = s.DBConnection.GetFirst(agent, "id = ?", idInt) if err != nil { - catcher.Error("failed to fetch agent", err, map[string]any{}) + utils.ALogger.ErrorF("failed to fetch agent: %v", err) return nil, status.Errorf(codes.NotFound, "agent not found") } @@ -157,7 +156,7 @@ func (s *AgentService) UpdateAgent(ctx context.Context, req *AgentRequest) (*Aut err = s.DBConnection.Upsert(&agent, "id = ?", nil, idInt) if err != nil { - catcher.Error("failed to update agent", err, map[string]any{}) + utils.ALogger.ErrorF("failed to update agent: %v", err) return nil, status.Errorf(codes.Internal, "failed to update agent: %v", err) } @@ -181,18 +180,18 @@ func (s *AgentService) DeleteAgent(ctx context.Context, req *DeleteRequest) (*Au err = s.DBConnection.Upsert(&models.Agent{}, "id = ?", map[string]interface{}{"deleted_by": req.DeletedBy}, id) if err != nil { - catcher.Error("unable to update delete_by field in agent", err, map[string]any{}) + utils.ALogger.ErrorF("unable to update delete_by field in agent: %v", err) } err = s.DBConnection.Delete(&models.AgentCommand{}, "agent_id = ?", false, uint(idInt)) if err != nil { - catcher.Error("unable to delete agent commands", err, map[string]any{}) + utils.ALogger.ErrorF("unable to delete agent commands: %v", err) return &AuthResponse{}, status.Error(codes.Internal, fmt.Sprintf("unable to delete agent commands: %v", err.Error())) } err = s.DBConnection.Delete(&models.Agent{}, "id = ?", false, id) if err != nil { - catcher.Error("unable to delete agent", err, map[string]any{}) + utils.ALogger.ErrorF("unable to delete agent: %v", err) return &AuthResponse{}, status.Error(codes.Internal, fmt.Sprintf("unable to delete agent: %v", err.Error())) } @@ -204,7 +203,7 @@ func (s *AgentService) DeleteAgent(ctx context.Context, req *DeleteRequest) (*Au delete(s.AgentStreamMap, uint(idInt)) s.AgentStreamMutex.Unlock() - catcher.Info("agent deleted", map[string]any{"agent_id": key, "deleted_by": req.DeletedBy}) + utils.ALogger.Info("Agent with key %s deleted by %s", key, req.DeletedBy) return &AuthResponse{ Id: uint32(idInt), @@ -219,7 +218,7 @@ func (s *AgentService) ListAgents(ctx context.Context, req *ListRequest) (*ListA agents := []models.Agent{} total, err := s.DBConnection.GetByPagination(&agents, page, filter, "", false) if err != nil { - catcher.Error("failed to fetch agents", err, map[string]any{}) + utils.ALogger.ErrorF("failed to fetch agents: %v", err) return nil, status.Errorf(codes.Internal, "failed to fetch agents: %v", err) } @@ -267,7 +266,7 @@ func (s *AgentService) AgentStream(stream AgentService_AgentStreamServer) error switch msg := in.StreamMessage.(type) { case *BidirectionalStream_Result: - catcher.Info("Received command result", map[string]any{"agent_id": msg.Result.AgentId, "result": msg.Result.Result}) + utils.ALogger.Info("Received command result from agent %s: %s", msg.Result.AgentId, msg.Result.Result) cmdID := msg.Result.GetCmdId() s.CommandResultChannelM.Lock() @@ -279,7 +278,7 @@ func (s *AgentService) AgentStream(stream AgentService_AgentStreamServer) error ExecutedAt: msg.Result.ExecutedAt, } } else { - catcher.Error("failed to find result channel for CmdID", nil, map[string]any{"cmd_id": cmdID}) + utils.ALogger.ErrorF("failed to find result channel for CmdID: %s", cmdID) } s.CommandResultChannelM.Unlock() } @@ -325,7 +324,7 @@ func (s *AgentService) ProcessCommand(stream PanelService_ProcessCommandServer) histCommand := createHistoryCommand(cmd, cmdID, uint(streamId)) err = s.DBConnection.Create(&histCommand) if err != nil { - catcher.Error("unable to create a new command history", err, map[string]any{}) + utils.ALogger.ErrorF("unable to create a new command history") } err = agentStream.Send(&BidirectionalStream{ @@ -349,7 +348,7 @@ func (s *AgentService) ProcessCommand(stream PanelService_ProcessCommandServer) cmd.AgentId, cmdID, ) if err != nil { - catcher.Error("failed to update command status", err, map[string]any{}) + utils.ALogger.ErrorF("failed to update command status: %v", err) } err = stream.Send(result) @@ -370,7 +369,7 @@ func (s *AgentService) ListAgentCommands(ctx context.Context, req *ListRequest) commands := []models.AgentCommand{} total, err := s.DBConnection.GetByPagination(&commands, page, filter, "", false) if err != nil { - catcher.Error("failed to fetch agent commands", err, map[string]any{}) + utils.ALogger.ErrorF("failed to fetch agent commands: %v", err) return nil, status.Errorf(codes.Internal, "failed to fetch agent commands: %v", err) } diff --git a/agent-manager/agent/collector_imp.go b/agent-manager/agent/collector_imp.go index 8034fb046..17b5434e0 100644 --- a/agent-manager/agent/collector_imp.go +++ b/agent-manager/agent/collector_imp.go @@ -4,13 +4,11 @@ import ( context "context" "fmt" "io" - "os" "strconv" sync "sync" "time" "github.com/google/uuid" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/config" "github.com/utmstack/UTMStack/agent-manager/database" "github.com/utmstack/UTMStack/agent-manager/models" @@ -63,8 +61,7 @@ func InitCollectorService() { collectors := []models.Collector{} _, err := CollectorServ.DBConnection.GetAll(&collectors, "") if err != nil { - catcher.Error("failed to fetch collectors", err, nil) - os.Exit(1) + utils.ALogger.Fatal("failed to fetch collectors: %v", err) } for _, c := range collectors { CollectorServ.CacheCollectorKey[c.ID] = c.CollectorKey @@ -79,7 +76,7 @@ func InitCollectorService() { moduleConfig := &types.ConfigurationSection{} moduleConfig, err = client.GetUTMConfig(moduleType) if err != nil { - catcher.Error("failed to get module config", err, nil) + utils.ALogger.ErrorF("failed to get module config: %v", err) time.Sleep(5 * time.Second) continue external } @@ -89,7 +86,7 @@ func InitCollectorService() { var idInt int idInt, err = strconv.Atoi(group.CollectorID) if err != nil { - catcher.Error("invalid collector ID", err, nil) + utils.ALogger.ErrorF("invalid collector ID: %v", err) continue } @@ -131,7 +128,7 @@ func (s *CollectorService) RegisterCollector(ctx context.Context, req *RegisterR Key: oldCollector.CollectorKey, }, nil } else { - catcher.Error("collector already registered with different IP", nil, map[string]any{"hostname": oldCollector.Hostname, "module": oldCollector.Module, "id": oldCollector.ID}) + utils.ALogger.ErrorF("collector %s(%s) with id %d already registered with different IP", oldCollector.Hostname, oldCollector.Module, oldCollector.ID) return nil, status.Errorf(codes.AlreadyExists, "hostname has already been registered") } } @@ -140,7 +137,7 @@ func (s *CollectorService) RegisterCollector(ctx context.Context, req *RegisterR collector.CollectorKey = key err = s.DBConnection.Create(collector) if err != nil { - catcher.Error("failed to create collector", err, map[string]any{}) + utils.ALogger.ErrorF("failed to create collector: %v", err) return nil, status.Error(codes.Internal, fmt.Sprintf("failed to create collector: %v", err)) } @@ -154,7 +151,7 @@ func (s *CollectorService) RegisterCollector(ctx context.Context, req *RegisterR LastPing: time.Now(), } - catcher.Info("collector registered", map[string]any{"hostname": collector.Hostname, "module": collector.Module, "id": collector.ID}) + utils.ALogger.Info("Collector %s(%s) with id %d registered correctly", collector.Hostname, collector.Module, collector.ID) return &AuthResponse{ Id: uint32(collector.ID), Key: key, @@ -173,12 +170,12 @@ func (s *CollectorService) DeleteCollector(ctx context.Context, req *DeleteReque err = s.DBConnection.Upsert(&models.Collector{}, "id = ?", map[string]interface{}{"deleted_by": req.DeletedBy}, id) if err != nil { - catcher.Error("unable to delete collector", err, map[string]any{}) + utils.ALogger.ErrorF("unable to delete collector: %v", err) } err = s.DBConnection.Delete(&models.Collector{}, "id = ?", false, id) if err != nil { - catcher.Error("unable to delete collector", err, map[string]any{}) + utils.ALogger.ErrorF("unable to delete collector: %v", err) return nil, status.Error(codes.Internal, fmt.Sprintf("unable to delete collector: %v", err.Error())) } @@ -190,7 +187,7 @@ func (s *CollectorService) DeleteCollector(ctx context.Context, req *DeleteReque delete(s.CollectorStreamMap, uint(idInt)) s.CollectorStreamMutex.Unlock() - catcher.Info("collector deleted", map[string]any{"collector_id": key, "deleted_by": req.DeletedBy}) + utils.ALogger.Info("Collector with key %s deleted by %s", key, req.DeletedBy) return &AuthResponse{ Id: uint32(idInt), Key: key, @@ -204,7 +201,7 @@ func (s *CollectorService) ListCollector(ctx context.Context, req *ListRequest) collectors := []models.Collector{} total, err := s.DBConnection.GetByPagination(&collectors, page, filter, "", false) if err != nil { - catcher.Error("failed to fetch collectors", err, map[string]any{}) + utils.ALogger.ErrorF("failed to fetch collectors: %v", err) return nil, status.Errorf(codes.Internal, "failed to fetch collectors: %v", err) } return convertModelToCollectorResponse(collectors, total), nil @@ -214,7 +211,7 @@ func (s *CollectorService) ProcessPendingConfigs() { for configs := range s.CollectorPendigConfigChan { collectorID, err := strconv.Atoi(configs.CollectorId) if err != nil { - catcher.Error("invalid collector ID", err, map[string]any{}) + utils.ALogger.ErrorF("invalid collector ID: %v", err) continue } @@ -231,7 +228,7 @@ func (s *CollectorService) ProcessPendingConfigs() { }, }) if err != nil { - catcher.Error("failed to send config to collector", err, map[string]any{}) + utils.ALogger.ErrorF("failed to send config to collector: %v", err) } } } @@ -276,7 +273,7 @@ func (s *CollectorService) CollectorStream(stream CollectorService_CollectorStre switch msg := in.StreamMessage.(type) { case *CollectorMessages_Result: - catcher.Info("Received Knowledge", map[string]any{"request_id": msg.Result.RequestId}) + utils.ALogger.Info("Received Knowlodge: %s", msg.Result.RequestId) case *CollectorMessages_Config: // Not implemented diff --git a/agent-manager/agent/lastseen_imp.go b/agent-manager/agent/lastseen_imp.go index a200a2f43..dace7d91e 100644 --- a/agent-manager/agent/lastseen_imp.go +++ b/agent-manager/agent/lastseen_imp.go @@ -7,7 +7,6 @@ import ( "sync" "time" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/database" "github.com/utmstack/UTMStack/agent-manager/models" "github.com/utmstack/UTMStack/agent-manager/utils" @@ -51,7 +50,7 @@ func (s *LastSeenService) InitPingSync() { for { _, err := s.DBConnection.GetAll(&pings, "") if err != nil { - catcher.Error("failed to get LastSeen items", err, nil) + utils.ALogger.ErrorF("failed to get LastSeen items: %v", err) time.Sleep(5 * time.Second) continue } @@ -78,7 +77,7 @@ func (s *LastSeenService) processPings() { } } - catcher.Info("processPings goroutine ended", nil) + utils.ALogger.Info("processPings goroutine ended") } func (s *LastSeenService) flushLastSeenToDB() { @@ -107,7 +106,7 @@ func (s *LastSeenService) flushLastSeenToDB() { // Database operations dbOpsCount := len(pings) - + if dbOpsCount == 0 { continue } @@ -131,7 +130,7 @@ func (s *LastSeenService) flushLastSeenToDB() { for ping := range pingChan { err := s.DBConnection.Upsert(&ping, "connector_id = ?", nil, ping.ConnectorID) if err != nil { - catcher.Error("failed to save LastSeen item", err, map[string]any{"connector_id": ping.ConnectorID}) + utils.ALogger.ErrorF("failed to save LastSeen item for connector %d: %v", ping.ConnectorID, err) select { case errorChan <- err: default: diff --git a/agent-manager/agent/parser.go b/agent-manager/agent/parser.go index d79fbe5f0..5e6bb388e 100644 --- a/agent-manager/agent/parser.go +++ b/agent-manager/agent/parser.go @@ -4,7 +4,6 @@ import ( "regexp" "strconv" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/config" "github.com/utmstack/UTMStack/agent-manager/models" "github.com/utmstack/UTMStack/agent-manager/utils" @@ -40,7 +39,7 @@ func createHistoryCommand(cmd *UtmCommand, cmdID string, agentId uint) *models.A func parseAgentToProto(agent models.Agent) *Agent { agentStatus, lastSeen, err := LastSeenServ.GetLastSeenStatus(agent.ID, "agent") if err != nil { - catcher.Error("failed to get last seen status for agent", err, map[string]any{"agent_id": agent.ID}) + utils.ALogger.ErrorF("failed to get last seen status for agent %d: %v", agent.ID, err) } agentResult := &Agent{ Id: uint32(agent.ID), @@ -108,7 +107,7 @@ func replaceSecretValues(input string) string { func modelToProtoCollector(model models.Collector) *Collector { collectorStatus, lastSeen, err := LastSeenServ.GetLastSeenStatus(model.ID, "collector") if err != nil { - catcher.Error("failed to get last seen status for collector", err, map[string]any{"collector_id": model.ID}) + utils.ALogger.ErrorF("failed to get last seen status for collector %d: %v", model.ID, err) } return &Collector{ Id: int32(model.ID), diff --git a/agent-manager/agent/utmgrpc.go b/agent-manager/agent/utmgrpc.go index ec8003e90..d26688805 100644 --- a/agent-manager/agent/utmgrpc.go +++ b/agent-manager/agent/utmgrpc.go @@ -3,10 +3,9 @@ package agent import ( "crypto/tls" "net" - "os" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/config" + "github.com/utmstack/UTMStack/agent-manager/utils" grpc "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/health" @@ -16,8 +15,7 @@ import ( func InitGrpcServer() { err := InitAgentService() if err != nil { - catcher.Error("failed to init agent service", err, nil) - os.Exit(1) + utils.ALogger.Fatal("failed to init agent service: %v", err) } go InitCollectorService() @@ -29,14 +27,14 @@ func InitGrpcServer() { func StartGrpcServer() { listener, err := net.Listen("tcp", "0.0.0.0:50051") if err != nil { - catcher.Error("failed to listen", err, nil) - os.Exit(1) + utils.ALogger.Fatal("failed to listen: %v", err) + return } loadedCert, err := tls.LoadX509KeyPair(config.CertPath, config.CertKeyPath) if err != nil { - catcher.Error("failed to load TLS credentials", err, nil) - os.Exit(1) + utils.ALogger.Fatal("failed to load TLS credentials: %v", err) + return } transportCredentials := credentials.NewTLS(&tls.Config{ @@ -59,9 +57,8 @@ func StartGrpcServer() { grpc_health_v1.RegisterHealthServer(grpcServer, healthServer) healthServer.SetServingStatus("", grpc_health_v1.HealthCheckResponse_SERVING) - catcher.Info("Starting gRPC server on 0.0.0.0:50051", nil) + utils.ALogger.Info("Starting gRPC server on 0.0.0.0:50051") if err := grpcServer.Serve(listener); err != nil { - catcher.Error("failed to serve", err, nil) - os.Exit(1) + utils.ALogger.Fatal("failed to serve: %v", err) } } diff --git a/agent-manager/go.mod b/agent-manager/go.mod index f3038aa2b..b06dfaa5f 100644 --- a/agent-manager/go.mod +++ b/agent-manager/go.mod @@ -40,7 +40,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect - github.com/threatwinds/go-sdk v1.0.45 github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.0 // indirect golang.org/x/arch v0.19.0 // indirect diff --git a/agent-manager/go.sum b/agent-manager/go.sum index f7155f938..0f9fd6ccf 100644 --- a/agent-manager/go.sum +++ b/agent-manager/go.sum @@ -90,8 +90,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO 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/go-sdk v1.0.45 h1:KZ3s3HviNRrOkg5EqjFnoauANFFzTqjNFyshPLY2SoI= -github.com/threatwinds/go-sdk v1.0.45/go.mod h1:tcWn6r6vqID/W/nL3UKfc5NafA3V/cSkiLvfJnwB58c= github.com/threatwinds/logger v1.2.2 h1:sVuT8yhbecPqP4tT8EwHfp1czNC6e1wdkE1ihNnuBdA= github.com/threatwinds/logger v1.2.2/go.mod h1:Amq0QI1y7fkTpnBUgeGVu2Z/C4u4ys2pNLUOuj3UAAU= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= diff --git a/agent-manager/main.go b/agent-manager/main.go index bd5e34617..0e1e39ba3 100644 --- a/agent-manager/main.go +++ b/agent-manager/main.go @@ -1,19 +1,19 @@ package main import ( - "os" - - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/agent-manager/agent" "github.com/utmstack/UTMStack/agent-manager/database" "github.com/utmstack/UTMStack/agent-manager/updates" + "github.com/utmstack/UTMStack/agent-manager/utils" ) func main() { + utils.InitLogger() + utils.ALogger.Info("Starting Agent Manager v1.0.0 ...") + err := database.MigrateDatabase() if err != nil { - _ = catcher.Error("failed to migrate database", err, map[string]any{}) - os.Exit(1) + utils.ALogger.Fatal("failed to migrate database: %v", err) } go updates.InitUpdatesManager() diff --git a/agent-manager/updates/updates.go b/agent-manager/updates/updates.go index e7b030b95..e23c76581 100644 --- a/agent-manager/updates/updates.go +++ b/agent-manager/updates/updates.go @@ -3,13 +3,12 @@ package updates import ( "crypto/tls" "net/http" - "os" "github.com/gin-contrib/gzip" - "github.com/threatwinds/go-sdk/catcher" "github.com/gin-gonic/gin" "github.com/utmstack/UTMStack/agent-manager/config" + "github.com/utmstack/UTMStack/agent-manager/utils" ) func InitUpdatesManager() { @@ -17,7 +16,7 @@ func InitUpdatesManager() { } func ServeDependencies() { - catcher.Info("Serving dependencies", map[string]any{"config": config.UpdatesDependenciesFolder}) + utils.ALogger.LogF(100, "Serving dependencies from %s", config.UpdatesDependenciesFolder) gin.SetMode(gin.ReleaseMode) r := gin.New() @@ -33,8 +32,7 @@ func ServeDependencies() { loadedCert, err := tls.LoadX509KeyPair(config.CertPath, config.CertKeyPath) if err != nil { - catcher.Error("failed to load TLS credentials", err, map[string]any{}) - os.Exit(1) + utils.ALogger.Fatal("failed to load TLS credentials: %v", err) } tlsConfig := &tls.Config{ @@ -55,9 +53,9 @@ func ServeDependencies() { TLSConfig: tlsConfig, } - catcher.Info("Starting HTTP server on port 8080", map[string]any{}) + utils.ALogger.Info("Starting HTTP server on port 8080") if err := server.ListenAndServeTLS("", ""); err != nil { - catcher.Error("error starting HTTP server", err, map[string]any{}) + utils.ALogger.ErrorF("error starting HTTP server: %v", err) return } } diff --git a/agent-manager/utils/logger.go b/agent-manager/utils/logger.go new file mode 100644 index 000000000..70c32fd8f --- /dev/null +++ b/agent-manager/utils/logger.go @@ -0,0 +1,31 @@ +package utils + +import ( + "os" + "strconv" + "sync" + + "github.com/threatwinds/logger" +) + +var ( + ALogger *logger.Logger + loggerOnceInstance sync.Once +) + +func InitLogger() { + logLevel := os.Getenv("LOG_LEVEL") + logLevelInt := 200 + if logLevel != "" { + logL, err := strconv.Atoi(logLevel) + if err == nil { + logLevelInt = logL + } + } + + loggerOnceInstance.Do(func() { + ALogger = logger.NewLogger( + &logger.Config{Format: "text", Level: logLevelInt, Output: "stdout"}, + ) + }) +} diff --git a/backend/pom.xml b/backend/pom.xml index 87bf8a432..abb29ba28 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -376,9 +376,6 @@ 3.0.5 - - - diff --git a/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java b/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java deleted file mode 100644 index b40932cca..000000000 --- a/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.park.utmstack.loggin; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.slf4j.MDC; - -import java.util.*; -import java.util.stream.Collectors; - -public class LogContextBuilder { - - private static final ObjectMapper mapper = new ObjectMapper(); - - public static void init(String traceCode, Map args, Throwable exception) { - MDC.put("code", traceCode != null ? traceCode : UUID.randomUUID().toString().replace("-", "")); - MDC.put("args", serializeArgs(args)); - MDC.put("trace", serializeStackTrace(exception)); - } - - public static void clear() { - MDC.remove("code"); - MDC.remove("args"); - MDC.remove("trace"); - } - - private static String serializeArgs(Map args) { - try { - return args != null ? mapper.writeValueAsString(args) : "{}"; - } catch (Exception e) { - return "{\"error\":\"Failed to serialize args\"}"; - } - } - - private static String serializeStackTrace(Throwable ex) { - if (ex == null) return "[]"; - List traceList = Arrays.stream(ex.getStackTrace()) - .map(ste -> ste.getClassName() + "." + ste.getMethodName() + " " + ste.getLineNumber()) - .collect(Collectors.toList()); - try { - return mapper.writeValueAsString(traceList); - } catch (Exception e) { - return "[\"Failed to serialize stack trace\"]"; - } - } -} - diff --git a/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java b/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java deleted file mode 100644 index 681b694bd..000000000 --- a/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.park.utmstack.loggin; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.slf4j.MDC; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -@Component -public class LoggingContextFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain) - throws ServletException, IOException { - - try { - String username = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) - .map(Authentication::getName) - .orElse("anonymous"); - - Map args = new HashMap<>(); - args.put("username", username); - args.put("requestId", UUID.randomUUID().toString()); - args.put("path", request.getRequestURI()); - args.put("method", request.getMethod()); - args.put("remoteAddr", request.getRemoteAddr()); - - MDC.put("args", new ObjectMapper().writeValueAsString(args)); - - filterChain.doFilter(request, response); - } finally { - MDC.clear(); - } - } -} - diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml index bcf89e606..4aa548af3 100644 --- a/backend/src/main/resources/logback-spring.xml +++ b/backend/src/main/resources/logback-spring.xml @@ -2,19 +2,6 @@ - - - {"app":"utmstack-backend"} - username - requestId - path - - - - - - - diff --git a/plugins/aws/config/config.go b/plugins/aws/config/config.go index 4694b7d21..2d0470900 100644 --- a/plugins/aws/config/config.go +++ b/plugins/aws/config/config.go @@ -2,6 +2,8 @@ package config import ( "context" + "fmt" + "log" "strings" sync "sync" "time" @@ -50,7 +52,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) + fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") time.Sleep(reconnectDelay) continue } @@ -131,7 +133,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - catcher.Info("Received configuration update", map[string]any{"config": message.Config}) + log.Printf("Received configuration update: %v", message.Config) cnf = message.Config } } diff --git a/plugins/azure/config/config.go b/plugins/azure/config/config.go index 2d42cc7e0..15a7bd078 100644 --- a/plugins/azure/config/config.go +++ b/plugins/azure/config/config.go @@ -2,6 +2,8 @@ package config import ( "context" + "fmt" + "log" "strings" sync "sync" "time" @@ -50,7 +52,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) + fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") time.Sleep(reconnectDelay) continue } @@ -131,7 +133,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - catcher.Info("Received configuration update", map[string]any{"config": message.Config}) + log.Printf("Received configuration update: %v", message.Config) cnf = message.Config } } diff --git a/plugins/bitdefender/config/config.go b/plugins/bitdefender/config/config.go index 3a9d6aa0f..214b5cdff 100644 --- a/plugins/bitdefender/config/config.go +++ b/plugins/bitdefender/config/config.go @@ -3,6 +3,8 @@ package config import ( "context" "encoding/json" + "fmt" + "log" "net/http" "strings" sync "sync" @@ -63,7 +65,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) + fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") time.Sleep(reconnectDelay) continue } @@ -144,7 +146,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - catcher.Info("Received configuration update", map[string]any{"config": message.Config}) + log.Printf("Received configuration update: %v", message.Config) cnf = message.Config go processConfigurations(cnf) } @@ -214,7 +216,7 @@ func apiPush(config BDGZModuleConfig, operation string) error { for i := 0; i < 5; i++ { response, err := fn(config) if err != nil { - _ = catcher.Error("operation failed", err, map[string]any{}) + _ = catcher.Error(fmt.Sprintf("%v", err), err, map[string]any{}) time.Sleep(1 * time.Minute) continue } diff --git a/plugins/bitdefender/server/certs.go b/plugins/bitdefender/server/certs.go index 1bc61ba05..6dc270bfa 100644 --- a/plugins/bitdefender/server/certs.go +++ b/plugins/bitdefender/server/certs.go @@ -2,6 +2,7 @@ package server import ( "crypto/tls" + "fmt" "os" "time" @@ -30,24 +31,18 @@ func loadCerts() (tls.Certificate, error) { certsFolder, err := utils.MkdirJoin(certsFolderConfig) if err != nil { - return tls.Certificate{}, catcher.Error("cannot create certificates directory", err, map[string]any{ - "certsFolder": certsFolderConfig, - }) + return tls.Certificate{}, fmt.Errorf("cannot create certificates directory: %v", err) } certPath := certsFolder.FileJoin("utm.crt") keyPath := certsFolder.FileJoin("utm.key") if _, err := os.Stat(certPath); os.IsNotExist(err) { - return tls.Certificate{}, catcher.Error("certificate file does not exist", err, map[string]any{ - "certPath": certPath, - }) + return tls.Certificate{}, fmt.Errorf("certificate file does not exist: %s", certPath) } if _, err := os.Stat(keyPath); os.IsNotExist(err) { - return tls.Certificate{}, catcher.Error("key file does not exist", err, map[string]any{ - "keyPath": keyPath, - }) + return tls.Certificate{}, fmt.Errorf("key file does not exist: %s", keyPath) } return tls.LoadX509KeyPair(certPath, keyPath) diff --git a/plugins/bitdefender/server/server.go b/plugins/bitdefender/server/server.go index 858d84f7f..cfcc5ea07 100644 --- a/plugins/bitdefender/server/server.go +++ b/plugins/bitdefender/server/server.go @@ -25,13 +25,14 @@ func GetLogs() http.HandlerFunc { if conf.ModuleActive { if r.Header.Get("authorization") == "" { - _ = catcher.Error("missing authorization header", nil, map[string]any{ - "status": http.StatusUnauthorized, - "endpoint": r.URL.Path, - "method": r.Method, - "remoteAddr": r.RemoteAddr, - }) - http.Error(w, "Missing Authorization Header", http.StatusUnauthorized) + message := "401 Missing Authorization Header" + _ = catcher.Error("missing authorization header", nil, map[string]any{}) + j, _ := json.Marshal(message) + w.WriteHeader(http.StatusUnauthorized) + _, err := w.Write(j) + if err != nil { + _ = catcher.Error("cannot write response", err, nil) + } return } @@ -43,46 +44,35 @@ func GetLogs() http.HandlerFunc { } } if !isAuth { - _ = catcher.Error("invalid authentication credentials", nil, map[string]any{ - "status": http.StatusUnauthorized, - "endpoint": r.URL.Path, - "method": r.Method, - "remoteAddr": r.RemoteAddr, - }) - http.Error(w, "Invalid Authentication Credentials", http.StatusUnauthorized) + message := "401 Invalid Authentication Credentials" + _ = catcher.Error("invalid authentication credentials", nil, map[string]any{}) + j, _ := json.Marshal(message) + w.WriteHeader(http.StatusUnauthorized) + _, err := w.Write(j) + if err != nil { + _ = catcher.Error("cannot write response", err, nil) + } return } var newBody schema.BodyEvents err := json.NewDecoder(r.Body).Decode(&newBody) if err != nil { - _ = catcher.Error("error decoding request body", err, map[string]any{ - "status": http.StatusBadRequest, - "endpoint": r.URL.Path, - "method": r.Method, - "remoteAddr": r.RemoteAddr, - }) - http.Error(w, "Invalid request body", http.StatusBadRequest) + _ = catcher.Error("error decoding body", err, map[string]any{}) return } events := newBody.Events CreateMessage(conf, events) + j, _ := json.Marshal("HTTP 200 OK") w.WriteHeader(http.StatusOK) - if _, err := w.Write([]byte("OK")); err != nil { - _ = catcher.Error("cannot write response", err, map[string]any{ - "status": http.StatusOK, - }) + _, err = w.Write(j) + if err != nil { + _ = catcher.Error("cannot write response", err, nil) } } else { - _ = catcher.Error("bitdefender module disabled", nil, map[string]any{ - "status": http.StatusServiceUnavailable, - "endpoint": r.URL.Path, - "method": r.Method, - "remoteAddr": r.RemoteAddr, - }) - http.Error(w, "Service Unavailable", http.StatusServiceUnavailable) + _ = catcher.Error("bitdefender module disabled", nil, map[string]any{}) } } } @@ -91,21 +81,8 @@ func StartServer() { r := mux.NewRouter().StrictSlash(false) r.HandleFunc("/api", GetLogs()).Methods("POST") r.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) { - catcher.Info("health check requested", map[string]any{ - "endpoint": r.URL.Path, - "method": r.Method, - "remoteAddr": r.RemoteAddr, - "userAgent": r.UserAgent(), - }) - w.WriteHeader(http.StatusOK) - if _, err := w.Write([]byte("Server is up and running")); err != nil { - _ = catcher.Error("cannot write health check response", err, map[string]any{ - "status": http.StatusOK, - "endpoint": r.URL.Path, - "remoteAddr": r.RemoteAddr, - }) - } + _, _ = w.Write([]byte("Server is up and running")) }).Methods("GET") loadedCerts, err := loadCerts() diff --git a/plugins/config/main.go b/plugins/config/main.go index 9029cf116..7d677fa5e 100644 --- a/plugins/config/main.go +++ b/plugins/config/main.go @@ -388,7 +388,7 @@ func connect() (*sql.DB, error) { func getPatterns(db *sql.DB) (map[string]string, error) { rows, err := db.Query("SELECT pattern_id, pattern_definition FROM utm_regex_pattern") if err != nil { - return nil, catcher.Error("failed to get patterns", err, map[string]any{}) + return nil, fmt.Errorf("failed to get patterns: %v", err) } defer func() { _ = rows.Close() }() @@ -401,7 +401,7 @@ func getPatterns(db *sql.DB) (map[string]string, error) { err = rows.Scan(&name, &pattern) if err != nil { - return nil, catcher.Error("failed to scan row", err, map[string]any{}) + return nil, fmt.Errorf("failed to scan row: %v", err) } patterns[name] = pattern @@ -413,7 +413,7 @@ func getPatterns(db *sql.DB) (map[string]string, error) { func getFilters(db *sql.DB) ([]Filter, error) { rows, err := db.Query("SELECT id, filter_name, logstash_filter FROM utm_logstash_filter WHERE is_active = true") if err != nil { - return nil, catcher.Error("failed to get filters", err, map[string]any{}) + return nil, fmt.Errorf("failed to get filters: %v", err) } defer func() { _ = rows.Close() }() @@ -429,7 +429,7 @@ func getFilters(db *sql.DB) ([]Filter, error) { err = rows.Scan(&id, &name, &body) if err != nil { - return nil, catcher.Error("failed to scan row", err, map[string]any{}) + return nil, fmt.Errorf("failed to scan row: %v", err) } filter := Filter{} @@ -447,7 +447,7 @@ func getFilters(db *sql.DB) ([]Filter, error) { func getAssets(db *sql.DB) ([]Asset, error) { rows, err := db.Query("SELECT id,asset_name,asset_hostname_list_def,asset_ip_list_def,asset_confidentiality,asset_integrity,asset_availability,last_update FROM utm_tenant_config") if err != nil { - return nil, catcher.Error("failed to get assets", err, map[string]any{}) + return nil, fmt.Errorf("failed to get assets: %v", err) } defer func() { _ = rows.Close() }() @@ -469,7 +469,7 @@ func getAssets(db *sql.DB) ([]Asset, error) { err = rows.Scan(&id, &name, &hostnames, &ips, &confidentiality, &integrity, &availability, &lastUpdate) if err != nil { - return nil, catcher.Error("failed to scan row", err, map[string]any{}) + return nil, fmt.Errorf("failed to scan row: %v", err) } asset := Asset{} @@ -485,7 +485,7 @@ func getAssets(db *sql.DB) ([]Asset, error) { func getRules(db *sql.DB) ([]Rule, error) { rows, err := db.Query("SELECT id,rule_name,rule_confidentiality,rule_integrity,rule_availability,rule_category,rule_technique,rule_description,rule_references_def,rule_definition_def,rule_adversary,rule_deduplicate_by_def,rule_after_events_def FROM utm_correlation_rules WHERE rule_active = true") if err != nil { - return nil, catcher.Error("failed to get rules", err, map[string]any{}) + return nil, fmt.Errorf("failed to get rules: %v", err) } defer func() { _ = rows.Close() }() @@ -512,7 +512,7 @@ func getRules(db *sql.DB) ([]Rule, error) { err = rows.Scan(&id, &ruleName, &confidentiality, &integrity, &availability, &category, &technique, &description, &references, &where, &adversary, &deduplicate, &after) if err != nil { - return nil, catcher.Error("failed to scan row", err, map[string]any{}) + return nil, fmt.Errorf("failed to scan row: %v", err) } rule := Rule{} @@ -533,7 +533,7 @@ func getRules(db *sql.DB) ([]Rule, error) { func getRuleDataTypes(db *sql.DB, ruleId int64) ([]string, error) { rows, err := db.Query("SELECT data_type_id FROM utm_group_rules_data_type WHERE rule_id = $1", ruleId) if err != nil { - return nil, catcher.Error("failed to get data types", err, map[string]any{}) + return nil, fmt.Errorf("failed to get data types: %v", err) } defer func() { _ = rows.Close() }() @@ -548,14 +548,14 @@ func getRuleDataTypes(db *sql.DB, ruleId int64) ([]string, error) { err = rows.Scan(&dataTypeId) if err != nil { - return nil, catcher.Error("failed to scan row", err, map[string]any{}) + return nil, fmt.Errorf("failed to scan row: %v", err) } row := db.QueryRow("SELECT data_type FROM utm_data_types WHERE id = $1", dataTypeId) err := row.Scan(&dataType) if err != nil { - return nil, catcher.Error("failed to scan row", err, map[string]any{}) + return nil, fmt.Errorf("failed to scan row: %v", err) } dataTypes = append(dataTypes, utils.CastString(dataType)) @@ -580,7 +580,7 @@ func listFiles(folder string) ([]string, error) { }) if err != nil { - return nil, catcher.Error("failed to list files", err, nil) + return nil, fmt.Errorf("failed to list files: %v", err) } return files, nil @@ -610,7 +610,7 @@ func cleanUpFilters(filters []Filter) error { if !keep { err := os.Remove(file) if err != nil { - return catcher.Error("failed to remove file", err, map[string]any{}) + return fmt.Errorf("failed to remove file: %v", err) } } } @@ -642,7 +642,7 @@ func cleanUpRules(rules []Rule) error { if !keep { err := os.Remove(file) if err != nil { - return catcher.Error("failed to remove file", err, map[string]any{}) + return fmt.Errorf("failed to remove file: %v", err) } } } @@ -659,17 +659,17 @@ func writeFilters(filters []Filter) error { file, err := os.Create(filtersFolder.FileJoin(fmt.Sprintf("%d.yaml", filter.Id))) if err != nil { - return catcher.Error("failed to create file", err, map[string]any{}) + return fmt.Errorf("failed to create file: %v", err) } _, err = file.WriteString(filter.Filter) if err != nil { - return catcher.Error("failed to write to file", err, map[string]any{}) + return fmt.Errorf("failed to write to file: %v", err) } err = file.Close() if err != nil { - return catcher.Error("failed to close file", err, map[string]any{}) + return fmt.Errorf("failed to close file: %v", err) } } @@ -684,7 +684,7 @@ func writeTenant(tenant Tenant) error { file, err := os.Create(pipelineFolder.FileJoin("tenants.yaml")) if err != nil { - return catcher.Error("failed to create file", err, map[string]any{}) + return fmt.Errorf("failed to create file: %v", err) } sdkTenant := plugins.Tenant(tenant) @@ -695,17 +695,17 @@ func writeTenant(tenant Tenant) error { bTenants, err := k8syaml.Marshal(tenants) if err != nil { - return catcher.Error("failed to marshal tenant", err, map[string]any{}) + return fmt.Errorf("failed to marshal tenant: %v", err) } _, err = file.Write(bTenants) if err != nil { - return catcher.Error("failed to write to file", err, map[string]any{}) + return fmt.Errorf("failed to write to file: %v", err) } err = file.Close() if err != nil { - return catcher.Error("failed to close file", err, map[string]any{}) + return fmt.Errorf("failed to close file: %v", err) } return nil @@ -720,22 +720,22 @@ func writeRules(rules []Rule) error { file, err := os.Create(filePath.FileJoin(fmt.Sprintf("%d.yaml", rule.Id))) if err != nil { - return catcher.Error("failed to create file", err, map[string]any{}) + return fmt.Errorf("failed to create file: %v", err) } bRule, err := yaml.Marshal([]Rule{rule}) if err != nil { - return catcher.Error("failed to marshal rule", err, map[string]any{}) + return fmt.Errorf("failed to marshal rule: %v", err) } _, err = file.Write(bRule) if err != nil { - return catcher.Error("failed to write to file", err, map[string]any{}) + return fmt.Errorf("failed to write to file: %v", err) } err = file.Close() if err != nil { - return catcher.Error("failed to close file", err, map[string]any{}) + return fmt.Errorf("failed to close file: %v", err) } } @@ -749,7 +749,7 @@ func writePatterns(patterns map[string]string) error { } file, err := os.Create(filePath.FileJoin("patterns.yaml")) if err != nil { - return catcher.Error("failed to create file", err, map[string]any{}) + return fmt.Errorf("failed to create file: %v", err) } config := plugins.Config{ @@ -758,17 +758,17 @@ func writePatterns(patterns map[string]string) error { bPatterns, err := k8syaml.Marshal(config) if err != nil { - return catcher.Error("failed to marshal patterns", err, map[string]any{}) + return fmt.Errorf("failed to marshal patterns: %v", err) } _, err = file.Write(bPatterns) if err != nil { - return catcher.Error("failed to write to file", err, map[string]any{}) + return fmt.Errorf("failed to write to file: %v", err) } err = file.Close() if err != nil { - return catcher.Error("failed to close file", err, map[string]any{}) + return fmt.Errorf("failed to close file: %v", err) } return nil diff --git a/plugins/gcp/config/config.go b/plugins/gcp/config/config.go index 5217999ee..d7b9fa2fd 100644 --- a/plugins/gcp/config/config.go +++ b/plugins/gcp/config/config.go @@ -2,6 +2,8 @@ package config import ( "context" + "fmt" + "log" "strings" sync "sync" "time" @@ -50,7 +52,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) + fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") time.Sleep(reconnectDelay) continue } @@ -131,7 +133,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - catcher.Info("Received configuration update", map[string]any{"config": message.Config}) + log.Printf("Received configuration update: %v", message.Config) cnf = message.Config } } diff --git a/plugins/inputs/main.go b/plugins/inputs/main.go index 9c0080f0f..af9b38988 100644 --- a/plugins/inputs/main.go +++ b/plugins/inputs/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "os" "runtime" "time" @@ -74,18 +75,18 @@ func loadCerts() (string, string, error) { certsFolder, err := utils.MkdirJoin(certsFolderPath) if err != nil { - return "", "", catcher.Error("cannot create certificates directory", err, map[string]any{"path": certsFolderPath}) + return "", "", fmt.Errorf("cannot create certificates directory: %v", err) } certPath := certsFolder.FileJoin(utmCertFileName) keyPath := certsFolder.FileJoin(utmCertFileKey) if _, err := os.Stat(certPath); os.IsNotExist(err) { - return "", "", catcher.Error("certificate file does not exist", err, map[string]any{"path": certPath}) + return "", "", fmt.Errorf("certificate file does not exist: %s", certPath) } if _, err := os.Stat(keyPath); os.IsNotExist(err) { - return "", "", catcher.Error("key file does not exist", err, map[string]any{"path": keyPath}) + return "", "", fmt.Errorf("key file does not exist: %s", keyPath) } return certPath, keyPath, nil diff --git a/plugins/inputs/middlewares.go b/plugins/inputs/middlewares.go index b869c4436..0142e1961 100644 --- a/plugins/inputs/middlewares.go +++ b/plugins/inputs/middlewares.go @@ -7,10 +7,6 @@ import ( "crypto/sha256" "errors" "fmt" - "io" - "strconv" - "strings" - "github.com/gin-gonic/gin" "github.com/threatwinds/go-sdk/catcher" "github.com/threatwinds/go-sdk/plugins" @@ -18,6 +14,9 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + "io" + "strconv" + "strings" ) type Middlewares struct { diff --git a/plugins/modules-config/config/config.go b/plugins/modules-config/config/config.go index de8e00038..28780d84b 100644 --- a/plugins/modules-config/config/config.go +++ b/plugins/modules-config/config/config.go @@ -129,7 +129,7 @@ func (s *ConfigServer) NotifyUpdate(moduleName string, section *ConfigurationSec case "SOPHOS": pluginType = PluginType_SOPHOS default: - _ = catcher.Error("unknown module name", nil, map[string]any{"module": moduleName}) + _ = catcher.Error("unknown module name", fmt.Errorf("module: %s", moduleName), nil) return } @@ -179,10 +179,7 @@ func (s *ConfigServer) SyncConfigs(backend string, internalKey string) { break } - _ = catcher.Error("error fetching configuration", err, map[string]any{ - "module": name, - "status": status, - }) + fmt.Printf("Error fetching configuration for %s: %v, status code: %d. Retrying...\n", name, err, status) time.Sleep(5 * time.Second) } } diff --git a/plugins/modules-config/handlers.go b/plugins/modules-config/handlers.go index a7925de26..54dd5604e 100644 --- a/plugins/modules-config/handlers.go +++ b/plugins/modules-config/handlers.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "net" "net/http" @@ -70,7 +71,7 @@ func UpdateModuleConfig(c *gin.Context) { if len(body) != 0 { config.GetConfigServer().NotifyUpdate(moduleName, &body[0]) } else { - catcher.Info("Received empty configuration body, no updates made", nil) + fmt.Println("Received empty configuration body, no updates made") } c.JSON(http.StatusOK, gin.H{"status": "Module configuration updated successfully"}) diff --git a/plugins/modules-config/validations/aws.go b/plugins/modules-config/validations/aws.go index 8d4f3c351..4ddb008bb 100644 --- a/plugins/modules-config/validations/aws.go +++ b/plugins/modules-config/validations/aws.go @@ -2,11 +2,11 @@ package validations import ( "context" + "fmt" awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/sts" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -14,7 +14,7 @@ func ValidateAwsConfig(config *config.ModuleGroup) error { var regionName, accessKey, secretAccessKey string if config == nil { - return catcher.Error("AWS_IAM_USER configuration is nil", nil, nil) + return fmt.Errorf("AWS_IAM_USER configuration is nil") } for _, cnf := range config.ModuleGroupConfigurations { @@ -29,13 +29,13 @@ func ValidateAwsConfig(config *config.ModuleGroup) error { } if regionName == "" { - return catcher.Error("Default Region is required in AWS_IAM_USER configuration", nil, nil) + return fmt.Errorf("Default Region is required in AWS_IAM_USER configuration") } if accessKey == "" { - return catcher.Error("Access Key is required in AWS_IAM_USER configuration", nil, nil) + return fmt.Errorf("Access Key is required in AWS_IAM_USER configuration") } if secretAccessKey == "" { - return catcher.Error("Secret Key is required in AWS_IAM_USER configuration", nil, nil) + return fmt.Errorf("Secret Key is required in AWS_IAM_USER configuration") } cfg, err := awsconfig.LoadDefaultConfig(context.TODO(), @@ -47,14 +47,14 @@ func ValidateAwsConfig(config *config.ModuleGroup) error { )), ) if err != nil { - return catcher.Error("failed to load AWS configuration", err, nil) + return fmt.Errorf("failed to load AWS configuration: %w", err) } stsClient := sts.NewFromConfig(cfg) _, err = stsClient.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{}) if err != nil { - return catcher.Error("AWS credentials validation failed", err, nil) + return fmt.Errorf("AWS credentials validation failed: %w", err) } return nil diff --git a/plugins/modules-config/validations/azure.go b/plugins/modules-config/validations/azure.go index 17b5f9c4e..120d622d4 100644 --- a/plugins/modules-config/validations/azure.go +++ b/plugins/modules-config/validations/azure.go @@ -2,12 +2,12 @@ package validations import ( "context" + "fmt" "strings" "time" "github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs/v2" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -15,7 +15,7 @@ func ValidateAzureConfig(config *config.ModuleGroup) error { var eventHubConnection, consumerGroup, storageContainer, storageConnection string if config == nil { - return catcher.Error("AZURE configuration is nil", nil, nil) + return fmt.Errorf("AZURE configuration is nil") } for _, cnf := range config.ModuleGroupConfigurations { @@ -32,28 +32,28 @@ func ValidateAzureConfig(config *config.ModuleGroup) error { } if eventHubConnection == "" { - return catcher.Error("eventHubConnection is required in AZURE configuration", nil, nil) + return fmt.Errorf("eventHubConnection is required in AZURE configuration") } if consumerGroup == "" { - return catcher.Error("consumerGroup is required in AZURE configuration", nil, nil) + return fmt.Errorf("consumerGroup is required in AZURE configuration") } if storageContainer == "" { - return catcher.Error("storageContainer is required in AZURE configuration", nil, nil) + return fmt.Errorf("storageContainer is required in AZURE configuration") } if storageConnection == "" { - return catcher.Error("storageConnection is required in AZURE configuration", nil, nil) + return fmt.Errorf("storageConnection is required in AZURE configuration") } eventHubParts := strings.Split(eventHubConnection, ";EntityPath=") if len(eventHubParts) != 2 { - return catcher.Error("invalid Event Hub connection string format: missing EntityPath", nil, nil) + return fmt.Errorf("invalid Event Hub connection string format: missing EntityPath") } eventHubConnectionBase := eventHubParts[0] eventHubName := eventHubParts[1] consumerClient, err := azeventhubs.NewConsumerClientFromConnectionString(eventHubConnectionBase, eventHubName, consumerGroup, nil) if err != nil { - return catcher.Error("failed to create Event Hub consumer client", err, nil) + return fmt.Errorf("failed to create Event Hub consumer client: %w", err) } defer consumerClient.Close(context.Background()) @@ -62,12 +62,12 @@ func ValidateAzureConfig(config *config.ModuleGroup) error { _, err = consumerClient.GetEventHubProperties(ctx, nil) if err != nil { - return catcher.Error("Event Hub connection validation failed", err, nil) + return fmt.Errorf("Event Hub connection validation failed: %w", err) } blobClient, err := azblob.NewClientFromConnectionString(storageConnection, nil) if err != nil { - return catcher.Error("failed to create Storage client", err, nil) + return fmt.Errorf("failed to create Storage client: %w", err) } containerClient := blobClient.ServiceClient().NewContainerClient(storageContainer) @@ -76,7 +76,7 @@ func ValidateAzureConfig(config *config.ModuleGroup) error { _, err = containerClient.GetProperties(ctx2, nil) if err != nil { - return catcher.Error("Storage container validation failed", err, nil) + return fmt.Errorf("Storage container validation failed: %w", err) } return nil diff --git a/plugins/modules-config/validations/bdgz.go b/plugins/modules-config/validations/bdgz.go index ca8a0e449..9b28e54b5 100644 --- a/plugins/modules-config/validations/bdgz.go +++ b/plugins/modules-config/validations/bdgz.go @@ -4,12 +4,12 @@ import ( "bytes" "encoding/base64" "encoding/json" + "fmt" "io" "net/http" "strings" "time" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -28,7 +28,7 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { var connectionKey, accessUrl, masterIp, companiesIDs string if config == nil { - return catcher.Error("Bitdefender configuration is nil", nil, nil) + return fmt.Errorf("Bitdefender configuration is nil") } for _, cnf := range config.ModuleGroupConfigurations { @@ -45,20 +45,20 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { } if connectionKey == "" { - return catcher.Error("Connection Key is required in Bitdefender configuration", nil, nil) + return fmt.Errorf("Connection Key is required in Bitdefender configuration") } if accessUrl == "" { - return catcher.Error("Access URL is required in Bitdefender configuration", nil, nil) + return fmt.Errorf("Access URL is required in Bitdefender configuration") } if masterIp == "" { - return catcher.Error("Master IP is required in Bitdefender configuration", nil, nil) + return fmt.Errorf("Master IP is required in Bitdefender configuration") } if companiesIDs == "" { - return catcher.Error("Companies IDs is required in Bitdefender configuration", nil, nil) + return fmt.Errorf("Companies IDs is required in Bitdefender configuration") } if !strings.HasPrefix(accessUrl, "http://") && !strings.HasPrefix(accessUrl, "https://") { - return catcher.Error("Access URL must start with http:// or https://", nil, nil) + return fmt.Errorf("Access URL must start with http:// or https://") } authCode := generateAuthCode(connectionKey) @@ -67,17 +67,17 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { JsonRPC: "2.0", Method: "getPushEventSettings", ID: "1", - Params: map[string]any{}, + Params: map[string]interface{}{}, } body, err := json.Marshal(testRequest) if err != nil { - return catcher.Error("failed to create test request", err, nil) + return fmt.Errorf("failed to create test request: %w", err) } req, err := http.NewRequest("POST", accessUrl+endpointPush, bytes.NewBuffer(body)) if err != nil { - return catcher.Error("failed to create HTTP request", err, nil) + return fmt.Errorf("failed to create HTTP request: %w", err) } req.Header.Add("Content-Type", "application/json") @@ -89,20 +89,25 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { resp, err := client.Do(req) if err != nil { - return catcher.Error("Bitdefender API request failed", err, nil) + return fmt.Errorf("Bitdefender API request failed: %w", err) } defer resp.Body.Close() // Read response body bodyBytes, _ := io.ReadAll(resp.Body) - var respBody map[string]any + // Log response for debugging (you can remove this later) + if len(bodyBytes) > 0 { + fmt.Printf("Response body: %s\n", string(bodyBytes)) + } + + var respBody map[string]interface{} if err := json.Unmarshal(bodyBytes, &respBody); err == nil { if errorField, ok := respBody["error"]; ok { - if errorMap, ok := errorField.(map[string]any); ok { + if errorMap, ok := errorField.(map[string]interface{}); ok { if code, ok := errorMap["code"].(float64); ok { details := "" - if dataMap, ok := errorMap["data"].(map[string]any); ok { + if dataMap, ok := errorMap["data"].(map[string]interface{}); ok { if d, ok := dataMap["details"].(string); ok { details = d } @@ -113,7 +118,7 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { } if code == -32001 || code == -32002 { - return catcher.Error("Bitdefender authentication failed: invalid Connection Key", nil, nil) + return fmt.Errorf("Bitdefender authentication failed: invalid Connection Key") } if message, ok := errorMap["message"].(string); ok { @@ -122,40 +127,40 @@ func ValidateBdgzConfig(config *config.ModuleGroup) error { strings.Contains(combinedError, "authentication") || strings.Contains(combinedError, "invalid api key") || strings.Contains(combinedError, "access denied") { - return catcher.Error("Bitdefender authentication failed", nil, map[string]any{"error": message}) + return fmt.Errorf("Bitdefender authentication failed: %s", message) } } } if message, ok := errorMap["message"].(string); ok { - return catcher.Error("Bitdefender API error", nil, map[string]any{"error": message}) + return fmt.Errorf("Bitdefender API error: %s", message) } } - return catcher.Error("Bitdefender API error", nil, map[string]any{"error": errorField}) + return fmt.Errorf("Bitdefender API error: %v", errorField) } if _, hasResult := respBody["result"]; !hasResult && resp.StatusCode == 200 { if _, hasId := respBody["id"]; !hasId { - return catcher.Error("Invalid response format from Bitdefender API", nil, nil) + return fmt.Errorf("Invalid response format from Bitdefender API") } } } else if resp.StatusCode == 200 { - return catcher.Error("Invalid JSON response from Bitdefender API", nil, nil) + return fmt.Errorf("Invalid JSON response from Bitdefender API") } if resp.StatusCode == 401 || resp.StatusCode == 403 { - return catcher.Error("Bitdefender authentication failed: invalid Connection Key", nil, nil) + return fmt.Errorf("Bitdefender authentication failed: invalid Connection Key") } if resp.StatusCode == 404 { - return catcher.Error("Bitdefender API endpoint not found. Please check the Access URL", nil, nil) + return fmt.Errorf("Bitdefender API endpoint not found. Please check the Access URL") } if resp.StatusCode >= 400 { - return catcher.Error("Bitdefender API returned error status", nil, map[string]any{"status_code": resp.StatusCode}) + return fmt.Errorf("Bitdefender API returned error status: %d", resp.StatusCode) } if resp.StatusCode != 200 { - return catcher.Error("Unexpected response status", nil, map[string]any{"status_code": resp.StatusCode}) + return fmt.Errorf("Unexpected response status: %d", resp.StatusCode) } return nil diff --git a/plugins/modules-config/validations/gcp.go b/plugins/modules-config/validations/gcp.go index ba8f4c2c4..4890304e1 100644 --- a/plugins/modules-config/validations/gcp.go +++ b/plugins/modules-config/validations/gcp.go @@ -6,7 +6,6 @@ import ( "time" "cloud.google.com/go/pubsub" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" "google.golang.org/api/option" ) @@ -15,7 +14,7 @@ func ValidateGcpConfig(config *config.ModuleGroup) error { var jsonKey, projectID, subscriptionID string if config == nil { - return catcher.Error("GCP configuration is nil", nil, nil) + return fmt.Errorf("GCP configuration is nil") } for _, cnf := range config.ModuleGroupConfigurations { @@ -30,13 +29,13 @@ func ValidateGcpConfig(config *config.ModuleGroup) error { } if jsonKey == "" { - return catcher.Error("JSON Key is required in GCP configuration", nil, nil) + return fmt.Errorf("JSON Key is required in GCP configuration") } if projectID == "" { - return catcher.Error("Project ID is required in GCP configuration", nil, nil) + return fmt.Errorf("Project ID is required in GCP configuration") } if subscriptionID == "" { - return catcher.Error("Subscription ID is required in GCP configuration", nil, nil) + return fmt.Errorf("Subscription ID is required in GCP configuration") } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) diff --git a/plugins/modules-config/validations/o365.go b/plugins/modules-config/validations/o365.go index 34f4edc89..d8e092a9c 100644 --- a/plugins/modules-config/validations/o365.go +++ b/plugins/modules-config/validations/o365.go @@ -9,7 +9,6 @@ import ( "net/url" "time" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -33,7 +32,7 @@ func ValidateO365Config(config *config.ModuleGroup) error { var clientId, clientSecret, tenantId string if config == nil { - return catcher.Error("O365 configuration is nil", nil, nil) + return fmt.Errorf("O365 configuration is nil") } for _, cnf := range config.ModuleGroupConfigurations { @@ -48,13 +47,13 @@ func ValidateO365Config(config *config.ModuleGroup) error { } if clientId == "" { - return catcher.Error("Client ID is required in O365 configuration", nil, nil) + return fmt.Errorf("Client ID is required in O365 configuration") } if clientSecret == "" { - return catcher.Error("Client Secret is required in O365 configuration", nil, nil) + return fmt.Errorf("Client Secret is required in O365 configuration") } if tenantId == "" { - return catcher.Error("Tenant ID is required in O365 configuration", nil, nil) + return fmt.Errorf("Tenant ID is required in O365 configuration") } // Validate credentials by attempting to get an access token @@ -72,36 +71,33 @@ func ValidateO365Config(config *config.ModuleGroup) error { req, err := http.NewRequest(http.MethodPost, requestUrl, bytes.NewBufferString(data.Encode())) if err != nil { - return catcher.Error("failed to create request", err, nil) + return fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") resp, err := client.Do(req) if err != nil { - return catcher.Error("O365 authentication request failed", err, nil) + return fmt.Errorf("O365 authentication request failed: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - return catcher.Error("failed to read response", err, nil) + return fmt.Errorf("failed to read response: %w", err) } var loginResp MicrosoftLoginResponse if err := json.Unmarshal(body, &loginResp); err != nil { - return catcher.Error("failed to parse response", err, nil) + return fmt.Errorf("failed to parse response: %w", err) } if loginResp.Error != "" { - return catcher.Error("O365 authentication failed", nil, map[string]any{ - "error": loginResp.Error, - "error_description": loginResp.ErrorDesc, - }) + return fmt.Errorf("O365 authentication failed: %s - %s", loginResp.Error, loginResp.ErrorDesc) } if loginResp.AccessToken == "" { - return catcher.Error("O365 authentication failed: no access token received", nil, nil) + return fmt.Errorf("O365 authentication failed: no access token received") } return nil diff --git a/plugins/modules-config/validations/socai.go b/plugins/modules-config/validations/socai.go index c9bdfb61f..352640dcc 100644 --- a/plugins/modules-config/validations/socai.go +++ b/plugins/modules-config/validations/socai.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" - "github.com/threatwinds/go-sdk/catcher" "github.com/threatwinds/go-sdk/utils" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -13,7 +12,7 @@ func ValidateSOCAIConfig(config *config.ModuleGroup) error { var apiKey, provider string if config == nil { - return catcher.Error("SOC_AI configuration is nil", nil, nil) + return fmt.Errorf("SOC_AI configuration is nil") } for _, cnf := range config.ModuleGroupConfigurations { @@ -26,10 +25,10 @@ func ValidateSOCAIConfig(config *config.ModuleGroup) error { } if apiKey == "" { - return catcher.Error("API Key is required in SOC_AI configuration", nil, nil) + return fmt.Errorf("API Key is required in SOC_AI configuration") } if provider == "" { - return catcher.Error("Provider is required in SOC_AI configuration", nil, nil) + return fmt.Errorf("Provider is required in SOC_AI configuration") } else if provider != "openai" { return nil } @@ -43,26 +42,13 @@ func ValidateSOCAIConfig(config *config.ModuleGroup) error { response, status, err := utils.DoReq[map[string]any](url, nil, "GET", headers) if err != nil || status != http.StatusOK { if status == http.StatusRequestTimeout { - return catcher.Error("SOC_AI connection timed out", err, map[string]any{ - "status_code": status, - "response": response, - }) + return fmt.Errorf("SOC_AI connection timed out") } if status == http.StatusUnauthorized { - return catcher.Error("SOC_AI API Key is invalid", err, map[string]any{ - "status_code": status, - "response": response, - }) + return fmt.Errorf("SOC_AI API Key is invalid") } - catcher.Info("Error validating SOC_AI connection", map[string]any{ - "error": err, - "status_code": status, - "response": response, - }) - return catcher.Error("SOC_AI API Key is invalid", err, map[string]any{ - "status_code": status, - "response": response, - }) + fmt.Printf("Error validating SOC_AI connection: %v, status code: %d, response: %v\n", err, status, response) + return fmt.Errorf("SOC_AI API Key is invalid") } return nil diff --git a/plugins/modules-config/validations/sophos.go b/plugins/modules-config/validations/sophos.go index e35b928e4..01c778420 100644 --- a/plugins/modules-config/validations/sophos.go +++ b/plugins/modules-config/validations/sophos.go @@ -3,12 +3,12 @@ package validations import ( "bytes" "encoding/json" + "fmt" "io" "net/http" "net/url" "time" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -21,7 +21,7 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { var clientID, clientSecret string if config == nil { - return catcher.Error("Sophos configuration is nil", nil, nil) + return fmt.Errorf("Sophos configuration is nil") } for _, cnf := range config.ModuleGroupConfigurations { @@ -34,10 +34,10 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { } if clientID == "" { - return catcher.Error("Client ID is required in Sophos configuration", nil, nil) + return fmt.Errorf("Client ID is required in Sophos configuration") } if clientSecret == "" { - return catcher.Error("Client Secret is required in Sophos configuration", nil, nil) + return fmt.Errorf("Client Secret is required in Sophos configuration") } data := url.Values{} @@ -48,7 +48,7 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { req, err := http.NewRequest(http.MethodPost, sophosAuthURL, bytes.NewBufferString(data.Encode())) if err != nil { - return catcher.Error("failed to create request", err, nil) + return fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") @@ -59,18 +59,18 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { resp, err := client.Do(req) if err != nil { - return catcher.Error("Sophos authentication request failed", err, nil) + return fmt.Errorf("Sophos authentication request failed: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - return catcher.Error("failed to read response", err, nil) + return fmt.Errorf("failed to read response: %w", err) } var response map[string]interface{} if err := json.Unmarshal(body, &response); err != nil { - return catcher.Error("failed to parse response", err, nil) + return fmt.Errorf("failed to parse response: %w", err) } if resp.StatusCode != http.StatusOK { @@ -80,29 +80,21 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { message = msg } if errorCode == "oauth.invalid_client_secret" { - return catcher.Error("Sophos authentication failed: Invalid Client Secret", nil, nil) + return fmt.Errorf("Sophos authentication failed: Invalid Client Secret") } if errorCode == "oauth.invalid_client_id" { - return catcher.Error("Sophos authentication failed: Invalid Client ID", nil, nil) + return fmt.Errorf("Sophos authentication failed: Invalid Client ID") } - return catcher.Error("Sophos authentication failed", nil, map[string]any{ - "error_code": errorCode, - "message": message, - }) + return fmt.Errorf("Sophos authentication failed: %v - %s", errorCode, message) } if errorCode, hasError := response["error"]; hasError { errorDesc := "" if desc, ok := response["error_description"].(string); ok { errorDesc = desc } - return catcher.Error("Sophos authentication failed", nil, map[string]any{ - "error_code": errorCode, - "message": errorDesc, - }) + return fmt.Errorf("Sophos authentication failed: %v - %s", errorCode, errorDesc) } - return catcher.Error("Sophos authentication failed", nil, map[string]any{ - "status_code": resp.StatusCode, - }) + return fmt.Errorf("Sophos authentication failed with status %d", resp.StatusCode) } accessToken, ok := response["access_token"].(string) @@ -111,9 +103,7 @@ func ValidateSophosConfig(config *config.ModuleGroup) error { for k := range response { fields = append(fields, k) } - return catcher.Error("Sophos authentication failed", nil, map[string]any{ - "response_fields": fields, - }) + return fmt.Errorf("Sophos authentication failed: no access token received. Response fields: %v", fields) } return nil diff --git a/plugins/modules-config/validations/validate.go b/plugins/modules-config/validations/validate.go index aa3c71d6b..ad5f85b92 100644 --- a/plugins/modules-config/validations/validate.go +++ b/plugins/modules-config/validations/validate.go @@ -3,7 +3,6 @@ package validations import ( "fmt" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/modules-config/config" ) @@ -38,7 +37,7 @@ func ValidateModuleConfig(moduleName string, config *config.ModuleGroup) error { // return fmt.Errorf("%v", err) // } default: - return catcher.Error("unsupported module", nil, map[string]any{"module": moduleName}) + return fmt.Errorf("unsupported module: %s", moduleName) } return nil diff --git a/plugins/o365/config/config.go b/plugins/o365/config/config.go index 3b997bbac..18f266909 100644 --- a/plugins/o365/config/config.go +++ b/plugins/o365/config/config.go @@ -2,6 +2,8 @@ package config import ( "context" + "fmt" + "log" "strings" sync "sync" "time" @@ -50,7 +52,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) + fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") time.Sleep(reconnectDelay) continue } @@ -131,8 +133,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - catcher.Info("Received configuration update", map[string]any{"config": message.Config}) - mu.Lock() + log.Printf("Received configuration update: %v", message.Config) cnf = message.Config } } diff --git a/plugins/soc-ai/config/config.go b/plugins/soc-ai/config/config.go index dd8b06779..8b995b101 100644 --- a/plugins/soc-ai/config/config.go +++ b/plugins/soc-ai/config/config.go @@ -2,12 +2,15 @@ package config import ( "context" + "fmt" + "log" "strings" sync "sync" "time" "github.com/threatwinds/go-sdk/catcher" "github.com/threatwinds/go-sdk/plugins" + "github.com/utmstack/UTMStack/plugins/soc-ai/utils" "google.golang.org/grpc" codes "google.golang.org/grpc/codes" "google.golang.org/grpc/connectivity" @@ -67,7 +70,7 @@ func StartConfigurationSystem() { configMutex.Unlock() if config.Backend == "" || config.InternalKey == "" || config.Opensearch == "" || config.ModulesConfigHost == "" { - catcher.Info("Backend, Internal key, Opensearch or Modules Config Host is not set, skipping UTMStack plugin execution", nil) + fmt.Println("Backend, Internal key, Opensearch or Modules Config Host is not set, skipping UTMStack plugin execution") time.Sleep(reconnectDelay) continue } @@ -148,7 +151,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - catcher.Info("Received configuration update", map[string]any{"config": message.Config}) + log.Printf("Received configuration update: %v", message.Config) updateConfigFromGRPC(message.Config) } } @@ -164,7 +167,7 @@ func updateConfigFromGRPC(grpcConf *ConfigurationSection) { defer configMutex.Unlock() if grpcConf == nil { - catcher.Info("Received nil configuration from gRPC", nil) + utils.Logger.LogF(100, "Received nil configuration from gRPC") return } @@ -192,7 +195,7 @@ func updateConfigFromGRPC(grpcConf *ConfigurationSection) { case "utmstack.socai.custom.url": customURL = c.ConfValue default: - catcher.Info("Unknown configuration key", map[string]any{"key": c.ConfKey}) + utils.Logger.LogF(100, "Unknown configuration key: %s", c.ConfKey) } } diff --git a/plugins/soc-ai/correlation/correlation.go b/plugins/soc-ai/correlation/correlation.go index f9ebd47fa..1e9536bce 100644 --- a/plugins/soc-ai/correlation/correlation.go +++ b/plugins/soc-ai/correlation/correlation.go @@ -6,7 +6,6 @@ import ( "sort" "strings" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/elastic" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" @@ -15,7 +14,7 @@ import ( func GetCorrelationContext(alert schema.AlertFields) (string, error) { relatedAlerts, err := findRelatedAlerts(alert) if err != nil { - return "", catcher.Error("error finding related alerts", err, nil) + return "", fmt.Errorf("error finding related alerts: %v", err) } if len(relatedAlerts.RelatedAlerts) > 0 { @@ -31,12 +30,12 @@ func findRelatedAlerts(current schema.AlertFields) (schema.AlertCorrelation, err result, err := elastic.ElasticSearch(config.ALERT_INDEX_PATTERN, "name", current.Name) if err != nil { - return correlation, catcher.Error("error getting historical alerts", err, nil) + return correlation, fmt.Errorf("error getting historical alerts: %v", err) } var alerts []schema.AlertFields if err := json.Unmarshal(result, &alerts); err != nil { - return correlation, catcher.Error("error unmarshalling alerts", err, nil) + return correlation, fmt.Errorf("error unmarshalling alerts: %v", err) } for _, hist := range alerts { diff --git a/plugins/soc-ai/elastic.go b/plugins/soc-ai/elastic.go index 1f656a228..2b5f27837 100644 --- a/plugins/soc-ai/elastic.go +++ b/plugins/soc-ai/elastic.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "strings" "github.com/threatwinds/go-sdk/catcher" @@ -18,16 +19,14 @@ func processAlertToElastic(alert *schema.AlertFields) error { if strings.Contains(err.Error(), "index_not_found_exception") || strings.Contains(err.Error(), "no such index") { if createErr := elastic.CreateIndexIfNotExist(config.SOC_AI_INDEX); createErr != nil { - return catcher.Error("error updating alert in elastic", err, map[string]any{ - "failed_to_create_index": createErr, - }) + return fmt.Errorf("error updating alert in elastic: %v (failed to create index: %v)", err, createErr) } if retryErr := elastic.ElasticQuery(config.SOC_AI_INDEX, resp, "update"); retryErr != nil { - return catcher.Error("error updating alert in elastic after index creation", retryErr, nil) + return fmt.Errorf("error updating alert in elastic after index creation: %v", retryErr) } } else { - return catcher.Error("error updating alert in elastic", err, nil) + return fmt.Errorf("error updating alert in elastic: %v", err) } } diff --git a/plugins/soc-ai/elastic/alerts.go b/plugins/soc-ai/elastic/alerts.go index eed538add..d9343fef4 100644 --- a/plugins/soc-ai/elastic/alerts.go +++ b/plugins/soc-ai/elastic/alerts.go @@ -2,9 +2,9 @@ package elastic import ( "encoding/json" + "fmt" "net/http" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" "github.com/utmstack/UTMStack/plugins/soc-ai/utils" @@ -20,15 +20,15 @@ func ChangeAlertStatus(id string, status int, dataSource string, observations st body := schema.ChangeAlertStatus{AlertIDs: []string{id}, Status: status, DataSource: dataSource, StatusObservation: observations} bodyBytes, err := json.Marshal(body) if err != nil { - return catcher.Error("error marshalling body", err, nil) + return fmt.Errorf("error marshalling body: %v", err) } resp, statusCode, err := utils.DoReq(url, bodyBytes, "POST", headers, config.HTTP_TIMEOUT) if err != nil || statusCode != http.StatusOK { - return catcher.Error("error while doing request", err, map[string]any{"status": statusCode, "response": string(resp)}) + return fmt.Errorf("error while doing request: %v, status: %d, response: %v", err, statusCode, string(resp)) } - catcher.Info("Alert status changed successfully", map[string]any{"alert_id": id}) + utils.Logger.LogF(100, "Alert %s status changed successfully", id) return nil } diff --git a/plugins/soc-ai/elastic/incidents.go b/plugins/soc-ai/elastic/incidents.go index f722b5ca4..8e8515f3a 100644 --- a/plugins/soc-ai/elastic/incidents.go +++ b/plugins/soc-ai/elastic/incidents.go @@ -7,7 +7,6 @@ import ( "strings" "time" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" "github.com/utmstack/UTMStack/plugins/soc-ai/utils" @@ -15,7 +14,7 @@ import ( func CreateNewIncident(alertDetails *schema.AlertFields) error { if alertDetails == nil { - return catcher.Error("CreateNewIncident: alertDetails is nil", nil, nil) + return fmt.Errorf("CreateNewIncident: alertDetails is nil") } url := config.GetConfig().Backend + config.API_INCIDENT_ENDPOINT @@ -39,22 +38,22 @@ func CreateNewIncident(alertDetails *schema.AlertFields) error { bodyBytes, err := json.Marshal(body) if err != nil { - return catcher.Error("error marshalling body", err, nil) + return fmt.Errorf("error marshalling body: %v", err) } resp, statusCode, err := utils.DoReq(url, bodyBytes, "POST", headers, config.HTTP_TIMEOUT) if err != nil || statusCode != http.StatusOK { - return catcher.Error("error while doing request", err, map[string]any{"status": statusCode, "response": string(resp)}) + return fmt.Errorf("error while doing request: %v, status: %d, response: %v", err, statusCode, string(resp)) } - catcher.Info("Incident created successfully", map[string]any{"incident_name": body.IncidentName}) + utils.Logger.LogF(100, "Incident %s created successfully", body.IncidentName) return nil } func AddAlertToIncident(incidentId int, alertDetails *schema.AlertFields) error { if alertDetails == nil { - return catcher.Error("AddAlertToIncident: alertDetails is nil", nil, nil) + return fmt.Errorf("AddAlertToIncident: alertDetails is nil") } url := config.GetConfig().Backend + config.API_INCIDENT_ADD_NEW_ALERT_ENDPOINT @@ -75,15 +74,15 @@ func AddAlertToIncident(incidentId int, alertDetails *schema.AlertFields) error bodyBytes, err := json.Marshal(body) if err != nil { - return catcher.Error("error marshalling body", err, nil) + return fmt.Errorf("error marshalling body: %v", err) } resp, statusCode, err := utils.DoReq(url, bodyBytes, "POST", headers, config.HTTP_TIMEOUT) if err != nil || (statusCode != http.StatusOK && statusCode != http.StatusCreated) { - return catcher.Error("error while doing request", err, map[string]any{"status": statusCode, "response": string(resp)}) + return fmt.Errorf("error while doing request: %v, status: %d, response: %v", err, statusCode, string(resp)) } - catcher.Info("Alert added to incident successfully", map[string]any{"alert_id": alertDetails.ID, "incident_id": incidentId}) + utils.Logger.LogF(100, "Alert %s added to incident %d successfully", alertDetails.ID, incidentId) return nil } @@ -102,13 +101,13 @@ func GetIncidentsByPattern(pattern string) ([]schema.IncidentResp, error) { resp, statusCode, err := utils.DoReq(url, nil, "GET", headers, config.HTTP_TIMEOUT) if err != nil || statusCode != http.StatusOK { - return nil, catcher.Error("error while doing request", err, map[string]any{"status": statusCode, "response": string(resp)}) + return nil, fmt.Errorf("error while doing request: %v, status: %d, response: %v", err, statusCode, string(resp)) } var incidents []schema.IncidentResp err = json.Unmarshal(resp, &incidents) if err != nil { - return nil, catcher.Error("error while unmarshalling response", err, nil) + return nil, fmt.Errorf("error while unmarshalling response: %v", err) } return incidents, nil diff --git a/plugins/soc-ai/elastic/index.go b/plugins/soc-ai/elastic/index.go index f47dbae36..2e6974d08 100644 --- a/plugins/soc-ai/elastic/index.go +++ b/plugins/soc-ai/elastic/index.go @@ -7,7 +7,6 @@ import ( "strings" "time" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" "github.com/utmstack/UTMStack/plugins/soc-ai/utils" @@ -35,7 +34,7 @@ func ElasticQuery(index string, query interface{}, op string) error { method = "POST" } default: - return catcher.Error("unsupported operation", nil, map[string]any{"operation": op}) + return fmt.Errorf("unsupported operation: %s", op) } headers := map[string]string{ "Content-Type": "application/json", @@ -43,12 +42,12 @@ func ElasticQuery(index string, query interface{}, op string) error { queryBytes, err := json.Marshal(query) if err != nil { - return catcher.Error("error marshalling query", err, nil) + return fmt.Errorf("error marshalling query: %v", err) } resp, statusCode, err := utils.DoReq(url, queryBytes, method, headers, config.HTTP_TIMEOUT) if err != nil || (statusCode != http.StatusOK && statusCode != http.StatusCreated) { - return catcher.Error("error while doing request", err, map[string]any{"status": statusCode, "response": string(resp)}) + return fmt.Errorf("error while doing request: %v, status: %d, response: %v", err, statusCode, string(resp)) } return nil @@ -65,12 +64,12 @@ func ElasticSearch(index, field, value string) ([]byte, error) { body := schema.SearchDetailsRequest{{Field: field, Operator: "IS", Value: value}} bodyBytes, err := json.Marshal(body) if err != nil { - return nil, catcher.Error("error marshalling body", err, nil) + return nil, fmt.Errorf("error marshalling body: %v", err) } resp, statusCode, err := utils.DoReq(url, bodyBytes, "POST", headers, config.HTTP_TIMEOUT) if err != nil || statusCode != http.StatusOK { - return nil, catcher.Error("error while doing request for get Alert Details", err, map[string]any{"status": statusCode, "response": string(resp)}) + return nil, fmt.Errorf("error while doing request for get Alert Details: %v: %s", err, string(resp)) } return resp, nil @@ -94,15 +93,15 @@ func IndexStatus(id, status, op string) error { if strings.Contains(err.Error(), "index_not_found_exception") || strings.Contains(err.Error(), "no such index") { // Try to create the index first if createErr := CreateIndexIfNotExist(config.SOC_AI_INDEX); createErr != nil { - return catcher.Error("error creating document in elastic", err, map[string]any{"message": createErr}) + return fmt.Errorf("error creating document in elastic: %v (failed to create index: %v)", err, createErr) } // Retry the create operation if retryErr := ElasticQuery(config.SOC_AI_INDEX, doc, op); retryErr != nil { - return catcher.Error("error creating document in elastic after index creation", retryErr, nil) + return fmt.Errorf("error creating document in elastic after index creation: %v", retryErr) } } else { - return catcher.Error("error creating document in elastic", err, nil) + return fmt.Errorf("error creating document in elastic: %v", err) } } return nil diff --git a/plugins/soc-ai/elastic/transf.go b/plugins/soc-ai/elastic/transf.go index b498147c6..7a00547d0 100644 --- a/plugins/soc-ai/elastic/transf.go +++ b/plugins/soc-ai/elastic/transf.go @@ -6,7 +6,6 @@ import ( "strings" "time" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" ) @@ -88,7 +87,7 @@ func buildScriptString(alert schema.GPTAlertResponse) (string, error) { source += fmt.Sprintf("ctx._source['%s'] = params.%s; ", jsonFieldName, jsonFieldName) } default: - return "", catcher.Error("unsupported type", nil, map[string]any{"kind": reflect.TypeOf(fieldValue).Kind()}) + return "", fmt.Errorf("unsupported type: %v", reflect.TypeOf(fieldValue).Kind()) } } diff --git a/plugins/soc-ai/llm.go b/plugins/soc-ai/llm.go index b5de6e745..35a60ea3f 100644 --- a/plugins/soc-ai/llm.go +++ b/plugins/soc-ai/llm.go @@ -6,7 +6,6 @@ import ( "strings" "time" - "github.com/threatwinds/go-sdk/catcher" "github.com/utmstack/UTMStack/plugins/soc-ai/config" "github.com/utmstack/UTMStack/plugins/soc-ai/correlation" "github.com/utmstack/UTMStack/plugins/soc-ai/schema" @@ -19,11 +18,11 @@ func sendRequestToLLM(alert *schema.AlertFields) error { content := config.LLM_INSTRUCTION if alert == nil { - return catcher.Error("sendRequestToOpenAI: alert is nil", nil, nil) + return fmt.Errorf("sendRequestToOpenAI: alert is nil") } correlationContext, err := correlation.GetCorrelationContext(*alert) if err != nil { - return catcher.Error("error getting correlation context", err, nil) + return fmt.Errorf("error getting correlation context: %v", err) } if correlationContext != "" { content = fmt.Sprintf("%s%s", content, correlationContext) @@ -31,7 +30,7 @@ func sendRequestToLLM(alert *schema.AlertFields) error { jsonContent, err := json.Marshal(alert) if err != nil { - return catcher.Error("error marshalling alert", err, nil) + return fmt.Errorf("error marshalling alert: %v", err) } req := schema.GPTRequest{ @@ -48,11 +47,11 @@ func sendRequestToLLM(alert *schema.AlertFields) error { }, } - catcher.Info("Sending request to LLM", map[string]any{"request": req}) + utils.Logger.LogF(100, "Sending request to LLM: %v", req) requestJson, err := json.Marshal(req) if err != nil { - return catcher.Error("error marshalling request", err, nil) + return fmt.Errorf("error marshalling request: %v", err) } headers := map[string]string{ @@ -66,13 +65,13 @@ func sendRequestToLLM(alert *schema.AlertFields) error { if err == nil && len(response.Choices) > 0 { err = processLLMResponse(alert, response.Choices[0].Message.Content) if err != nil { - return catcher.Error("error processing LLM response", err, nil) + return fmt.Errorf("error processing LLM response: %v", err) } return nil } if status == 401 { - return catcher.Error("invalid api-key", nil, nil) + return fmt.Errorf("invalid api-key") } lastErr = fmt.Errorf("attempt %d failed: %v (status: %d)", attempt, err, status) @@ -81,25 +80,21 @@ func sendRequestToLLM(alert *schema.AlertFields) error { } } - catcher.Error("LLM appears to be DOWN", lastErr, map[string]any{ - "attempts": maxRetries, - "alert": alert.ID, - "provider": config.GetConfig().Provider, - "url": config.GetConfig().Url, - }) + utils.Logger.LogF(500, "LLM appears to be DOWN - all %d attempts failed for alert %s. Provider: %s, URL: %s, Last error: %v", + maxRetries, alert.ID, config.GetConfig().Provider, config.GetConfig().Url, lastErr) - return catcher.Error("all attempts to call LLM failed", lastErr, map[string]any{}) + return fmt.Errorf("all attempts to call LLM failed: %v", lastErr) } func processLLMResponse(alert *schema.AlertFields, response string) error { response, err := clearJson(response) if err != nil { - return catcher.Error("error clearing json", err, nil) + return fmt.Errorf("error clearing json: %v", err) } alertResponse, err := utils.ConvertFromJsonToStruct[schema.GPTAlertResponse](response) if err != nil { - return catcher.Error("error converting json to struct", err, nil) + return fmt.Errorf("error converting json to struct: %v", err) } nextSteps := []string{} @@ -120,7 +115,7 @@ func clearJson(s string) (string, error) { end := strings.LastIndex(s, "}") if start == -1 || end == -1 { - return "", catcher.Error("no valid json found in gpt response", nil, nil) + return "", fmt.Errorf("no valid json found in gpt response") } return s[start : end+1], nil diff --git a/plugins/soc-ai/main.go b/plugins/soc-ai/main.go index 44386b5e8..f6ff75ea7 100644 --- a/plugins/soc-ai/main.go +++ b/plugins/soc-ai/main.go @@ -10,6 +10,7 @@ import ( "github.com/threatwinds/go-sdk/plugins" twutil "github.com/threatwinds/go-sdk/utils" "github.com/utmstack/UTMStack/plugins/soc-ai/config" + "github.com/utmstack/UTMStack/plugins/soc-ai/utils" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" ) @@ -19,6 +20,8 @@ type socAiServer struct { } func main() { + utils.Logger.Info("Starting soc-ai plugin...") + go config.StartConfigurationSystem() time.Sleep(2 * time.Second) @@ -33,7 +36,7 @@ func main() { for retry := 0; retry < maxRetries; retry++ { socketsFolder, err = twutil.MkdirJoin(plugins.WorkDir, "sockets") if err == nil { - catcher.Info("socket directory created", map[string]any{"directory": socketsFolder}) + utils.Logger.LogF(100, "Socket directory %s created", socketsFolder) break } @@ -63,7 +66,7 @@ func main() { for retry := 0; retry < maxRetries; retry++ { unixAddress, err = net.ResolveUnixAddr("unix", socketFile) if err == nil { - catcher.Info("Socket file created", map[string]any{"socketFile": socketFile}) + utils.Logger.LogF(100, "Socket file %s created", socketFile) break } @@ -90,7 +93,7 @@ func main() { for retry := 0; retry < maxRetries; retry++ { listener, err = net.ListenUnix("unix", unixAddress) if err == nil { - catcher.Info("Listening on unix socket", map[string]any{"socketFile": socketFile}) + utils.Logger.LogF(100, "Listening on %s", socketFile) break } @@ -151,12 +154,12 @@ func (p *socAiServer) Correlate(_ context.Context, // Check if the module is active before processing the alert if config.GetConfig() == nil || !config.GetConfig().ModuleActive { - catcher.Info("SOC-AI module is disabled, skipping alert", map[string]any{"alert": alert.Id}) + utils.Logger.LogF(100, "SOC-AI module is disabled, skipping alert: %s", alert.Id) return &emptypb.Empty{}, nil } if !EnqueueAlert(alert) { - catcher.Info("Alert was dropped due to full queue", map[string]any{"alert": alert.Id}) + utils.Logger.LogF(300, "Alert %s was dropped due to full queue", alert.Id) return &emptypb.Empty{}, nil } diff --git a/plugins/soc-ai/queue.go b/plugins/soc-ai/queue.go index 91c5c28a4..cf330db89 100644 --- a/plugins/soc-ai/queue.go +++ b/plugins/soc-ai/queue.go @@ -66,15 +66,12 @@ func InitializeQueue() { go alertQueue.metricsLogger() - catcher.Info("Alert queue initialized", map[string]any{ - "workers": DefaultWorkerCount, - "queueSize": DefaultQueueSize, - }) + utils.Logger.LogF(100, "Alert queue initialized with %d workers and queue size %d", DefaultWorkerCount, DefaultQueueSize) } func EnqueueAlert(alert *plugins.Alert) bool { if alertQueue == nil { - catcher.Error("Alert queue not initialized", nil, nil) + utils.Logger.LogF(500, "Alert queue not initialized") return false } @@ -88,7 +85,7 @@ func EnqueueAlert(alert *plugins.Alert) bool { atomic.AddInt64(&alertQueue.queueSize, 1) // Reset consecutive drops counter on successful enqueue atomic.StoreInt64(&alertQueue.consecutiveDrops, 0) - catcher.Info("Alert enqueued for processing", map[string]any{"alert": alert.Id}) + utils.Logger.LogF(100, "Alert %s enqueued for processing", alert.Id) return true case <-time.After(QueueFullTimeout): atomic.AddInt64(&alertQueue.droppedCount, 1) @@ -106,19 +103,10 @@ func EnqueueAlert(alert *plugins.Alert) bool { "consecutive_drops": consecutiveDrops, }).Error(), }) - catcher.Error("Alert dropped due to full queue", nil, map[string]any{ - "alert": alert.Id, - "queue_size": currentQueueSize, - "max_queue_size": DefaultQueueSize, - "total_dropped": totalDropped, - "consecutive_drops": consecutiveDrops, - }) + utils.Logger.ErrorF("QUEUE FULL - Alert %s DROPPED! Queue size: %d/%d, Total dropped: %d, Consecutive: %d.", + alert.Id, currentQueueSize, DefaultQueueSize, totalDropped, consecutiveDrops) - catcher.Error("Alert dropped - Queue FULL", nil, map[string]any{ - "alert": alert.Id, - "queue_size": currentQueueSize, - "max_queue_size": DefaultQueueSize, - }) + elastic.RegisterError(fmt.Sprintf("Alert dropped - Queue FULL (%d/%d)", currentQueueSize, DefaultQueueSize), alert.Id) alertQueue.lastDropAlert = time.Now() return false } @@ -146,10 +134,7 @@ func (aq *AlertQueue) processAlert(workerID int, item *AlertQueueItem) { startTime := time.Now() alert := cleanAlerts(alertToAlertFields(item.Alert)) - catcher.Info("Processing alert", map[string]any{ - "alert": alert.ID, - "workerID": workerID, - }) + utils.Logger.LogF(100, "Worker %d processing alert: %s", workerID, alert.ID) defer func() { if r := recover(); r != nil { @@ -164,7 +149,7 @@ func (aq *AlertQueue) processAlert(workerID int, item *AlertQueueItem) { }() if config.GetConfig() == nil || !config.GetConfig().ModuleActive { - catcher.Info("SOC-AI module is disabled, skipping alert", map[string]any{"alert": alert.ID}) + utils.Logger.LogF(100, "SOC-AI module is disabled, skipping alert: %s", alert.ID) atomic.AddInt64(&aq.processedCount, 1) return } @@ -196,12 +181,8 @@ func (aq *AlertQueue) processAlert(workerID int, item *AlertQueueItem) { duration := time.Since(startTime) queueTime := startTime.Sub(item.Timestamp) - catcher.Info("Alert processed successfully", map[string]any{ - "alert": alert.ID, - "workerID": workerID, - "duration": duration.String(), - "queue_time": queueTime.String(), - }) + utils.Logger.LogF(100, "Worker %d completed alert %s in %v (queue time: %v)", + workerID, alert.ID, duration, queueTime) } func (aq *AlertQueue) metricsLogger() { @@ -218,11 +199,8 @@ func (aq *AlertQueue) metricsLogger() { errors := atomic.LoadInt64(&aq.errorCount) queueSize := atomic.LoadInt64(&aq.queueSize) - catcher.Info("Queue metrics", map[string]any{ - "processed": processed, - "dropped": dropped, - "errors": errors, - "queue_size": queueSize}) + utils.Logger.LogF(200, "Queue metrics - Processed: %d, Dropped: %d, Errors: %d, Current queue size: %d", + processed, dropped, errors, queueSize) } } } diff --git a/plugins/soc-ai/utils/convert.go b/plugins/soc-ai/utils/convert.go index 89df9ff72..caf07fc16 100644 --- a/plugins/soc-ai/utils/convert.go +++ b/plugins/soc-ai/utils/convert.go @@ -2,14 +2,13 @@ package utils import ( "encoding/json" - - "github.com/threatwinds/go-sdk/catcher" + "fmt" ) func ConvertFromStructToJsonString(alert interface{}) (string, error) { bytes, err := json.Marshal(alert) if err != nil { - return "", catcher.Error("error marshalling alert", err, nil) + return "", fmt.Errorf("error marshalling alert: %v", err) } return string(bytes), nil @@ -19,7 +18,7 @@ func ConvertFromJsonToStruct[responseType any](jsonString string) (responseType, var response responseType err := json.Unmarshal([]byte(jsonString), &response) if err != nil { - return *new(responseType), catcher.Error("error unmarshalling GPT response", err, nil) + return *new(responseType), fmt.Errorf("error unmarshalling GPT response: %v", err) } return response, nil diff --git a/plugins/soc-ai/utils/env.go b/plugins/soc-ai/utils/env.go index 89b3149ac..99979fcfd 100644 --- a/plugins/soc-ai/utils/env.go +++ b/plugins/soc-ai/utils/env.go @@ -1,22 +1,21 @@ package utils import ( + "log" "os" - - "github.com/threatwinds/go-sdk/catcher" ) func Getenv(key string, isMandatory bool) string { value, defined := os.LookupEnv(key) if !defined { if isMandatory { - catcher.Error("Error loading environment variable", nil, map[string]any{"key": key}) + log.Fatalf("Error loading environment variable: %s: environment variable does not exist\n", key) } else { return "" } } if (value == "" || value == " ") && isMandatory { - catcher.Error("Error loading environment variable", nil, map[string]any{"key": key}) + log.Fatalf("Error loading environment variable: %s: empty environment variable\n", key) } return value } diff --git a/plugins/soc-ai/utils/files.go b/plugins/soc-ai/utils/files.go index 8fc4fb304..d483f886d 100644 --- a/plugins/soc-ai/utils/files.go +++ b/plugins/soc-ai/utils/files.go @@ -1,10 +1,9 @@ package utils import ( + "fmt" "os" "path/filepath" - - "github.com/threatwinds/go-sdk/catcher" ) func GetMyPath() (string, error) { @@ -20,10 +19,10 @@ func GetMyPath() (string, error) { func CreatePathIfNotExist(path string) error { if _, err := os.Stat(path); os.IsNotExist(err) { if err := os.Mkdir(path, 0755); err != nil { - return catcher.Error("error creating path", err, map[string]any{"path": path}) + return fmt.Errorf("error creating path: %v", err) } } else if err != nil { - return catcher.Error("error checking path", err, map[string]any{"path": path}) + return fmt.Errorf("error checking path: %v", err) } return nil } diff --git a/plugins/soc-ai/utils/logger.go b/plugins/soc-ai/utils/logger.go new file mode 100644 index 000000000..4332e7f5a --- /dev/null +++ b/plugins/soc-ai/utils/logger.go @@ -0,0 +1,31 @@ +package utils + +import ( + "log" + "os" + "strconv" + + "github.com/threatwinds/logger" +) + +var Logger *logger.Logger + +func init() { + lenv := os.Getenv("LOG_LEVEL") + var level int + var err error + + if lenv != "" && lenv != " " { + level, err = strconv.Atoi(lenv) + if err != nil { + log.Fatalln(err) + } + } else { + level = 200 + } + + Logger = logger.NewLogger(&logger.Config{ + Format: "text", + Level: level, + }) +} diff --git a/plugins/sophos/config/config.go b/plugins/sophos/config/config.go index 98e677867..da3a0dbfa 100644 --- a/plugins/sophos/config/config.go +++ b/plugins/sophos/config/config.go @@ -2,6 +2,8 @@ package config import ( "context" + "fmt" + "log" "strings" sync "sync" "time" @@ -50,7 +52,7 @@ func StartConfigurationSystem() { modulesConfigHost = pluginConfig.Get("modulesConfig").String() if internalKey == "" || modulesConfigHost == "" { - catcher.Info("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution", nil) + fmt.Println("Internal key or Modules Config Host is not set, skipping UTMStack plugin execution") time.Sleep(reconnectDelay) continue } @@ -131,7 +133,7 @@ func StartConfigurationSystem() { switch message := in.Payload.(type) { case *BiDirectionalMessage_Config: - catcher.Info("Recived configuration update", map[string]any{"config": message.Config}) + log.Printf("Received configuration update: %v", message.Config) cnf = message.Config } } From 352e07e76a4afdf4a144af51a89ee108180fa779 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 8 Sep 2025 10:59:33 -0500 Subject: [PATCH 016/422] fix(alert-view): correct totalItems assignment after fetching alerts --- .../alert-management/alert-view/alert-view.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts index 256f0757d..5dcc3039c 100644 --- a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts +++ b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts @@ -318,7 +318,6 @@ export class AlertViewComponent implements OnInit, OnDestroy { (res: HttpResponse) => { this.totalItems = Number(res.headers.get('X-Total-Count')); this.alerts = res.body; - this.totalItems = this.alerts.length; this.loading = false; this.refreshingAlert = false; }, From 24d999ef60e6651c4ad7df071faa370a111e3aed Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 8 Sep 2025 13:56:26 -0500 Subject: [PATCH 017/422] fix(utm-group-rules): add correlation rules entries and clean up SQL inserts --- .../liquibase/data/utm_correlation_rules.sql | 1383 ++++++----------- .../data/utm_group_rules_data_type.sql | 63 +- .../resources/config/liquibase/master.xml | 2 +- 3 files changed, 501 insertions(+), 947 deletions(-) diff --git a/backend/src/main/resources/config/liquibase/data/utm_correlation_rules.sql b/backend/src/main/resources/config/liquibase/data/utm_correlation_rules.sql index 2822156f7..09668a7fa 100644 --- a/backend/src/main/resources/config/liquibase/data/utm_correlation_rules.sql +++ b/backend/src/main/resources/config/liquibase/data/utm_correlation_rules.sql @@ -1,3 +1,5 @@ + + INSERT INTO public.utm_correlation_rules VALUES (106, 'AWS EC2 Network Access Control List Deletion', 3, 2, 2, 'Impair Defenses', 'Defense Evasion', 'Identifies the deletion of an Amazon Elastic Compute Cloud (EC2) network access control list (ACL) or one of its ingress/egress entries', '["https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/delete-network-acl.html","https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteNetworkAcl.html","https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/delete-network-acl-entry.html","https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteNetworkAclEntry.html","https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/"]', 'safe("log.eventSource", "") == ''ec2.amazonaws.com'' && (safe("log.eventName", "") == ''DeleteNetworkAcl'' || safe("log.eventName", "") == ''DeleteNetworkAclEntry'')', '2025-09-05 14:20:15.303074', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (267, 'Azure Container Registry Critical Vulnerability Detected', 3, 3, 2, 'Initial Access', 'T1190 - Exploit Public-Facing Application', 'Detects critical or high severity vulnerabilities in container images within Azure Container Registry, including newly pushed images or recently scanned images with security issues. @@ -10,6 +12,18 @@ Next Steps: 6. Monitor for any exploitation attempts against vulnerable containers ', '["https://learn.microsoft.com/en-us/azure/defender-for-cloud/defender-for-container-registries-introduction","https://attack.mitre.org/techniques/T1190/"]', 'safe("log.type", "") == "ContainerRegistryRepositoryEvents" && safe("log.OperationName", "").contains("Microsoft.ContainerRegistry") && (safe("log.ResultType", "") == "VulnerabilityFound" || safe("log.Category", "") == "SecurityAssessment") && (safe("log.Severity", "") == "Critical" || safe("log.Severity", "") == "High" || safe("log.statusCode", 0.0) >= double(400))', '2025-09-05 14:31:01.997598', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (124, 'AWS Management Console Root Login', 3, 3, 3, 'Valid Accounts', 'Initial Access', 'Identifies a successful login to the AWS Management Console by the Root user.
Adversaries may obtain and abuse credentials of a cloud account as a means of gaining Initial Access, Persistence, Privilege Escalation, or Defense Evasion.
Compromised credentials for cloud accounts can be used to harvest sensitive data from online storage accounts and databases.
Potential false positives
It’s strongly recommended that the root user is not used for everyday tasks, including the administrative ones. Verify whether the IP address, location, and/or hostname should be logging in as root in your environment. Unfamiliar root logins should be investigated immediately. If known behavior is causing false positives, it can be exempted from the rule.', '["https://attack.mitre.org/tactics/TA0001/","https://attack.mitre.org/techniques/T1078/","https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html"]', 'safe("log.eventSource", "") == "signin.amazonaws.com" && safe("log.eventName", "") == "ConsoleLogin" && safe("log.userIdentityType", "") == "root"', '2025-09-05 14:57:12.368894', true, false, 'target', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (148, 'Lambda Function Privilege Escalation', 3, 3, 2, 'Privilege Escalation, Defense Evasion', 'T1548 - Abuse Elevation Control Mechanism', 'Detects potential privilege escalation through Lambda functions when IAM policies are attached to roles or users that can be exploited. This may indicate an attacker attempting to escalate privileges by attaching administrative policies to Lambda execution roles. + +Next Steps: +1. Verify if the policy attachment was authorized and follows change management procedures +2. Review the attached policy permissions, especially if AdministratorAccess or IAMFullAccess policies were attached +3. Check the Lambda function''s code and recent invocations for suspicious activity +4. Review CloudTrail logs for other IAM changes by the same user/role +5. Validate if the Lambda function legitimately requires the elevated permissions +6. Consider revoking the policy attachment if unauthorized and investigate the source of the change +7. Check for any unusual Lambda function executions following the policy attachment +8. Review the user/role history for previous privilege escalation attempts +', '["https://bishopfox.com/blog/privilege-escalation-in-aws","https://attack.mitre.org/techniques/T1548/"]', 'safe("log.eventSource", "") == "iam.amazonaws.com" && (safe("log.eventName", "") == "AttachRolePolicy" || safe("log.eventName", "") == "AttachUserPolicy") && safe("log.errorCode", "") == "" && (safe("log.requestParameters.roleArn", "").contains("lambda") || safe("log.userIdentity.arn", "").contains("lambda") || safe("log.requestParameters.policyArn", "").contains("AdministratorAccess")) || safe("log.requestParameters.policyArn", "").contains("IAMFullAccess")', '2025-09-05 16:19:06.100601', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1412, 'Windows: Outlook add-in was loaded by powershell, possible use for email collection', 3, 2, 1, 'Potentially Malicious Activity', '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/"]', 'safe("log.message", "").contains("(Microsoft.Office.Interop.Outlook)")', '2025-09-04 20:57:05.797932', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (118, 'AWS EC2 Encryption Disabled', 3, 2, 2, 'Data Manipulation', 'Impact', 'Identifies disabling of Amazon Elastic Block Store (EBS) encryption by default in the current region. Disabling encryption by default does not change the encryption status of your existing volumes', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1565/","https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html","https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/disable-ebs-encryption-by-default.html","https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DisableEbsEncryptionByDefault.html"]', 'safe("log.eventSource", "") == "ec2.amazonaws.com" && safe("log.eventName", "") == "DisableEbsEncryptionByDefault"', '2025-09-03 12:27:56.05736', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (139, 'AWS Config Compliance Violation', 2, 3, 2, 'Defense Evasion, Persistence, Privilege Escalation, Initial Access', 'T1078.004 - Valid Accounts: Cloud Accounts', 'Detects AWS Config compliance violations indicating resources that are non-compliant with defined configuration rules and security policies. These violations may indicate configuration drift, unauthorized changes, or potential security misconfigurations that could expose the organization to security risks. @@ -26,6 +40,356 @@ Next Steps: 9. Update compliance rules if legitimate configuration changes require policy updates 10. Monitor for recurring violations that may indicate systemic configuration issues ', '["https://docs.aws.amazon.com/config/latest/developerguide/evaluate-config.html","https://attack.mitre.org/techniques/T1078/004/"]', 'safe("log.eventSource", "") == "config.amazonaws.com" && (safe("log.eventName", "") == "PutEvaluations" || safe("log.eventName", "") == "PutComplianceItems") && (safe("log.responseElements", "") == "NON_COMPLIANT" || safe("log.requestParameters", "") == "NON_COMPLIANT")', '2025-09-05 15:10:15.226045', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (162, 'AWS VPC Flow Log Anomalies', 3, 2, 2, 'Discovery', 'T1046 - Network Service Discovery', 'Detects anomalies in VPC Flow Logs configuration that could indicate attempts to hide malicious network activity. Monitors for deletion or modification of flow log configurations. + +Next Steps: +1. Verify if the flow log changes were authorized by reviewing the AWS CloudTrail logs for the userIdentityArn +2. Check if the source IP address belongs to known administrative systems or jump boxes +3. Review other activities from the same source IP and user identity in the past 24-48 hours +4. Examine the affected VPC and its resources to understand the impact of disabled flow logging +5. If unauthorized, immediately re-enable flow logs and investigate what network activity may have occurred while logging was disabled +6. Review IAM permissions for the user/role that made these changes to ensure least privilege +7. Consider implementing preventive controls using AWS Config rules or SCPs to prevent unauthorized flow log modifications +', '["https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html","https://attack.mitre.org/techniques/T1046/"]', 'safe("log.eventSource", "") == "ec2.amazonaws.com" && safe("log.eventName", "") in ["DeleteFlowLogs", "CreateFlowLogs", "ModifyFlowLogsAttribute"] && safe("log.sourceIPAddress", "") != "" && safe("log.errorCode", "") == "" && ( safe("log.eventName", "") == "DeleteFlowLogs" || (safe("log.eventName", "") == "CreateFlowLogs" && safe("log.requestParameters.deliverLogsStatus", "").contains("FAILED")) || (safe("log.eventName", "") == "ModifyFlowLogsAttribute" && safe("log.requestParameters.deliverLogsStatus", "") == "INACTIVE") )', '2025-09-05 16:21:18.20793', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (158, 'AWS Security Group Modifications', 2, 3, 2, 'Defense Evasion', 'T1562.007 - Impair Defenses: Disable or Modify Cloud Firewall', 'Detects modifications to AWS security groups that could weaken network security posture. Monitors for changes that add permissive rules or remove restrictive rules, particularly those allowing unrestricted access (0.0.0.0/0 or ::/0). + +Next Steps: +1. Review the security group change details in CloudTrail logs +2. Verify if the change was authorized and follows security policies +3. Check the user/role that made the modification +4. Assess if the new rules expose sensitive resources +5. If unauthorized, immediately revert the changes +6. Review other recent activities from the same source IP or user +7. Consider implementing preventive controls via AWS Config rules or SCPs +', '["https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html","https://attack.mitre.org/techniques/T1562/007/"]', 'safe("log.eventSource", "") == "ec2.amazonaws.com" && safe("log.eventName", "") in ["AuthorizeSecurityGroupIngress", "AuthorizeSecurityGroupEgress", "RevokeSecurityGroupIngress", "RevokeSecurityGroupEgress", "CreateSecurityGroup", "DeleteSecurityGroup", "ModifySecurityGroupRules"] && safe("log.sourceIPAddress", "") != "" && safe("log.errorCode", "") == "" && (safe("log.requestParameters.ipPermissions.ipProtocol", "").contains("-1") || safe("log.requestParameters.ipPermissions.cidrIp", "").contains("0.0.0.0/0") || safe("log.requestParameters.ipPermissions.ipv6CidrIp", "").contains("::/0")) || safe("log.requestParameters.ipRanges.cidrIp", "").contains("0.0.0.0/0") || safe("log.requestParameters.ipv6Ranges.cidrIpv6", "").contains("::/0")', '2025-09-05 16:48:59.11058', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (828, 'AppArmor Profile Changes Detected', 2, 3, 1, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', 'Detects AppArmor profile modifications, denials, or attempts to disable AppArmor security policies that could indicate attempts to bypass mandatory access controls. AppArmor is a Linux kernel security module that restricts programs'' capabilities with per-program profiles. + +Next Steps: +1. Review the specific AppArmor event and determine if it was authorized +2. Check if the profile change was part of legitimate system administration +3. Verify the user account that triggered the change has appropriate privileges +4. Examine recent system activities on the affected host for signs of compromise +5. If unauthorized, restore the AppArmor profile and investigate potential security breach +', '["https://attack.mitre.org/techniques/T1562/001/","https://ubuntu.com/server/docs/apparmor"]', 'safe("action", "").contains("system") && + ( + safe("log.message", "").contains("apparmor") || + safe("log.message", "").contains("AppArmor") + ) && + ( + safe("log.message", "").contains("DENIED") || + safe("log.message", "").contains("complain") || + safe("log.message", "").contains("enforce") || + safe("log.message", "").contains("audit") || + safe("log.message", "").contains("profile") || + safe("log.message", "").contains("aa-") || + safe("log.message", "").contains("apparmor_parser") || + safe("log.message", "").contains("teardown") + )', '2025-09-05 17:28:10.624111', true, false, 'origin', '["origin.host"]', '[]'); +INSERT INTO public.utm_correlation_rules VALUES (833, 'Network Configuration Changes Detected', 2, 3, 2, 'Discovery', 'T1016 - System Network Configuration Discovery', 'Detects changes to network configuration files or network interface settings that could indicate system compromise or preparation for data exfiltration. This rule monitors for modifications to critical network configuration files and commands that alter network settings. + +Next Steps: +1. Verify if the network configuration changes were authorized and scheduled +2. Check if the changes align with legitimate administrative activities +3. Review the user account that made the changes and their privileges +4. Examine the timing of the changes for patterns that suggest malicious activity +5. Correlate with other security events from the same host +6. Validate current network configuration against baseline settings +7. Monitor for any unusual network traffic patterns following the changes +', '["https://attack.mitre.org/techniques/T1016/","https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html"]', 'safe("log.message", "") != "" && + ( + safe("log.message", "").contains("/etc/network/") || + safe("log.message", "").contains("/etc/sysconfig/network") || + safe("log.message", "").contains("ifconfig") || + safe("log.message", "").contains("ip addr") || + safe("log.message", "").contains("netplan") || + safe("log.message", "").contains("/etc/resolv.conf") || + safe("log.message", "").contains("/etc/hosts") || + safe("log.message", "").contains("iptables") || + safe("log.message", "").contains("route add") || + safe("log.message", "").contains("ip route") + )', '2025-09-05 18:06:31.791605', true, false, 'origin', '["origin.host","log.message"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":[],"within":"now-1h","count":5}]'); +INSERT INTO public.utm_correlation_rules VALUES (829, 'Firewall Rule Modifications Detected', 2, 3, 2, 'Defense Evasion', 'T1562.004 - Impair Defenses: Disable or Modify System Firewall', 'Detects modifications to firewall rules including iptables, firewalld, or ufw that could indicate attempts to disable security controls or open backdoor access. This rule monitors system logs for firewall configuration changes that may compromise network security boundaries. + +Next Steps: +1. Verify if the firewall modification was authorized and documented +2. Check who performed the modification and from which system +3. Review the specific rules that were added, deleted, or modified +4. Examine surrounding log entries for additional suspicious activity +5. Validate current firewall configuration against security baseline +6. If unauthorized, immediately review and restore proper firewall rules +7. Consider implementing change management controls for firewall modifications +', '["https://attack.mitre.org/techniques/T1562/004/","https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html"]', 'safe("action", "").contains("system") && + ( + safe("log.message", "").contains("iptables") || + safe("log.message", "").contains("ip6tables") || + safe("log.message", "").contains("firewall-cmd") || + safe("log.message", "").contains("ufw") || + safe("log.message", "").contains("nftables") || + safe("log.message", "").contains("ebtables") + ) && + ( + safe("log.message", "").contains("ACCEPT") || + safe("log.message", "").contains("DROP") || + safe("log.message", "").contains("REJECT") || + safe("log.message", "").contains("flush") || + safe("log.message", "").contains("delete") || + safe("log.message", "").contains("insert") || + safe("log.message", "").contains("--dport") || + safe("log.message", "").contains("--sport") + )', '2025-09-05 18:11:09.199185', true, false, 'origin', '["origin.host"]', '[]'); +INSERT INTO public.utm_correlation_rules VALUES (835, 'PAM Configuration Changes Detected', 2, 3, 2, 'Credential Access, Defense Evasion, Persistence', 'T1556.003 - Modify Authentication Process: Pluggable Authentication Modules', 'Detects modifications to PAM (Pluggable Authentication Module) configuration files which could indicate an attempt to bypass authentication mechanisms or establish persistence. PAM configuration changes can allow attackers to weaken authentication requirements, add backdoor authentication methods, or bypass existing security controls. + +Next Steps: +1. Verify the legitimacy of the PAM configuration change with system administrators +2. Review the specific PAM module that was modified and assess the security impact +3. Check for unauthorized access attempts before and after the configuration change +4. Examine system logs for other suspicious administrative activities +5. Validate that proper change management procedures were followed +6. Consider reverting the changes if they are unauthorized +7. Review user accounts and privileges that may have been affected +', '["https://attack.mitre.org/techniques/T1556/003/","https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html"]', 'safe("action", "") == "system.auth" && + safe("log.message", "").contains("pam") && + ( + safe("log.message", "").contains("/etc/pam.d/") || + safe("log.message", "").contains("pam.conf") || + ( + safe("log.message", "").contains("pam_") && + safe("log.message", "").contains("modified") + ) + )', '2025-09-05 18:17:34.630353', true, false, 'origin', '["origin.host"]', '[]'); +INSERT INTO public.utm_correlation_rules VALUES (837, 'SELinux Policy Violations Detected', 3, 3, 2, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', 'Detects SELinux policy violations, AVC denials, or attempts to disable SELinux that could indicate security bypass attempts or system compromise. SELinux violations may indicate malicious activity attempting to bypass mandatory access controls. + +Next Steps: +1. Review the specific SELinux violation details and affected resources +2. Check if the violation is from legitimate application behavior or malicious activity +3. Examine system logs around the time of violation for additional suspicious activity +4. Verify SELinux policy configuration and ensure it hasn''t been tampered with +5. If violations are from legitimate applications, consider updating SELinux policies or application configurations +6. Monitor for repeated violations from the same process or user that may indicate persistent bypass attempts +', '["https://attack.mitre.org/techniques/T1562/001/","https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html-single/using_selinux/index"]', 'safe("action", "").contains("system") && + ( + safe("log.message", "").contains("avc:") || + safe("log.message", "").contains("selinux") || + safe("log.message", "").contains("SELinux") + ) && + ( + safe("log.message", "").contains("denied") || + safe("log.message", "").contains("permissive") || + safe("log.message", "").contains("disabled") || + safe("log.message", "").contains("setenforce") || + safe("log.message", "").contains("setsebool") || + safe("log.message", "").contains("restorecon") || + safe("log.message", "").contains("audit2allow") + )', '2025-09-05 18:18:47.422071', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (840, 'System Boot Anomalies', 1, 3, 3, 'Persistence, Privilege Escalation', 'T1037 - Boot or Logon Initialization Scripts', 'Detects anomalous system boot patterns including unexpected reboots, boot failures, kernel panics, or unusual boot sequences that may indicate system compromise or hardware issues. This rule identifies critical system events that could signal attempts to tamper with the boot process or system initialization. + +Next Steps: +1. Immediately verify the affected system''s current operational status +2. Check system logs around the time of the boot anomaly for additional context +3. Examine hardware status and recent system changes or updates +4. Verify integrity of boot components (bootloader, kernel, initramfs) +5. Check for signs of unauthorized modifications to system files +6. Review recent administrative activities and user access logs +7. Consider isolating the system if tampering is suspected +8. Document all findings for potential forensic analysis +', '["https://attack.mitre.org/techniques/T1037/","https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html"]', '( + safe("log.message", "").matches("(?i)(kernel panic|unable to mount root|failed to start|boot failed|emergency mode|rescue mode)") || + safe("log.message", "").matches("(?i)(unexpected system reboot|power loss detected|fsck failed|filesystem errors detected)") || + (safe("action", "") == "system.syslog" && safe("log.message", "").matches("(?i)(kernel:.*oops|kernel:.*bug|kernel:.*call trace)")) || + (safe("log.program", "") == "systemd" && safe("log.message", "").matches("(?i)(failed to start|dependency failed|timeout|activating failed)")) || + safe("log.message", "").matches("(?i)(grub.*error|bootloader.*fail|initramfs.*error|dracut.*warning)") + )', '2025-09-05 18:20:00.219508', true, false, 'origin', '["origin.host"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":[],"within":"now-10m","count":3}]'); +INSERT INTO public.utm_correlation_rules VALUES (836, 'Process Execution Anomalies', 3, 3, 2, 'Defense Evasion, Privilege Escalation', 'T1055 - Process Injection', 'Detects anomalous process behavior including unexpected terminations, segmentation faults, and suspicious process spawning patterns. This rule identifies processes that have crashed, been killed abnormally, or are exhibiting zombie/defunct behavior which could indicate malicious activity or system compromise. + +Next Steps: +1. Investigate the affected host and identify the specific processes involved +2. Review system logs around the time of the anomalous process behavior +3. Check for signs of memory corruption, privilege escalation attempts, or malware injection +4. Examine parent-child process relationships for suspicious spawning patterns +5. Verify system integrity and check for unauthorized modifications +6. Monitor for additional process anomalies on the same host +', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1055/"]', '( + safe("log.message", "") != "" && + ( + safe("log.message", "").contains("segfault") || + safe("log.message", "").contains("core dumped") || + safe("log.message", "").contains("Killed process") || + ( + safe("log.message", "").contains("process") && + ( + safe("log.message", "").contains("abnormal") || + safe("log.message", "").contains("terminated") || + safe("log.message", "").contains("signal 9") || + safe("log.message", "").contains("signal 11") || + safe("log.message", "").contains("defunct") || + safe("log.message", "").contains("zombie") + ) + ) + ) && + safe("origin.host", "") != "" + )', '2025-09-05 18:37:07.345737', true, false, 'origin', '["origin.host"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":[],"within":"now-30m","count":3}]'); +INSERT INTO public.utm_correlation_rules VALUES (839, 'Sudo Privilege Escalation Attempts', 3, 3, 2, 'Privilege Escalation, Defense Evasion', 'T1548.003 - Abuse Elevation Control Mechanism: Sudo and Sudo Caching', 'Detects unauthorized sudo usage attempts and privilege escalation through sudo command execution. This rule identifies failed sudo attempts including users not in sudoers file, authentication failures, and command restrictions. + +Next Steps: +1. Verify the legitimacy of the user attempting sudo access +2. Review the specific command that was attempted +3. Check if this is part of a broader privilege escalation campaign +4. Examine system logs for additional suspicious activity from this user/host +5. Consider implementing additional sudo restrictions if unauthorized access attempts persist +6. Correlate with other authentication events from the same user or host +', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1548/003/"]', 'safe("log.message", "").contains("sudo") && + ( + safe("log.message", "").contains("NOT in sudoers") || + safe("log.message", "").contains("authentication failure") || + safe("log.message", "").contains("command not allowed") + ) && + safe("origin.host", "") != ""', '2025-09-05 19:15:32.09205', true, false, 'origin', '["origin.host","origin.user"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":[],"within":"now-10m","count":3}]'); +INSERT INTO public.utm_correlation_rules VALUES (844, 'System Resource Exhaustion Detection', 1, 2, 3, 'Impact', 'Resource Hijacking', 'Detects potential system resource exhaustion including CPU, memory, or disk space issues that could lead to denial of service', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1496/"]', 'safe("log.message", "") != "" && + (safe("log.message", "").contains("Out of memory") || + safe("log.message", "").contains("No space left on device") || + safe("log.message", "").contains("Cannot allocate memory") || + safe("log.message", "").contains("fork: retry: Resource temporarily unavailable") || + safe("log.message", "").contains("too many open files") || + safe("log.message", "").contains("cannot fork") || + safe("log.message", "").contains("malloc failed")) && + safe("origin.host", "") != ""', '2025-09-05 19:44:11.610032', true, false, 'origin', '["origin.host"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":[],"within":"now-10m","count":10}]'); +INSERT INTO public.utm_correlation_rules VALUES (85, 'Kernel-Level Threat Detection', 3, 3, 3, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects kernel-level threats including rootkits, kernel exploits, and malicious kernel drivers that attempt to gain elevated privileges or hide malicious activity at the kernel level. + +Next Steps: +1. Immediately isolate the affected endpoint to prevent lateral movement +2. Capture memory dump and disk image for forensic analysis +3. Review process creation logs for suspicious parent-child relationships +4. Check for persistence mechanisms (scheduled tasks, services, registry keys) +5. Verify kernel driver signatures and compare against known good baselines +6. Investigate any network connections from kernel-level processes +7. Run deep endpoint scan with updated signatures +8. Contact security team for incident response procedures +', '["https://www.sentinelone.com/blog/decrypting-sentinelone-detection-the-behavioral-ai-engine-in-real-time-cwpp/","https://attack.mitre.org/techniques/T1068/"]', 'safe("log.eventDescription", "").contains("kernel") && (safe("log.confidencelevel", 0.0) >= double(80) || safe("log.dveventtype", "").contains("kernel") || safe("log.indicatorname", "").contains("kernel"))', '2025-09-05 19:56:21.624506', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (924, 'RHEL-Specific Kernel Exploitation Attempt', 3, 3, 3, 'Privilege Escalation', 'Exploitation for Privilege Escalation', 'Detects potential exploitation attempts targeting RHEL-specific kernel vulnerabilities, including memory corruption, privilege escalation through kernel bugs, or attempts to bypass kernel security mechanisms like SELinux or kernel module protections.', '["https://access.redhat.com/security/vulnerabilities","https://attack.mitre.org/techniques/T1068/"]', '(safe("log.facility", "") == "kern" || safe("log.process", "") == "kernel") && + ((safe("log.message", "").contains("segfault") && (safe("log.message", "").contains("ip") || safe("log.message", "").contains("sp"))) || + safe("log.message", "").contains("kernel BUG") || + safe("log.message", "").contains("Oops:") || + safe("log.message", "").contains("kernel exploit") || + safe("log.message", "").contains("privilege escalation") || + (safe("log.message", "").contains("SELinux: avc: denied") && safe("log.message", "").contains("capability")) || + safe("log.message", "").contains("KASLR bypass") || + (safe("log.message", "").contains("CVE-") && safe("log.message", "").contains("exploit")))', '2025-09-05 20:01:02.504489', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"},{"field":"log.facility.keyword","operator":"filter_term","value":"kern"}],"or":[],"within":"now-5m","count":3}]'); +INSERT INTO public.utm_correlation_rules VALUES (1481, 'Windows: Signed Proxy Execution via MS Work Folders', 1, 2, 3, 'Defense Evasion', '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/"]', 'safe("log.winlogEventDataProcessName", "").contains("control.exe") && safe("log.winlogEventDataParentProcessName", "").contains("workfolders.exe") && !safe("log.winlogEventDataProcessName", "").matches("(:\\Windows\\(System32|SysWOW64)\\control.exe)")', '2025-09-05 20:09:24.518358', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1343, 'Windows: Execution via MSSQL xp_cmdshell Stored Procedure', 3, 3, 2, 'Execution', '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/"]', 'safe("log.message", "") in [''diskfree'', ''rmdir'', ''mkdir'', ''dir'', ''del'', ''rename'', ''bcp'', ''XMLNAMESPACES''] && + safe("log.winlogEventDataProcessName", "").contains(''cmd.exe'') && safe("log.winlogEventDataParentProcessName", "").contains(''sqlservr.exe'')', '2025-09-05 20:13:56.074859', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1399, 'Windows: Microsoft Exchange Worker Spawning Suspicious Processes', 2, 3, 2, 'Initial Access', '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/"]', 'safe("log.winlogEventDataParentProcessName", "").contains("w3wp.exe") && safe("log.message", "").matches("(MSExchange(.+)AppPool)") && + safe("log.winlogEventDataProcessName", "").matches("(md.exe|powershell.exe|pwsh.dll|powershell_ise.exe)")', '2025-09-05 20:16:58.35805', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1400, 'Windows: Suspicious MS Office Child Process', 1, 2, 3, 'Initial Access', '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/"]', 'safe("log.winlogEventDataProcessName", "") in ["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"] && + safe("log.winlogEventDataParentProcessName", "") in ["eqnedt32.exe", "excel.exe", "fltldr.exe", "msaccess.exe", "mspub.exe", "powerpnt.exe", "winword.exe", "outlook.exe"]', '2025-09-05 20:17:59.318295', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (961, 'System Linux: Namespace Manipulation Using Unshare', 3, 3, 2, 'Privilege Escalation', 'Create or Modify System Process', 'Identifies suspicious usage of unshare to manipulate system namespaces. Unshare can be utilized to escalate privileges or escape container security boundaries. Threat actors have utilized this binary to allow themselves to escape to the host and access other resources or escalate privileges.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1543/"]', 'safe("log.message", "").contains("unshare") && !safe("log.message", "").contains("/usr/bin/snap") && !safe("log.message", "").matches("(\\/usr\\/bin\\/udevadm|\\/lib\\/systemd\\/systemd-udevd|\\/usr\\/bin\\/unshare)")', '2025-09-05 20:29:55.79613', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (952, 'System Linux: Modification of OpenSSH Binaries', 3, 3, 2, 'Persistence', 'Create or Modify System Process', 'Adversaries may modify SSH related binaries for persistence or credential access by patching sensitive functions to enable unauthorized access or by logging SSH credentials for exfiltration.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1543/"]', 'safe("log.message", "").matches("libkeyutils.so") && !safe("log.message", "").matches("(dpkg|yum|dnf|dnf-automatic)") && safe("log.message", "").matches("(/usr/sbin/sshd|/usr/bin/ssh|/usr/bin/sftp|/usr/bin/scp)")', '2025-09-05 20:31:42.804147', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (951, 'System Linux: System Log File Deletion', 3, 3, 3, 'Defense Evasion', 'Clear Linux or Mac System Logs', 'Identifies the deletion of sensitive Linux system logs. This may indicate an attempt to evade detection or destroy forensic evidence on a system.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1070/002/"]', 'safe("log.message", "").matches("(/var/run/utmp|/var/log/wtmp|/var/log/btmp|/var/log/lastlog|/var/log/faillog|/var/log/syslog|/var/log/messages|/var/log/secure|/var/log/auth.log|/var/log/boot.log|/var/log/kern.log)") && !safe("log.message", "").contains("gzip") && safe("log.message", "").matches("(rm |shred -u)")', '2025-09-05 20:32:41.848549', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (933, 'Critical System Configuration Changes in RHEL', 3, 3, 3, 'Persistence, Privilege Escalation', 'System Configuration Modification', 'Detects critical system configuration changes in RHEL including modifications to system services, SELinux settings, firewall rules, and other security-relevant configurations that could indicate unauthorized access or system compromise. + +Next Steps: +1. Verify the legitimacy of the configuration change with the system administrator +2. Review the change details and determine if it aligns with authorized maintenance windows +3. Check if the user account making the change has proper authorization +4. Examine system logs for any suspicious activity preceding the configuration change +5. If unauthorized, immediately investigate for potential compromise and consider reverting changes +6. Review access logs and authentication events for the affected user account +', '["https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/html/security_hardening/","https://attack.mitre.org/techniques/T1543/"]', '(safe("log.type", "") in ["CONFIG_CHANGE", "SYSCALL", "PATH"] && + (safe("log.path", "").matches("/etc/selinux|/etc/systemd|/etc/security|/etc/pam\\.d|/etc/sudoers|/etc/ssh") || + safe("log.name", "").matches("/etc/selinux|/etc/systemd|/etc/security|/etc/pam\\.d|/etc/sudoers|/etc/ssh"))) || + (safe("log.program", "") in ["systemctl", "semanage", "setsebool", "firewall-cmd", "authconfig"] && + safe("log.message", "").matches("changed|modified|updated|disabled|enabled")) || + (safe("log.message", "").matches("SELinux.*disabled|SELinux.*permissive|firewall.*stopped|service.*masked") && + safe("log.severity", "") in ["warning", "error", "critical"]) || + (safe("log.audit_type", "") == "CONFIG_CHANGE" && + safe("log.success", "") == "yes" && + safe("log.exe", "").matches("/usr/bin/yum|/usr/bin/dnf|/usr/bin/rpm"))', '2025-09-05 20:44:53.70031', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1252, 'AdminSDHolder Abuse Detection', 3, 3, 2, 'Persistence, Privilege Escalation', 'T1098 - Account Manipulation', '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"]', 'safe("log.eventCode", 0.0) in [double(4662), double(5136), double(4670)] && + safe("log.winlogChannel", "") == "Security" && + (safe("log.winlogEventDataObjectName", "").matches(".*CN=AdminSDHolder,CN=System.*") || + safe("log.winlogEventDataObjectDN", "").matches(".*CN=AdminSDHolder,CN=System.*")) && + (safe("log.winlogEventDataOperationType", "") in ["Object Access", "Write Property"] || + safe("log.winlogEventDataAccessMask", "") in ["0x20000", "0x40000", "0x80000"] || + safe("log.action", "").matches(".*Permissions.*changed.*")) && + safe("log.winlogEventDataSubjectUserName", "") != "SYSTEM"', '2025-09-05 21:41:00.717062', true, false, 'origin', '["log.winlogEventDataSubjectUserName","log.winlogEventDataObjectName"]', '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1311, 'Windows Defender: Antimalware engine found malware or other potentially unwanted software', 1, 2, 3, 'Execution', '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/"]', 'safe("log.eventCode", 0.0) in [double(1006), double(1015), double(1116)] && safe("log.winlogProviderName", "") == ''SecurityCenter''', '2025-09-05 22:06:37.01159', true, false, 'target', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1304, 'Windows: A user account was added to administration groups', 1, 2, 3, 'Privilege Escalation', 'Domain or Tenant Policy Modification', 'This alert is generated every time a user account is added to administrations groups. This event generates on domain controllers, member servers, and workstations', '["https://attack.mitre.org/tactics/TA0004","https://attack.mitre.org/techniques/T1484/"]', 'safe("log.eventCode", 0.0) in [double(4732), double(4728)] && + (safe("log.winlogEventDataTargetUserSid","").endsWith(''-544'') || + safe("log.winlogEventDataTargetUserSid","").endsWith(''-512'') || + safe("log.winlogEventDataTargetUserSid","").endsWith(''-518'') || + safe("log.winlogEventDataTargetUserSid","").endsWith(''-519'') || + safe("log.winlogEventDataTargetUserSid","").endsWith(''-520'') || + safe("log.winlogEventDataTargetUserSid","").endsWith(''-500'') || + safe("log.winlogEventDataTargetUserSid","").endsWith(''-548'') || + safe("log.winlogEventDataTargetUserSid","").endsWith(''-549''))', '2025-09-05 22:26:54.876677', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1306, 'Windows: A user account was removed from administration groups', 1, 2, 3, 'Impact', 'Account Access Removal', 'When an alert is generated, it is essential to immediately investigate the removal of the user from the administrators group. Verify if the action was authorized and performed by legitimate administrators. If the deletion was unauthorized, immediately restore the user to the administrators group and assess any potential impact caused by this unwanted action. Also, review and enforce the security of the environment to prevent unauthorized changes to the membership of the administrators group.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1531/"]', 'safe("log.eventCode", 0.0) in [double(4733), double(4729)] && (safe("log.winlogEventDataTargetUserSid","").endsWith(''-544'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-512'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-518'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-519'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-520'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-500'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-548'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-549''))', '2025-09-05 22:05:07.288944', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1312, 'Windows Defender: Antimalware platform failed performing an action to protect you from potentially unwanted software', 1, 2, 3, 'Defense Evasion', 'Impair Defenses', 'This rule is triggered when the system detects that the antimalware platform has experienced a failure or error while trying to perform a protection action against potentially unwanted software. This alert is essential to identify situations where the system could not be adequately protected against unwanted software, which can increase the risk of infections and compromise system security.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/"]', 'safe("log.eventCode", 0.0) in [double(1118), double(1119)] && safe("log.winlogProviderName", "") == "SecurityCenter"', '2025-09-05 22:07:14.821138', true, false, 'target', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1313, 'Windows Defender: Protection Disabled', 1, 2, 3, 'Defense Evasion', '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/"]', 'safe("log.eventCode", 0.0) in [double(5001), double(5012)]', '2025-09-05 22:07:51.368625', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1360, 'Windows: Microsoft security essentials - Virus detected', 3, 3, 2, 'Privilege Escalation', '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/"]', 'safe("log.eventCode", 0.0) in [double(1107), double(1117), double(1116), double(1118), double(1119)] && safe("log.winlogProviderName", "") == "Microsoft Antimalware"', '2025-09-05 22:08:55.011182', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1263, 'NTDS.dit Extraction Attempt', 3, 3, 1, 'Credential Access', 'T1003.003 - OS Credential Dumping: NTDS', '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"]', ' safe("log.eventCode", 0.0) in [double(4663), double(4656)] && + safe("log.winlogChannel", "") == "Security" && + (safe("log.winlogEventDataObjectName", "").matches(".*\\\\ntds\\.dit") || + safe("log.winlogEventDataObjectName", "").matches(".*\\\\NTDS\\\\.*") || + safe("log.winlogEventDataProcessName", "").matches(".*\\\\ntdsutil\\.exe") || + safe("log.winlogEventDataProcessName", "").matches(".*\\\\vssadmin\\.exe")) && + safe("log.winlogEventDataAccessMask", "") != "0x0"', '2025-09-05 22:26:26.550879', true, false, 'origin', '["adversary.host"]', '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1410, 'Windows: User logged using Remote Desktop Connection from loopback address, possible exploit over reverse tunneling using stolen credentials', 3, 2, 1, 'Potentially Compromised System', '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/"]', 'safe("log.eventData.logonType", "") == "10" && safe("log.origin.ips", "") in [''::1'',''127.0.0.1''] && safe("log.eventCode", 0.0) in [double(528), double(540), double(673), double(4624), double(4769)]', '2025-09-05 22:30:01.475187', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1489, 'Windows: A powered user account was created', 2, 2, 3, 'Privilege Escalation', 'Domain Policy Modification', 'This alert is generated every time a new powered account is created. This event generates on domain controllers, member servers, and workstations.', '["https://attack.mitre.org/tactics/TA0004","https://attack.mitre.org/techniques/T1484/","https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/appendix-l--events-to-monitor"]', 'safe("log.eventDataTargetSid", "").endsWith("-547") && safe("log.eventCode", 0.0) in [double(4732), double(4728), double(632), double(636)]', '2025-09-05 22:35:42.880566', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":"4720"},{"field":"log.eventCode","operator":"filter_term","value":"624"}],"or":[],"within":"now-60s","count":1}]'); +INSERT INTO public.utm_correlation_rules VALUES (1490, 'Windows: An administrative account was removed from power user group', 2, 3, 1, 'Impact', 'Account Access Removal', 'This alert is generated every time a user account is removed from power user groups. This event generates on domain controllers, member servers, and workstations', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1531/","https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/appendix-l--events-to-monitor"]', 'safe("log.eventCode", 0.0) in [double(4733), double(637), double(4729), double(633)] && safe("log.eventDataTargetSid", "").endsWith("-547")', '2025-09-05 22:36:04.037241', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1478, 'Windows Firewall rule has been added, modified or deleted to the Windows Defender Firewall exception list.', 3, 3, 2, 'Potentially Malicious Activity', 'Defense Evasion', 'Detects changes to the Windows Defender Firewall exception list, which may indicate an attempt to evade detection.', '["https://learn.microsoft.com/en-us/windows/security/threat-protection/windows-firewall/firewall-event-ids"]', 'safe("log.winlogEventDataLogName", "") == "Microsoft-Windows-Windows Firewall With Advanced Security/Firewall" && safe("log.eventCode", 0.0) in [double(2004), double(2005), double(2006)]', '2025-09-05 22:36:37.021315', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1268, 'SID History Injection Attempt', 3, 3, 1, 'Defense Evasion, Privilege Escalation', 'T1134.005 - Access Token Manipulation: SID-History Injection', '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"]', 'safe("log.eventCode", 0.0) in [double(4765), double(4766)] && safe("log.winlogChannel", "") == "Security"', '2025-09-05 22:41:12.679709', true, false, 'origin', '["target.user","target.host"]', '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1318, 'Clearing Windows Event Logs with wevtutil', 1, 2, 3, 'Defense Evasion', '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/"]', 'safe("log.message", "").matches(''(/e:false|cl|clear-log|Clear-EventLog)'') && safe("log.winlogEventDataLogonProcessName", "").matches(''wevtutil.exe'')', '2025-09-05 22:59:17.588549', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1319, 'Windows: NTDS or SAM Database File Copied', 3, 3, 3, 'Credential Access', '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/"]', 'safe("log.winlogEventDataLogonProcessName", "").matches(''(Cmd.Exe|PowerShell.EXE|XCOPY.EXE|esentutl.exe|cmd.exe|powershell.exe)'') && + safe("log.message", "").matches(''(copy|xcopy|Copy-Item|move|cp|mv|/y|/vss|/d)'') && safe("log.message", "").matches(''(\\ntds.dit|\\config\\SAM|\\(.+)\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy(.+)\\|/system32/config/SAM)'')', '2025-09-05 23:15:49.443817', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1348, 'Windows: IIS HTTP Logging Disabled', 3, 2, 3, 'Defense Evasion', '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/"]', 'safe("log.winlogEventDataProcessName", "").contains(''appcmd.exe'') && safe("log.message", "").matches(''(/dontLog(.+):(.+)True)'') && !(safe("log.parentProcessName", "").contains(''iissetup.exe''))', '2025-09-05 23:50:32.415112', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1391, 'Windows: Suspicious Execution from mounted device', 1, 2, 3, 'Defense Evasion', '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/"]', 'safe("log.eventType", "") == ''start'' && safe("log.process.executable", "").matches("(^C:\\\\)") && safe("log.processWorkingDirectory", "").matches("((^\\w:\\\\)") && !safe("log.processWorkingDirectory", "").matches("(^C:\\\\)") && + safe("log.processParentName", "").contains("explorer.exe") && safe("log.processName", "") in [''rundll32.exe'', ''mshta.exe'', ''powershell.exe'', ''pwsh.exe'', ''cmd.exe'', ''regsvr32.exe'', ''cscript.exe'', ''wscript.exe'']', '2025-09-06 00:01:14.413568', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1416, 'Windows: Persistence via Update Orchestrator Service Hijack', 2, 3, 2, 'Persistence', '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/"]', 'safe("log.message", "").contains("UsoSvc") && + safe("log.eventDataParentProcessName", "").matches("C:\\\\Windows\\\\System32\\\\svchost\\.exe") && + !safe("log.winlogEventDataProcessName", "").matches("MoUsoCoreWorker\\.exe|OfficeC2RClient\\.exe") && + !safe("log.winlogEventDataProcessName", "").matches("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")', '2025-09-06 00:25:27.423917', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1422, 'Windows: Suspicious PowerShell Engine ImageLoad', 1, 3, 2, 'Execution', '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/"]', '!(safe("log.winlogEventDataProcessName", "") in ["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"]) && + !(safe("log.winlogEventDataProcessName", "").matches(''(C:\\Windows\\System32\\RemoteFXvGPUDisablement.exe|C:\\Windows\\System32\\sdiagnhost.exe|C:\\Program Files( \\(x86\\))?\\(.+)\\.exe)'')) && + safe("log.message", "") in ["System.Management.Automation.ni.dll", "System.Management.Automation.dll"]', '2025-09-06 00:31:58.012388', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1432, 'Windows: SeDebugPrivilege Enabled by a Suspicious Process', 1, 3, 2, 'Privilege Escalation', '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/"]', 'safe("log.action", "").matches(''(Token Right Adjusted Events)'') && safe("log.eventProvider", "").matches(''(Microsoft-Windows-Security-Auditing)'') && + safe("log.eventDataEnabledPrivilegeList", "").matches(''(SeDebugPrivilege)'') && !(safe("log.eventDataSubjectUserSid", "") in [''S-1-5-18'', ''S-1-5-19'', ''S-1-5-20'']) && !(safe("log.eventDataProcessName", "").matches(''(:\\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)''))', '2025-09-06 00:57:58.530185', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1441, 'Windows: Bypass UAC via Sdclt', 3, 3, 2, 'Privilege Escalation', 'Abuse Elevation Control Mechanism: Bypass User Account Control', 'Identifies User Account Control (UAC) bypass via sdclt.exe. Attackers bypass UAC to stealthily execute code with elevated permissions.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1548/002/"]', 'safe("log.winlogEventDataProcessName", "").contains("sdclt.exe") && safe("log.message", "").contains("/kickoffelev") && + safe("log.winlogEventDataParentProcessName", "").contains("sdclt.exe") && !safe("log.winlogEventDataProcessName", "").matches("(C:\\Windows\\System32\\sdclt.exe|C:\\Windows\\System32\\control.exe|C:\\Windows\\SysWOW64\\sdclt.exe|C:\\Windows\\SysWOW64\\control.exe)")', '2025-09-06 00:59:06.118034', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.winlogEventDataProcessID.keyword","operator":"filter_term","value":"{{.log.winlogEventDataProcessID}}"}],"or":[],"within":"now-5m","count":3}]'); +INSERT INTO public.utm_correlation_rules VALUES (1491, 'Windows: New ActiveSyncAllowedDeviceID Added via PowerShell', 3, 2, 1, 'Persistence', '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/"]', 'safe("log.eventDataProcessName", "") in ["powershell.exe", "pwsh.exe", "powershell_ise.exe"] && safe("log.message", "").matches(''(Set-CASMailbox(.+)ActiveSyncAllowedDeviceIDs)'')', '2025-09-06 11:41:32.264958', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1500, 'Windows: Possible ransomware attack detected. High Volume of File Deletion Events.', 1, 3, 3, 'Ransomware', 'Impact: 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 high volume of file deletion events has been detected, potentially indicating ransomware activity.', '["https://attack.mitre.org/tactics/TA0040/"]', 'safe("log.eventCode", 0.0) == double(4660)', '2025-09-06 11:50:20.67611', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1503, 'Windows: Possible ransomware attack detected. Unexpected PowerShell Activity.', 2, 3, 2, 'Ransomware', 'Execution: Scripting', '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. An unexpected PowerShell execution has been detected, potentially indicating ransomware or other malicious activity.', '["https://attack.mitre.org/techniques/T1059/"]', ' safe("log.eventDataObjectName", "").contains(".ps1") && !(safe("log.winlogEventDataSubjectUserName", "") in [''SYSTEM'', ''NETWORK SERVICE'', ''LOCAL SERVICE'']) && + safe("log.winlogEventDataProcessName", "").matches(''(cmd.exe|powershell.exe|mmc.exe|regedit.exe)'') && (safe("log.eventCode", 0.0) == double(4688) || safe("log.eventCode", 0.0) == double(592))', '2025-09-06 12:02:26.06531', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (119, 'AWS IAM Deactivation of MFA Device', 3, 2, 2, 'Account Access Removal', 'Impact', 'Identifies the deactivation of a specified multi-factor authentication (MFA) device and removes it from association with the user name for which it was originally enabled. In AWS Identity and Access Management (IAM), a device must be deactivated before it can be deleted', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1531/","https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/deactivate-mfa-device.html","https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeactivateMFADevice.html"]', 'safe("log.eventSource", "") == "iam.amazonaws.com" && (safe("log.eventName", "") == "DeactivateMFADevice" || safe("log.eventName", "") == "DeleteVirtualMFADevice")', '2025-09-03 12:27:56.855017', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (120, 'AWS IAM Group Deletion', 3, 2, 2, 'Account Access Removal', 'Impact', 'Identifies the deletion of a specified AWS Identity and Access Management (IAM) resource group. Deleting a resource group does not delete resources that are members of the group; it only deletes the group structure', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1531/","https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iam/delete-group.html","https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteGroup.html"]', 'safe("log.eventSource", "") == "iam.amazonaws.com" && safe("log.eventName", "") == "DeleteGroup"', '2025-09-03 12:27:57.736381', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (121, 'AWS RDS Cluster Deletion', 3, 2, 2, 'Data Destruction', 'Impact', 'Identifies the deletion of an Amazon Relational Database Service (RDS) Aurora database cluster or global database cluster', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1485/","https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/delete-db-cluster.html","https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DeleteDBCluster.html","https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/delete-global-cluster.html","https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DeleteGlobalCluster.html"]', 'safe("log.eventSource", "") == "rds.amazonaws.com" && safe("log.eventName", "") in ["DeleteDBCluster", "DeleteGlobalCluster"]', '2025-09-03 12:27:58.696605', true, false, 'origin', NULL, '[]'); @@ -55,7 +419,7 @@ Next Steps: 5. If malicious, revoke the application consent and remove the app registration 6. Consider implementing application consent policies to prevent unauthorized app installations ', '["https://learn.microsoft.com/en-us/defender-office-365/detect-and-remediate-illicit-consent-grants","https://attack.mitre.org/techniques/T1098/003/"]', 'safe("action", "") == "Consent to application" && safe("actionResult", "") == "Success"', '2025-09-03 14:46:46.741549', true, false, 'origin', '["origin.user","log.appAccessContextClientAppId"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1484, 'Windows: Probable spoofing attack', 2, 1, 3, 'Impact', 'IP address spoofing', 'IPsec dropped an inbound clear text packet that should have been secured. This is usually due to the remote computer changing its IPsec policy without informing this computer. This could also be a spoofing attack attempt.', '["https://attack.mitre.org/tactics/TA0040","https://attack.mitre.org/techniques/T1499/","https://www.windows-security.org/windows-event-id/4963-ipsec-dropped-an-inbound-clear-text-packet-that-should-have-been-secured-this-is-usually-due-to-the"]', 'safe("log.eventCode", 0.0) in [4961, 4649]', '2025-09-04 21:15:03.324958', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1484, 'Windows: Probable spoofing attack', 2, 1, 3, 'Impact', 'IP address spoofing', 'IPsec dropped an inbound clear text packet that should have been secured. This is usually due to the remote computer changing its IPsec policy without informing this computer. This could also be a spoofing attack attempt.', '["https://attack.mitre.org/tactics/TA0040","https://attack.mitre.org/techniques/T1499/","https://www.windows-security.org/windows-event-id/4963-ipsec-dropped-an-inbound-clear-text-packet-that-should-have-been-secured-this-is-usually-due-to-the"]', 'safe("log.eventCode", 0.0) in [double(4961), double(4649)]', '2025-09-05 22:32:56.803293', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (110, 'AWS WAF Rule or Rule Group Deletion', 3, 2, 2, 'Impair Defenses', 'Defense Evasion', 'Identifies the deletion of a specified AWS Web Application Firewall (WAF) rule or rule group', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/","https://awscli.amazonaws.com/v2/documentation/api/latest/reference/waf/delete-rule-group.html","https://docs.aws.amazon.com/waf/latest/APIReference/API_waf_DeleteRuleGroup.html"]', 'safe("log.eventName", "") == ''DeleteRule'' || safe("log.eventName", "") == ''DeleteRuleGroup''', '2025-09-03 12:27:13.368517', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (123, 'AWS RDS Instance/Cluster Stoppage', 3, 2, 2, 'Service Stop', 'Impact', 'Identifies that an Amazon Relational Database Service (RDS) cluster or instance has been stopped. Adversaries may stop or disable services on a system to render those services unavailable to legitimate users. Stopping critical services or processes can inhibit or stop response to an incident or aid in the adversary''s overall objectives to cause damage to the environment.

Potential false positives
Valid clusters or instances may be stopped by a system administrator. Verify whether the user identity, user agent, and/or hostname should be making changes in your environment. Cluster or instance stoppages from unfamiliar users or hosts should be investigated. If known behavior is causing false positives, it can be exempted from the rule.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1489/","https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/stop-db-cluster.html","https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_StopDBCluster.html","https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/stop-db-instance.html","https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_StopDBInstance.html"]', 'safe("log.eventSource", "") == "rds.amazonaws.com" && safe("log.eventName", "") in ["StopDBCluster", "StopDBInstance"]', '2025-09-03 12:27:59.994174', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (332, 'GCP probable hijacked account', 3, 3, 2, 'Collection', 'Archive Collected Data', 'A user''s account was disabled because Google has detected a suspicious activity indicating it might have been compromised. Hijacked account can be used to perform other attacks like data collection and exfiltration', '["https://attack.mitre.org/tactics/TA0009/","https://attack.mitre.org/techniques/T1560"]', 'safe("log.protoPayload.methodName", "") == ''google.login.LoginService.accountDisabledHijacked''', '2025-09-03 14:59:31.226528', true, false, 'target', NULL, '[]'); @@ -128,21 +492,10 @@ Next Steps: - Consider manually triggering updates on affected endpoints - Review system logs for any related errors during the update window ', '["https://www.bitdefender.com/business/support/en/77212-237089-event-types.html","https://attack.mitre.org/techniques/T1562/001/"]', 'safe("log.eventType", "") == "Update" && (safe("log.message", "").contains("failed") || safe("log.message", "").contains("error")) && safe("log.hostId", "") != ""', '2025-09-03 11:53:41.082004', true, false, 'origin', '["log.hostId"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (905, 'SystemD Service Manipulation', 1, 3, 2, 'Persistence, Privilege Escalation', 'T1543.002 - Create or Modify System Process: Systemd Services', 'Detects suspicious systemd service manipulations including creation of new services, modification of existing services, or attempts to mask/disable critical system services which could indicate persistence mechanisms or system sabotage. - -Next Steps: -1. Verify if the service manipulation was authorized and performed by legitimate administrators -2. Examine the specific service being modified - check if it''s a critical system service -3. Review the user account and host performing the action for signs of compromise -4. Investigate any new service files created in /etc/systemd/system/ or /lib/systemd/system/ -5. Check for persistence mechanisms by examining service files for suspicious executables or scripts -6. Correlate with other system activities around the same time to identify potential attack chains -7. If unauthorized, immediately disable the suspicious service and investigate the source of compromise -', '["https://attack.mitre.org/techniques/T1543/002/","https://www.freedesktop.org/software/systemd/man/systemd.service.html"]', '(safe("log.process", "").contains("systemctl") || safe("log.process", "").contains("systemd")) && (safe("log.message", "").contains("Created symlink") && safe("log.message", "").contains("/etc/systemd") || safe("log.message", "").contains("Removed symlink") && safe("log.message", "").contains("/etc/systemd") || safe("log.message", "").contains("enable") && safe("log.message", "").contains(".service") || safe("log.message", "").contains("disable") && safe("log.message", "").contains(".service") || safe("log.message", "").contains("mask") && safe("log.message", "").contains(".service") || safe("log.message", "").contains("unmask") && safe("log.message", "").contains(".service") || safe("log.message", "").contains("daemon-reload") || safe("log.message", "").contains("Failed to start") && safe("log.message", "").contains(".service") || safe("log.message", "").contains("Reloading") && safe("log.message", "").contains(".service"))', '2025-09-03 16:33:39.381903', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1473, 'Windows Defender: ERROR: REMOVE NOT SUPPORTED', 1, 2, 3, 'Potentially Compromised System', 'User Execution', 'Detects the ''REMOVE NOT SUPPORTED'' error event in Windows Defender, which may indicate that a removal operation is not supported for a specific threat.', '["https://learn.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/troubleshoot"]', 'safe("log.winlogEventDataLogName", "") == "Microsoft-Windows-Windows Defender/Operational" && safe("log.winlogEventDataErrorCode", "") == "0x80508026"', '2025-09-04 21:12:14.013707', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (944, 'System Linux: Kernel module load via insmod', 3, 3, 3, 'Persistence', 'Kernel Modules and Extensions', 'Detects the use of the insmod binary to load a Linux kernel object file. Threat actors can use this binary, given they have root privileges, to load a rootkit on a system providing them with complete control and the ability to hide from security products. Manually loading a kernel module in this manner should not be at all common and can indicate suspcious or malicious behavior.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1547/006/"]', 'safe("log.message", "").matches("(insmod (.+).ko)")', '2025-09-03 16:36:57.09112', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1409, 'Windows: Probable Dos Attack', 1, 2, 3, 'Impact', 'Denial of Service (DoS)', 'A Denial of Service (DoS) attack is an attempt to make a machine or network resource unavailable to the intended users. One common method of attack involves saturating the target machine with external communications requests so that it cannot respond to legitimate traffic or the machine responds so slowly that it is essentially useless.', '["https://attack.mitre.org/tactics/TA0040","https://attack.mitre.org/techniques/T1499/","https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5149","https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5148"]', 'safe("log.eventCode", 0.0) in [5148, 5149, 550]', '2025-09-04 20:52:54.100248', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1411, 'Windows: Probable defense evasion attack, IPsec or Firewall not working', 2, 3, 1, 'Defense Evasion', 'Impair Defenses: Disable or Modify Tools', 'The adversary is trying to avoid being detected. Defense Evasion consists of techniques that adversaries use to avoid detection throughout their compromise. Techniques used for defense evasion include uninstalling/disabling security software or obfuscating/encrypting data and scripts. Adversaries also leverage and abuse trusted processes to hide and masquerade their malware. Other tactics’ techniques are cross-listed here when those techniques include the added benefit of subverting defenses. Please see the logs attached to this alert for additional details.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/001/"]', 'safe("log.eventCode", 0.0) in [4710, 5479, 5483, 5484, 5025, 5030]', '2025-09-04 20:52:54.905064', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1409, 'Windows: Probable Dos Attack', 1, 2, 3, 'Impact', 'Denial of Service (DoS)', 'A Denial of Service (DoS) attack is an attempt to make a machine or network resource unavailable to the intended users. One common method of attack involves saturating the target machine with external communications requests so that it cannot respond to legitimate traffic or the machine responds so slowly that it is essentially useless.', '["https://attack.mitre.org/tactics/TA0040","https://attack.mitre.org/techniques/T1499/","https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5149","https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5148"]', 'safe("log.eventCode", 0.0) in [double(5148), double(5149), double(550)]', '2025-09-05 22:29:35.540502', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1411, 'Windows: Probable defense evasion attack, IPsec or Firewall not working', 2, 3, 1, 'Defense Evasion', 'Impair Defenses: Disable or Modify Tools', 'The adversary is trying to avoid being detected. Defense Evasion consists of techniques that adversaries use to avoid detection throughout their compromise. Techniques used for defense evasion include uninstalling/disabling security software or obfuscating/encrypting data and scripts. Adversaries also leverage and abuse trusted processes to hide and masquerade their malware. Other tactics’ techniques are cross-listed here when those techniques include the added benefit of subverting defenses. Please see the logs attached to this alert for additional details.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/001/"]', 'safe("log.eventCode", 0.0) in [double(4710), double(5479), double(5483), double(5484), double(5025), double(5030)]', '2025-09-05 22:30:23.156276', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (8, 'Fileless Malware Detection', 2, 2, 3, 'Defense Evasion, Privilege Escalation', 'T1055 - Process Injection', 'Detects fileless malware attacks including PowerShell-based attacks, memory injection, and living-off-the-land techniques using Bitdefender GravityZone''s HyperDetect and Command-Line Scanner modules. These attacks execute malicious code directly in memory without writing to disk, making them harder to detect with traditional antivirus. Next Steps: @@ -241,7 +594,7 @@ Next Steps: safe("log.tcp_option_mss", 0) > 65535 || safe("log.tcp_option_window_scale", 0) > 14) ', '2025-09-04 16:08:51.764178', true, false, 'origin', '["adversary.ip","target.ip","log.tcp_options_signature"]', '[{"indexPattern":"v11-log-syslog-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"},{"field":"log.tcp_malformed_options","operator":"filter_term","value":"true"}],"or":null,"within":"now-30m","count":10}]'); -INSERT INTO public.utm_correlation_rules VALUES (1502, 'Windows: Possible ransomware attack detected. Ransomware Note Creation.', 3, 3, 2, 'Ransomware', 'Impact: 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/"]', 'safe("log.eventCode", 0.0) == double(4663) && safe("log.wineventlogEventDataFileName").matches("(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)$")', '2025-09-04 21:17:12.545168', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":"{{.log.eventCode}}"},{"field":"log.wineventlogEventDataFileName.keyword","operator":"filter_term","value":"{{.log.wineventlogEventDataFileName}}"}],"or":null,"within":"now-60s","count":5}]'); +INSERT INTO public.utm_correlation_rules VALUES (1502, 'Windows: Possible ransomware attack detected. Ransomware Note Creation.', 3, 3, 2, 'Ransomware', 'Impact: 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/"]', 'safe("log.eventCode", 0.0) == double(4663) && safe("log.wineventlogEventDataFileName", "").matches("(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)$")', '2025-09-05 20:10:43.209673', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":"{{.log.eventCode}}"},{"field":"log.wineventlogEventDataFileName.keyword","operator":"filter_term","value":"{{.log.wineventlogEventDataFileName}}"}],"or":[],"within":"now-60s","count":5}]'); INSERT INTO public.utm_correlation_rules VALUES (1393, 'Windows: Suspicious WMI Image Load from MS Office', 1, 2, 3, 'Execution', '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/"]', 'safe("log.eventDataProcessName", "").matches("(WINWORD.EXE|EXCEL.EXE|POWERPNT.EXE|MSPUB.EXE|MSACCESS.EXE)") && safe("log.message", "").contains("wmiutils.dll")', '2025-09-04 20:46:19.103873', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (12, 'Malware Outbreak Detection - Multiple Hosts Infected', 3, 3, 2, 'Command and Control', 'T1105 - Ingress Tool Transfer', 'Detects when the same malware signature or threat is detected on multiple endpoints within a short time window. This pattern indicates a potential malware outbreak spreading across the network environment. @@ -258,6 +611,7 @@ Next Steps: 10. Consider engaging incident response team if outbreak involves critical systems ', '["https://www.bitdefender.com/business/support/en/77212-237089-event-types.html","https://attack.mitre.org/techniques/T1105/"]', 'safe("log.eventType", "") == "AntiMalware" && safe("log.severity", "") in ["4", "5"] && safe("log.signatureID", "") != "" && safe("log.syslogHostIP", "") != ""', '2025-09-03 11:55:44.95701', true, false, 'origin', '["log.signatureID","log.syslogHostIP"]', '[{"indexPattern":"v11-log-antivirus-bitdefender-gz-*","with":[{"field":"log.signatureID.keyword","operator":"filter_term","value":"{{.log.signatureID}}"},{"field":"log.eventType.keyword","operator":"filter_term","value":"AntiMalware"}],"or":null,"within":"now-2h","count":10}]'); INSERT INTO public.utm_correlation_rules VALUES (1324, 'Windows: Deleting Backup Catalogs with Wbadmin', 1, 2, 3, 'Impact', '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/"]', 'safe("log.message", "").matches(''(delete(.+)catalog|catalog(.+)delete)'') && safe("log.winlogEventDataProcessName", "").matches(''(wbadmin.exe|WBADMIN.EXE)'')', '2025-09-04 20:23:16.232', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1488, 'Windows: A user account was added to power user groups', 2, 3, 1, 'Privilege Escalation', 'Domain Policy Modification', 'This alert is generated every time a user account is added to power user groups. This event generates on domain controllers, member servers, and workstations', '["https://attack.mitre.org/tactics/TA0004","https://attack.mitre.org/techniques/T1484/","https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/appendix-l--events-to-monitor"]', 'safe("log.eventCode", 0.0) in [double(4732), double(4728), double(632), double(636)] && safe("log.eventDataTargetSid", "").endsWith("-547")', '2025-09-05 22:33:18.158334', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (13, 'Memory-Based Threat Detection', 3, 3, 2, 'Defense Evasion, Privilege Escalation', 'T1055 - Process Injection', 'Detects memory-based threats including process injection, memory manipulation, and fileless malware executing in memory based on Bitdefender GravityZone event types. Next Steps: @@ -292,7 +646,6 @@ Next Steps: - Consider reimaging if system is severely compromised ', '["https://www.bitdefender.com/business/support/en/77212-237089-event-types.html","https://attack.mitre.org/techniques/T1105/"]', 'safe("origin.ip", "") != "" && int(safe("log.severity", "0")) >= 4 && ( safe("log.eventType", "") == "AntiMalware" || safe("log.signatureID", "") == "70000" || safe("log.signatureID", "") == "70001" || safe("log.eventType", "").lowe().contains("malware") || safe("log.eventType", "").lowe().contains("virus") || safe("log.eventType", "").lowe().contains("trojan") || safe("log.eventType", "").lowe().contains("ransomware") || safe("log.eventType", "").lowe().contains("infected") )', '2025-09-03 11:55:46.414372', true, false, 'origin', '["origin.ip"]', '[{"indexPattern":"v11-log-antivirus-bitdefender-gz-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"},{"field":"log.severity","operator":"filter_term","value":"4"}],"or":null,"within":"now-1h","count":5}]'); INSERT INTO public.utm_correlation_rules VALUES (1482, 'Windows: Printer driver failed to load, possible remote code execution using PrinterNightmare exploit: CVE-2021-34527', 3, 2, 1, 'Potentially Compromised System', '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/"]', 'safe("log.eventCode", 0.0) == double(808) && safe("log.severityLabel", "") in [''Error'', ''error'']', '2025-09-04 21:15:01.684678', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1488, 'Windows: A user account was added to power user groups', 2, 3, 1, 'Privilege Escalation', 'Domain Policy Modification', 'This alert is generated every time a user account is added to power user groups. This event generates on domain controllers, member servers, and workstations', '["https://attack.mitre.org/tactics/TA0004","https://attack.mitre.org/techniques/T1484/","https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/appendix-l--events-to-monitor"]', 'safe("log.eventCode", 0.0) in [4732, 4728, 632, 636] && safe("log.eventDataTargetSid", "").endsWith("-547")', '2025-09-04 21:15:05.801137', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (15, 'Multiple Malware Detections from Single Source', 3, 3, 2, 'Command and Control', 'T1105 - Ingress Tool Transfer', 'Detects when multiple malware threats are detected on a single host within a short time period. This could indicate a compromised system actively spreading malware or an attacker launching multiple malware variants. Next Steps: @@ -759,7 +1112,6 @@ Next Steps: (safe("log.message", "").matches("(?i)Syslog\\.global\\.log") && safe("log.message", "").matches("(?i)Set-VMHost")) || safe("log.message", "").matches("(?i)Get-VMHostSysLogServer") ', '2025-09-04 19:00:06.725494', true, false, 'origin', '["origin.hostname"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1329, 'Windows: Domain admins group changed', 3, 3, 3, 'Privilege Escalation', 'Domain Policy Modification', 'Adversaries may modify the configuration settings of a domain to evade defenses and/or escalate privileges in domain environments. Domains provide a centralized means of managing how computer resources (ex: computers, user accounts) can act, and interact with each other, on a network. The policy of the domain also includes configuration settings that may apply between domains in a multi-domain/forest environment. Modifications to domain settings may include altering domain Group Policy Objects (GPOs) or changing trust settings for domains, including federation trusts.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1484/"]', 'safe("log.eventDataTargetSid", "").matches("(^%{S-1-5-21(.+)-512}$|^S-1-5-21(.+)-512$)', '2025-09-04 20:23:18.271111', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (47, 'Machine Learning Detection Anomalies', 3, 3, 2, 'Execution', 'T1204.002 - User Execution: Malicious File', 'Identifies threats detected by ESET''s machine learning engine that analyzes file behavior patterns and characteristics to identify previously unknown malware variants. Machine learning detection indicates advanced malware that may evade signature-based detection methods. Next Steps: @@ -1303,29 +1655,6 @@ Next Steps: 8. Implement additional monitoring for IoT device communications ', '["https://www.sentinelone.com/platform/singularity-ranger/","https://attack.mitre.org/techniques/T1203/"]', 'safe("log.eventDescription", "") != "" && ( safe("log.eventDescription", "").lower().contains("iot") || safe("log.eventDescription", "").lower().contains("embedded") || safe("log.eventDescription", "").lower().contains("firmware") || safe("log.eventDescription", "").lower().contains("smart device") || safe("log.eventDescription", "").lower().contains("industrial") || safe("log.eventDescription", "").lower().contains("scada") || safe("log.eventDescription", "").lower().contains("ics") || safe("log.eventDescription", "").lower().contains("operational technology") || safe("log.eventDescription", "").lower().contains("hmi") || safe("log.eventDescription", "").lower().contains("plc") ) && ( safe("log.eventDescription", "").lower().contains("compromise") || safe("log.eventDescription", "").lower().contains("unauthorized") || safe("log.eventDescription", "").lower().contains("malicious") || safe("log.eventDescription", "").lower().contains("anomaly") || safe("log.eventDescription", "").lower().contains("suspicious") || safe("log.eventDescription", "").lower().contains("backdoor") || safe("log.eventDescription", "").lower().contains("botnet") || safe("log.eventDescription", "").lower().contains("exploit") || safe("log.eventDescription", "").lower().contains("infection") || safe("log.eventDescription", "").lower().contains("threat") )', '2025-09-03 12:19:13.335447', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1453, 'Windows: Volume Shadow Copy Deleted or Resized via VssAdmin', 1, 2, 3, 'Impact', '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/"]', 'safe("log.message", "").matches("((delete|resize)(.+)shadows|shadows(.+)(delete|resize))") && safe("log.eventDataProcessName", "").contains("vssadmin.exe")', '2025-09-04 21:02:16.147511', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (84, 'Kernel-Level Threat Detection', 3, 3, 3, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects kernel-level threats including rootkits, kernel exploits, driver manipulation, and other low-level system attacks that attempt to compromise the operating system kernel. - -Next Steps: -1. Immediately isolate the affected system from the network to prevent lateral movement -2. Verify the threat detection in SentinelOne console and check threat details -3. Run a full system scan on the affected endpoint -4. Check for persistence mechanisms and suspicious kernel modules/drivers -5. Review system logs for any unauthorized kernel-level changes or driver installations -6. If rootkit is confirmed, consider reimaging the system after forensic data collection -7. Update endpoint protection signatures and kernel protection modules -', '["https://attack.mitre.org/techniques/T1068/","https://attack.mitre.org/techniques/T1014/"]', 'safe("log.eventDescription", "").matches("(?i)(kernel|rootkit|driver|ring.?0|system.?level)") || (safe("log.eventDescription", "") != "" && safe("log.eventDescription", "").matches("(?i)(threat|malicious|exploit|compromise|detect|block|prevent|isolate)"))', '2025-09-03 12:19:14.079466', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (85, 'Kernel-Level Threat Detection', 3, 3, 3, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects kernel-level threats including rootkits, kernel exploits, and malicious kernel drivers that attempt to gain elevated privileges or hide malicious activity at the kernel level. - -Next Steps: -1. Immediately isolate the affected endpoint to prevent lateral movement -2. Capture memory dump and disk image for forensic analysis -3. Review process creation logs for suspicious parent-child relationships -4. Check for persistence mechanisms (scheduled tasks, services, registry keys) -5. Verify kernel driver signatures and compare against known good baselines -6. Investigate any network connections from kernel-level processes -7. Run deep endpoint scan with updated signatures -8. Contact security team for incident response procedures -', '["https://www.sentinelone.com/blog/decrypting-sentinelone-detection-the-behavioral-ai-engine-in-real-time-cwpp/","https://attack.mitre.org/techniques/T1068/"]', 'safe("log.eventDescription", "").contains("kernel") && (safe("log.confidencelevel", 0) >= 80 || safe("log.dveventtype", "").contains("kernel") || safe("log.indicatorname", "").contains("kernel"))', '2025-09-03 12:19:14.931295', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (86, 'SentinelOne Mass Deployment Anomalies', 2, 2, 1, 'Resource Development', 'T1608.002 - Deploy: Upload Tool', 'Detects anomalous mass deployment activities in SentinelOne, including rapid agent installations, bulk policy changes, or mass updates that could indicate unauthorized deployment, supply chain attack, or insider threat activity. Next Steps: @@ -1587,6 +1916,7 @@ Next Steps: 6. Check if the source volume contains sensitive data 7. Consider implementing preventive controls using IAM policies or SCPs to restrict snapshot sharing ', '["https://securitylabs.datadoghq.com/cloud-security-atlas/attacks/sharing-ebs-snapshot/","https://attack.mitre.org/techniques/T1537/"]', 'safe("log.eventSource", "") == "ec2.amazonaws.com" && safe("log.eventName", "") == "ModifySnapshotAttribute" && safe("log.errorCode", "") == "" && ( safe("log.requestParameters", "").contains("CREATE_VOLUME_PERMISSION") || safe("log.requestParameters", "").contains("createVolumePermission") || (safe("log.requestParameters", "").contains("group") && safe("log.requestParameters", "").contains("all")) )', '2025-09-03 12:43:00.65438', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (245, 'Azure Event Hub Deletion', 1, 2, 3, 'Impair Defenses', 'Defense Evasion', 'Identifies an Event Hub deletion in Azure. An Event Hub is an event processing service that ingests and processes large volumes of events and data. An adversary may delete an Event Hub in an attempt to evade detection.', '["https://attack.mitre.org/techniques/T1562/","https://attack.mitre.org/tactics/TA0005/"]', 'safe("log.resourceProviderNameValue", "").contains("azure.activitylogs") && safe("log.operationNameValue", "").contains("MICROSOFT.EVENTHUB/NAMESPACES/EVENTHUBS/DELETE") && safe("log.statusValue", "").contains("Succeeded")', '2025-09-03 13:19:31.353676', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (144, 'EC2 Instance Metadata Abuse', 3, 2, 1, 'Credential Access', 'T1552.005 - Unsecured Credentials: Cloud Instance Metadata API', 'Detects potential abuse of EC2 instance metadata service (IMDS) which could indicate SSRF exploitation or credential theft. Monitors for unusual API calls using credentials with IMDSv1 role delivery or suspicious patterns of EC2 metadata access. Next Steps: @@ -1596,7 +1926,7 @@ Next Steps: 4. Investigate any web applications running on the instance for SSRF vulnerabilities 5. Check CloudTrail logs for unusual API calls using the instance profile credentials 6. If unauthorized access is confirmed, rotate the instance profile credentials and enforce IMDSv2 -', '["https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/","https://attack.mitre.org/techniques/T1552/005/"]', '(contains(safe("log.userAgent", ""), "169.254.169.254") || contains(safe("log.sourceIPAddress", ""), "169.254.169.254") || (safe("log.requestParameters", "") != "" && contains(safe("log.requestParameters", ""), "ec2:RoleDelivery\":\"1.0"))) || (safe("log.eventSource", "") == "ec2.amazonaws.com" && safe("log.eventName", "") == "ModifyInstanceMetadataOptions" && (contains(safe("log.requestParameters", ""), "HttpTokens\":\"optional") || (contains(safe("log.requestParameters", ""), "HttpPutResponseHopLimit") && !contains(safe("log.requestParameters", ""), "HttpPutResponseHopLimit\":\"1"))))', '2025-09-03 12:44:58.585608', true, false, 'origin', NULL, '[]'); +', '["https://hackingthe.cloud/aws/exploitation/ec2-metadata-ssrf/","https://attack.mitre.org/techniques/T1552/005/"]', 'safe("log.userAgent", "").contains("169.254.169.254") || safe("log.sourceIPAddress", "").contains("169.254.169.254") || (safe("log.requestParameters", "") != "" && safe("log.requestParameters", "").contains("ec2:RoleDelivery:1.0")) || (safe("log.eventSource", "") == "ec2.amazonaws.com" && safe("log.eventName", "") == "ModifyInstanceMetadataOptions" && (safe("log.requestParameters", "").contains("HttpTokens:optional") || safe("log.requestParameters", "").contains("HttpPutResponseHopLimit") && !safe("log.requestParameters", "").contains("HttpPutResponseHopLimit:1")))', '2025-09-05 15:56:03.245743', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (145, 'AWS GuardDuty High-Severity Finding', 3, 3, 2, 'Defense Evasion, Persistence, Privilege Escalation, Initial Access', 'T1078 - Valid Accounts', 'Detects high-severity findings from AWS GuardDuty indicating potential security threats such as malicious activity, unauthorized access, or compromised instances. GuardDuty analyzes CloudTrail events, VPC Flow Logs, and DNS logs to identify threats. Next Steps: @@ -1607,8 +1937,7 @@ Next Steps: 5. If compromise is confirmed, rotate credentials and review IAM permissions 6. Enable AWS CloudTrail logging if not already enabled 7. Consider implementing AWS Security Hub for centralized security findings -', '["https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html","https://attack.mitre.org/techniques/T1078/"]', 'safe("log.eventSource", "") == "guardduty.amazonaws.com" && safe("log.severity", 0) >= 7', '2025-09-03 12:44:58.924607', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (245, 'Azure Event Hub Deletion', 1, 2, 3, 'Impair Defenses', 'Defense Evasion', 'Identifies an Event Hub deletion in Azure. An Event Hub is an event processing service that ingests and processes large volumes of events and data. An adversary may delete an Event Hub in an attempt to evade detection.', '["https://attack.mitre.org/techniques/T1562/","https://attack.mitre.org/tactics/TA0005/"]', 'safe("log.resourceProviderNameValue", "").contains("azure.activitylogs") && safe("log.operationNameValue", "").contains("MICROSOFT.EVENTHUB/NAMESPACES/EVENTHUBS/DELETE") && safe("log.statusValue", "").contains("Succeeded")', '2025-09-03 13:19:31.353676', true, false, 'origin', NULL, '[]'); +', '["https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html","https://attack.mitre.org/techniques/T1078/"]', 'safe("log.eventSource", "") == "guardduty.amazonaws.com" && safe("log.severity", 0.0) >= double(7)', '2025-09-05 16:04:09.438961', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (146, 'AWS IAM Backdoor Creation Attempts', 3, 3, 2, 'Persistence', 'T1136.003 - Create Account: Cloud Account', 'Detects potential IAM backdoor creation attempts through suspicious IAM user creation, access key generation, or policy attachment activities that could provide persistent access. Next Steps: @@ -1633,18 +1962,6 @@ Next Steps: 9. Review IAM policies for the user/role that made the change to ensure least privilege 10. Document the incident and update security policies if necessary ', '["https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html","https://attack.mitre.org/techniques/T1552/"]', 'safe("log.eventSource", "") == "kms.amazonaws.com" && (safe("log.eventName", "") == "PutKeyPolicy" || safe("log.eventName", "") == "CreateGrant" || safe("log.eventName", "") == "ScheduleKeyDeletion") && safe("log.errorCode", "") == ""', '2025-09-03 12:45:00.685455', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (148, 'Lambda Function Privilege Escalation', 3, 3, 2, 'Privilege Escalation, Defense Evasion', 'T1548 - Abuse Elevation Control Mechanism', 'Detects potential privilege escalation through Lambda functions when IAM policies are attached to roles or users that can be exploited. This may indicate an attacker attempting to escalate privileges by attaching administrative policies to Lambda execution roles. - -Next Steps: -1. Verify if the policy attachment was authorized and follows change management procedures -2. Review the attached policy permissions, especially if AdministratorAccess or IAMFullAccess policies were attached -3. Check the Lambda function''s code and recent invocations for suspicious activity -4. Review CloudTrail logs for other IAM changes by the same user/role -5. Validate if the Lambda function legitimately requires the elevated permissions -6. Consider revoking the policy attachment if unauthorized and investigate the source of the change -7. Check for any unusual Lambda function executions following the policy attachment -8. Review the user/role history for previous privilege escalation attempts -', '["https://bishopfox.com/blog/privilege-escalation-in-aws","https://attack.mitre.org/techniques/T1548/"]', 'safe("log.eventSource", "") == "iam.amazonaws.com" && (safe("log.eventName", "") == "AttachRolePolicy" || safe("log.eventName", "") == "AttachUserPolicy") && safe("log.errorCode", "") == "" && (contains(safe("log.requestParameters.roleArn", ""), "lambda") || contains(safe("log.userIdentity.arn", ""), "lambda") || contains(safe("log.requestParameters.policyArn", ""), "AdministratorAccess") || contains(safe("log.requestParameters.policyArn", ""), "IAMFullAccess"))', '2025-09-03 12:45:01.303508', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (149, 'AWS Macie Sensitive Data Exposure', 3, 1, 1, 'Collection', 'T1530 - Data from Cloud Storage Object', 'Detects when Amazon Macie identifies sensitive data exposure such as personally identifiable information (PII), financial data, or credentials in S3 buckets. Next Steps: @@ -1670,7 +1987,7 @@ Next Steps: 6. Consider implementing SCPs (Service Control Policies) to prevent mass deletions 7. Review and restore deleted resources from backups if necessary 8. Document the incident and update IAM policies to prevent future occurrences -', '["https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html","https://attack.mitre.org/techniques/T1485/"]', '(safe("log.eventName", "") contains "Delete" || safe("log.eventName", "") contains "Terminate" || safe("log.eventName", "") contains "Remove") && safe("log.errorCode", "") == "" && safe("log.eventSource", "") != "s3.amazonaws.com"', '2025-09-03 12:45:02.864894', true, false, 'origin', NULL, '[]'); +', '["https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html","https://attack.mitre.org/techniques/T1485/"]', '(safe("log.eventName", "").contains("Delete") || safe("log.eventName", "").contains("Terminate") || safe("log.eventName", "").contains("Remove")) && safe("log.errorCode", "") == "" && safe("log.eventSource", "") != "s3.amazonaws.com"', '2025-09-05 16:35:08.326306', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (151, 'AWS Network ACL Changes', 2, 3, 2, 'Defense Evasion', 'T1562.007 - Impair Defenses: Disable or Modify Cloud Firewall', 'Detects changes to AWS Network ACLs that could compromise network security. Network ACLs act as a virtual firewall for controlling traffic in and out of subnets. This rule monitors for creation, modification, or deletion of network ACL rules that may allow unauthorized access or disrupt network connectivity. Next Steps: @@ -1754,17 +2071,6 @@ Next Steps: 5. If unauthorized, immediately rotate the accessed secrets and review IAM permissions 6. Check for any subsequent API calls using potentially compromised credentials ', '["https://docs.aws.amazon.com/secretsmanager/latest/userguide/monitoring-cloudtrail.html","https://attack.mitre.org/techniques/T1552/004/"]', 'safe("log.eventSource", "") == "secretsmanager.amazonaws.com" && (safe("log.eventName", "") == "GetSecretValue" || safe("log.eventName", "") == "BatchGetSecretValue") && safe("log.errorCode", "") == ""', '2025-09-03 12:46:00.825764', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (158, 'AWS Security Group Modifications', 2, 3, 2, 'Defense Evasion', 'T1562.007 - Impair Defenses: Disable or Modify Cloud Firewall', 'Detects modifications to AWS security groups that could weaken network security posture. Monitors for changes that add permissive rules or remove restrictive rules, particularly those allowing unrestricted access (0.0.0.0/0 or ::/0). - -Next Steps: -1. Review the security group change details in CloudTrail logs -2. Verify if the change was authorized and follows security policies -3. Check the user/role that made the modification -4. Assess if the new rules expose sensitive resources -5. If unauthorized, immediately revert the changes -6. Review other recent activities from the same source IP or user -7. Consider implementing preventive controls via AWS Config rules or SCPs -', '["https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html","https://attack.mitre.org/techniques/T1562/007/"]', 'safe("log.eventSource", "") == "ec2.amazonaws.com" && safe("log.eventName", "") in ["AuthorizeSecurityGroupIngress", "AuthorizeSecurityGroupEgress", "RevokeSecurityGroupIngress", "RevokeSecurityGroupEgress", "CreateSecurityGroup", "DeleteSecurityGroup", "ModifySecurityGroupRules"] && safe("log.sourceIPAddress", "") != "" && safe("log.errorCode", "") == "" && ( (safe("log.requestParameters.ipPermissions.ipProtocol", "").contains("-1") || safe("log.requestParameters.ipPermissions.cidrIp", "").contains("0.0.0.0/0") || safe("log.requestParameters.ipPermissions.ipv6CidrIp", "").contains("::/0") )) || ( safe("log.requestParameters.ipRanges.cidrIp", "").contains("0.0.0.0/0")) || (safe("log.requestParameters.ipv6Ranges.cidrIpv6", "").contains("::/0")) )', '2025-09-03 12:46:01.648341', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (159, 'AWS STS Token Abuse Detection', 3, 3, 1, 'Defense Evasion, Persistence, Privilege Escalation, Initial Access', 'T1078.004 - Cloud Accounts', 'Detects potential abuse of AWS STS AssumeRole operations. This rule identifies when roles are assumed from unusual IP addresses or when there are multiple role assumptions in a short time period, which could indicate lateral movement or privilege escalation. The rule specifically flags AssumeRole operations performed without MFA authentication. Next Steps: @@ -1786,36 +2092,6 @@ Next Steps: 5. If unauthorized, immediately disable the compromised credentials 6. Document all support case details for potential incident response ', '["https://docs.aws.amazon.com/awssupport/latest/user/case-management.html","https://attack.mitre.org/techniques/T1078/004/"]', 'safe("log.eventSource", "") == "support.amazonaws.com" && (safe("log.eventName", "") == "CreateCase" || safe("log.eventName", "") == "AddCommunicationToCase")', '2025-09-03 12:46:02.826418', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (161, 'AWS Unusual API Call Patterns', 3, 2, 1, 'Execution', 'T1106 - Execution through API', 'Detects unusual API call patterns in AWS that may indicate unauthorized access or reconnaissance activities. This rule triggers when multiple sensitive API calls are made from the same source IP within a short time window, suggesting potential enumeration or discovery activities by attackers. - -Next Steps: -- Review the source IP address and determine if it''s authorized for AWS API access -- Check if the user identity associated with these calls is legitimate and expected -- Examine the specific API calls made to understand the reconnaissance pattern and scope -- Review CloudTrail logs for the full session to identify any successful exploitation attempts -- Check if any resources were modified or accessed following the reconnaissance activities -- Verify if the API calls originated from expected geographical locations -- Consider blocking the source IP if unauthorized activity is confirmed -- Implement additional monitoring for the affected account and resources -', '["https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html","https://attack.mitre.org/techniques/T1106/"]', 'safe("log.eventSource", "") != "" && -safe("log.sourceIPAddress", "") != "" && -safe("log.eventName", "") != "" && -( - safe("log.eventName", "") in ["DescribeSecurityGroups", "DescribeNetworkAcls", "DescribeVpcs", "DescribeSubnets", "DescribeRouteTables", "DescribeInstances", "DescribeSnapshots", "DescribeVolumes", "DescribeImages", "DescribeKeyPairs", "ListBuckets", "GetBucketAcl", "GetBucketPolicy", "ListAccessKeys", "ListUsers", "ListRoles", "ListPolicies", "GetAccountAuthorizationDetails", "GenerateCredentialReport", "GetCredentialReport"] -) && -safe("log.errorCode", "") == "" -', '2025-09-03 12:46:03.340048', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (162, 'AWS VPC Flow Log Anomalies', 3, 2, 2, 'Discovery', 'T1046 - Network Service Discovery', 'Detects anomalies in VPC Flow Logs configuration that could indicate attempts to hide malicious network activity. Monitors for deletion or modification of flow log configurations. - -Next Steps: -1. Verify if the flow log changes were authorized by reviewing the AWS CloudTrail logs for the userIdentityArn -2. Check if the source IP address belongs to known administrative systems or jump boxes -3. Review other activities from the same source IP and user identity in the past 24-48 hours -4. Examine the affected VPC and its resources to understand the impact of disabled flow logging -5. If unauthorized, immediately re-enable flow logs and investigate what network activity may have occurred while logging was disabled -6. Review IAM permissions for the user/role that made these changes to ensure least privilege -7. Consider implementing preventive controls using AWS Config rules or SCPs to prevent unauthorized flow log modifications -', '["https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html","https://attack.mitre.org/techniques/T1046/"]', 'safe("log.eventSource", "") == "ec2.amazonaws.com" && safe("log.eventName", "") in ["DeleteFlowLogs", "CreateFlowLogs", "ModifyFlowLogsAttribute"] && safe("log.sourceIPAddress", "") != "" && safe("log.errorCode", "") == "" && ( safe("log.eventName", "") == "DeleteFlowLogs" || (safe("log.eventName", "") == "CreateFlowLogs" && contains(safe("log.requestParameters.deliverLogsStatus", ""), "FAILED")) || (safe("log.eventName", "") == "ModifyFlowLogsAttribute" && safe("log.requestParameters.deliverLogsStatus", "") == "INACTIVE") )', '2025-09-03 12:46:03.900551', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (163, 'Access Control List (ACL) Modifications', 3, 3, 2, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', 'Detects modifications to Access Control Lists (ACLs) on Cisco ASA devices. Unauthorized ACL changes can open security holes, allow malicious traffic, or block legitimate communications. Monitors for access-list, access-group commands and related syslog messages. Next Steps: @@ -1838,6 +2114,25 @@ Next Steps: 7. Check for any other alerts or incidents associated with the same user or IP address 8. Monitor for repeated anomalies from the same user/IP combination ', '["https://attack.mitre.org/techniques/T1071/","https://www.cisco.com/c/en/us/td/docs/security/asa/syslog/b_syslog/syslogs9.html"]', '(safe("log.messageId", "").matches("^722") || safe("log.messageId", "") == "716058") && safe("log.message", "").matches("(?i)(anyconnect.*lost.*connection|no.*ipv6.*address.*available|svc.*connection.*fail|anyconnect.*session.*inactive|ssl.*tunnel.*drop)")', '2025-09-03 12:54:18.096241', true, false, 'origin', '["origin.user","origin.ip"]', '[{"indexPattern":"v11-log-cisco-asa-*","with":[{"field":"origin.user.keyword","operator":"filter_term","value":"{{.origin.user}}"},{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"}],"or":null,"within":"now-4h","count":5}]'); +INSERT INTO public.utm_correlation_rules VALUES (161, 'AWS Unusual API Call Patterns', 3, 2, 1, 'Execution', 'T1106 - Execution through API', 'Detects unusual API call patterns in AWS that may indicate unauthorized access or reconnaissance activities. This rule triggers when multiple sensitive API calls are made from the same source IP within a short time window, suggesting potential enumeration or discovery activities by attackers. + +Next Steps: +- Review the source IP address and determine if it''s authorized for AWS API access +- Check if the user identity associated with these calls is legitimate and expected +- Examine the specific API calls made to understand the reconnaissance pattern and scope +- Review CloudTrail logs for the full session to identify any successful exploitation attempts +- Check if any resources were modified or accessed following the reconnaissance activities +- Verify if the API calls originated from expected geographical locations +- Consider blocking the source IP if unauthorized activity is confirmed +- Implement additional monitoring for the affected account and resources +', '["https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html","https://attack.mitre.org/techniques/T1106/"]', 'safe("log.eventSource", "") != "" && +safe("log.sourceIPAddress", "") != "" && +safe("log.eventName", "") != "" && +( + safe("log.eventName", "") in ["DescribeSecurityGroups", "DescribeNetworkAcls", "DescribeVpcs", "DescribeSubnets", "DescribeRouteTables", "DescribeInstances", "DescribeSnapshots", "DescribeVolumes", "DescribeImages", "DescribeKeyPairs", "ListBuckets", "GetBucketAcl", "GetBucketPolicy", "ListAccessKeys", "ListUsers", "ListRoles", "ListPolicies", "GetAccountAuthorizationDetails", "GenerateCredentialReport", "GetCredentialReport"] +) && +safe("log.errorCode", "") == "" +', '2025-09-05 17:14:33.114619', true, false, 'origin', '["log.eventName","log.eventSource"]', '[]'); INSERT INTO public.utm_correlation_rules VALUES (250, 'Azure Resource Group Deletion', 1, 2, 3, 'Data Destruction', 'Impact', 'Identifies the deletion of a resource group in Azure, which includes all resources within the group. Deletion is permanent and irreversible. An adversary may delete a resource group in an attempt to evade defenses or intentionally destroy data. ', '["https://attack.mitre.org/techniques/T1485/","https://attack.mitre.org/tactics/TA0040/"]', 'safe("log.resourceProviderNameValue", "").contains("azure.activitylogs") && safe("log.operationNameValue", "").contains("MICROSOFT.RESOURCES/SUBSCRIPTIONS/RESOURCEGROUPS/DELETE") && safe("log.statusValue", "").contains("Succeeded")', '2025-09-03 13:20:05.164603', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (334, 'GCP account is probably used for spamming', 1, 2, 3, 'Initial Access', 'Phishing', 'A user''s account was disabled because Google has become aware that it was used to engage in spamming. Usually, spamming is used to perform other attacks like phishing or spread malware.', '["https://attack.mitre.org/tactics/TA0001","https://attack.mitre.org/techniques/T1566/"]', 'safe("log.protoPayload.methodName ", "") == ''google.login.LoginService.accountDisabledSpamming'' || safe("log.protoPayload.methodName ", "") == ''google.login.LoginService.accountDisabledSpammingThroughRelay''', '2025-09-03 14:59:32.20314', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (165, 'Botnet Command and Control Traffic Detected', 3, 2, 1, 'Command and Control', 'T1071 - Application Layer Protocol', 'Detects botnet command and control traffic identified by Cisco ASA''s dynamic filter/botnet database. Message IDs 338001-338002 indicate blacklisted traffic from/to malicious addresses. This could indicate compromised hosts communicating with known botnet infrastructure. @@ -3569,7 +3864,6 @@ safe("log.message", "").matches("(?i)LW_ERROR_PASSWORD_MISMATCH") || safe("log.message", "").matches("(?i)rejected password for user") || (safe("log.message", "").matches("(?i)vmtoolsd") && safe("log.message", "").matches("(?i)exploit")) ', '2025-09-04 19:00:53.25138', true, false, 'origin', '["origin.hostname"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1338, 'Windows: Control Panel Process with Unusual Arguments', 1, 3, 1, 'Defense Evasion', 'Control Panel', 'Identifies unusual instances of Control Panel with suspicious keywords or paths in the process command line value. Adversaries may abuse control.exe to proxy execution of malicious code.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1218/002/"]', 'safe("log.winlogEventDataProcessName", "").matches("(:\\Windows\\SysWOW64\\control.exe|:\\Windows\\System32\\control.exe)") && safe("log.message", "").matches("(.jpg|.png|.gif|.bmp|.jpeg|.TIFF|.inf|.cpl:(.+)/|../../..|/AppData/Local/|:\\Users\\Public\\|\\AppData\\Local\\)")', '2025-09-04 20:23:55.352152', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1340, 'Windows: Microsoft Build Engine Started by a Script Process', 3, 3, 2, 'Defense Evasion', '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/"]', 'safe("log.winlogEventDataProcessName", "").matches("MSBuild.exe") && safe("log.winlogEventDataParentProcessName", "").matches("(cmd.exe|powershell.exe|wscript.exe|cscript.exe)")', '2025-09-04 20:23:56.172089', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1455, 'Windows: Volume Shadow Copy Deletion via WMIC', 1, 2, 3, 'Impact', '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/"]', 'safe("log.message", "").matches("(delete(.+)shadowcopy|shadowcopy(.+)delete)") && safe("log.eventDataProcessName", "").contains("WMIC.exe")', '2025-09-04 21:02:17.191849', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (340, 'GCP probable Defense Evasion, Pub/Sub Topic Deletion', 2, 3, 3, 'Defense Evasion', 'Impair Defenses', 'Defense Evasion consists of techniques that adversaries use to avoid detection throughout their compromise. Techniques used for defense evasion include uninstalling/disabling security software or obfuscating/encrypting data and scripts. Adversaries also leverage and abuse trusted processes to hide and masquerade their malware. Other tactics are cross-listed here when those techniques include the added benefit of subverting defenses.
Identifies the deletion of a topic in Google Cloud Platform (GCP). In GCP, the publisher-subscriber relationship (Pub/Sub) is an asynchronous messaging service that decouples event-producing and event-processing services. A publisher application creates and sends messages to a topic. Deleting a topic can interrupt message flow in the Pub/Sub pipeline.', '["https://cloud.google.com/pubsub/docs/overview","https://attack.mitre.org/techniques/T1562/","https://attack.mitre.org/tactics/TA0005/"]', 'safe("log.protoPayload.methodName ", "").matches("((.+)?topic(s)?\\.delete|(.+)?v(\\w+)\\.Publisher\\.DeleteTopic)")', '2025-09-03 14:59:34.82448', true, false, 'origin', NULL, '[]'); @@ -3618,7 +3912,6 @@ Next Steps: (safe("log.type", "") == "Event::Endpoint::TamperProtection" && safe("log.action", "") == "blocked") ', '2025-09-03 15:02:42.042005', true, false, 'origin', '["lastEvent.target.host","lastEvent.target.ip"]', '[]'); INSERT INTO public.utm_correlation_rules VALUES (394, 'Probable use of High-risk applications', 3, 3, 2, 'Lateral Movement', 'Application Layer Protocol', 'The Sophos firewall device has detected a use of potential risked applications in enterprise environment like Torrent Clients P2P. Please see the logs attached to this alert for additional details', '["https://attack.mitre.org/tactics/TA0008/"]', 'safe("log.application", "").matches("(Torrent|Proxy|EXE File Download|eMule P2P|Ares P2P)") || safe("log.applicationName", "").matches("(Torrent|Proxy|EXE File Download|eMule P2P|Ares P2P)")', '2025-09-03 15:05:41.765149', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1220, 'Medium level Suricata alert', 2, 2, 2, 'Medium level Suricata alert category', 'Network Scanning', 'Suricata has detected a medium severity alert that may indicate reconnaissance or scanning attempts. This could include port scanning, protocol anomalies, or suspicious network patterns that warrant investigation but may not represent an immediate threat.', '["https://suricata.readthedocs.io/en/latest/"]', 'safe("log.eventType", "") == "alert" && safe("severity", "") == "medium"', '2025-09-04 19:10:15.09095', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (382, 'Active Threat Investigation Case', 3, 3, 2, 'Defense Evasion, Privilege Escalation', 'T1055 - Process Injection', 'Detects when threat cases are opened for investigation indicating potential security incidents requiring analysis, particularly focusing on cases with multiple related detections or high-risk indicators. This rule identifies active threat investigation cases that suggest advanced persistent threats or coordinated attacks. Next Steps: @@ -3674,6 +3967,7 @@ Next Steps: safe("log.response_code", 0) in [401, 403, 429] || safe("log.error", "").matches("(?i)(unauthorized|forbidden|rate.?limit)")) ', '2025-09-03 15:01:27.52098', true, false, 'origin', '["origin.ip","target.host"]', '[{"indexPattern":"v11-log-sophos-central-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"}],"or":null,"within":"now-15m","count":10}]'); +INSERT INTO public.utm_correlation_rules VALUES (1220, 'Medium level Suricata alert', 2, 2, 2, 'Medium level Suricata alert category', 'Network Scanning', 'Suricata has detected a medium severity alert that may indicate reconnaissance or scanning attempts. This could include port scanning, protocol anomalies, or suspicious network patterns that warrant investigation but may not represent an immediate threat.', '["https://suricata.readthedocs.io/en/latest/"]', 'safe("log.eventType", "") == "alert" && safe("severity", "") == "medium"', '2025-09-05 23:19:29.873771', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (363, 'Application Control Block Event', 2, 3, 2, 'Execution', 'T1204 - User Execution', 'Detects when Sophos Central blocks an application from running based on application control policies. This may indicate attempted execution of unauthorized or potentially malicious applications. Next Steps: @@ -3942,7 +4236,6 @@ Next Steps: INSERT INTO public.utm_correlation_rules VALUES (386, 'Probable Sophos denial of service (DoS) attack', 3, 3, 3, 'Impact', 'Denial of Service (DoS)', 'A Denial of Service (DoS) attack is an attempt to make a machine or network resource unavailable to the intended users. One common method of attack involves saturating the target machine with external communications requests so that it cannot respond to legitimate traffic or the machine responds so slowly that it is essentially useless.', '["https://support.sophos.com/support/s/article/KB-000035754?language=en_US","https://docs.sophos.com/nsg/sophos-firewall/18.5/PDF/SF%20syslog%20guide%2018.5.pdf","https://attack.mitre.org/tactics/TA0040","https://attack.mitre.org/techniques/T1499/"]', 'safe("log.component", "").matches("(D|d)o(S|s) (a|A)ttacks") && safe("log.subType", "").matches("^(D|d)o(S|s)$")', '2025-09-03 15:05:36.529391', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (387, 'Probable malware detected by sophos firewall', 3, 3, 3, 'Malware', 'Phishing', 'The Sophos firewall has detected a suspicious event and made some actions according to his configuration', '["https://www.mcafee.com/en-us/antivirus/malware.html","https://www.fortinet.com/resources/cyberglossary/computer-virus"]', 'safe("log.subType", "").matches("^((V|v)irus|PUA)$")', '2025-09-03 15:05:36.999003', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (388, 'High severity Sophos firewall traffic denegations', 3, 3, 3, 'Firewall Alerts', 'General', 'The Sophos firewall device has detected a high severity event asociated with traffic denegations. For the alert purpose high severity is when log priority is: Emergency, Critical or Alert. Please see the logs attached to this alert for additional details.', '["https://docs.sophos.com/central/Customer/help/en-us/central/Customer/concepts/FirewallAlerts.html","https://docs.sophos.com/nsg/sophos-firewall/17.5/Help/en-us/webhelp/onlinehelp/nsg/concepts/LogIDStructure.html","https://docs.sophos.com/nsg/sophos-firewall/17.5/Help/en-us/webhelp/onlinehelp/nsg/sfos/concepts/LogMessages.html","https://docs.sophos.com/nsg/sophos-firewall/18.5/PDF/SF%20syslog%20guide%2018.5.pdf","https://attack.mitre.org/"]', 'safe("log.component", "").matches("(F|f)irewall (R|r)ule") && safe("log.subType", "") in [''Deny'', ''deny'', ''Drop'', ''drop'', ''Denied'', ''denied'', ''Dropped'', ''dropped''] && safe("log.priority", "") in [''Emergency'', ''Critical'', ''Alert'']', '2025-09-03 15:05:37.567253', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (961, 'System Linux: Namespace Manipulation Using Unshare', 3, 3, 2, 'Privilege Escalation', 'Create or Modify System Process', 'Identifies suspicious usage of unshare to manipulate system namespaces. Unshare can be utilized to escalate privileges or escape container security boundaries. Threat actors have utilized this binary to allow themselves to escape to the host and access other resources or escalate privileges.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1543/"]', 'safe("log.message", "").contains("unshare") && !(safe("log.message", "").contains("/usr/bin/snap")) && !(safe("log.message", "").matches("(\\/usr\\/bin\\/udevadm|\\/lib\\/systemd\\/systemd-udevd|\\/usr\\/bin\\/unshare)"))', '2025-09-04 14:16:25.480966', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (389, 'Probable Sophos FTP-bounce attack', 3, 3, 3, 'Exfiltration', 'FTP-bounce attack', 'Sophos firewall has detected a probable FTP-bounce attack. FTP bounce attack is an exploit of the FTP protocol whereby an attacker is able to use the PORT command to request access to ports indirectly through the use of the victim machine, which serves as a proxy for the request, similar to an Open mail relay using SMTP. This technique can be used to port scan hosts discreetly, and to potentially bypass a network Access-control list to access specific ports that the attacker cannot access through a direct connection, for example with the nmap port scanner. Please see the logs attached to this alert for additional details.', '["https://docs.sophos.com/nsg/sophos-firewall/18.5/Help/en-us/webhelp/onlinehelp/CommandLineHelp/DeviceConsole/Set/index.html#advanced-firewall","https://attack.mitre.org/tactics/TA0010/","https://attack.mitre.org/techniques/T1048/","http://www.cert.org/tech_tips/ftp_port_attacks.html"]', 'safe("log.Id", "").endsWith(''010202130'')', '2025-09-03 15:05:38.073649', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1494, 'Windows: Suspicious PrintSpooler Service Executable File Creation', 2, 3, 1, 'Privilege Escalation', '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/"]', '!safe("log.file.path", "").matches("(\\Windows\\System32\\spool\\|:\\Windows\\Temp\\|:\\Users\\)") && safe("log.winlogEventDataProcessName", "").contains("spoolsv.exe") ', '2025-09-04 21:16:05.635994', true, false, 'origin', NULL, '[]'); @@ -4062,7 +4355,6 @@ safe("log.subType", "") == "System" && safe("log.logMessage", "").contains("disconnect") || safe("log.logMessage", "").contains("error"))) ', '2025-09-03 15:06:41.073832', true, false, 'origin', '["log.deviceName","log.component"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1343, 'Windows: Execution via MSSQL xp_cmdshell Stored Procedure', 3, 3, 2, 'Execution', '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/"]', 'safe("log.message", "") in [''diskfree'', ''rmdir'', ''mkdir'', ''dir'', ''del'', ''rename'', ''bcp'', ''XMLNAMESPACES''] && safe("log.winlogEventDataProcessName", ""). contains(''cmd.exe'') && safe("log.winlogEventDataParentProcessName", "").contains(''sqlservr.exe'')', '2025-09-04 20:24:33.013597', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (407, 'GCP probable Impact, Virtual Private Cloud Network Deletion', 1, 2, 3, 'Impact', 'Stop Service', 'Impact consists of techniques that adversaries use to disrupt availability or compromise integrity by manipulating business and operational processes. Techniques used for impact can include destroying or tampering with data. In some cases, business processes can look fine, but may have been altered to benefit the adversaries goals. These techniques might be used by adversaries to follow through on their end goal or to provide cover for a confidentiality breach.
Identifies when a Virtual Private Cloud (VPC) network is deleted in Google Cloud Platform (GCP). A VPC network is a virtual version of a physical network within a GCP project. Each VPC network has its own subnets, routes, and firewall, as well as other elements. An adversary may delete a VPC network in order to disrupt their target''s network and business operations.', '["https://cloud.google.com/vpc/docs/vpc","https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1489"]', 'safe("log.protopayload.methodName", "").matches("(^(.+)\\.(sub)?network(s)?\\.delete$|^(sub)?network(s)?\\.delete$)")', '2025-09-03 15:06:11.916424', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (408, 'GCP probable Impact, Virtual Private Cloud Route Creation', 1, 2, 3, 'Impact', 'Data Destruction', 'Impact consists of techniques that adversaries use to disrupt availability or compromise integrity by manipulating business and operational processes. Techniques used for impact can include destroying or tampering with data. In some cases, business processes can look fine, but may have been altered to benefit the adversaries goals. These techniques might be used by adversaries to follow through on their end goal or to provide cover for a confidentiality breach.
Identifies when a virtual private cloud (VPC) route is created in Google Cloud Platform (GCP). Google Cloud routes define the paths that network traffic takes from a virtual machine (VM) instance to other destinations. These destinations can be inside a Google VPC network or outside it. An adversary may create a route in order to impact the flow of network traffic in their target''s cloud environment.', '["https://cloud.google.com/vpc/docs/routes","https://attack.mitre.org/tactics/TA0040/"]', 'safe("log.protopayload.methodName", "").matches("^((.+)\\.)?route(s)?\\.insert$")', '2025-09-03 15:06:12.340215', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (511, 'ModSecurity Rule Violations Detected', 2, 3, 2, 'Execution', 'T1203 - Exploitation for Client Execution', 'Detects when ModSecurity Web Application Firewall rules are triggered, indicating potential web application attacks or policy violations. This could include SQL injection, XSS, path traversal, or other OWASP Top 10 attack attempts. @@ -4149,25 +4441,6 @@ Next Steps: 7. Consider isolating the endpoint if threat is confirmed 8. Update endpoint security policies if configuration issues are found ', '["https://docs.sophos.com/nsg/sophos-firewall/20.0/Help/en-us/webhelp/onlinehelp/AdministratorHelp/SynchronizedSecurity/index.html","https://attack.mitre.org/techniques/T1518/001/"]', '(safe("log.type", "") == "Event" && safe("log.component", "") == "Synchronized Security") || (safe("log.hb_health", "") != "" && !(safe("log.hb_health", "") in ["No Heartbeat", "Green"])) || (safe("log.component", "") == "Heartbeat" && safe("log.subType", "") in ["Missing", "Red", "Yellow"])', '2025-09-03 15:06:42.932097', true, false, 'origin', '["adversary.ip","adversary.host"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1252, 'AdminSDHolder Abuse Detection', 3, 3, 2, 'Persistence, Privilege Escalation', 'T1098 - Account Manipulation', '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"]', 'safe("log.eventCode", 0.0 in [4662, 5136, 4670] && -safe("log.winlogChannel", "") == "Security" && -(safe("log.winlogEventDataObjectName", "").matches(".*CN=AdminSDHolder,CN=System.*") || - safe("log.winlogEventDataObjectDN", "").matches(".*CN=AdminSDHolder,CN=System.*")) && -(safe("log.winlogEventDataOperationType", "") in ["Object Access", "Write Property"] || - safe("log.winlogEventDataAccessMask", "") in ["0x20000", "0x40000", "0x80000"] || - safe("log.action", "").matches(".*Permissions.*changed.*")) && -safe("log.winlogEventDataSubjectUserName", "") != "SYSTEM" -', '2025-09-04 19:28:20.614904', true, false, 'origin', '["log.winlogEventDataSubjectUserName","log.winlogEventDataObjectName"]', '[]'); INSERT INTO public.utm_correlation_rules VALUES (1253, 'AMSI Bypass Detection', 3, 3, 2, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', '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: @@ -5133,7 +5406,6 @@ Next Steps: 9. Verify the integrity of the repository and look for other suspicious activities 10. Review access logs for the user account that made the modifications ', '["https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows","https://attack.mitre.org/techniques/T1195/001/"]', '(safe("action", "") == "workflows.updated" || safe("action", "") == "workflows.created" || safe("action", "") == "workflow_run.completed" || safe("action", "") == "workflow_dispatch") && safe("log.repositoryName", "") != "" && safe("log.senderLogin", "") != "" && (safe("log.headCommitModified", "").contains(".github/workflows/") || safe("log.headCommitAdded", "").contains(".github/workflows/") || safe("log.path", "").contains(".github/workflows/") || safe("log.filename", "").contains(".github/workflows/"))', '2025-09-03 15:24:13.089027', true, false, 'origin', '["origin.user","target.host"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1348, 'Windows: IIS HTTP Logging Disabled', 3, 2, 3, 'Defense Evasion', '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/"]', 'safe("log.winlogEventDataProcessName", "").contains(''appcmd.exe'') && safe("log.menssage", "").matches(''(/dontLog(.+):(.+)True)'') && !(parentProcessName.contains(''iissetup.exe'')'')', '2025-09-04 20:24:35.471823', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (483, 'Pattern Matching Timeout Attack', 1, 2, 3, 'Impact', 'Algorithmic Complexity Attack', 'Detects input patterns designed to cause excessive processing time in pattern matching algorithms, potentially causing timeouts and service degradation. This rule identifies various malicious patterns including: - Excessively long email addresses or domain names (100+ characters each) - Abnormally long URL hostnames (200+ characters) @@ -6405,26 +6677,6 @@ Next Steps: 6. Review audit logs for patterns of similar violations 7. Check system logs for any privilege escalation attempts around the same time ', '["https://attack.mitre.org/techniques/T1005/","https://documentation.suse.com/sles/15-SP6/html/SLES-all/cha-audit-comp.html"]', 'safe("log.type", "") == "PATH" && safe("log.key", "") != "" && (safe("log.key", "").contains("sensitive") || safe("log.key", "").contains("monitor") || safe("log.key", "").contains("watch")) && safe("log.success", "") == "no"', '2025-09-03 15:30:15.44696', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1263, 'NTDS.dit Extraction Attempt', 3, 3, 1, 'Credential Access', 'T1003.003 - OS Credential Dumping: NTDS', '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"]', 'safe("log.eventCode", 0.0) in [4663, 4656] && -safe("log.winlogChannel", "") == "Security" && -(safe("log.winlogEventDataObjectName", "").matches(".*\\\\ntds\\.dit") || - safe("log.winlogEventDataObjectName", "").matches(".*\\\\NTDS\\\\.*") || - safe("log.winlogEventDataProcessName", "").matches(".*\\\\ntdsutil\\.exe") || - safe("log.winlogEventDataProcessName", "").matches(".*\\\\vssadmin\\.exe")) && -safe("log.winlogEventDataAccessMask", "") != "0x0" -', '2025-09-04 19:30:03.841981', true, false, 'origin', '["adversary.host"]', '[]'); INSERT INTO public.utm_correlation_rules VALUES (1352, 'Windows: LSASS Memory Dump Handle Access', 2, 3, 3, 'Credential Access', '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/"]', 'safe("log.eventCode", 0.0) == double(4656) && safe("log.winlogEventDataObjectName", "").matches(''(:\\Windows\\System32\\lsass.exe|\\Device\\HarddiskVolume[A-Za-z?:\\]([A-Za-z?])?\\Windows\\System32\\lsass.exe)'') && !safe("log.winlogEventDataProcessName", "").matches(''(:\\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)'') && safe("log.winlogEventDataAccessMask", "") in [''0x1fffff'', ''0x1010'', ''0x120089'', ''0x1F3FFF'']', '2025-09-04 20:25:23.950708', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (578, 'Elasticsearch Audit Log Gaps Detected', 2, 3, 2, 'Defense Evasion', 'T1070 - Indicator Removal on Host', 'Detects gaps in Elasticsearch audit logging which may indicate log tampering, service disruption, or attempts to evade detection by disabling audit functionality. This rule identifies audit trail disruptions, audit component errors, or security configuration changes that could compromise logging integrity. @@ -6565,9 +6817,6 @@ Next Steps: - Monitor for any data access or privilege escalation following token abuse - Consider implementing additional authentication controls for token services ', '["https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-token.html","https://attack.mitre.org/techniques/T1078/"]', '(safe("log.authenticationType", "") == "TOKEN" && (safe("actionResult", "") == "failure" || safe("log.eventType", "") == "access_denied")) || (safe("action", "").contains("cluster:admin/xpack/security/token/") && (safe("action", "").contains("/create") || safe("action", "").contains("/invalidate") || safe("action", "").contains("/refresh"))) || (safe("log.component", "") == "Security" && safe("log.message", "").contains("token") && safe("log.level", "") in ["ERROR", "WARN"])', '2025-09-03 15:32:28.504303', true, false, 'origin', '["origin.ip","origin.user"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (951, 'System Linux: System Log File Deletion', 3, 3, 3, 'Defense Evasion', 'Clear Linux or Mac System Logs', 'Identifies the deletion of sensitive Linux system logs. This may indicate an attempt to evade detection or destroy forensic evidence on a system.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1070/002/"]', 'safe("log.message", "").matches("(/var/run/utmp|/var/log/wtmp|/var/log/btmp|/var/log/lastlog|/var/log/faillog|/var/log/syslog|/var/log/messages|/var/log/secure|/var/log/auth.log|/var/log/boot.log|/var/log/kern.log)") && -!(safe("log.message", "").contains("gzip")) && safe("log.message", "").matches("(rm |shred -u)") -', '2025-09-03 16:37:33.303486', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (590, 'Cross-Cluster Search Abuse Detected', 3, 2, 3, 'Collection', 'T1213 - Data from Information Repositories', 'Detects suspicious cross-cluster search activities that may indicate unauthorized data access, reconnaissance, or data exfiltration attempts across multiple Elasticsearch clusters. Cross-cluster search allows querying multiple clusters, and unusual patterns may indicate an attacker attempting to access data across distributed environments. Next Steps: @@ -6669,7 +6918,6 @@ Next Steps: 6. Check for any suspicious processes or scripts that may be modifying map files 7. Analyze network traffic for potential injection payloads targeting HAProxy map functionality ', '["https://www.haproxy.com/blog/introduction-to-haproxy-maps","https://attack.mitre.org/techniques/T1190/"]', 'safe("log.message", "") != "" && (safe("log.message", "").contains("map") || safe("log.message", "").contains("acl")) && (safe("log.message", "").contains("invalid entry") || safe("log.message", "").contains("map update failed") || safe("log.message", "").contains("pattern mismatch") || safe("log.message", "").contains("illegal character") || safe("log.message", "").contains("map reload error") || safe("log.message", "").matches("map .* contains suspicious")', '2025-09-03 15:34:23.980003', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (952, 'System Linux: Modification of OpenSSH Binaries', 3, 3, 2, 'Persistence', 'Create or Modify System Process', 'Adversaries may modify SSH related binaries for persistence or credential access by patching sensitive functions to enable unauthorized access or by logging SSH credentials for exfiltration.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1543/"]', 'safe("log.message", "").matches("libkeyutils.so") && !(safe("log.message", "").matches("(dpkg|yum|dnf|dnf-automatic)")) && safe("log.message", "").matches("(/usr/sbin/sshd|/usr/bin/ssh|/usr/bin/sftp|/usr/bin/scp)")', '2025-09-03 16:37:33.765728', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (601, 'Elasticsearch Transform Abuse Detected', 3, 2, 3, 'Resource Development', 'T1587 - Develop Capabilities', 'Detects suspicious usage of Elasticsearch transforms that could be used for unauthorized data aggregation, exfiltration preparation, or resource exhaustion attacks. Transform operations can be abused to process large amounts of data, create continuous data pipelines for exfiltration, or consume cluster resources maliciously. Next Steps: @@ -7172,7 +7420,6 @@ Next Steps: 5. Consider implementing rate limiting for OPTIONS requests 6. Review web server logs for other reconnaissance activities from the same source ', '["https://attack.mitre.org/techniques/T1595/","https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/02-Configuration_and_Deployment_Management_Testing/06-Test_HTTP_Methods"]', 'safe("log.method", "") == "OPTIONS" && safe("statusCode", 0.0) == double(200) && (safe("target.path", "") == "/" || safe("target.path", "") == "*" || safe("target.path", "").endsWith("/")) && safe("target.ip", "") != "" || !target.ip.matches("^(10\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.|192\\.168\\.)") || safe("log.userAgent", "").matches("(?i).*(scanner|nikto|nessus|qualys|nmap|burp|zap|acunetix|sqlmap|havij).*") || safe("log.userAgent", "") == "" || safe("log.userAgent", "") == "-")', '2025-09-03 15:37:12.314122', true, false, 'origin', '["target.ip","origin.ip"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1398, 'Windows: Microsoft Exchange Worker Spawning Suspicious Processes', 2, 3, 2, 'Initial Access', '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/"]', 'safe("log.winlogEventDataParentProcessName", "").matches("(UMService.exe|UMWorkerProcess.exe)") && !(safe("log.winlogEventDataProcessName").matches("(:\\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)"))', '2025-09-04 20:46:21.663632', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (642, 'IIS Request Filtering Evasion Attempt', 3, 3, 2, 'Defense Evasion', 'T1027 - Obfuscated Files or Information', 'Detects attempts to evade IIS request filtering through various encoding, padding, and obfuscation techniques including URL padding, file extension manipulation, control character injection, and malformed URL encoding. Next Steps: @@ -7206,19 +7453,6 @@ INSERT INTO public.utm_correlation_rules VALUES (645, 'IIS Unicode Bypass Direct ', '["https://www.kb.cert.org/vuls/id/111677","https://attack.mitre.org/techniques/T1027/"]', 'safe("target.path", "").contains("%c0%af") || safe("target.path", "").contains("%c0%ae") || safe("target.path", "").contains("%c1%9c") || safe("target.path", "").contains("%c0%2f") || safe("target.path", "").contains("%c0%5c") || safe("target.path", "").contains("%c1%8s") || safe("target.path", "").contains("%c1%1c") || safe("target.path", "").contains("%c1%af") || safe("target.path", "").contains("%e0%80%af") || safe("target.path", "").contains("%f0%80%80%af") || safe("target.path", "").contains("%u2215") || safe("target.path", "").contains("%u2216") || safe("target.path", "").contains("%uff0e") || safe("target.path", "").contains("..%c0%af") || (safe("target.path", "").contains("winnt") && safe("target.path", "").contains("system32") || (safe("target.path", "").contains("cmd.exe") || safe("target.path", "").contains("command.com")', '2025-09-03 15:37:14.735512', true, false, 'origin', '["origin.ip"]', '[]'); INSERT INTO public.utm_correlation_rules VALUES (956, 'System Linux: Interactive Terminal Spawned via Python', 3, 2, 3, 'Execution', 'Command and Scripting Interpreter', 'Identifies when a terminal (tty) is spawned via Python. Attackers may upgrade a simple reverse shell to a fully interactive tty after obtaining initial access to a host.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1059/"]', 'safe("log.message", "").contains("python") && safe("log.message", "").matches("(import pty; pty.spawn\\(\\''\\/bin\\/(sh|dash|bash)\\''\\))")', '2025-09-03 16:37:35.656883', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1471, 'Windows Defender: ERROR: BAD DB: REMOVE FAILED', 1, 2, 3, 'Potentially Compromised System', 'User Execution', 'Detects the ''BAD DB: REMOVE FAILED'' error event in Windows Defender, which may indicate issues with the removal process from the database.', '["https://learn.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/troubleshoot"]', 'safe("log.winlogEventDataLogName", "") == "Microsoft-Windows-Windows Defender/Operational" && safe("log.winlogEventDataErrorCode", "") == "0x8050801"', '2025-09-04 21:12:13.020381', true, false, 'target', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1268, 'SID History Injection Attempt', 3, 3, 1, 'Defense Evasion, Privilege Escalation', 'T1134.005 - Access Token Manipulation: SID-History Injection', '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"]', 'safe("log.eventCode", 0.0 in [4765, 4766] && -safe("log.winlogChannel", "") == "Security" -', '2025-09-04 19:30:07.077582', true, false, 'origin', '["target.user","target.host"]', '[]'); INSERT INTO public.utm_correlation_rules VALUES (646, 'IIS URL Rewrite Rule Bypass Attempt', 3, 3, 2, 'Defense Evasion', 'T1211 - Exploitation for Defense Evasion', 'Detects attempts to bypass URL rewrite rules in IIS through encoding tricks, double encoding, or path manipulation techniques. Attackers may use various encoding schemes and path traversal techniques to bypass URL rewrite rules and access restricted resources. Next Steps: @@ -7361,7 +7595,6 @@ Next Steps: 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/"]', 'safe("log.eventCode", 0.0) == double(3000) && safe("log.winlogProviderName", "") == "Microsoft-Windows-SMBServer" && safe("log.message", "").contains("SMB1")', '2025-09-04 19:30:07.528889', true, false, 'origin', '["origin.ip","origin.host"]', '[]'); INSERT INTO public.utm_correlation_rules VALUES (1270, 'Windows Token Manipulation', 3, 3, 2, '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/"]', 'safe("log.eventCode", 0.0) == double(4672) && safe("log.winlogEventDataPrivilegeList", "") != "" && (safe("log.winlogEventDataPrivilegeList", "").contains("SeDebugPrivilege") || safe("log.winlogEventDataPrivilegeList", "").contains("SeImpersonatePrivilege") || safe("log.winlogEventDataPrivilegeList", "").contains("SeTcbPrivilege") || safe("log.winlogEventDataPrivilegeList", "").contains("SeAssignPrimaryTokenPrivilege") || safe("log.winlogEventDataPrivilegeList", "").contains("SeLoadDriverPrivilege") || safe("log.winlogEventDataPrivilegeList", "").contains("SeRestorePrivilege"))', '2025-09-04 19:30:08.164651', true, false, 'origin', '["origin.host","log.winlogEventDataTargetUserName","log.winlogEventDataTargetLogonId"]', '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"},{"field":"log.winlogEventDataTargetLogonId.keyword","operator":"filter_term","value":"{{.log.winlogEventDataTargetLogonId}}"}],"or":[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"},{"field":"log.winlogEventDataTargetLogonId.keyword","operator":"filter_term","value":"{{.log.winlogEventDataTargetLogonId}}"},{"field":"log.eventCode","operator":"filter_term","value":4673}],"or":null,"within":"now-10m","count":1},{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"},{"field":"log.winlogEventDataTargetLogonId.keyword","operator":"filter_term","value":"{{.log.winlogEventDataTargetLogonId}}"},{"field":"log.eventCode","operator":"filter_term","value":4674}],"or":null,"within":"now-10m","count":1}],"within":"now-10m","count":2}]'); -INSERT INTO public.utm_correlation_rules VALUES (1489, 'Windows: A powered user account was created', 2, 2, 3, 'Privilege Escalation', 'Domain Policy Modification', 'This alert is generated every time a new powered account is created. This event generates on domain controllers, member servers, and workstations.', '["https://attack.mitre.org/tactics/TA0004","https://attack.mitre.org/techniques/T1484/","https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/appendix-l--events-to-monitor"]', 'safe("log.eventDataTargetSid", "").endsWith("-547") && safe("log.eventCode", 0.0) in [4732, 4728, 632, 636]', '2025-09-04 21:15:06.804085', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventDataTargetSid.keyword","operator":"filter_term","value":"{{.log.eventDataTargetSid}}"},{"field":"log.eventCode","operator":"filter_term","value":"4720"},{"field":"log.eventCode","operator":"filter_term","value":"624"}],"or":null,"within":"now-60s","count":1}]'); INSERT INTO public.utm_correlation_rules VALUES (660, 'Consumer Group Manipulation Attempt', 2, 3, 3, 'Impact', 'T1565.001 - Data Manipulation: Stored Data Manipulation', 'Detects potential manipulation of Kafka consumer groups, including unauthorized group membership changes, offset resets, or group deletion attempts. This could indicate an attacker trying to disrupt message processing or steal data. Next Steps: @@ -7515,7 +7748,6 @@ Next Steps: 6. Consider temporarily disabling the affected user account if token abuse is suspected 7. Review OIDC provider logs for additional context on token validation failures ', '["https://www.elastic.co/guide/en/elasticsearch/reference/current/oidc-guide.html","https://attack.mitre.org/techniques/T1550/"]', '(safe("log.actionMessage", "").contains("authentication") || safe("log.actionMessage", "").contains("token")) && safe("log.message", "").contains("oidc") && (safe("actionResult", "") == "failure" || safe("log.severityLabel", "") == "error" || safe("log.severityLabel", "") == "critical")', '2025-09-03 15:44:18.083267', true, false, 'origin', '["origin.user","log.actionMessage"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1490, 'Windows: An administrative account was removed from power user group', 2, 3, 1, 'Impact', 'Account Access Removal', 'This alert is generated every time a user account is removed from power user groups. This event generates on domain controllers, member servers, and workstations', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1531/","https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/appendix-l--events-to-monitor"]', 'safe("log.eventCode", 0.0) in [4733, 637, 4729, 633] && safe("log.eventDataTargetSid", "").endsWith("-547")', '2025-09-04 21:15:07.223479', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (677, 'PKI Certificate Authentication Failure', 3, 2, 1, 'Credential Access', 'T1606.002 - Forge Web Credentials: SAML Tokens', 'Detects PKI certificate authentication failures that may indicate certificate spoofing, invalid certificates, or attempts to bypass client certificate validation. This rule identifies failed authentication attempts using PKI certificates which could indicate an attacker attempting to use forged, expired, or stolen certificates to gain unauthorized access. Next Steps: @@ -7633,7 +7865,6 @@ Next Steps: 6. Consider temporarily disabling the user account if exploitation is confirmed 7. Update Watcher access controls and review role-based permissions ', '["https://www.elastic.co/guide/en/kibana/current/watcher-ui.html","https://attack.mitre.org/techniques/T1068/"]', 'safe("log.component", "") == "plugins.watcher" && (safe("log.severityLabel", "") == "error" || safe("log.actionMessage", "").contains("unauthorized") || safe("log.actionMessage", "").contains("permission") && safe("log.actionMessage", "").contains("denied") || safe("log.actionMessage", "").contains("watcher") && safe("log.actionMessage", "").contains("error") || safe("log.actionMessage", "").contains("access") && safe("log.actionMessage", "").contains("denied"))', '2025-09-03 15:44:46.983332', true, false, 'origin', '["origin.user","origin.ip"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1491, 'Windows: New ActiveSyncAllowedDeviceID Added via PowerShell', 3, 2, 1, 'Persistence', '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/"]', 'safe("log.eventDataProcessName") in ["powershell.exe", "pwsh.exe", "powershell_ise.exe"] && safe("log.message", "").matches("(Set-CASMailbox(.+)ActiveSyncAllowedDeviceIDs))")', '2025-09-04 21:15:07.825324', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1499, 'Windows: Suspicious Microsoft Diagnostics Wizard Execution with args IT_RebrowseForFile=, ms-msdt:/id, ms-msdt:-id or FromBase64', 2, 3, 1, 'Defense Evasion', '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/"]', 'safe("log.winlogEventDataProcessName", "").contains("msdt.exe") && safe("log.message", "").matches("(IT_RebrowseForFile=|ms-msdt:/id|ms-msdt:-id|FromBase64)")', '2025-09-04 21:16:08.68268', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (689, 'XSS Attack Attempt Detection', 3, 3, 1, 'Execution', 'T1059.007 - Cross-Site Scripting', 'Detects potential XSS attack attempts in Kibana through malicious script injection in search queries, field formatters, or visualization titles. This rule identifies common XSS patterns including script tags, JavaScript execution, and event handlers that could indicate an attempt to inject malicious code into Kibana interfaces. @@ -7735,7 +7966,6 @@ Next Steps: 7. Review network connections from the affected host for any unusual outbound traffic 8. Check for any concurrent suspicious activities on the system during the time of the alert ', '["https://book.hacktricks.xyz/linux-hardening/privilege-escalation/logstash","https://attack.mitre.org/techniques/T1059/004/"]', 'safe("log.component", "") == "logstash.config.sourceloader" && safe("log.msg", "") != "" && ( safe("log.msg", "").contains("exec") && (safe("log.msg", "").contains("command") || safe("log.msg", "").contains("interval")) || safe("log.msg", "").contains("ruby") && safe("log.msg", "").contains("code") || safe("log.msg", "").contains("file") && safe("log.msg", "").contains("/etc/") || safe("log.msg", "").contains("pipeline") && safe("log.msg", "").contains("reload") )', '2025-09-03 15:46:33.629404', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1500, 'Windows: Possible ransomware attack detected. High Volume of File Deletion Events.', 1, 3, 3, 'Ransomware', 'Impact: 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 high volume of file deletion events has been detected, potentially indicating ransomware activity.', '["https://attack.mitre.org/tactics/TA0040/"]', 'safe("log.eventCode", 0.0) == double(4660', '2025-09-04 21:16:09.339994', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1501, 'Windows: Possible ransomware attack detected. Multiple File Deletion.', 1, 3, 2, 'Ransomware', 'Impact: 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 high number of file deletion events have been detected in a short period, potentially indicating ransomware activity.', '["https://attack.mitre.org/tactics/TA0040/"]', 'safe("log.eventCode", 0.0) == double(4663) && safe("log.winlogEeventDataAccessMask", "") in [''0x100080'',''0x40'',''0x10000'']', '2025-09-04 21:16:09.944074', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (697, 'Logstash Filter Bypass Attempts', 3, 3, 2, 'Defense Evasion', 'T1562.006 - Impair Defenses: Indicator Blocking', 'Detects attempts to bypass or disable Logstash filter plugins that could lead to unprocessed malicious data or security control evasion. This rule identifies various indicators of filter manipulation including skip operations, exceptions, and conditional bypasses that could allow malicious data to pass through unprocessed. @@ -8298,7 +8528,6 @@ Next Steps: safe("log.winlogEventDataCommandLine", "").contains("DisableIOAVProtection") || safe("log.winlogEventDataCommandLine", "").contains("DisableBehaviorMonitoring"))) ', '2025-09-04 19:30:33.95102', true, false, 'origin', '["origin.host","log.winlogEventDataSubjectUserName"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1360, 'Windows: Microsoft security essentials - Virus detected', 3, 3, 2, 'Privilege Escalation', '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/"]', 'safe("log.eventCode", 0.0) in [1107, 1117, 1116, 1118, 1119] && safe("log.winlogProviderName", "") == "Microsoft Antimalware"', '2025-09-04 20:25:27.311503', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1390, '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, 'Potentially Compromised System', '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/"]', 'safe("log.eventDataImagePath", "").matches("(^%systemroot%\\(.+)\\(.+).exe)") && safe("log.eventCode", 0.0) == double(7045) && safe("log.logName", "") in [''system'', ''System'']', '2025-09-04 20:45:21.6867', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (739, 'SQL Injection Attempts Detected', 3, 3, 2, 'Initial Access', 'T1190 - Exploit Public-Facing Application', 'Detects potential SQL injection attempts through error messages indicating syntax errors, unauthorized access attempts, or suspicious SQL patterns in MySQL logs. SQL injection attacks can lead to unauthorized data access, data modification, or complete database compromise. @@ -8456,8 +8685,6 @@ Next Steps: 9. Correlate with other authentication events across the infrastructure 10. Review NATS access logs for additional suspicious activity from the same sources ', '["https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/jwt","https://attack.mitre.org/techniques/T1110/"]', 'safe("log.action", "") == "nats.log" && ( safe("log.message", "").contains("JWT") && safe("log.message", "").contains("invalid") || safe("log.message", "").contains("JWT") && safe("log.message", "").contains("expired") || safe("log.message", "").contains("token") && safe("log.message", "").contains("verification") && safe("log.message", "").contains("failed") || safe("log.message", "").contains("JWT") && safe("log.message", "").contains("signature") || safe("log.message", "").contains("malformed") && safe("log.message", "").contains("JWT") || safe("log.message", "").contains("JWT") && safe("log.message", "").contains("decode") && safe("log.message", "").contains("error") || (safe("log.auth_type", "") == "jwt" && safe("log.status", "") == "failed") || (safe("log.msg_type", "") == "CONNECT" && safe("log.message", "").contains("JWT")) )', '2025-09-03 15:57:09.523366', true, false, 'origin', '["origin.ip","origin.user"]', '[{"indexPattern":"v11-log-nats-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"}],"or":null,"within":"now-10m","count":5}]'); -INSERT INTO public.utm_correlation_rules VALUES (844, 'System Resource Exhaustion Detection', 1, 2, 3, 'Impact', 'Resource Hijacking', 'Detects potential system resource exhaustion including CPU, memory, or disk space issues that could lead to denial of service', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1496/"]', 'safe("log.message", "") != "" && (safe("log.message", "").contains("Out of memory") || safe("log.message", "").contains("No space left on device") || safe("log.message", "").contains("Cannot allocate memory") || safe("log.message", "").contains("fork: retry: Resource temporarily unavailable") || safe("log.message", "").contains("too many open files") || safe("log.message", "").contains("cannot fork") || safe("log.message", "").contains("malloc failed") && safe("origin.host", "") != "" -', '2025-09-03 16:17:15.968664', true, false, 'origin', '["origin.host"]', '[{"indexPattern":"v11-log-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":null,"within":"now-10m","count":10}]'); INSERT INTO public.utm_correlation_rules VALUES (759, 'NATS Resolver Exploitation Detection', 3, 3, 2, 'Execution', 'T1203 - Exploitation for Client Execution', 'Detects attempts to exploit NATS resolver functionality including JWT resolver, account resolver, or DNS resolver issues that could lead to authentication bypass or unauthorized access. Next Steps: @@ -8944,7 +9171,6 @@ Next Steps: 6. Consider rotating credentials and reviewing osquery access controls 7. Update osquery to the latest version if vulnerability exploitation is confirmed ', '["https://www.sqlite.org/cves.html","https://github.com/osquery/osquery/blob/master/SECURITY.md","https://attack.mitre.org/techniques/T1210/"]', '(safe("log.message", "").contains("SQL injection") && safe("action", "") == "osquery.result") || (safe("log.message", "").contains("maliciously crafted SQL") && safe("log.eventName", "") != "") || (safe("log.message", "").contains("SQLite") && safe("log.message", "").contains("memory leak")) || (safe("log.message", "").contains("corrupt") && safe("log.message", "").contains("database")) || (safe("actionResult", "") == "error" && safe("log.message", "").contains("SQL syntax")) || (safe("log.message", "").contains("SQLite authorizer") && safe("log.message", "").contains("denied"))', '2025-09-03 16:06:55.016508', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1367, 'Windows: Multiple remote access login failures', 3, 2, 2, 'Potentially Malicious Activity', '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/"]', 'safe("log.eventCode", 0.0) in [20187, 20014, 20078, 20050, 20049, 2018] && (safe("log.message", "").matches("(?i)(authentication failed|login failed|access denied|authentication error|invalid credentials)"))', '2025-09-04 20:39:50.128758', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1368, 'Windows: Multiple TS Gateway login failures', 3, 3, 2, 'Potentially Malicious Activity', '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/"]', 'safe("log.evenCode", "") == "1001" && safe("log.message", "").contains("Microsoft-Windows-TerminalServices")', '2025-09-04 20:39:50.648159', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (795, 'Osquery Subscription Exhaustion Attack', 1, 2, 3, 'Impact', 'Endpoint Denial of Service', 'Detects potential subscription exhaustion attacks through excessive event subscriptions, memory exhaustion patterns, or configuration-based resource consumption attempts. Attackers may attempt to overwhelm osquery by creating excessive subscriptions, large configurations, or triggering memory exhaustion to degrade endpoint monitoring capabilities or cause denial of service. @@ -9277,15 +9503,6 @@ Next Steps: 6. Consider implementing memory usage alerts and automatic eviction policies 7. Review and potentially block suspicious client IPs if attack is ongoing ', '["https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/memory-optimization/","https://www.trendmicro.com/en_us/research/20/d/exposed-redis-instances-abused-for-remote-code-execution-cryptocurrency-mining.html","https://attack.mitre.org/techniques/T1499/"]', 'safe("log.msg", "") != "" && ( safe("log.msg", "").contains("OOM command not allowed") || safe("log.msg", "").contains("out of memory") || safe("log.msg", "").contains("memory exhaustion") || (safe("log.msg", "").contains("used_memory") && safe("log.msg", "").contains("maxmemory")) || safe("log.msg", "").contains("can''t allocate memory") || safe("log.msg", "").contains("Background save terminated by signal") || safe("log.msg", "").contains("MISCONF Redis is configured to save RDB snapshots") || (safe("log.msg", "").contains("evicted") && safe("log.msg", "").contains("keys")) || (safe("log.msg", "").contains("maxclients") && safe("log.msg", "").contains("reached")) || (safe("log.msg", "").contains("Too many") && safe("log.msg", "").contains("allocate")) || (safe("log.msg", "").contains("memory") && safe("log.msg", "").contains("limit")) || (safe("log.levelEnc", "") == "warning" && safe("log.msg", "").contains("memory")) )', '2025-09-03 16:11:50.598387', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (828, 'AppArmor Profile Changes Detected', 2, 3, 1, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', 'Detects AppArmor profile modifications, denials, or attempts to disable AppArmor security policies that could indicate attempts to bypass mandatory access controls. AppArmor is a Linux kernel security module that restricts programs'' capabilities with per-program profiles. - -Next Steps: -1. Review the specific AppArmor event and determine if it was authorized -2. Check if the profile change was part of legitimate system administration -3. Verify the user account that triggered the change has appropriate privileges -4. Examine recent system activities on the affected host for signs of compromise -5. If unauthorized, restore the AppArmor profile and investigate potential security breach -', '["https://attack.mitre.org/techniques/T1562/001/","https://ubuntu.com/server/docs/apparmor"]', 'safe("action", "").contains("system") && (safe("log.message", "").contains("apparmor") || safe("log.message", "").contains("AppArmor") && (safe("log.message", "").contains("DENIED") || safe("log.message", "").contains("complain") || safe("log.message", "").contains("enforce") || safe("log.message", "").contains("audit") || safe("log.message", "").contains("profile") || safe("log.message", "").contains("aa-") || safe("log.message", "").contains("apparmor_parser") || safe("log.message", "").contains("teardown")', '2025-09-03 16:16:03.458496', true, false, 'origin', '["origin.host"]', '[]'); INSERT INTO public.utm_correlation_rules VALUES (882, 'Prototype Pollution Attempt Detected', 2, 3, 2, 'Defense Evasion, Privilege Escalation', 'Prototype Pollution', 'Detects attempts to pollute JavaScript object prototypes through malicious JSON input containing __proto__, constructor, or prototype properties. This attack can lead to application compromise by modifying the behavior of all objects in the application. Next Steps: @@ -9374,28 +9591,7 @@ Next Steps: 7. Review other systems for similar brute force attempts from the same IP 8. Consider adding the IP to threat intelligence feeds if confirmed malicious ', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1110/001/"]', 'safe("log.message", "").contains("Failed password") && safe("log.message", "").contains("ssh") && safe("origin.ip", "") != ""', '2025-09-03 16:16:47.171281', true, false, 'origin', '["origin.ip"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"},{"field":"log.message.keyword","operator":"filter_match","value":"Failed password"}],"or":null,"within":"now-5m","count":10}]'); -INSERT INTO public.utm_correlation_rules VALUES (839, 'Sudo Privilege Escalation Attempts', 3, 3, 2, 'Privilege Escalation, Defense Evasion', 'T1548.003 - Abuse Elevation Control Mechanism: Sudo and Sudo Caching', 'Detects unauthorized sudo usage attempts and privilege escalation through sudo command execution. This rule identifies failed sudo attempts including users not in sudoers file, authentication failures, and command restrictions. - -Next Steps: -1. Verify the legitimacy of the user attempting sudo access -2. Review the specific command that was attempted -3. Check if this is part of a broader privilege escalation campaign -4. Examine system logs for additional suspicious activity from this user/host -5. Consider implementing additional sudo restrictions if unauthorized access attempts persist -6. Correlate with other authentication events from the same user or host -', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1548/003/"]', 'safe("log.message", "").contains("sudo") && (safe("log.message", "").contains("NOT in sudoers") || safe("log.message", "").contains("authentication failure") || safe("log.message", "").contains("command not allowed") && safe("origin.host", "") != ""', '2025-09-03 16:16:47.611296', true, false, 'origin', '["origin.host","origin.user"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":null,"within":"now-10m","count":3}]'); INSERT INTO public.utm_correlation_rules VALUES (1375, 'Windows: Security enabled group created', 2, 3, 1, 'Potentially Malicious Activity', 'Domain Policy Modification: Group Policy Modification', 'Adversaries may modify Group Policy Objects (GPOs) to subvert the intended discretionary access controls for a domain, usually with the intention of escalating privileges on the domain. Group policy allows for centralized management of user and computer settings in Active Directory (AD).', '["https://attack.mitre.org/techniques/T1484/001/"]', 'safe("log.eventCode", 0.0) == double(631) || safe("log.eventCode", 0.0) == double(635) || safe("log.eventCode", 0.0) == double(658)', '2025-09-04 20:44:34.578276', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (829, 'Firewall Rule Modifications Detected', 2, 3, 2, 'Defense Evasion', 'T1562.004 - Impair Defenses: Disable or Modify System Firewall', 'Detects modifications to firewall rules including iptables, firewalld, or ufw that could indicate attempts to disable security controls or open backdoor access. This rule monitors system logs for firewall configuration changes that may compromise network security boundaries. - -Next Steps: -1. Verify if the firewall modification was authorized and documented -2. Check who performed the modification and from which system -3. Review the specific rules that were added, deleted, or modified -4. Examine surrounding log entries for additional suspicious activity -5. Validate current firewall configuration against security baseline -6. If unauthorized, immediately review and restore proper firewall rules -7. Consider implementing change management controls for firewall modifications -', '["https://attack.mitre.org/techniques/T1562/004/","https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html"]', 'safe("action", "").contains("system") && (safe("log.message", "").contains("iptables") || safe("log.message", "").contains("ip6tables") || safe("log.message", "").contains("firewall-cmd") || safe("log.message", "").contains("ufw") || safe("log.message", "").contains("nftables") || safe("log.message", "").contains("ebtables") && (safe("log.message", "").contains("ACCEPT") || safe("log.message", "").contains("DROP") || safe("log.message", "").contains("REJECT") || safe("log.message", "").contains("flush") || safe("log.message", "").contains("delete") || safe("log.message", "").contains("insert") || safe("log.message", "").contains("--dport") || safe("log.message", "").contains("--sport")', '2025-09-03 16:16:03.886304', true, false, 'origin', '["origin.host"]', '[]'); INSERT INTO public.utm_correlation_rules VALUES (830, 'Hardware Error Patterns', 1, 2, 3, 'Initial Access', 'T1200 - Hardware Additions', 'Detects hardware-related errors including memory failures, disk errors, CPU issues, temperature warnings, and other hardware anomalies that may impact system stability or indicate potential hardware tampering. Next Steps: @@ -9406,7 +9602,30 @@ Next Steps: 5. Consider running hardware diagnostic tools (memtest, smartctl, sensors) 6. Check for patterns of errors that could indicate malicious hardware implants 7. Coordinate with physical security team if hardware tampering is suspected -', '["https://attack.mitre.org/techniques/T1200/","https://www.kernel.org/doc/html/latest/admin-guide/ras.html"]', '(safe("log.message", "").matches("(?i)(mce:.*hardware error|machine check exception|cpu.*error detected|thermal throttling)") || safe("log.message", "").matches("(?i)(ecc.*error|memory.*parity error|dimm.*failure|corrected.*error|uncorrectable.*error)") || safe("log.message", "").matches("(?i)(ata.*error|sata.*fail|disk.*error|i/o error|bad sector|smart.*fail)") || safe("log.message", "").matches("(?i)(pci.*error|pcie.*error|bus error|dma.*fail|irq.*nobody cared)") || safe("log.message", "").matches("(?i)(power supply.*warn|voltage.*out of range|fan.*fail|temperature.*critical)") || (safe("log.program", "").matches("(?i)(smartd|mcelog|sensors)" && safe("log.message", "").matches("(?i)(warning|error|critical|fail)"))', '2025-09-03 16:16:04.353578', true, false, 'origin', '["origin.host"]', '[]'); +', '["https://attack.mitre.org/techniques/T1200/","https://www.kernel.org/doc/html/latest/admin-guide/ras.html"]', '( + safe("log.message", "").matches("(?i)(mce:.*hardware error|machine check exception|cpu.*error detected|thermal throttling)") || + safe("log.message", "").matches("(?i)(ecc.*error|memory.*parity error|dimm.*failure|corrected.*error|uncorrectable.*error)") || + safe("log.message", "").matches("(?i)(ata.*error|sata.*fail|disk.*error|i/o error|bad sector|smart.*fail)") || + safe("log.message", "").matches("(?i)(pci.*error|pcie.*error|bus error|dma.*fail|irq.*nobody cared)") || + safe("log.message", "").matches("(?i)(power supply.*warn|voltage.*out of range|fan.*fail|temperature.*critical)") || + (safe("log.program", "").matches("(?i)(smartd|mcelog|sensors)") && safe("log.message", "").matches("(?i)(warning|error|critical|fail)")) + )', '2025-09-05 18:15:13.77651', true, false, 'origin', '["origin.host"]', '[]'); +INSERT INTO public.utm_correlation_rules VALUES (832, 'Memory Usage Spike Detection', 1, 2, 3, 'Impact', 'Resource Exhaustion', 'Detects sudden memory usage spikes and memory pressure events that could indicate memory leaks or resource exhaustion attacks', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1499/"]', '( + safe("log.message", "") != "" && + ( + safe("log.message", "").contains("oom-killer") || + safe("log.message", "").contains("Out of memory: Kill process") || + safe("log.message", "").contains("Memory cgroup out of memory") || + safe("log.message", "").contains("page allocation failure") || + ( + safe("log.message", "").contains("swapfile") && + safe("log.message", "").contains("full") + ) || + safe("log.message", "").contains("memory pressure") || + safe("log.message", "").contains("low on memory") + ) && + safe("origin.host", "") != "" + )', '2025-09-05 18:33:42.947311', true, false, 'origin', '["origin.host"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":[],"within":"now-5m","count":3}]'); INSERT INTO public.utm_correlation_rules VALUES (831, 'Kernel Parameter Modifications', 2, 3, 2, 'Persistence, Privilege Escalation', 'T1547.006 - Boot or Logon Autostart Execution: Kernel Modules and Extensions', 'Detects modifications to kernel parameters or kernel module loading that could indicate rootkit installation or system compromise. This rule identifies suspicious kernel-related activities including module loading/unloading operations and sysctl parameter changes. Next Steps: @@ -9417,20 +9636,14 @@ Next Steps: 5. Examine network connections and processes spawned after kernel modifications 6. If unauthorized, immediately isolate the affected system and conduct forensic analysis 7. Review user accounts and privileges that may have been used to perform these operations -', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1547/006/"]', '(safe("log.message", "").contains("kernel") && (safe("log.message", "").contains("module") || safe("log.message", "").contains("insmod") || safe("log.message", "").contains("modprobe") || safe("log.message", "").contains("rmmod")) || (safe("log.message", "").contains("sysctl") && safe("origin.host", "") != "")', '2025-09-03 16:16:05.00014', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (832, 'Memory Usage Spike Detection', 1, 2, 3, 'Impact', 'Resource Exhaustion', 'Detects sudden memory usage spikes and memory pressure events that could indicate memory leaks or resource exhaustion attacks', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1499/"]', 'safe("log.message", "") != "" && (safe("log.message", "").contains("oom-killer") || safe("log.message", "").contains("Out of memory: Kill process") || safe("log.message", "").contains("Memory cgroup out of memory") || safe("log.message", "").contains("page allocation failure") || safe("log.message", "").contains("swapfile") && safe("log.message", "").contains("full") || safe("log.message", "").contains("memory pressure") || safe("log.message", "").contains("low on memory") && safe("origin.host", "") != "" -', '2025-09-03 16:16:05.458564', true, false, 'origin', '["origin.host"]', '[{"indexPattern":"v11-log-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":null,"within":"now-5m","count":3}]'); -INSERT INTO public.utm_correlation_rules VALUES (833, 'Network Configuration Changes Detected', 2, 3, 2, 'Discovery', 'T1016 - System Network Configuration Discovery', 'Detects changes to network configuration files or network interface settings that could indicate system compromise or preparation for data exfiltration. This rule monitors for modifications to critical network configuration files and commands that alter network settings. - -Next Steps: -1. Verify if the network configuration changes were authorized and scheduled -2. Check if the changes align with legitimate administrative activities -3. Review the user account that made the changes and their privileges -4. Examine the timing of the changes for patterns that suggest malicious activity -5. Correlate with other security events from the same host -6. Validate current network configuration against baseline settings -7. Monitor for any unusual network traffic patterns following the changes -', '["https://attack.mitre.org/techniques/T1016/","https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html"]', 'safe("log.message", "") != "" && (safe("log.message", "").contains("/etc/network/") || safe("log.message", "").contains("/etc/sysconfig/network") || safe("log.message", "").contains("ifconfig") || safe("log.message", "").contains("ip addr") || safe("log.message", "").contains("netplan") || safe("log.message", "").contains("/etc/resolv.conf") || safe("log.message", "").contains("/etc/hosts") || safe("log.message", "").contains("iptables") || safe("log.message", "").contains("route add") || safe("log.message", "").contains("ip route")', '2025-09-03 16:16:05.920698', true, false, 'origin', '["origin.host","log.message"]', '[{"indexPattern":"v11-log-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":null,"within":"now-1h","count":5}]'); +', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1547/006/"]', '(safe("log.message", "").contains("kernel") && + (safe("log.message", "").contains("module") || + safe("log.message", "").contains("insmod") || + safe("log.message", "").contains("modprobe") || + safe("log.message", "").contains("rmmod"))) || + safe("log.message", "").contains("sysctl") && + safe("origin.host", "") != ""', '2025-09-05 20:00:29.280459', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1372, 'Windows Script Interpreter Executing Process via WMI', 2, 2, 2, 'Initial Access', '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/"]', 'safe("log.winlogProcessName", "") in ["wscript.exe", "cscript.exe"] && safe("log.message", "").contains("wmiutils.dll")', '2025-09-04 20:41:22.132921', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.winlogEventDataParentProcessName.keyword","operator":"filter_term","value":"wmiprvse.exe"},{"field":"log.winlogEventDataUserDomain.keyword","operator":"filter_not_match","value":"NT AUTHORITY"},{"field":"log.message.keyword","operator":"filter_match","value":"(C:\\Users\\(.+).exe|C:\\ProgramData\\(.+).exe|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)"}],"or":null,"within":"now-60s","count":1}]'); INSERT INTO public.utm_correlation_rules VALUES (834, 'Suspicious Package Installation Activity', 3, 3, 2, 'Execution, Lateral Movement', 'T1072 - Enterprise Application Deployment', 'Detects unusual package installation or removal activities that could indicate system compromise or unauthorized software deployment. This rule identifies systems with high frequency package management operations using apt, yum, dpkg, rpm, or snap, which may indicate automated malware installation, privilege escalation attempts, or unauthorized system modifications. Next Steps: @@ -9441,61 +9654,21 @@ Next Steps: 5. Examine the source repositories and package signatures for integrity verification 6. Correlate with other security events on the same host for potential attack patterns 7. If unauthorized, initiate incident response procedures and consider host isolation -', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1072/"]', '(safe("log.message", "").contains("apt") || safe("log.message", "").contains("yum") || safe("log.message", "").contains("dpkg") || safe("log.message", "").contains("rpm") || safe("log.message", "").contains("snap") && (safe("log.message", "").contains("install") || safe("log.message", "").contains("remove") || safe("log.message", "").contains("purge") && safe("origin.host", "") != ""', '2025-09-03 16:16:06.366401', true, false, 'origin', '["origin.host"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (835, 'PAM Configuration Changes Detected', 2, 3, 2, 'Credential Access, Defense Evasion, Persistence', 'T1556.003 - Modify Authentication Process: Pluggable Authentication Modules', 'Detects modifications to PAM (Pluggable Authentication Module) configuration files which could indicate an attempt to bypass authentication mechanisms or establish persistence. PAM configuration changes can allow attackers to weaken authentication requirements, add backdoor authentication methods, or bypass existing security controls. - -Next Steps: -1. Verify the legitimacy of the PAM configuration change with system administrators -2. Review the specific PAM module that was modified and assess the security impact -3. Check for unauthorized access attempts before and after the configuration change -4. Examine system logs for other suspicious administrative activities -5. Validate that proper change management procedures were followed -6. Consider reverting the changes if they are unauthorized -7. Review user accounts and privileges that may have been affected -', '["https://attack.mitre.org/techniques/T1556/003/","https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html"]', 'safe("action", "") == "system.auth" && safe("log.message", "").contains("pam") && (safe("log.message", "").contains("/etc/pam.d/") || safe("log.message", "").contains("pam.conf") || (safe("log.message", "").contains("pam_" && safe("log.message", "") contains "modified"))', '2025-09-03 16:16:06.772244', true, false, 'origin', '["origin.host"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (836, 'Process Execution Anomalies', 3, 3, 2, 'Defense Evasion, Privilege Escalation', 'T1055 - Process Injection', 'Detects anomalous process behavior including unexpected terminations, segmentation faults, and suspicious process spawning patterns. This rule identifies processes that have crashed, been killed abnormally, or are exhibiting zombie/defunct behavior which could indicate malicious activity or system compromise. - -Next Steps: -1. Investigate the affected host and identify the specific processes involved -2. Review system logs around the time of the anomalous process behavior -3. Check for signs of memory corruption, privilege escalation attempts, or malware injection -4. Examine parent-child process relationships for suspicious spawning patterns -5. Verify system integrity and check for unauthorized modifications -6. Monitor for additional process anomalies on the same host -', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1055/"]', 'safe("log.message", "") != "" && (safe("log.message", "").contains("segfault") || safe("log.message", "").contains("core dumped") || safe("log.message", "").contains("Killed process") || safe("log.message", "").contains("process") && (safe("log.message", "").contains("abnormal") || safe("log.message", "").contains("terminated") || safe("log.message", "").contains("signal 9") || safe("log.message", "").contains("signal 11") || safe("log.message", "").contains("defunct") || safe("log.message", "").contains("zombie") && safe("origin.host", "") != ""', '2025-09-03 16:16:07.210887', true, false, 'origin', '["origin.host"]', '[{"indexPattern":"v11-log-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":null,"within":"now-30m","count":3}]'); -INSERT INTO public.utm_correlation_rules VALUES (837, 'SELinux Policy Violations Detected', 3, 3, 2, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', 'Detects SELinux policy violations, AVC denials, or attempts to disable SELinux that could indicate security bypass attempts or system compromise. SELinux violations may indicate malicious activity attempting to bypass mandatory access controls. - -Next Steps: -1. Review the specific SELinux violation details and affected resources -2. Check if the violation is from legitimate application behavior or malicious activity -3. Examine system logs around the time of violation for additional suspicious activity -4. Verify SELinux policy configuration and ensure it hasn''t been tampered with -5. If violations are from legitimate applications, consider updating SELinux policies or application configurations -6. Monitor for repeated violations from the same process or user that may indicate persistent bypass attempts -', '["https://attack.mitre.org/techniques/T1562/001/","https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html-single/using_selinux/index"]', 'safe("action", "").contains("system") && (safe("log.message", "").contains("avc:") || safe("log.message", "").contains("selinux") || safe("log.message", "").contains("SELinux") && (safe("log.message", "").contains("denied") || safe("log.message", "").contains("permissive") || safe("log.message", "").contains("disabled") || safe("log.message", "").contains("setenforce") || safe("log.message", "").contains("setsebool") || safe("log.message", "").contains("restorecon") || safe("log.message", "").contains("audit2allow")', '2025-09-03 16:16:07.693051', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1372, 'Windows Script Interpreter Executing Process via WMI', 2, 2, 2, 'Initial Access', '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/"]', 'safe("log.winlogProcessName", "") in ["wscript.exe", "cscript.exe"] && safe("log.message", "").contains("wmiutils.dll")', '2025-09-04 20:41:22.132921', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.winlogEventDataParentProcessName.keyword","operator":"filter_term","value":"wmiprvse.exe"},{"field":"log.winlogEventDataUserDomain.keyword","operator":"filter_not_match","value":"NT AUTHORITY"},{"field":"log.message.keyword","operator":"filter_match","value":"(C:\\Users\\(.+).exe|C:\\ProgramData\\(.+).exe|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)"}],"or":null,"within":"now-60s","count":1}]'); -INSERT INTO public.utm_correlation_rules VALUES (840, 'System Boot Anomalies', 1, 3, 3, 'Persistence, Privilege Escalation', 'T1037 - Boot or Logon Initialization Scripts', 'Detects anomalous system boot patterns including unexpected reboots, boot failures, kernel panics, or unusual boot sequences that may indicate system compromise or hardware issues. This rule identifies critical system events that could signal attempts to tamper with the boot process or system initialization. - -Next Steps: -1. Immediately verify the affected system''s current operational status -2. Check system logs around the time of the boot anomaly for additional context -3. Examine hardware status and recent system changes or updates -4. Verify integrity of boot components (bootloader, kernel, initramfs) -5. Check for signs of unauthorized modifications to system files -6. Review recent administrative activities and user access logs -7. Consider isolating the system if tampering is suspected -8. Document all findings for potential forensic analysis -', '["https://attack.mitre.org/techniques/T1037/","https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html"]', '(safe("log.message", "").matches("(?i)(kernel panic|unable to mount root|failed to start|boot failed|emergency mode|rescue mode)") || safe("log.message", "").matches("(?i)(unexpected system reboot|power loss detected|fsck failed|filesystem errors detected)") || (safe("action", "") == "system.syslog" && safe("log.message", "").matches("(?i)(kernel:.*oops|kernel:.*bug|kernel:.*call trace)") || (safe("log.program", "") == "systemd" && safe("log.message", "").matches("(?i)(failed to start|dependency failed|timeout|activating failed)") || (safe("log.message", "").matches("(?i)(grub.*error|bootloader.*fail|initramfs.*error|dracut.*warning)"))', '2025-09-03 16:16:48.120954', true, false, 'origin', '["origin.host"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":null,"within":"now-10m","count":3}]'); -INSERT INTO public.utm_correlation_rules VALUES (841, 'Critical System Service Failures', 1, 2, 3, 'Persistence, Privilege Escalation', 'T1543.002 - Create or Modify System Process: Systemd Service', 'Detects critical system service failures that could indicate system instability or potential attacks targeting system services. Multiple service failures in a short time window may indicate a compromise or deliberate service disruption. - -Next Steps: -- Review the specific services that failed and their criticality to system operations -- Check system logs for patterns of failure or unusual activity preceding the failures -- Verify if the failures are isolated to a single host or affecting multiple systems -- Investigate if the service failures correlate with other security events such as privilege escalation attempts -- Review recent system changes, updates, or administrative activities -- Consider checking process execution logs for unauthorized service manipulation commands -', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1543/002/"]', 'safe("log.message", "").contains("systemd") && (safe("log.message", "").contains("Failed") || safe("log.message", "").contains("failed") || safe("log.message", "").contains("error") || safe("log.message", "").contains("crash") && safe("origin.host", "") != ""', '2025-09-03 16:16:48.572229', true, false, 'origin', '["origin.host"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"},{"field":"log.message.keyword","operator":"filter_match","value":"systemd"},{"field":"log.message.keyword","operator":"filter_match","value":"Failed"}],"or":null,"within":"now-15m","count":3}]'); +', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1072/"]', '( + ( + safe("log.message", "").contains("apt") || + safe("log.message", "").contains("yum") || + safe("log.message", "").contains("dpkg") || + safe("log.message", "").contains("rpm") || + safe("log.message", "").contains("snap") + ) && + ( + safe("log.message", "").contains("install") || + safe("log.message", "").contains("remove") || + safe("log.message", "").contains("purge") + ) && + safe("origin.host", "") != "" + )', '2025-09-05 18:29:42.059758', true, false, 'origin', '["origin.host"]', '[]'); INSERT INTO public.utm_correlation_rules VALUES (842, 'System Update Failures', 2, 3, 3, 'Persistence', 'T1505.001 - Server Software Component: Web Shell', 'Detects failures in system update processes including package manager errors, dependency conflicts, repository issues, and failed patches that may leave systems vulnerable. Next Steps: @@ -9511,7 +9684,36 @@ Next Steps: 10. Assess impact on system security posture due to missing updates 11. Review automated update schedules and maintenance windows 12. Consider implementing redundant update mechanisms or mirrors -', '["https://attack.mitre.org/techniques/T1505/001/","https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html"]', '(safe("log.program", "").matches("(?i)(apt|apt-get|dpkg|yum|dnf|zypper|rpm|snap|flatpak)") && safe("log.message", "").matches("(?i)(error|failed|unable|cannot|dependency.*problem|conflict|broken|corrupt)") || safe("log.message", "").matches("(?i)(repository.*not found|gpg.*fail|signature.*invalid|checksum.*mismatch|verification.*fail)" || safe("log.message", "").matches("(?i)(unmet dependencies|held broken packages|unable to correct problems|dpkg.*error|rpm.*fail)" || safe("log.message", "").matches("(?i)(upgrade.*abort|update.*fail|patch.*error|kernel.*update.*fail|security update.*fail)" || (safe("log.program", "") == "unattended-upgrades" && safe("log.message", "").matches("(?i)(error|warning|fail)") || safe("log.message", "").matches("(?i)(package.*hash.*mismatch|mirror.*fail|cdn.*unreachable|timeout.*repository)"', '2025-09-03 16:16:49.0604', true, false, 'origin', '["origin.host"]', '[]'); +', '["https://attack.mitre.org/techniques/T1505/001/","https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html"]', '( + (safe("log.program", "").matches("(?i)(apt|apt-get|dpkg|yum|dnf|zypper|rpm|snap|flatpak)") && + safe("log.message", "").matches("(?i)(error|failed|unable|cannot|dependency.*problem|conflict|broken|corrupt)") + ) || + safe("log.message", "").matches("(?i)(repository.*not found|gpg.*fail|signature.*invalid|checksum.*mismatch|verification.*fail)") || + safe("log.message", "").matches("(?i)(unmet dependencies|held broken packages|unable to correct problems|dpkg.*error|rpm.*fail)") || + safe("log.message", "").matches("(?i)(upgrade.*abort|update.*fail|patch.*error|kernel.*update.*fail|security update.*fail)") || + ( + safe("log.program", "") == "unattended-upgrades" && + safe("log.message", "").matches("(?i)(error|warning|fail)") + ) || + safe("log.message", "").matches("(?i)(package.*hash.*mismatch|mirror.*fail|cdn.*unreachable|timeout.*repository)") + )', '2025-09-05 18:21:24.725466', true, false, 'origin', '["origin.host"]', '[]'); +INSERT INTO public.utm_correlation_rules VALUES (841, 'Critical System Service Failures', 1, 2, 3, 'Persistence, Privilege Escalation', 'T1543.002 - Create or Modify System Process: Systemd Service', 'Detects critical system service failures that could indicate system instability or potential attacks targeting system services. Multiple service failures in a short time window may indicate a compromise or deliberate service disruption. + +Next Steps: +- Review the specific services that failed and their criticality to system operations +- Check system logs for patterns of failure or unusual activity preceding the failures +- Verify if the failures are isolated to a single host or affecting multiple systems +- Investigate if the service failures correlate with other security events such as privilege escalation attempts +- Review recent system changes, updates, or administrative activities +- Consider checking process execution logs for unauthorized service manipulation commands +', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1543/002/"]', 'safe("log.message", "").contains("systemd") && + ( + safe("log.message", "").contains("Failed") || + safe("log.message", "").contains("failed") || + safe("log.message", "").contains("error") || + safe("log.message", "").contains("crash") + ) && + safe("origin.host", "") != ""', '2025-09-05 19:23:09.504299', true, false, 'origin', '["origin.host"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"},{"field":"log.message.keyword","operator":"filter_term","value":"systemd"},{"field":"log.message.keyword","operator":"filter_term","value":"Failed"}],"or":[],"within":"now-15m","count":3}]'); INSERT INTO public.utm_correlation_rules VALUES (843, 'User Account Modifications', 3, 3, 2, 'Persistence', 'T1136 - Create Account', 'Detects user account creation, modification, or deletion activities that could indicate unauthorized access or privilege escalation attempts. This rule monitors for commands like useradd, usermod, userdel, adduser, deluser, and passwd that are commonly used to manage user accounts on Linux systems. Next Steps: @@ -9524,8 +9726,8 @@ Next Steps: - Verify if any new SSH keys were added to authorized_keys files - Check for privilege escalation attempts following the account modification 5. If unauthorized, immediately disable the affected account and investigate the compromise -', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1136/"]', 'safe("origin.host", "") != "" && (safe("log.message", "").contains("useradd") || safe("log.message", "").contains("usermod") || safe("log.message", "").contains("userdel") || safe("log.message", "").contains("adduser") || safe("log.message", "").contains("deluser") || (safe("log.message", "").contains("passwd") && safe("log.message", "").contains("changed"))', '2025-09-03 16:16:49.559432', true, false, 'origin', '["origin.host","origin.user"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1376, 'Windows: Security Software Discovery using WMIC', 3, 2, 1, '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/"]', 'safe("log.message", "").matches("(namespace:\\\\root\\SecurityCenter2)") safe("log.message", "").contains("(Get") && safe("log.winlogEventDataProcessName").contains("wmic.exe")', '2025-09-04 20:44:35.10355', true, false, 'origin', NULL, '[]'); +', '["https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-system.html","https://attack.mitre.org/techniques/T1136/"]', 'safe("origin.host", "") != "" && (safe("log.message", "").contains("useradd") || safe("log.message", "").contains("usermod") || safe("log.message", "").contains("userdel") || safe("log.message", "").contains("adduser") || safe("log.message", "").contains("deluser") || (safe("log.message", "").contains("passwd") && safe("log.message", "").contains("changed")))', '2025-09-05 19:27:31.581839', true, false, 'origin', '["origin.host","origin.user"]', '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1376, 'Windows: Security Software Discovery using WMIC', 3, 2, 1, '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/"]', 'safe("log.message", "").matches("(namespace:\\\\root\\SecurityCenter2)") && safe("log.message", "").contains("(Get") && safe("log.winlogEventDataProcessName", "").contains("wmic.exe")', '2025-09-05 20:11:59.210319', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (845, 'Access Log Injection Attack', 3, 3, 2, 'Defense Evasion', 'T1070 - Indicator Removal on Host', 'Detects attempts to inject malicious content into access logs through request fields, potentially corrupting log integrity or executing log parser exploits. Attackers may use log injection to hide their activities, corrupt log analysis tools, or exploit vulnerabilities in log processing systems. Next Steps: @@ -9727,7 +9929,6 @@ Next Steps: 6. Review Traefik retry configuration to ensure appropriate limits are set 7. Monitor backend service health to ensure they''re not overwhelmed by retry traffic ', '["https://doc.traefik.io/traefik/middlewares/http/retry/","https://attack.mitre.org/techniques/T1499/002/"]', 'safe("log.retries", 0) > 3 && (safe("log.origin_status", 0) >= 500 || safe("log.origin_status", 0.0) == double(0) || safe("log.message", "").contains("retry") || safe("log.message", "").contains("attempt")) && safe("log.duration", 0) > 5000000000', '2025-09-03 16:18:59.714789', true, false, 'origin', '["origin.host","target.host"]', '[{"indexPattern":"v11-log-traefik-*","with":[{"field":"log.client_host.keyword","operator":"filter_term","value":"{{.log.client_host}}"},{"field":"log.request_host.keyword","operator":"filter_term","value":"{{.log.request_host}}"}],"or":null,"within":"now-5m","count":30}]'); -INSERT INTO public.utm_correlation_rules VALUES (1399, 'Windows: Microsoft Exchange Worker Spawning Suspicious Processes', 2, 3, 2, 'Initial Access', '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/"]', 'safe("log.winlogEventDataParentProcessName").contains("w3wp.exe") && safe("log.message", "").matches("(MSExchange(.+)AppPool)") && safe("log.winlogEventDataProcessName").matches("(md.exe|powershell.exe|pwsh.dll|powershell_ise.exe)")', '2025-09-04 20:46:22.196028', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (860, 'Service Discovery Poisoning Attempt', 3, 3, 2, 'Credential Access, Collection', 'T1557 - Man-in-the-Middle: LLMNR/NBT-NS Poisoning and SMB Relay', 'Detects attempts to poison Traefik''s service discovery mechanism by injecting malicious service endpoints or manipulating provider configurations. This could allow an attacker to redirect traffic to malicious servers or disrupt service availability. Next Steps: @@ -9889,7 +10090,6 @@ Next Steps: 5. Review application logs for related unauthorized access attempts 6. Consider implementing stricter JSON pointer validation and access controls ', '["https://datatracker.ietf.org/doc/html/rfc6901","https://bishopfox.com/blog/json-interoperability-vulnerabilities","https://attack.mitre.org/techniques/T1083/"]', '(safe("log.json_pointer", "") != "" || safe("log.path", "").contains("/")) && (safe("log.json_pointer", "").contains("~0") || safe("log.json_pointer", "").contains("~1") || safe("log.path", "").contains("/../") || safe("log.path", "").contains("/..%2F") || safe("log.path", "").contains("/..%252F") || safe("log.body", "").matches(".*\"path\"\\s*:\\s*\"[^\"]*(\\.\\.|~0~1|~1~0).*\".*") || safe("log.query_params", "").contains("pointer=/../"))', '2025-09-03 16:29:35.725865', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1478, 'Windows Firewall rule has been added, modified or deleted to the Windows Defender Firewall exception list.', 3, 3, 2, 'Potentially Malicious Activity', 'Defense Evasion', 'Detects changes to the Windows Defender Firewall exception list, which may indicate an attempt to evade detection.', '["https://learn.microsoft.com/en-us/windows/security/threat-protection/windows-firewall/firewall-event-ids"]', 'safe("log.winlogEventDataLogName", "") == "Microsoft-Windows-Windows Firewall With Advanced Security/Firewall" && safe("log.eventCode", 0.0) in [2004, 2005, 2006]', '2025-09-04 21:12:17.001869', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (874, 'JSONP Vulnerability Exploitation Detected', 3, 3, 1, 'Execution', 'JSONP Exploitation', 'Detects attempts to exploit JSONP endpoints through callback manipulation, XSS injection via callbacks, or unauthorized cross-domain data access. This rule identifies malicious callback parameters containing script tags, JavaScript execution functions, or non-standard callback names that could indicate exploitation attempts. Next Steps: @@ -9993,156 +10193,8 @@ Next Steps: 5. Consider implementing stricter JSON schema validation 6. Monitor for any subsequent malicious activity from the same source ', '["https://bishopfox.com/blog/json-interoperability-vulnerabilities","https://attack.mitre.org/techniques/T1055/"]', '(safe("log.algorithm", "") != "" && safe("log.expected_algorithm", "") != "" && log.algorithm != log.expected_algorithm) || (safe("log.data_type_mismatch", false) == true) || (safe("log.parser_error", "").contains("type mismatch")) || (safe("log.validation_error", "").contains("unexpected type")) || (safe("log.json_field_type", "") != "" && safe("log.expected_field_type", "") != "" && log.json_field_type != log.expected_field_type)', '2025-09-03 16:30:03.575152', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (904, 'Snap Package Security Violations', 3, 3, 2, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects security-related events in Snap package management including confinement violations, interface connection attempts, AppArmor denials for snaps, and suspicious snap installations that could indicate privilege escalation attempts or malicious snap packages. - -Next Steps: -1. Examine the specific snap package involved in the security violation -2. Review snap confinement settings and interface connections for the affected package -3. Check if the snap was installed from official store or sideloaded -4. Investigate user activities around the time of the violation -5. Verify snap package signatures and assertions -6. Review AppArmor logs for additional context on denied operations -7. Consider removing or updating the problematic snap package -8. Monitor for additional security events from the same host or user -', '["https://attack.mitre.org/techniques/T1068/","https://snapcraft.io/docs/security-sandboxing"]', '(safe("log.process", "").contains("snap") || safe("log.message", "").contains("snapd") || safe("log.message", "").contains("snap.")) && (safe("log.message", "").contains("AppArmor") && safe("log.message", "").contains("DENIED") || safe("log.message", "").contains("confinement violation") || safe("log.message", "").contains("interface connection denied") || safe("log.message", "").contains("cannot connect interface") || safe("log.message", "").contains("dangerous snap") || safe("log.message", "").contains("classic confinement") && safe("log.message", "").contains("warning") || safe("log.message", "").contains("assertion not valid") || safe("log.message", "").contains("signature verification failed") || safe("log.message", "").contains("snap is unusable") || safe("log.message", "").contains("cannot perform operation") || safe("log.message", "").contains("permission denied") && safe("log.message", "").contains("snap") || safe("log.message", "").contains("sandbox escape") || safe("log.message", "").contains("seccomp violation")', '2025-09-03 16:33:38.747101', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (887, 'AppArmor Policy Violation', 2, 3, 2, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', 'Detects AppArmor policy violations and denied operations which may indicate unauthorized access attempts, privilege escalation, or malicious processes trying to bypass security restrictions. AppArmor is a Linux kernel security module that restricts programs'' capabilities with per-program profiles. - -Next Steps: -1. Review the specific AppArmor profile and operation that was denied -2. Investigate the process or application that triggered the violation -3. Examine system logs for additional suspicious activities from the same host -4. Verify if the violation represents legitimate application behavior or potential security threat -5. Consider updating AppArmor profiles if the violation is legitimate, or implement additional security controls if malicious -6. Check for patterns of multiple violations that might indicate systematic bypass attempts -', '["https://attack.mitre.org/techniques/T1562/001/","https://wiki.ubuntu.com/AppArmor"]', '(safe("log.process", "").contains("kernel") || safe("log.process", "").contains("audit") || safe("log.facility", "") == "kern") && safe("log.message", "").contains("apparmor") && (safe("log.message", "").contains("DENIED") || safe("log.message", "").contains("ALLOWED") && safe("log.message", "").contains("requested_mask") || safe("log.message", "").contains("profile") && safe("log.message", "").contains("denied_mask") || safe("log.message", "").contains("operation") && safe("log.message", "").contains("name=") || safe("log.message", "").contains("info=\"Failed name lookup\"") || safe("log.message", "").contains("error=-13")', '2025-09-03 16:33:02.423292', true, false, 'origin', '["origin.host","log.apparmor_profile"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (897, 'GRUB Security Events', 2, 3, 2, 'Defense Evasion', 'T1014 - Rootkit', 'Detects suspicious GRUB bootloader activities including password bypass attempts, configuration changes, or module loading that could indicate boot-level compromise. This rule identifies potential rootkit installation or boot process manipulation attempts. - -Next Steps: -1. Verify the legitimacy of GRUB-related activities and check if they were authorized -2. Examine the system boot process and verify GRUB configuration integrity -3. Check for unauthorized changes to /boot/grub/ directory contents -4. Review system logs for additional indicators of boot-level compromise -5. Validate GRUB modules and configuration against known good baseline -6. Consider re-imaging the system if boot-level compromise is confirmed -', '["https://attack.mitre.org/techniques/T1014/","https://www.gnu.org/software/grub/manual/grub/grub.html","https://wiki.debian.org/GrubEFIReinstall"]', '(safe("log.message", "").contains("GRUB") && (safe("log.message", "").contains("authentication") || safe("log.message", "").contains("password") || safe("log.message", "").contains("bypass"))) || (safe("log.process_name", "") == "grub-probe" && safe("log.user", "") != "root") || (safe("log.file_path", "").contains("/boot/grub/grub.cfg") && safe("log.event_type", "") == "file_read" && safe("log.user", "") != "root") || (safe("log.command_line", "").contains("grub") && (safe("log.command_line", "").contains("--unrestricted") || safe("log.command_line", "").contains("--users") || safe("log.command_line", "").contains("insmod"))) || (safe("log.message", "").contains("grub rescue") || safe("log.message", "").contains("minimal BASH-like")) || (safe("log.file_path", "").contains("/boot/grub/i386-pc/") && safe("log.event_type", "") == "file_create")', '2025-09-03 16:33:35.655969', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1279, 'Windows: Remote System Discovery Commands', 3, 1, 2, 'Discovery', '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/"]', 'safe("log.message", "").matches("(-n|-s|-a)") && safe("log.winlogEventDataProcessName", "").matches("(nbtstat.exe|arp.exe)")', '2025-09-04 19:58:21.089075', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1384, 'Windows: High Number of Process and/or Service Terminations', 1, 2, 3, 'Impact', 'Service Stop', 'This rule identifies a high number (10) of process terminations (stop, delete, or suspend) from the same host within a short time period.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1489/"]', 'safe("log.eventData.processName", "").matches("(net.exe|sc.exe|taskkill.exe)") && safe("log.message", "").matches("(stop|pause|delete|/PID|/IM|/T|/F|/t|/f|/im|/pid)")', '2025-09-04 20:45:17.952367', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (888, 'APT Repository Tampering Detected', 2, 3, 2, 'Initial Access', 'T1195.002 - Supply Chain Compromise: Compromise Software Supply Chain', 'Detects attempts to modify APT repository configurations, add untrusted repositories, or bypass package signing verification, which could indicate supply chain attacks or malicious package installation attempts. - -Next Steps: -1. Verify the legitimacy of any repository modifications by checking with system administrators -2. Review the specific APT commands executed and their parameters -3. Check if the repository modifications were authorized and documented -4. Examine the source of the untrusted repository being added -5. Verify GPG keys and signatures for any packages installed from modified repositories -6. Monitor for subsequent package installations from untrusted sources -7. Consider rolling back unauthorized repository changes -', '["https://attack.mitre.org/techniques/T1195/002/","https://wiki.debian.org/SecureApt"]', '(safe("log.process", "").contains("apt") || safe("log.process", "").contains("apt-get") || safe("log.process", "").contains("apt-key")) && (safe("log.message", "").contains("sources.list") || safe("log.message", "").contains("add-apt-repository") || safe("log.message", "").contains("trusted.gpg") || safe("log.message", "").contains("NO_PUBKEY") || safe("log.message", "").contains("GPG error") || safe("log.message", "").contains("could not be verified") || safe("log.message", "").contains("repository") && safe("log.message", "").contains("unsigned"))', '2025-09-03 16:33:02.888123', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (889, 'Boot Loader Tampering', 2, 3, 3, 'Defense Evasion, Persistence', 'T1542.003 - Bootkit', 'Detects attempts to modify boot loader configuration files, kernel parameters, or boot scripts that could be used to establish persistence or bypass security controls. This includes modifications to GRUB configuration files, kernel images, or initrd files. - -Next Steps: -1. Immediately isolate the affected system to prevent further damage -2. Verify the integrity of boot loader files using checksums or file integrity monitoring baselines -3. Check system logs for any unauthorized access or privilege escalation attempts -4. Examine the modified files to understand what changes were made -5. Review user accounts and access logs to identify who made the modifications -6. Consider reimaging the system if bootkit installation is confirmed -7. Update security controls to prevent similar tampering attempts -', '["https://attack.mitre.org/techniques/T1542/003/","https://www.debian.org/doc/manuals/securing-debian-manual/ch3.en.html"]', '(safe("log.file_path", "").contains("/boot/grub/") && safe("log.event_type", "") == "file_modify") || (safe("log.file_path", "") == "/etc/default/grub" && safe("log.event_type", "") == "file_modify") || (safe("log.process_name", "").contains("grub-install") || safe("log.process_name", "").contains("grub-mkconfig") || safe("log.process_name", "").contains("update-grub")) || (safe("log.file_path", "").contains("/boot/vmlinuz") && safe("log.event_type", "") == "file_modify") || (safe("log.file_path", "").contains("/boot/initrd") && safe("log.event_type", "") == "file_modify") || (safe("log.command_line", "").contains("grub-editenv") && safe("log.command_line", "").contains("set"))', '2025-09-03 16:33:03.367525', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (890, 'Configuration File Changes Detection', 2, 3, 2, 'Persistence, Privilege Escalation', 'T1543 - Create or Modify System Process', 'Detects unauthorized or suspicious changes to system configuration files that could indicate system compromise or persistence mechanisms. This rule monitors package management activities, configuration file modifications, and changes to critical system files in Debian-based systems. - -Next Steps: -1. Verify if the configuration changes were authorized and part of legitimate system maintenance -2. Check the specific files modified and assess their security implications -3. Review recent package installations or updates that may have triggered the changes -4. Examine system logs for any concurrent suspicious activities -5. Validate that only authorized administrators performed the changes -6. Consider reverting unauthorized changes and implementing file integrity monitoring -', '["https://www.debian.org/doc/debian-policy/ch-files.html","https://attack.mitre.org/techniques/T1543/"]', '( (safe("log.process", "") == "dpkg" && safe("log.message", "").matches(".*(conffile|configuration file).*")) || (safe("log.process", "") == "apt" && safe("log.message", "").matches(".*Setting up.*")) || safe("log.message", "").matches(".*(modified|changed|differ).*(/etc/|conf|config).*") || safe("log.message", "").matches(".*Configuration file.*locally modified.*") || safe("log.message", "").matches(".*(maintainer script|postinst|preinst|postrm|prerm).*") || safe("log.message", "").matches(".*dpkg-divert.*") || (safe("log.facility", "") == "authpriv" && safe("log.message", "").matches(".*/etc/(passwd|shadow|group|sudoers).*")) ) && safe("log.severity", "") != "info"', '2025-09-03 16:33:03.793192', true, false, 'origin', '["adversary.host"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (891, 'CVE Vulnerability Detection', 3, 3, 2, 'Initial Access', 'T1190 - Exploit Public-Facing Application', 'Detects attempts to exploit known CVE vulnerabilities in Debian-based systems through log analysis of package operations and security updates. This rule identifies CVE references in system logs, vulnerability-related messages, and security package operations. - -Next Steps: -1. Verify the specific CVE mentioned in the log message -2. Check if the affected package is installed and its current version -3. Review system logs for any exploitation attempts or unusual activities -4. Verify if security patches are available and schedule immediate updates -5. Check for indicators of compromise on the affected system -6. Review network traffic from the affected host for suspicious connections -7. Consider isolating the system if active exploitation is suspected -', '["https://www.debian.org/security/","https://attack.mitre.org/techniques/T1190/"]', '(safe("log.facility", "") == "daemon" || safe("log.facility", "") == "syslog") && ( (safe("log.message", "").matches(".*CVE-\\d{4}-\\d{4,}.*")) || (safe("log.message", "").matches(".*(vulnerable|vulnerability|exploit).*")) || (safe("log.process", "") == "apt" && safe("log.message", "").matches(".*(security|critical).*")) || (safe("log.process", "") == "dpkg" && safe("log.message", "").matches(".*(security update|patch).*")) )', '2025-09-03 16:33:04.269205', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (898, 'Init System Tampering Detection', 2, 3, 3, 'Persistence, Privilege Escalation', 'T1037 - Boot or Logon Initialization Scripts', 'Detects tampering with init system components including systemd, SysV init scripts, and upstart configurations which could indicate persistence mechanisms, rootkit installation, or system-level backdoors. - -Next Steps: -1. Investigate the specific file or service being modified -2. Review the user account performing the changes and verify authorization -3. Check for unauthorized scheduled tasks or services being created -4. Examine system logs for concurrent suspicious activities -5. Verify the integrity of critical system files using package managers -6. Review network connections from the affected system -7. Consider isolating the system if unauthorized changes are confirmed -', '["https://attack.mitre.org/techniques/T1037/","https://www.freedesktop.org/wiki/Software/systemd/","https://wiki.debian.org/Init"]', '(safe("log.path", "").contains("/etc/init.d/") || safe("log.path", "").contains("/etc/systemd/") || safe("log.path", "").contains("/lib/systemd/") || safe("log.path", "").contains("/etc/init/") || safe("log.message", "").contains("/etc/init.d/") || safe("log.message", "").contains("/etc/systemd/") || safe("log.message", "").contains("/lib/systemd/") && (safe("log.message", "").contains("modified") || safe("log.message", "").contains("created") || safe("log.message", "").contains("deleted") || safe("log.message", "").contains("chmod") || safe("log.message", "").contains("chown") || safe("log.message", "").contains("update-rc.d") || safe("log.message", "").contains("insserv") || safe("log.message", "").contains("systemctl preset") || safe("log.message", "").contains("systemctl set-default") || safe("log.message", "").contains("Failed to validate unit file") || safe("log.message", "").contains("Invalid unit file") || safe("log.message", "").contains("Suspicious") || safe("log.message", "").contains("generator") && safe("log.message", "").contains("failed")', '2025-09-03 16:33:36.089591', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (892, 'Debian-Specific Kernel Exploit Attempts', 3, 3, 3, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects potential kernel exploitation attempts specific to Debian systems including dirty COW variants, overlayfs exploits, namespace escapes, and other kernel vulnerability exploitation indicators. This rule monitors for kernel crashes, segmentation faults, exploit attempts, and other anomalous kernel behavior that may indicate an active exploitation attempt. - -Next Steps: -1. Immediately isolate the affected system from the network -2. Review the specific kernel messages and error patterns detected -3. Check system logs around the time of the alert for additional indicators -4. Verify the kernel version and patch level against known vulnerabilities -5. Examine running processes for suspicious activity or privilege escalation -6. Review file system integrity and check for unauthorized changes -7. Consider reimaging the system if compromise is confirmed -8. Update kernel to latest patched version and implement additional hardening -', '["https://attack.mitre.org/techniques/T1068/","https://www.debian.org/security/","https://security-tracker.debian.org/tracker/"]', '(safe("log.message", "").contains("kernel") || safe("origin.process", "").contains("kernel")) && -(safe("log.message", "").contains("exploit") || - safe("log.message", "").contains("segfault") && safe("log.message", "").contains("ip") && safe("log.message", "").contains("sp") || - safe("log.message", "").contains("BUG: unable to handle kernel") || - safe("log.message", "").contains("Kernel panic") || - safe("log.message", "").contains("Oops:") || - safe("log.message", "").contains("overlayfs") && safe("log.message", "").contains("denied") || - safe("log.message", "").contains("namespace escape") || - safe("log.message", "").contains("capabilities") && safe("log.message", "").contains("denied") || - safe("log.message", "").contains("PTRACE") && safe("log.message", "").contains("denied") || - safe("log.message", "").contains("dirty cow") || - safe("log.message", "").contains("mmap_min_addr") || - safe("log.message", "").contains("vsyscall attempted") || - safe("log.message", "").contains("stack smashing detected") || - safe("log.message", "").contains("buffer overflow detected") || - safe("log.message", "").contains("Return-oriented programming") || - safe("log.message", "").contains("ROP chain") -', '2025-09-03 16:33:04.788679', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (893, 'Debian Security Advisory Match Detection', 3, 3, 2, 'Initial Access', 'T1190 - Exploit Public-Facing Application', 'Detects activities matching known Debian Security Advisories (DSA) including vulnerable package usage, CVE exploitation attempts, and unpatched system indicators. This rule identifies systems that may be running vulnerable packages or have security issues that match published Debian security advisories. - -**Next Steps:** -1. Immediately verify the affected system''s package versions and security status -2. Check if security updates are available and apply them promptly -3. Review the specific CVE details and assess potential impact on your environment -4. Examine system logs for signs of exploitation attempts targeting the vulnerability -5. Consider isolating the affected system if critical vulnerabilities are confirmed -6. Update vulnerability management procedures to prevent similar exposures -7. Verify that automatic security updates are properly configured if applicable -', '["https://www.debian.org/security/","https://security-tracker.debian.org/tracker/","https://attack.mitre.org/techniques/T1190/"]', '(safe("log.packageName", "") != "" && safe("log.cveId", "").matches("CVE-\\d{4}-\\d{4,}") && safe("log.severity", "") in ["critical", "high"]) || (safe("log.message", "").contains("DSA-") || safe("log.message", "").contains("DLA-") || safe("log.message", "").contains("DTSA-")) && safe("log.eventType", "") in ["vulnerability_detected", "exploit_attempt", "package_vulnerable"] || (safe("log.aptOutput", "").contains("Security updates available") || safe("log.aptOutput", "").contains("vulnerable version installed") || safe("log.aptOutput", "").contains("security patch required")) || (safe("log.service", "") == "unattended-upgrades" && (safe("log.message", "").contains("security update failed") || safe("log.message", "").contains("held back due to phased updates") || safe("log.message", "").contains("dependency resolution failed"))) || (safe("log.auditType", "") == "SYSCALL" && safe("log.exe", "") != "" && safe("log.vulnerabilityStatus", "") in ["unpatched", "vulnerable", "exploitable"]) || (safe("log.dpkgStatus", "").contains("security") && safe("action", "") in ["hold", "downgrade"] && (safe("log.reason", "").contains("vulnerability") || safe("log.reason", "").contains("CVE") || safe("log.reason", "").contains("security"))))', '2025-09-03 16:33:05.268571', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (894, 'Debian-Specific Rootkits', 3, 3, 3, 'Defense Evasion', 'T1014 - Rootkit', 'Detects indicators of known Debian/Linux rootkits including file modifications, hidden processes, network backdoors, and kernel module manipulation commonly seen in Debian environments. This rule identifies various rootkit behaviors such as unauthorized modifications to system binaries, kernel module loading, hidden processes, and suspicious network activity. - -Next Steps: -1. Immediately isolate the affected system from the network -2. Examine the detected file paths and processes for signs of tampering -3. Check for additional indicators of compromise using antimalware tools -4. Review system logs for the timeline of infection -5. Verify integrity of critical system files using package manager verification (dpkg -V) -6. Consider reimaging the system if rootkit persistence is confirmed -7. Analyze network connections for command and control communications -8. Update security tools and perform full system scan -', '["https://attack.mitre.org/techniques/T1014/","https://www.debian.org/security/","https://wiki.debian.org/Teams/Security"]', '(safe("log.file_path", "").contains("/usr/bin/passwd") && safe("log.event_type", "") == "file_modify" && safe("log.process_name", "") != "dpkg") || (safe("log.file_path", "").contains("/etc/ld.so.preload") && safe("log.event_type", "") in ["file_create", "file_modify"]) || (safe("log.process_name", "") in ["reptile", "bdvl", "azazel", "jynx", "xorddos"] || safe("log.file_path", "").contains("reptile") || safe("log.file_path", "").contains("bdvl")) || (safe("log.command_line", "").contains("insmod") && (safe("log.command_line", "").contains("rootkit") || safe("log.command_line", "").contains("hide") || safe("log.command_line", "").contains("backdoor")) || (safe("log.file_path", "").contains("/proc/") && safe("log.file_path", "").contains("/maps") && safe("log.message", "").contains("deleted")) || (safe("log.network_port", 0) in [31337, 12345, 6666] && safe("log.event_type", "") == "network_listen") || (safe("log.file_path", "").contains("/dev/ptmx") && safe("log.event_type", "") == "file_create" && safe("log.user", "") != "root") || (safe("log.message", "").contains("LKM") && (safe("log.message", "").contains("rootkit") || safe("log.message", "").contains("hiding")) || (safe("log.file_path", "").matches("/lib/modules/.*/.*\\.ko") && safe("log.event_type", "") == "file_create" && safe("log.process_name", "") != "dpkg")', '2025-09-03 16:33:05.756082', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (895, 'Desktop Environment Attack Detection', 3, 3, 2, 'Collection, Credential Access', 'T1056 - Input Capture', 'Detects attacks targeting desktop environments like GNOME, KDE, XFCE including keyloggers, screen capture attempts, and session hijacking. This rule identifies suspicious activities such as desktop process crashes, authentication bypass attempts, unauthorized screen recording, and unauthorized access to X11 authentication files. - -Next Steps: -1. Examine the affected user account and host for signs of compromise -2. Check for unauthorized processes accessing desktop environment APIs -3. Review system logs for evidence of privilege escalation or lateral movement -4. Verify the legitimacy of any screen recording or input monitoring activities -5. Inspect X11 authentication files for unauthorized modifications -6. Consider isolating the affected system if malicious activity is confirmed -', '["https://www.debian.org/security/","https://attack.mitre.org/techniques/T1056/"]', '(safe("log.process_name", "") in ["gnome-shell", "plasmashell", "xfce4-panel", "mate-panel"] && safe("log.signal", "") in ["SIGSEGV", "SIGABRT"]) || (safe("log.component", "") in ["gdm", "gdm3", "lightdm", "sddm"] && safe("log.event_type", "") in ["authentication_failure", "session_hijack", "bypass_attempt"]) || (safe("log.message", "").contains("keylogger detected") || safe("log.message", "").contains("screen capture") || safe("log.message", "").contains("clipboard access") || safe("log.message", "").contains("input monitoring") || safe("log.message", "").contains("desktop recording")) || (safe("log.dbus_method", "") in ["Screenshot", "RecordDesktop", "GetClipboard"] && !(safe("log.caller", "") in ["gnome-screenshot", "spectacle", "flameshot"])) || ((safe("log.file_path", "").contains(".Xauthority") || safe("log.file_path", "").contains(".ICEauthority") || safe("log.file_path", "").contains("/tmp/.X11-unix/")) && safe("log.operation", "") in ["read", "write", "modify"] && !(safe("log.process_name", "") in ["Xorg", "gdm", "lightdm"]))', '2025-09-03 16:33:06.206942', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1019, 'Unusual Traffic Volume Anomaly Detection', 2, 1, 3, 'Exfiltration', 'T1048 - Exfiltration Over Alternative Protocol', 'Detects unusual traffic volumes that could indicate data exfiltration, DDoS attacks, or other anomalous network behavior. This rule triggers when a single source IP generates traffic exceeding normal thresholds within a short time window. Next Steps: @@ -10154,58 +10206,6 @@ Next Steps: 6. If confirmed malicious, implement IP blocking and investigate potential data compromise 7. Monitor for similar patterns from other IP addresses ', '["https://www.cisco.com/en/US/technologies/tk648/tk362/technologies_white_paper09186a00800a3db9.html","https://attack.mitre.org/techniques/T1048/"]', 'safe("origin.ip", "") != "" && safe("origin.bytesSent", 0.0) > double(10000000) && safe("origin.packagesSent", 0.0) > double(10000)', '2025-09-04 15:18:51.256799', true, false, 'origin', '["origin.ip","target.ip"]', '[{"indexPattern":"v11-log-netflow-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"}],"or":null,"within":"now-15m","count":3}]'); -INSERT INTO public.utm_correlation_rules VALUES (907, 'System Update Failures Detection', 1, 3, 3, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', 'Detects system update failures that could indicate system compromise, network issues, or attempts to prevent security patches from being applied. Failed updates may leave systems vulnerable to known security issues and could indicate malicious interference with system maintenance processes. - -Next Steps: -1. Verify the specific nature of the update failure by examining the complete error message -2. Check network connectivity and repository accessibility from the affected system -3. Investigate if the failure is isolated to one system or affects multiple systems -4. Review system logs for any suspicious activities around the time of the update failure -5. Ensure that package repositories have not been tampered with or redirected -6. Check for any unauthorized changes to package management configuration files -7. Verify system integrity and check for signs of compromise if failures persist -', '["https://wiki.debian.org/AptCLI","https://attack.mitre.org/techniques/T1562/001/"]', '(safe("log.process", "") == "apt" || safe("log.process", "") == "apt-get" || safe("log.process", "") == "dpkg") && -( - safe("log.message", "").matches(".*(failed|error|unable to fetch|404 Not Found|Hash Sum mismatch).*") || - safe("log.message", "").matches(".*(dpkg was interrupted|broken packages|unmet dependencies).*") || - safe("log.message", "").matches(".*(E: Failed to fetch|W: Failed to fetch|Err:).*") || - safe("log.message", "").matches(".*(Could not resolve|Temporary failure resolving).*") -) -', '2025-09-03 16:33:57.577951', true, false, 'origin', '["origin.host"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (896, 'DPKG Database Corruption Detected', 1, 3, 3, 'Impact', 'T1485 - Data Destruction', 'Detects signs of DPKG database corruption or manipulation which could prevent package management, system updates, or indicate system compromise attempts through package database tampering. This could indicate malicious activity targeting system integrity or accidental corruption affecting system maintenance. - -Next Steps: -1. Verify the integrity of the dpkg database by running ''dpkg --audit'' and ''dpkg --verify'' -2. Check system logs for any unauthorized access or modification attempts to /var/lib/dpkg/ -3. Review recent package installation or removal activities for anomalies -4. Examine file permissions and ownership of dpkg database files -5. Consider restoring from backup if corruption is confirmed and cannot be repaired -6. Investigate potential root cause including disk errors, improper shutdowns, or malicious activity -', '["https://attack.mitre.org/techniques/T1485/","https://wiki.debian.org/Teams/Dpkg"]', 'safe("log.process", "").contains("dpkg") && (safe("log.message", "").contains("database is locked") || safe("log.message", "").contains("corrupted database") || safe("log.message", "").contains("status database area is locked") || safe("log.message", "").contains("failed to open package info file") || safe("log.message", "").contains("failed to read package info") || safe("log.message", "").contains("parse error") && safe("log.message", "").contains("/var/lib/dpkg") || safe("log.message", "").contains("unable to access dpkg status area") || safe("log.message", "").contains("dpkg was interrupted") || safe("log.message", "").contains("triggers file contains unknown directive")', '2025-09-03 16:33:06.563931', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (922, 'Package Verification Failures in RHEL', 3, 3, 3, 'Impact', 'T1565.001 - Data Manipulation: Stored Data Manipulation', 'Detects package verification failures in RHEL systems indicating potential tampering, corruption, or unauthorized modifications to installed packages. This includes checksum mismatches, missing files, and permission changes. - -Next Steps: -1. Investigate which specific package failed verification and the nature of the failure -2. Check the package''s source and installation history using ''rpm -qa --last'' and ''yum history'' -3. Verify package integrity using ''rpm -Va'' for all packages or ''rpm -V '' for specific packages -4. Review system logs for unauthorized package installations or modifications -5. Check for signs of malware or unauthorized access to the system -6. Consider reinstalling the affected package from a trusted source -7. Examine file system integrity and compare against known good baselines -', '["https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/7/html/security_guide/sec-verifying_signed_packages","https://attack.mitre.org/techniques/T1565/001/"]', '(safe("log.program", "") in ["rpm", "yum", "dnf"] && (safe("log.message", "").matches("verify.*failed|verification.*failed|checksum.*mismatch|digest.*mismatch") || safe("log.message", "").matches("missing.*file|modified.*file|size.*differs|mode.*differs|md5.*differs") || safe("log.verify_status", "") == "failed" || safe("log.rpm_verify", "").matches("[SM5DLUGTP]")) || (safe("log.type", "") == "rpm-verify" && safe("log.result", "") != "pass") || (safe("log.message", "").matches("Package.*tampered|Package.*corrupted|RPM.*database.*corrupted"))', '2025-09-03 16:36:01.758227', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-linux-*","with":[{"field":"host.name.keyword","operator":"filter_term","value":"{{.host.name}}"},{"field":"log.package_name.keyword","operator":"filter_term","value":"{{.log.package_name}}"}],"or":null,"within":"now-12h","count":2}]'); -INSERT INTO public.utm_correlation_rules VALUES (923, 'Repository GPG Key Issues in RHEL', 2, 3, 1, 'Initial Access', 'T1195.001 - Supply Chain Compromise: Compromise Software Dependencies and Development Tools', 'Detects GPG key verification failures, missing GPG keys, or unauthorized repository key changes in RHEL systems. These issues could indicate compromised repositories, man-in-the-middle attacks, or supply chain compromises that may lead to malicious package installation. - -Next Steps: -1. Immediately verify the integrity of the affected repository by checking its official GPG keys -2. Cross-reference the repository URL and GPG key fingerprint with official sources -3. Check for any recent changes to repository configurations in /etc/yum.repos.d/ -4. Review recent package installations and updates on the affected system -5. Verify network connectivity and check for potential man-in-the-middle attacks -6. Consider temporarily disabling the affected repository until verification is complete -7. Audit system logs for any suspicious package installations or system modifications -', '["https://www.redhat.com/en/blog/rpm-gpg-verify-packages","https://attack.mitre.org/techniques/T1195/001/"]', 'safe("log.program", "") in ["yum", "dnf", "rpm"] && (safe("log.message", "").matches("GPG.*key.*error|GPG.*check.*failed|GPG.*signature.*invalid|GPG.*key.*not.*found|GPG.*verification.*failed") || safe("log.message", "").matches("NOKEY|MISSING_KEY|BAD_SIGNATURE|key.*expired|key.*revoked") || safe("log.gpgcheck", "") == "failed" || safe("log.signature_status", "") in ["invalid", "missing", "expired", "untrusted"])) || (safe("log.message", "").matches("Repository.*key.*changed|gpgkey.*modified|Importing GPG key") && safe("origin.user", "") != "root")', '2025-09-03 16:36:02.328303', true, false, 'origin', '["target.host","log.repository"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (924, 'RHEL-Specific Kernel Exploitation Attempt', 3, 3, 3, 'Privilege Escalation', 'Exploitation for Privilege Escalation', 'Detects potential exploitation attempts targeting RHEL-specific kernel vulnerabilities, including memory corruption, privilege escalation through kernel bugs, or attempts to bypass kernel security mechanisms like SELinux or kernel module protections.', '["https://access.redhat.com/security/vulnerabilities","https://attack.mitre.org/techniques/T1068/"]', '(safe("log.facility", "") == "kern" || safe("log.process", "") == "kernel") && (safe("log.message", "").contains("segfault") && (safe("log.message", "").contains("ip") || safe("log.message", "").contains("sp")) || safe("log.message", "").contains("kernel BUG") || safe("log.message", "").contains("Oops:") || safe("log.message", "").contains("kernel exploit") || safe("log.message", "").contains("privilege escalation") || safe("log.message", "").contains("SELinux: avc: denied") && safe("log.message", "").contains("capability") || safe("log.message", "").contains("KASLR bypass") || safe("log.message", "").contains("CVE-") && safe("log.message", "").contains("exploit")) -', '2025-09-03 16:36:02.792064', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"},{"field":"log.facility.keyword","operator":"filter_term","value":"kern"}],"or":null,"within":"now-5m","count":3}]'); INSERT INTO public.utm_correlation_rules VALUES (1020, 'Traffic Redirection and Hijacking Detection', 3, 3, 2, 'Credential Access, Collection', 'Man-in-the-Middle', 'Detects potential traffic redirection, BGP hijacking, or man-in-the-middle attacks by identifying unusual routing patterns, unexpected next-hop changes, or traffic flowing through suspicious intermediate nodes. This rule monitors for anomalous network behaviors including BGP route modifications, TTL variance indicating rerouting, asymmetric routing patterns, and traffic flowing through unexpected geographic locations. Next Steps: @@ -10220,88 +10220,7 @@ Next Steps: 9. Contact network administrators to verify any recent routing changes 10. Consider implementing additional network monitoring for affected segments ', '["https://attack.mitre.org/techniques/T1557/","https://documentation.meraki.com/MX/Monitoring_and_Reporting/NetFlow_Overview"]', '( safe("log.next_hop_changed", "false") == "true" && safe("log.flow_count", 0.0) > double(100) ) || ( safe("log.bgp_next_hop", "") != safe("log.expected_next_hop", "") && safe("log.bytes", 0.0) > double(10485760) ) || ( safe("log.ttl_variance", 0.0) > double(5) && safe("origin.ip", "") == safe("log.previous_origin_ip", "") ) || ( safe("target.port", 0.0) == 179 && safe("log.bgp_updates", 0.0) > double(50) ) || ( safe("log.route_changed", "false") == "true" && safe("log.sensitive_traffic", "false") == "true" ) || ( safe("origin.geolocation.country", "") != safe("log.expected_country", "") && safe("log.critical_infrastructure", "false") == "true" ) || ( safe("log.asymmetric_routing", "false") == "true" && safe("log.bytes", 0.0) > double(52428800) )', '2025-09-04 15:18:51.769729', true, false, 'origin', '["origin.ip","target.ip","log.next_hop"]', '[{"indexPattern":"v11-log-*","with":[{"field":"target.ip.keyword","operator":"filter_term","value":"{{.target.ip}}"}],"or":null,"within":"now-2h","count":20}]'); -INSERT INTO public.utm_correlation_rules VALUES (899, 'Network Manager Exploit Attempt', 2, 3, 2, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects potential exploitation attempts against Network Manager service including privilege escalation, D-Bus manipulation, and configuration tampering. Network Manager exploits can lead to system compromise, privilege escalation, and unauthorized network access. - -Next Steps: -1. Examine the affected system for signs of compromise -2. Review Network Manager logs and configuration files -3. Check for unusual D-Bus activity and process behavior -4. Verify user privileges and recent authentication events -5. Scan the system for malware and unauthorized modifications -6. Update Network Manager to the latest security patches -7. Monitor for lateral movement attempts from the compromised system -', '["https://www.debian.org/security/","https://attack.mitre.org/techniques/T1068/"]', '(safe("log.process_name", "") == "NetworkManager" && safe("log.event_type", "") in ["segfault", "crash", "error"]) || (safe("log.service", "") == "NetworkManager" && (safe("log.message", "").contains("privilege escalation") || safe("log.message", "").contains("unauthorized access") || safe("log.message", "").contains("buffer overflow") || safe("log.message", "").contains("segmentation fault"))) || (safe("log.dbus_interface", "").contains("NetworkManager") && safe("log.error_code", "") != "" && safe("log.action", "") in ["denied", "failed", "unauthorized"]) || ((safe("log.file_path", "").contains("/etc/NetworkManager/") || safe("log.file_path", "").contains("/var/lib/NetworkManager/")) && safe("log.operation", "") in ["modify", "delete", "write"] && safe("log.user", "") != "root")', '2025-09-03 16:33:36.585411', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"},{"field":"target.host.keyword","operator":"filter_term","value":"{{.target.host}}"}],"or":null,"within":"now-30m","count":3}]'); -INSERT INTO public.utm_correlation_rules VALUES (900, 'Package Dependency Attack Detection', 2, 3, 2, 'Initial Access', 'T1195.001 - Supply Chain Compromise: Compromise Software Dependencies and Development Tools', 'Detects potential dependency confusion or hijacking attacks through unusual package installation patterns, version conflicts, or suspicious dependency resolutions in Debian-based systems. This rule identifies suspicious package management activities that could indicate supply chain attacks. - -Next Steps: -1. Investigate the specific packages being installed or causing dependency conflicts -2. Verify the legitimacy of package repositories and sources -3. Check for unauthorized package installations or configuration changes -4. Review package installation logs for signs of typosquatting or dependency confusion -5. Validate that packages come from trusted official repositories -6. Check for any recently added or modified package sources in /etc/apt/sources.list -7. Investigate any forced installations that bypass dependency checks -', '["https://www.debian.org/doc/debian-policy/ch-relationships.html","https://attack.mitre.org/techniques/T1195/001/"]', '(safe("log.process", "") == "apt" || safe("log.process", "") == "apt-get" || safe("log.process", "") == "dpkg") && ( safe("log.message", "").matches(".*(dependency problems|but it is not going to be installed).*" || safe("log.message", "").matches(".*(package.*has no installation candidate|held broken packages).*" || safe("log.message", "").matches(".*(Conflicts:|Breaks:|but.*is to be installed).*" || safe("log.message", "").matches(".*(downgrade|force-depends|force-conflicts).*" || safe("log.message", "").matches(".*(attempting to overwrite|which is also in package).*" || (safe("log.message", "").matches(".*install.*" && safe("log.message", "").matches(".*(unofficial|unknown|untrusted).*")) )', '2025-09-03 16:33:36.872378', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":null,"within":"now-2h","count":4}]'); -INSERT INTO public.utm_correlation_rules VALUES (901, 'Package Signature Verification Failure', 3, 3, 2, 'Initial Access', 'Supply Chain Compromise', 'Detects package signature verification failures during APT or DPKG operations, which may indicate tampered packages, compromised repositories, or man-in-the-middle attacks on package downloads. These failures can signal attempts to deliver malicious packages through supply chain attacks. - -Next Steps: -1. Immediately halt any ongoing package installations on the affected system -2. Verify the integrity of package repositories and GPG keys -3. Check network logs for signs of man-in-the-middle attacks during package downloads -4. Review recent package installation history and validate installed packages -5. Update repository keys and retry package operations from trusted sources -6. Consider isolating the system until package integrity can be verified -7. Review system logs for other signs of compromise that may have preceded this event -', '["https://attack.mitre.org/techniques/T1195/","https://wiki.debian.org/SecureApt#How_to_tell_if_the_key_is_safe"]', '(safe("log.process", "").contains("dpkg") || safe("log.process", "").contains("apt") || safe("log.process", "").contains("apt-get")) && -(safe("log.message", "").contains("signature could not be verified") || - safe("log.message", "").contains("WARNING: The following packages cannot be authenticated") || - safe("log.message", "").contains("BADSIG") || - safe("log.message", "").contains("NO_PUBKEY") || - safe("log.message", "").contains("invalid signature") || - safe("log.message", "").contains("Hash Sum mismatch") || - safe("log.message", "").contains("Size mismatch") || - safe("log.message", "").contains("Verification failed") -', '2025-09-03 16:33:37.341184', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (902, 'Repository Key Issues Detection', 3, 3, 2, 'Initial Access', 'Supply Chain Compromise', 'Detects issues with APT repository keys that could indicate attempts to inject malicious packages or compromise the software supply chain. This rule identifies GPG key validation failures, expired keys, unsigned repositories, and other key-related security issues that could be exploited by attackers to deliver malicious software updates. - -Next Steps: -1. Verify the legitimacy of the repository and its GPG keys -2. Check if this is a known repository with valid signing keys -3. Investigate any recent changes to repository configurations -4. Review package installation logs for suspicious activity -5. Validate the integrity of installed packages from affected repositories -6. Consider temporarily disabling the repository until key issues are resolved -7. Monitor for any unauthorized package installations or system changes -', '["https://wiki.debian.org/SecureApt","https://attack.mitre.org/techniques/T1195/"]', '(safe("log.process", "") == "apt" || safe("log.process", "") == "apt-key" || safe("log.process", "") == "gpg") && -( - safe("log.message", "").matches(".*(NO_PUBKEY|GPG error|key is not certified|KEYEXPIRED).*") || - safe("log.message", "").matches(".*(public key is not available|signatures couldn''t be verified).*") || - safe("log.message", "").matches(".*(WARNING: apt does not have a stable CLI interface).*") || - safe("log.message", "").matches(".*(repository.*not signed|Release file.*not valid yet).*") || - safe("log.message", "").matches(".*(apt-key is deprecated|untrusted packages).*") -) -', '2025-09-03 16:33:37.81804', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":null,"within":"now-1h","count":3}]'); INSERT INTO public.utm_correlation_rules VALUES (1385, 'Windows: Detection of SUNBURST command and control activity', 3, 3, 2, 'Command and Control', '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/"]', 'safe("log.eventDataProcessName", "").matches("(ConfigurationWizard.exe|NetFlowService.exe|NetflowDatabaseMaintenance.exe|SolarWinds.Administration.exe|SolarWinds.BusinessLayerHost.exe|SolarWinds.BusinessLayerHostx64.exe|SolarWinds.Collector.Service.exe|SolarwindsDiagnostics.exe)") && safe("log.message", "").matches("(/swip/Upload.ashx(.+)(POST|PUT)|(POST|PUT)(.+)/swip/Upload.ashx|/swip/SystemDescription(.+)(GET|HEAD)|(GET|HEAD)(.+)/swip/SystemDescription)")', '2025-09-04 20:45:18.629258', true, false, 'target', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (903, 'Secure Boot Violations', 3, 3, 2, 'Defense Evasion, Persistence', 'T1542.001 - System Firmware', 'Detects secure boot violations, unsigned kernel module loading, or attempts to disable UEFI secure boot protections that could allow malicious code execution at boot time. - -Next Steps: -1. Verify the system''s secure boot status with ''mokutil --sb-state'' -2. Check for unauthorized kernel modules with ''lsmod'' and ''modinfo'' -3. Review system boot logs for additional evidence of tampering -4. Examine EFI variables for unauthorized modifications -5. Verify system integrity using package verification tools -6. Consider reimaging the system if compromise is confirmed -', '["https://attack.mitre.org/techniques/T1542/001/","https://wiki.debian.org/SecureBoot","https://www.debian.org/releases/stable/amd64/release-notes/ch-whats-new.en.html"]', '(safe("log.message", "").contains("Secure Boot") && (safe("log.message", "").contains("violation") || safe("log.message", "").contains("disabled") || safe("log.message", "").contains("failed"))) || (safe("log.message", "").contains("UEFI") && (safe("log.message", "").contains("unsigned") || safe("log.message", "").contains("invalid signature")) || (safe("log.facility", "") == "kern" && safe("log.message", "").contains("module verification failed") || (safe("log.command_line", "").contains("mokutil") && (safe("log.command_line", "").contains("--disable-validation") || safe("log.command_line", "").contains("--import") || safe("log.command_line", "").contains("--reset")) || (safe("log.file_path", "").contains("/sys/firmware/efi/efivars/") && safe("log.event_type", "") == "file_modify") || (safe("log.message", "").contains("shim") && (safe("log.message", "").contains("verification failed") || safe("log.message", "").contains("signature invalid"))) || (safe("log.process_name", "") == "sbsign" || safe("log.process_name", "") == "pesign")', '2025-09-03 16:33:38.267114', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (906, 'System Service Modifications', 2, 3, 3, 'Persistence, Privilege Escalation', 'T1543.001 - Create or Modify System Process: Systemd Service', 'Detects modifications to system services including systemd units, init scripts, or service configurations that could indicate persistence attempts or system compromise. Attackers often create or modify system services to maintain persistence on compromised systems. - -Next Steps: -1. Verify if the service modification was authorized and performed by legitimate administrators -2. Review the specific service being modified and its configuration changes -3. Check for any suspicious processes or files associated with the modified service -4. Examine system logs for other indicators of compromise around the same timeframe -5. Validate the user account performing the modifications has appropriate privileges -6. Consider isolating the system if unauthorized modifications are confirmed -', '["https://attack.mitre.org/techniques/T1543/001/","https://www.debian.org/doc/manuals/debian-reference/ch03.en.html"]', '(safe("log.process_name", "") == "systemctl" && (safe("log.command_line", "").contains("enable") || safe("log.command_line", "").contains("disable") || safe("log.command_line", "").contains("mask") || safe("log.command_line", "").contains("unmask")) || (safe("log.file_path", "").contains("/etc/systemd/system/") && safe("log.event_type", "") == "file_modify") || (safe("log.file_path", "").contains("/etc/init.d/") && safe("log.event_type", "") == "file_modify") || (safe("log.message", "").contains("Created symlink") && safe("log.message", "").contains("/etc/systemd/system/")', '2025-09-03 16:33:39.835092', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (934, 'SystemD Unit File Attack Detected', 2, 3, 2, 'Persistence, Privilege Escalation', 'T1543.002 - Create or Modify System Process: Systemd Service', 'Detects malicious SystemD unit file creation or modification attempts, including privilege escalation through invalid usernames, suspicious service directives, or attempts to create persistent backdoors through systemd generators or malicious service configurations. Next Steps: @@ -10313,171 +10232,21 @@ Next Steps: 6. Check file permissions and ownership of modified unit files 7. Look for persistence mechanisms in systemd configuration 8. Consider isolating the affected system if malicious activity is confirmed -', '["https://attack.mitre.org/techniques/T1543/002/","https://www.linuxjournal.com/content/systemd-service-strengthening"]', '(safe("log.process", "") == "systemd" || safe("log.process", "") == "systemctl") && ( (safe("log.message", "").contains("Failed to parse user") && safe("log.message", "").matches("[0-9].*") ) || (safe("log.message", "").contains("unit file") && (safe("log.message", "").contains("ExecStart") || safe("log.message", "").contains("ExecStartPre") || safe("log.message", "").contains("ExecStartPost")) && (safe("log.message", "").contains("/tmp/") || safe("log.message", "").contains("/dev/shm/") || safe("log.message", "").contains("bash -c") || safe("log.message", "").contains("sh -c")) || (safe("log.message", "").contains("User=") && safe("log.message", "").matches("User=[0-9].*") || (safe("log.message", "").contains("systemd-generator") && (safe("log.message", "").contains("created") || safe("log.message", "").contains("modified"))) || safe("log.message", "").contains("NoNewPrivileges=false") || safe("log.message", "").contains("ProtectSystem=false") )', '2025-09-03 16:36:27.843079', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (912, 'Anaconda Installer Security Event Detected', 3, 3, 2, 'Defense Evasion', 'T1601 - Modify System Image', 'Detects suspicious activities during RHEL Anaconda installer execution, including unauthorized modifications to installation parameters, attempts to bypass security profiles, or suspicious package sources that could indicate supply chain attacks or system compromise attempts. - -Next Steps: -1. Review the specific security policy violation or error message in the logs -2. Verify the integrity of installation sources and repositories -3. Check if GPG signatures are properly configured and valid -4. Examine the installation media for signs of tampering -5. Review system configuration profiles for unauthorized modifications -6. Monitor for additional installation anomalies on the same system -7. Consider reimaging the system if compromise is confirmed -', '["https://anaconda-installer.readthedocs.io/en/latest/common-bugs.html","https://attack.mitre.org/techniques/T1601/"]', '(safe("log.process", "") == "anaconda" || safe("log.file", "").contains("anaconda")) && (safe("log.message", "").contains("security policy violation") || safe("log.message", "").contains("OSCAP addon error") || safe("log.message", "").contains("untrusted repository") || safe("log.message", "").contains("GPG check failed") || safe("log.message", "").contains("installation source error") || (safe("log.message", "").contains("traceback") && (safe("log.message", "").contains("permission denied") || safe("log.message", "").contains("authentication"))))', '2025-09-03 16:35:30.044106', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (908, 'UFW Firewall Configuration Changes', 2, 3, 2, 'Defense Evasion', 'T1562.004 - Impair Defenses: Disable or Modify System Firewall', 'Detects changes to UFW (Uncomplicated Firewall) configuration including rule modifications, firewall disable/enable operations, and logging level changes which could indicate attempts to bypass security controls or create backdoor access. - -Next Steps: -1. Verify the legitimacy of the firewall configuration change -2. Check if the change was authorized and performed by legitimate administrators -3. Review the specific UFW rule changes and their potential security impact -4. Examine concurrent system activities and user sessions -5. Validate that firewall policies still meet security requirements -6. Consider reverting unauthorized changes immediately -7. Review user privileges and access controls for firewall management -', '["https://attack.mitre.org/techniques/T1562/004/","https://help.ubuntu.com/community/UFW"]', '(safe("log.process", "").contains("ufw") || safe("log.message", "").contains("ufw")) && (safe("log.message", "").contains("Rule added") || safe("log.message", "").contains("Rule deleted") || safe("log.message", "").contains("Rule inserted") || safe("log.message", "").contains("Rule updated") || safe("log.message", "").contains("Firewall stopped") || safe("log.message", "").contains("Firewall reloaded") || safe("log.message", "").contains("Default policy changed") || safe("log.message", "").contains("Logging level changed") || safe("log.message", "").contains("ALLOW") || safe("log.message", "").contains("DENY") || safe("log.message", "").contains("REJECT") || safe("log.message", "").contains("disabled firewall") || safe("log.message", "").contains("enabled firewall"))', '2025-09-03 16:33:58.143325', true, false, 'origin', '["origin.host","origin.user"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (913, 'RHEL Boot Loader Attack Detection', 2, 3, 2, 'Defense Evasion, Persistence', 'T1542.003 - Pre-OS Boot: Bootkit', 'Detects attempts to modify or tamper with the boot loader configuration, including unauthorized changes to GRUB files, boot parameters, or kernel modules that could indicate bootkit installation attempts. - -Next Steps: -1. Verify the legitimacy of the boot loader modification - check if it was part of a scheduled maintenance or system update -2. Examine the source of the modification (user account, process, remote connection) -3. Review system logs for other suspicious activities around the same time -4. Check file integrity of critical boot files (/boot/grub2/, /etc/default/grub, kernel files) -5. Scan the system for malware and rootkits -6. If unauthorized, isolate the system and perform forensic analysis -7. Verify boot process integrity and consider restoring from known good backups -', '["https://attack.mitre.org/techniques/T1542/003/","https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/protecting-systems-against-intrusive-usb-devices_security-hardening"]', '(safe("log.file_path", "").contains("/boot/grub2/") && safe("log.action", "") in ["modify", "write", "delete"]) || (safe("log.file_path", "") == "/etc/default/grub" && safe("log.action", "") in ["modify", "write"]) || (safe("log.command", "").contains("grub2-mkconfig") || (safe("log.command", "").contains("grub2-install") || (safe("log.file_path", "").contains("/boot/vmlinuz") && safe("log.action", "") in ["modify", "replace"]) || (safe("log.file_path", "").contains("/boot/initramfs") && safe("log.action", "") in ["modify", "replace"]) || (safe("log.event_type", "") == "boot_loader_modification") || (safe("log.module_name", "") != "" && safe("log.action", "") == "kernel_module_load" && safe("log.module_path", "").contains("/boot/"))', '2025-09-03 16:35:30.567185', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (918, 'GRUB2 Security Event Detection', 2, 3, 2, 'Defense Evasion', 'T1014 - Rootkit', 'Detects security-related events in GRUB2 bootloader including password bypass attempts, configuration tampering, unauthorized menu entry modifications, and attempts to disable security features. This rule identifies potential boot-level attacks targeting the GRUB2 bootloader. - -Next Steps: -1. Immediately verify the integrity of GRUB2 configuration files -2. Check /boot/grub2/grub.cfg and /boot/grub2/user.cfg for unauthorized modifications -3. Review system boot logs for any anomalous kernel command line parameters -4. Verify GRUB2 password configuration is intact and properly secured -5. Check for any unauthorized access to the /boot partition -6. Investigate any recent system administration activities around boot time -7. Consider temporarily disabling network boot if enabled until investigation is complete -', '["https://attack.mitre.org/techniques/T1014/","https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/system_design_guide/protecting-grub-with-a-password_system-design-guide"]', '(safe("log.process", "") == "grub2-setpassword" && safe("log.action", "") in ["execute", "modify"]) || (safe("log.file_path", "") == "/boot/grub2/user.cfg" && safe("log.action", "") in ["modify", "delete"]) || (safe("log.file_path", "") == "/boot/grub2/grub.cfg" && safe("log.action", "") in ["modify", "write"] && safe("log.content", "").contains("password")) || (safe("log.command", "").contains("grub2-mkpasswd-pbkdf2")) || (safe("log.event_type", "") == "grub_authentication_failure") || (safe("log.message", "").contains("GRUB") && (safe("log.message", "").contains("bypass") || safe("log.message", "").contains("unauthorized") || safe("log.message", "").contains("security"))) || (safe("log.file_path", "").contains("/boot/efi/EFI/redhat/") && safe("log.action", "") in ["modify", "delete"]) || (safe("log.kernel_cmdline", "").contains("init=/bin/bash") || safe("log.kernel_cmdline", "").contains("single") || safe("log.kernel_cmdline", "").contains("rd.break") || safe("log.kernel_cmdline", "").contains("systemd.debug_shell"))', '2025-09-03 16:35:32.937063', true, false, 'origin', NULL, '[]'); +', '["https://attack.mitre.org/techniques/T1543/002/","https://www.linuxjournal.com/content/systemd-service-strengthening"]', '(safe("log.process", "") == "systemd" || safe("log.process", "") == "systemctl") && + ( + (safe("log.message", "").contains("Failed to parse user") && safe("log.message", "").matches("[0-9].*")) || + (safe("log.message", "").contains("unit file") && + (safe("log.message", "").contains("ExecStart") || safe("log.message", "").contains("ExecStartPre") || safe("log.message", "").contains("ExecStartPost")) && + (safe("log.message", "").contains("/tmp/") || safe("log.message", "").contains("/dev/shm/") || safe("log.message", "").contains("bash -c") || safe("log.message", "").contains("sh -c"))) || + (safe("log.message", "").contains("User=") && safe("log.message", "").matches("User=[0-9].*")) || + (safe("log.message", "").contains("systemd-generator") && (safe("log.message", "").contains("created") || safe("log.message", "").contains("modified"))) || + safe("log.message", "").contains("NoNewPrivileges=false") || + safe("log.message", "").contains("ProtectSystem=false") + )', '2025-09-05 20:40:24.496233', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (943, 'System Linux: File Transfer or Listener Established via Netcat', 1, 2, 3, 'Execution', 'Unix Shell', 'A netcat process is engaging in network activity on a Linux host. Netcat is often used as a persistence mechanism by exporting a reverse shell or by serving a shell on a listening port. Netcat is also sometimes used for data exfiltration.', '["https://attack.mitre.org/tactics/TA0002/","https://attack.mitre.org/techniques/T1059/004/"]', 'safe("log.message", "").matches("((nc|ncat|netcat|netcat.openbsd|netcat.traditional) (-l|-p|-lp|(-e)(.+)(\\/bin\\/bash|\\/bin\\/sh)|(\\/bin\\/bash|\\/bin\\/sh)(.+)(-e)))")', '2025-09-03 16:36:56.603343', true, false, 'target', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (935, 'System Update Failures in RHEL', 1, 3, 3, 'Impact', 'T1495 - Firmware Corruption', 'Detects failed system updates in RHEL systems including yum/dnf transaction failures, dependency conflicts, and update interruptions. These failures could indicate system issues, malicious interference, or compromised repositories. - -Next Steps: -1. Review the specific error messages in system logs to determine the root cause -2. Check network connectivity to package repositories -3. Verify repository integrity and digital signatures -4. Examine disk space availability and file system health -5. Review recent system changes that might have caused dependency conflicts -6. Check for signs of repository tampering or man-in-the-middle attacks -7. Validate system clock synchronization as it affects package verification -8. Consider rolling back recent changes if corruption is suspected -', '["https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/html/configuring_basic_system_settings/","https://attack.mitre.org/techniques/T1495/"]', 'safe("log.program", "") in ["yum", "dnf", "rpm", "systemd"] && (safe("log.message", "").matches("update.*failed|upgrade.*failed|transaction.*failed|dependency.*error|conflict|broken packages") || safe("log.exit_code", 0) != 0 || safe("log.status", "") in ["failed", "error", "aborted"] || safe("log.message", "").matches("Error downloading packages|Cannot download|Failed to download|Transaction check error")', '2025-09-03 16:36:28.233282', true, false, 'origin', '["adversary.host"]', '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.host.keyword","operator":"filter_term","value":"{{.origin.host}}"}],"or":null,"within":"now-1h","count":5}]'); INSERT INTO public.utm_correlation_rules VALUES (942, 'System Linux: File Deletion via Shred', 1, 2, 3, 'Defense Evasion', 'File Deletion', 'Malware or other files dropped or created on a system by an adversary may leave traces behind as to what was done within a network and how. Adversaries may remove these files over the course of an intrusion to keep their footprint low or remove them at the end as part of the post-intrusion cleanup process.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1070/004/"]', 'safe("log.message", "").matches("(shred (-u|--remove|-z|--zero))")', '2025-09-03 16:36:56.177543', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1305, 'Windows: An administrative account was deleted', 3, 3, 3, 'Impact', 'Account Access Removal', 'This alert is generated every time a user account is deleted. This event generates on domain controllers, member servers, and workstations', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1531/"]', '(safe("log.wineventlogEventDataTargetSid","").endsWith(''-544'') || safe("log.wineventlogEventDataTargetSid","").endsWith(''-512'') || safe("log.wineventlogEventDataTargetSid","").endsWith(''-518'') || safe("log.wineventlogEventDataTargetSid","").endsWith(''-519'') || safe("log.wineventlogEventDataTargetSid","").endsWith(''-520'') || safe("log.wineventlogEventDataTargetSid","").endsWith(''-500'') || safe("log.wineventlogEventDataTargetSid","").endsWith(''-548'') || safe("log.wineventlogEventDataTargetSid","").endsWith(''-549'')) && safe("log.wineventlogEventId","") in [''4733'', ''4729'', ''4726'', ''4743'']', '2025-09-04 20:22:06.412245', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (909, 'Unattended Upgrades Security Failures', 2, 3, 3, 'Initial Access', 'T1190 - Exploit Public-Facing Application', 'Detects failures in unattended security upgrades including authentication errors, package conflicts, and repository issues which could leave systems vulnerable to known exploits or indicate tampering with the update mechanism. - -Next Steps: -1. Check the system''s package manager logs (/var/log/apt/ and /var/log/unattended-upgrades/) -2. Verify disk space availability and system resources -3. Check repository connectivity and authentication credentials -4. Review any recent system configuration changes -5. Manually attempt package updates to identify specific issues -6. Ensure security patches are applied through alternative means if automated updates fail -7. Monitor for repeated failures which may indicate system compromise or misconfiguration -', '["https://attack.mitre.org/techniques/T1190/","https://wiki.debian.org/UnattendedUpgrades"]', '(safe("log.process", "").contains("unattended-upgrade") || safe("log.message", "").contains("unattended-upgrades")) && (safe("log.message", "").contains("ERROR") || safe("log.message", "").contains("FAILED") || safe("log.message", "").contains("Could not get lock") || safe("log.message", "").contains("dpkg was interrupted") || safe("log.message", "").contains("Package configuration problem") || safe("log.message", "").contains("Unmet dependencies") || safe("log.message", "").contains("Authentication failure") || safe("log.message", "").contains("Repository problem") || safe("log.message", "").contains("No space left on device") || safe("log.message", "").contains("Connection failed") || safe("log.message", "").contains("Hash Sum mismatch") || safe("log.message", "").contains("Packages kept back") || safe("log.message", "").contains("Shutdown during upgrade")', '2025-09-03 16:33:58.642513', true, false, 'origin', '["target.host"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (910, 'Wayland Security Event Detection', 3, 3, 2, 'Collection, Credential Access', 'T1056 - Input Capture', 'Detects security events in Wayland display server including compositor vulnerabilities, protocol violations, and unauthorized client connections. This rule monitors for various security issues that could indicate compromise or misuse of the Wayland display server infrastructure. - -Next Steps: -1. Verify the legitimacy of the Wayland process and its configuration -2. Check system logs for related security events or crashes -3. Examine the specific error messages and protocol violations -4. Review user permissions and access controls for Wayland sockets -5. Investigate any unauthorized client connections or injection attempts -6. Monitor for privilege escalation attempts through display server vulnerabilities -7. Check for any suspicious application installations or modifications -', '["https://wayland.freedesktop.org/security.html","https://attack.mitre.org/techniques/T1056/"]', '(safe("log.process_name", "") in ["wayland", "weston", "sway", "mutter", "kwin_wayland"] && safe("log.event_type", "") in ["protocol_error", "security_violation", "client_error"]) || (safe("log.wayland_interface", "") != "" && (safe("log.error", "").contains("Permission denied") || safe("log.error", "").contains("Protocol error") || safe("log.error", "").contains("Security context violation") || safe("log.error", "").contains("Unauthorized request"))) || (safe("log.message", "").contains("Wayland protocol error") || safe("log.message", "").contains("compositor crash") || safe("log.message", "").contains("invalid surface") || safe("log.message", "").contains("buffer overflow in compositor")) || (safe("log.socket_path", "").contains("/run/user/") && safe("log.socket_name", "").contains("wayland") && safe("log.operation", "") in ["unauthorized_connect", "injection_attempt"]) || (safe("log.dbus_interface", "").contains("org.freedesktop.portal") && safe("log.method", "").contains("Screenshot") && safe("log.denied", "") == "true") || (safe("log.subsystem", "") == "drm" && safe("log.compositor", "") != "" && safe("log.event", "") in ["mode_setting_failed", "unauthorized_gpu_access"])', '2025-09-03 16:33:59.107575', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (911, 'X11 Security Violation Detection', 3, 3, 2, 'Lateral Movement', 'T1021 - Remote Services', 'Detects X11 security violations including unauthorized display access, MIT-MAGIC-COOKIE bypass attempts, and X11 forwarding abuse. This rule identifies attempts to compromise X11 authentication mechanisms, unauthorized remote display access, and manipulation of X11 authorization files. - -Next Steps: -1. Investigate the source IP address and user account involved in the violation -2. Check X11 authentication logs and .Xauthority files for tampering -3. Review network connections to X11 displays and identify unauthorized access patterns -4. Examine system logs for privilege escalation attempts related to X11 services -5. Verify X11 forwarding configurations and SSH tunnel usage -6. Check for unauthorized xhost commands or display export attempts -7. Monitor for additional suspicious activity from the same source -', '["https://www.x.org/wiki/Development/Security/","https://attack.mitre.org/techniques/T1021/"]', '(safe("log.facility", "") == "auth" && safe("log.message", "").contains_any(["X11 authentication failed", "Invalid MIT-MAGIC-COOKIE", "X11 connection rejected"])) || (safe("log.process_name", "") == "Xorg" && safe("log.error", "").contains_any(["Permission denied", "Authorization required", "Access control enabled"])) || (safe("log.xauth_event", "") in ["cookie_mismatch", "display_access_denied", "unauthorized_client"]) || (safe("log.command", "").contains_any(["xhost +", "xhost -ac", "export DISPLAY="]) && safe("log.user", "") != "root") || (safe("log.file_path", "").contains_any([".Xauthority", "/tmp/.X11-unix/", "/var/run/xauth/"]) && safe("log.operation", "") in ["unauthorized_read", "tamper", "steal"]) || (safe("log.network_protocol", "") == "X11" && safe("log.source_ip", "") != "127.0.0.1" && safe("log.event_type", "") in ["connection_attempt", "authentication_bypass"])', '2025-09-03 16:33:59.586241', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (929, 'Red Hat Security Advisory Matches', 3, 3, 3, 'Execution', 'T1203 - Exploitation for Client Execution', 'Detects when system activities match patterns associated with Red Hat Security Advisories (RHSA), indicating potential exploitation of known vulnerabilities or missing security patches. This rule identifies systems that may be vulnerable to known security issues or are experiencing active exploitation attempts. - -Next Steps: -1. Immediately verify the affected system''s patch level and installed packages -2. Check if the identified CVE or RHSA has been applied to the system -3. Review system logs for signs of actual exploitation or compromise -4. Validate package signatures and integrity using rpm verification -5. Apply missing security updates immediately if patches are available -6. Monitor the affected system for unusual network activity or process execution -7. Consider isolating the system if active exploitation is suspected -8. Document the vulnerability status and remediation actions taken -9. Review patch management processes to prevent future exposure -10. Coordinate with security team for incident response if exploitation is confirmed -', '["https://access.redhat.com/security/security-updates/","https://attack.mitre.org/techniques/T1203/"]', '(safe("log.source", "").contains("rhel") || safe("log.distribution", "") == "rhel") && ( safe("log.message", "").contains("CVE-") || safe("log.message", "").contains("RHSA-") || safe("log.message", "").contains("security advisory") || safe("log.vulnerability_id", "") != "" || safe("log.event_type", "") == "vulnerability_exploit" || (safe("log.package_name", "") != "" && safe("log.package_version", "") != "" && safe("log.vulnerability_severity", "") in ["Critical", "Important"]) || safe("log.audit_type", "") == "CRYPTO_KEY_USER" || safe("log.audit_type", "") == "CRYPTO_SESSION" || (safe("log.service", "") == "yum" && safe("log.message", "") contains "security update available") || (safe("log.program", "") == "rpm" && safe("log.message", "") contains "signature verification failed") )', '2025-09-03 16:36:05.044535', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (914, 'Cockpit Web Console Security Events', 3, 2, 2, 'Lateral Movement', 'T1563 - Remote Service Session Hijacking', 'Detects security events related to the Cockpit web console including authentication failures, session hijacking attempts, unauthorized access to system management functions, and suspicious API calls. This rule monitors for multiple security events that may indicate an attack against the Cockpit web management interface. - -Next Steps: -1. Review the authentication logs and identify the source IP address -2. Check if the user account exists and verify its legitimacy -3. Examine system logs for any successful logins from the same IP -4. Review Cockpit access logs for any successful administrative actions -5. Consider blocking the source IP if confirmed malicious -6. Verify system integrity and check for any unauthorized changes -7. Review user permissions and disable compromised accounts if necessary -', '["https://cockpit-project.org/guide/latest/privileges","https://attack.mitre.org/techniques/T1563/"]', '(safe("log.service", "") == "cockpit" || safe("log.program", "") == "cockpit-ws" || safe("log.process_name", "").contains("cockpit")) && ( safe("log.message", "").contains("authentication failed") || safe("log.message", "").contains("invalid session") || safe("log.message", "").contains("unauthorized request") || safe("log.message", "").contains("permission denied") || safe("log.message", "").contains("session hijack") || safe("log.event_type", "") == "login_failed" || (safe("log.http_status", 0) >= 400 && safe("log.http_status", 0) < 500) || safe("log.action", "") in ["sudo_command", "system_config_change", "service_control"] && safe("log.result", "") == "failed" )', '2025-09-03 16:35:30.905444', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"},{"field":"log.service.keyword","operator":"filter_term","value":"cockpit"}],"or":null,"within":"now-30m","count":5}]'); -INSERT INTO public.utm_correlation_rules VALUES (915, 'Container Platform Security Attacks', 3, 3, 2, 'Privilege Escalation', 'T1611 - Escape to Host', 'Detects attacks against container platforms including Docker, Podman, and CRI-O such as container escape attempts, privilege escalation, unauthorized image pulls, and runtime manipulation. This rule identifies suspicious container activities that may indicate an adversary attempting to break out of container isolation or manipulate container runtime environments. - -Next Steps: -1. Investigate the affected container and host system for signs of compromise -2. Review container logs and runtime configurations for unauthorized changes -3. Check for unusual process execution or file system modifications -4. Verify container image integrity and scan for malicious content -5. Review security policies for containers (seccomp, AppArmor, SELinux) -6. Consider isolating the affected container and host system -7. Update container security policies and runtime configurations as needed -', '["https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/building_running_and_managing_containers/index","https://attack.mitre.org/techniques/T1611/"]', '(safe("origin.process", "") in ["docker", "podman", "crio", "containerd"] || safe("origin.process", "") in ["dockerd", "podman", "crio", "containerd"] || safe("log.service", "") in ["docker", "podman", "crio", "containerd"] || safe("log.process_name", "") in ["dockerd", "podman", "crio", "containerd"]) && ( safe("log.message", "").contains("container escape") || safe("log.message", "").contains("privilege escalation") || safe("log.message", "").contains("unauthorized pull") || safe("log.message", "").contains("runtime manipulation") || safe("log.message", "").contains("seccomp violation") || safe("log.message", "").contains("AppArmor violation") || safe("log.message", "").contains("SELinux denial") || (safe("action", "") == "exec" && safe("log.command", "").contains("/proc/self/exe")) || (safe("action", "") == "mount" && safe("log.path", "").contains("/sys")) || safe("log.event_type", "") == "container_breakout" || (safe("log.syscall", "").in(["mount", "pivot_root", "chroot"]) && safe("log.result", "") == "denied") )', '2025-09-03 16:35:31.53883', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (916, 'CVE Vulnerability Detection in RHEL System', 3, 3, 2, 'Execution', 'T1203 - Exploitation for Client Execution', 'Detects when a CVE vulnerability is identified in RHEL system packages during security scans or update checks. This rule monitors for CVE identifiers in package management logs, security advisories, and system vulnerability assessments. - -Next Steps: -1. Immediately review the specific CVE details and affected packages -2. Check if the vulnerability affects critical system components or services -3. Verify the current patch level and available security updates -4. Assess the exploitability and exposure of the vulnerable system -5. Apply security patches or implement compensating controls as appropriate -6. Monitor for any signs of exploitation attempts targeting the identified vulnerability -7. Update vulnerability management tracking and reporting systems -', '["https://access.redhat.com/security/vulnerabilities","https://attack.mitre.org/techniques/T1203/"]', '(safe("log.message", "").matches("CVE-\\d{4}-\\d{4,}") || safe("log.cve", "") != "" || safe("log.vulnerability", "") != "" || (safe("log.program", "") in ["yum", "dnf", "rpm"] && safe("log.message", "").matches("vulnerability|CVE")) && safe("log.severity", "") in ["critical", "important", "high", "moderate"]', '2025-09-03 16:35:32.008918', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (917, 'Suspicious Firewalld Rule Modification', 2, 3, 3, 'Defense Evasion', 'T1562.004 - Impair Defenses: Disable or Modify System Firewall', 'Detects unauthorized or suspicious modifications to firewalld rules that could indicate attempts to bypass network security controls or establish persistence. This rule monitors for dangerous firewall commands including adding risky ports, disabling logging, direct rule manipulation, and unauthorized configuration file modifications. - -Next Steps: -1. Verify the legitimacy of the firewall rule changes with the system administrator -2. Check if the changes were part of authorized maintenance or configuration updates -3. Review the specific ports/services being opened and assess their security implications -4. Examine the user context and timing of the changes for suspicious patterns -5. Check for any corresponding network connections or services using the newly opened ports -6. Review audit logs for other configuration changes made by the same user/process -7. If unauthorized, immediately review and revert the firewall changes -8. Investigate the source of the unauthorized access and potential compromise -', '["https://attack.mitre.org/techniques/T1562/004/","https://firewalld.org/documentation/man-pages/firewall-cmd.html"]', '( safe("log.comm", "") in ["firewall-cmd", "firewalld"] && ( (safe("log.args", "").contains("--add-port") && (safe("log.args", "").contains("22/tcp") || safe("log.args", "").contains("3389/tcp") || safe("log.args", "").contains("4444/tcp") || safe("log.args", "").contains("5900/tcp")) || (safe("log.args", "").contains("--add-service" && safe("log.args", "").contains("ssh") && safe("log.args", "").contains("--permanent") || safe("log.args", "").contains("--add-rich-rule") || safe("log.args", "").contains("--add-forward-port") || safe("log.args", "").contains("--direct") || safe("log.args", "").contains("--set-log-denied=off") || (safe("log.args", "").contains("--zone=public") && safe("log.args", "").contains("--add-source") ) ) || ( safe("log.path", "").contains("/etc/firewalld/") && safe("log.syscall", "") in ["2", "257", "82", "83"] && safe("log.comm", "") not in ["firewalld", "firewall-cmd", "systemd"] ) || ( safe("log.message", "").contains("firewalld") && ( safe("log.message", "").contains("rule added") || safe("log.message", "").contains("rule removed") || safe("log.message", "").contains("zone changed") || safe("log.message", "").contains("panic mode") ) && safe("log.uid", "") != "0" )', '2025-09-03 16:35:32.491109', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1280, 'Windows: Remote Logon followed by Scheduled Task Creation', 3, 3, 2, 'Lateral Movement', '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/"]', 'safe("log.action", "") == ''logged-in'' && safe("actionResult", "") == ''success'' && !safe("log.wineventlogUserName", "").contains("ANONYMOUS LOGON") && !safe("log.wineventlogUserDomain", "").contains("NT AUTHORITY")', '2025-09-04 19:58:21.616399', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.winlogHostId.keyword","operator":"filter_term","value":"{{.log.winlogHostId}}"},{"field":"log.winlogEventDataSubjectLogonId.keyword","operator":"filter_term","value":"scheduled-task-created"}],"or":null,"within":"now-60s","count":3}]'); -INSERT INTO public.utm_correlation_rules VALUES (919, 'RHEL Kickstart Configuration Exploitation Attempt', 3, 3, 2, 'Initial Access', 'T1195.002 - Supply Chain Compromise: Compromise Software Supply Chain', 'Detects potential exploitation attempts targeting RHEL Kickstart configurations, including malicious package injection, firewall disabling, unauthorized user creation, or security feature bypass attempts during automated installations. This rule identifies suspicious kickstart configuration patterns that could indicate supply chain compromise or malicious automated installation attempts. - -Next Steps: -1. Immediately isolate the affected system from the network -2. Review the complete kickstart configuration file (ks.cfg) for unauthorized modifications -3. Check the source of the kickstart file and verify its integrity -4. Examine package repositories referenced in the configuration for malicious content -5. Validate user accounts and privileges created during installation -6. Review firewall and SELinux configurations for unauthorized changes -7. Scan the system for indicators of compromise using updated antivirus/EDR tools -8. Consider rebuilding the system from trusted installation media if compromise is confirmed -', '["https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/assembly_securing-rhel-during-installation-security-hardening","https://attack.mitre.org/techniques/T1195/002/"]', '(safe("log.facility", "") == "daemon" || safe("log.process", "") == "anaconda" || safe("log.program", "") == "anaconda") && (safe("log.message", "").contains("kickstart") || safe("log.message", "").contains("ks.cfg") || safe("log.message", "").contains("%packages") || safe("log.message", "").contains("%pre") || safe("log.message", "").contains("%post")) && (safe("log.message", "").contains("firewall --disabled") || safe("log.message", "").contains("selinux --disabled") || safe("log.message", "").contains("rootpw --plaintext") || safe("log.message", "").contains("user --password") || safe("log.message", "").contains("repo --baseurl=http:") || safe("log.message", "").contains("auth --enableldap") || safe("log.message", "").contains("skipx") || safe("log.message", "").contains("network --bootproto=none"))', '2025-09-03 16:35:33.338133', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (920, 'NetworkManager Exploit Detection', 3, 3, 2, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects potential exploitation attempts against NetworkManager service including privilege escalation attempts, unauthorized configuration changes, and suspicious network profile modifications. Next Steps: @@ -10489,65 +10258,7 @@ Next Steps: 6. Check for other suspicious activities from the same source IP or user account 7. Consider isolating the affected system if privilege escalation is confirmed ', '["https://access.redhat.com/security/vulnerabilities/networkmanager","https://attack.mitre.org/techniques/T1068/"]', 'safe("log.process_name", "") == "NetworkManager" || safe("log.service", "") == "NetworkManager" && ( safe("log.message", "").contains("privilege escalation") || safe("log.message", "").contains("unauthorized access") || safe("log.message", "").contains("segmentation fault") || safe("log.message", "").contains("buffer overflow") || safe("log.message", "").contains("failed to authenticate") || (safe("log.event_type", "") == "config_change" && safe("origin.user", "") != "root") || safe("log.error_code", "") in ["EPERM", "EACCES"] )', '2025-09-03 16:35:33.794734', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"}],"or":null,"within":"now-15m","count":3}]'); -INSERT INTO public.utm_correlation_rules VALUES (921, 'OpenShift Security Violations', 3, 3, 2, 'Credential Access', 'T1552.007 - Unsecured Credentials: Container API', 'Detects security violations in OpenShift Container Platform including unauthorized API access, RBAC violations, security context constraint breaches, and malicious pod deployments. These violations indicate potential security breaches or misconfigurations that could lead to unauthorized access or privilege escalation within the cluster. - -Next Steps: -1. Immediately investigate the user account and source IP involved in the violation -2. Review the specific OpenShift namespace and resources being accessed -3. Check RBAC configurations and Security Context Constraints (SCC) policies -4. Examine pod specifications for security violations or privileged access attempts -5. Verify API audit logs for additional unauthorized access patterns -6. Review admission controller and webhook configurations -7. Consider temporarily restricting access for the affected user/service account -8. Document findings and update security policies if needed -', '["https://docs.openshift.com/container-platform/latest/security/index.html","https://attack.mitre.org/techniques/T1552/007/"]', '(safe("log.service", "").contains("openshift") || safe("log.program", "").contains("openshift") || safe("log.kubernetes_component", "") != "" || safe("log.namespace", "").contains("openshift")) && ( safe("log.message", "").contains("RBAC violation") || safe("log.message", "").contains("SCC violation") || safe("log.message", "").contains("unauthorized API call") || safe("log.message", "").contains("forbidden") || safe("log.message", "").contains("admission webhook denied") || safe("log.message", "").contains("security policy violation") || safe("log.audit_verb", "").in(["create", "delete", "patch"]) && safe("log.audit_response_code", 0.0) == double(403) || safe("log.event_type", "") == "pod_security_violation" || safe("log.privileged", false) == true && !safe("log.user", "").startsWith("system:serviceaccount:openshift") || (safe("log.resource", "") in ["secrets", "configmaps", "serviceaccounts"] && safe("log.verb", "") == "get" && safe("log.response_code", 0.0) == double(403)) )', '2025-09-03 16:35:34.153517', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (933, 'Critical System Configuration Changes in RHEL', 3, 3, 3, 'Persistence, Privilege Escalation', 'System Configuration Modification', 'Detects critical system configuration changes in RHEL including modifications to system services, SELinux settings, firewall rules, and other security-relevant configurations that could indicate unauthorized access or system compromise. - -Next Steps: -1. Verify the legitimacy of the configuration change with the system administrator -2. Review the change details and determine if it aligns with authorized maintenance windows -3. Check if the user account making the change has proper authorization -4. Examine system logs for any suspicious activity preceding the configuration change -5. If unauthorized, immediately investigate for potential compromise and consider reverting changes -6. Review access logs and authentication events for the affected user account -', '["https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/html/security_hardening/","https://attack.mitre.org/techniques/T1543/"]', '(safe("log.type", "") in ["CONFIG_CHANGE", "SYSCALL", "PATH"] && - (safe("log.path", "").matches("/etc/selinux|/etc/systemd|/etc/security|/etc/pam\\.d|/etc/sudoers|/etc/ssh") || - safe("log.name", "").matches("/etc/selinux|/etc/systemd|/etc/security|/etc/pam\\.d|/etc/sudoers|/etc/ssh")) || -(safe("log.program", "") in ["systemctl", "semanage", "setsebool", "firewall-cmd", "authconfig"] && - safe("log.message", "").matches("changed|modified|updated|disabled|enabled") || -(safe("log.message", "").matches("SELinux.*disabled|SELinux.*permissive|firewall.*stopped|service.*masked") && - safe("log.severity", "") in ["warning", "error", "critical"]) || -(safe("log.audit_type", "") == "CONFIG_CHANGE" && - safe("log.success", "") == "yes" && - safe("log.exe", "").matches("/usr/bin/yum|/usr/bin/dnf|/usr/bin/rpm") -', '2025-09-03 16:36:27.429997', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1404, 'Windows: PowerShell PSReflect Script', 2, 3, 1, 'Execution', '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/"]', 'safe("log.message", "").matches("(New-InMemoryModule|Add-Win32Type|psenum|DefineDynamicAssembly|DefineDynamicModule|Reflection.TypeAttributes|Reflection.Emit.OpCodes|Reflection.Emit.CustomAttributeBuilder|Runtime.InteropServices.DllImportAttribute)")', '2025-09-04 20:52:52.197938', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (925, 'RHEL-Specific Malware Detection', 3, 3, 2, 'Defense Evasion, Privilege Escalation', 'T1055 - Process Injection', 'Detects indicators of malware specifically targeting RHEL systems including known rootkits, suspicious kernel modules, process injection attempts, and malicious RPM packages or scripts exploiting RHEL-specific vulnerabilities. - -Next Steps: -1. Immediately isolate the affected system from the network -2. Identify the specific malware family and infection vector -3. Collect memory dumps and disk images for forensic analysis -4. Check for persistence mechanisms (startup scripts, cron jobs, systemd services) -5. Verify RPM package integrity using ''rpm -Va'' command -6. Review kernel module integrity with ''lsmod'' and compare against baseline -7. Scan for additional compromised systems using IOCs -8. Update antivirus signatures and security patches -9. Review access logs to determine initial compromise method -10. Implement additional monitoring for similar attack patterns -', '["https://attack.mitre.org/techniques/T1055/","https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/checking-integrity-with-aide_security-hardening"]', '(safe("log.process_name", "") in ["reptile", "bdvl", "kovid", "suterusu", "rooty", "vlany"] && safe("action", "") == "process_create") || (safe("log.file_path", "").contains("/proc/kallsyms") || safe("log.file_path", "").contains("/dev/kmem") || safe("log.file_path", "").contains("/dev/mem")) && safe("action", "").in(["read", "write"]) || (safe("log.rpm_package", "") != "" && safe("log.rpm_signature", "").in(["NOKEY", "NOTTRUSTED", "BAD"])) || (safe("log.command", "").contains("LD_PRELOAD=") || safe("log.command", "").contains("LD_LIBRARY_PATH=")) && (safe("log.library_path", "").contains("/tmp/") || safe("log.library_path", "").contains("/var/tmp/") || safe("log.library_path", "").contains("/dev/shm/")) || (safe("log.kernel_module", "") != "" && (safe("log.module_path", "").contains("/tmp/") || safe("log.module_path", "").contains("/var/tmp/") || safe("log.module_path", "").contains("/dev/shm/")) && safe("action", "") == "load") || (safe("log.process_injection", "") == "true" && safe("log.target_process", "").in(["systemd", "sshd", "httpd", "firewalld"])) || (safe("log.file_hash", "").in(["known_malware_hash_1", "known_malware_hash_2"])) || (safe("log.network_connection", "") != "" && safe("log.destination_port", "").in([4444, 5555, 6666, 7777, 8888]) && safe("log.process_name", "").in(["bash", "sh", "python", "perl"])) || (safe("log.syscall", "").in(["ptrace", "process_vm_write", "setns"]) && safe("log.target_pid", "") == 1) || (safe("log.selinux_context", "").contains("unconfined") && (safe("log.process_name", "").contains("backdoor") || safe("log.process_name", "").contains("rootkit") || safe("log.process_name", "").contains("miner")))', '2025-09-03 16:36:03.226999', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (926, 'RPM Database Tampering Detection', 2, 3, 2, 'Defense Evasion', 'T1070 - Indicator Removal', 'Detects potential tampering with the RPM database which could indicate an attacker attempting to hide malicious package installations or system modifications. This includes unauthorized access to RPM database files, non-privileged users attempting to rebuild the database, or corruption indicators in RPM logs. - -Next Steps: -1. Immediately investigate the source host and verify system integrity -2. Check for unauthorized package installations or removals using ''rpm -qa --last'' -3. Verify RPM database integrity with ''rpm --verifydb'' -4. Review system logs for suspicious process execution around the time of detection -5. Examine file system integrity and check for signs of rootkit installation -6. Correlate with other security events from the same host -7. If compromise is confirmed, isolate the system and initiate incident response procedures -8. Consider rebuilding the system from known good backups if extensive tampering is detected -', '["https://attack.mitre.org/techniques/T1070/","https://access.redhat.com/articles/3763"]', '( safe("log.path", "").contains("/var/lib/rpm/") && ( safe("log.syscall", "") in ["2", "257", "82", "83", "87"] || safe("log.operation", "") in ["write", "unlink", "rename", "truncate"] ) && safe("log.comm", "") not in ["rpm", "yum", "dnf", "packagekitd", "systemd"] ) || ( safe("log.exe", "") in ["/usr/bin/rpm", "/usr/bin/rpmdb"] && safe("log.args", "").contains("--rebuilddb") && safe("log.uid", "") != "0" ) || ( safe("log.eventType", "") == "FILE_MODIFIED" && safe("log.file_path", "") in ["/var/lib/rpm/Packages", "/var/lib/rpm/.rpm.lock", "/var/lib/rpm/__db.001", "/var/lib/rpm/__db.002", "/var/lib/rpm/__db.003"] && safe("log.process_name", "") not in ["rpm", "yum", "dnf", "packagekitd"] ) || ( safe("log.message", "").contains("rpmdb") && ( safe("log.message", "").contains("corrupted") || safe("log.message", "").contains("checksum") || safe("log.message", "").contains("mismatch") ) )', '2025-09-03 16:36:03.564715', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (927, 'RHEL Satellite Client Anomalies Detected', 3, 3, 2, 'Command and Control', 'T1071 - Application Layer Protocol', 'Detects anomalous behavior in RHEL Satellite client communications, including unexpected connection patterns, authentication failures, and suspicious command executions that may indicate compromised satellite clients or unauthorized management attempts. This activity could suggest system compromise, man-in-the-middle attacks, or unauthorized access to satellite infrastructure. **Next Steps:** @@ -10561,70 +10272,8 @@ INSERT INTO public.utm_correlation_rules VALUES (927, 'RHEL Satellite Client Ano 8. Review other satellite clients for similar anomalous patterns 9. Validate the satellite server''s security posture and access controls ', '["https://docs.redhat.com/en/documentation/red_hat_satellite/6.16/html/administering_red_hat_satellite/logging_and_reporting_problems_admin","https://attack.mitre.org/techniques/T1071/"]', 'safe("log.process", "") == "satellite-client" && (safe("log.message", "").contains("authentication failed") || safe("log.message", "").contains("unexpected disconnect") || safe("log.message", "").contains("command execution failed") || safe("log.message", "").contains("certificate validation failed") || safe("log.message", "").contains("unauthorized request"))', '2025-09-03 16:36:03.984185', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-linux-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"}],"or":null,"within":"now-15m","count":10}]'); -INSERT INTO public.utm_correlation_rules VALUES (928, 'RHEL Secure Boot Violation Detection', 2, 3, 2, 'Defense Evasion', 'T1553.006 - Subvert Trust Controls: Code Signing Policy Modification', 'Detects violations of UEFI Secure Boot policy including unsigned kernel modules, tampering with MOK (Machine Owner Key) database, attempts to disable Secure Boot, or loading of unauthorized boot components. This indicates potential compromise of the boot chain integrity or attempts to bypass security controls. - -Next Steps: -1. Immediately isolate the affected system to prevent lateral movement -2. Verify current Secure Boot status with ''mokutil --sb-state'' command -3. Check for unauthorized kernel modules with ''lsmod'' and compare against baseline -4. Review recent system changes, updates, or administrative activities -5. Examine MOK database contents with ''mokutil --list-enrolled'' -6. Check system logs for any unauthorized access or privilege escalation -7. Validate boot chain integrity and consider system restoration from known good backup -8. Review and strengthen access controls for system administration -', '["https://attack.mitre.org/techniques/T1553/006/","https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/using-uefi-secure-boot_security-hardening"]', '(safe("log.event_type", "") == "secure_boot_violation") || (safe("log.message", "").contains("Secure Boot") && (safe("log.message", "").contains("violation") || safe("log.message", "").contains("failed") || safe("log.message", "").contains("unsigned") || safe("log.message", "").contains("invalid"))) || (safe("log.command", "").contains("mokutil") && (safe("log.command", "").contains("--disable-validation") || safe("log.command", "").contains("--reset") || safe("log.command", "").contains("--delete"))) || ((safe("log.file_path", "").contains("/sys/firmware/efi/efivars/") || safe("log.file_path", "").contains("/boot/efi/EFI/redhat/")) && safe("log.action", "") in ["modify", "delete"]) || (safe("log.kernel_message", "").contains("PKCS#7 signature not signed with a trusted key") || safe("log.kernel_message", "").contains("module verification failed") || safe("log.kernel_message", "").contains("Secure Boot forbids loading")) || (safe("log.module_sig_status", "") in ["unsigned", "invalid", "rejected"]) || (safe("log.efi_variable", "") in ["SecureBoot", "SetupMode", "PK", "KEK", "db", "dbx"] && safe("log.action", "") in ["modify", "delete"]) || (safe("log.shim_message", "").contains("verification failed") || safe("log.shim_message", "").contains("signature invalid"))', '2025-09-03 16:36:04.576447', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1281, 'Windows: Privilege Escalation via Windir Environment Variable', 2, 3, 2, 'Privilege Escalation', '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/"]', 'safe("log.message", "").matches("(C:\\windows|%SystemRoot%)") && safe("log.message", "").matches("(HKEY_USERS\\(.+)\\Environment\\windir|HKEY_USERS\\(.+)\\Environment\\systemroot)")', '2025-09-04 19:58:22.197477', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (930, 'Critical SELinux Policy Violation', 3, 3, 2, 'Privilege Escalation', 'T1068 - Exploitation for Privilege Escalation', 'Detects critical SELinux policy violations that may indicate privilege escalation attempts, unauthorized access, or malicious activity bypassing mandatory access controls. This rule identifies AVC denials for sensitive capabilities, attempts to access shadow files, modifications to critical system directories, unconfined processes accessing executables, and unauthorized SELinux management commands. - -Next Steps: -1. Immediately review the specific SELinux violation details including source context (scontext), target context (tcontext), and attempted permission -2. Identify the process (comm field) and user attempting the action -3. Determine if this is legitimate administrative activity or potential attack -4. Check system logs around the time of violation for related suspicious activities -5. Verify current SELinux enforcement status using ''getenforce'' command -6. Review recent policy changes and user activities on the affected system -7. If malicious, isolate the system and perform forensic analysis -8. Update SELinux policies if legitimate access was incorrectly denied -9. Monitor for additional violations from the same source -', '["https://attack.mitre.org/techniques/T1068/","https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/using_selinux/troubleshooting-problems-related-to-selinux_using-selinux"]', '( safe("log.type", "") == "AVC" && safe("log.selinux_denied", "") == "denied" && ( (safe("log.tclass", "") in ["capability", "capability2", "process", "security"] && safe("log.permission", "") in ["sys_admin", "sys_module", "sys_ptrace", "setuid", "setgid", "dac_override", "dac_read_search"]) || (safe("log.tclass", "") == "file" && safe("log.tcontext", "").contains("shadow_t") && safe("log.permission", "") in ["read", "write", "append"]) || (safe("log.tclass", "") == "dir" && safe("log.path", "") in ["/etc", "/boot", "/usr/lib", "/usr/lib64"] && safe("log.permission", "") in ["write", "add_name", "remove_name"]) || (safe("log.scontext", "").contains("unconfined_t") && safe("log.tcontext", "").contains("_exec_t") || (safe("log.comm", "") in ["setenforce", "semodule", "restorecon"] && safe("log.scontext", "").not.contains("unconfined_t") ) ) || ( safe("log.message", "").contains("SELinux") && ( safe("log.message", "").contains("enforcing") && safe("log.message", "").contains("disabled") ) )', '2025-09-03 16:36:05.502645', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (931, 'RHEL Service Modifications Detection', 3, 3, 3, 'Persistence, Privilege Escalation', 'T1543.003 - Create or Modify System Process', 'Detects unauthorized modifications to systemd services, including creation of new services, modification of existing service files, or changes to service configurations in RHEL systems. This could indicate an attacker attempting to establish persistence or modify system behavior. - -Next Steps: -1. Verify the legitimacy of the service modification by checking with system administrators -2. Review the specific service that was modified and its configuration -3. Check the user account that performed the modification and verify it has appropriate privileges -4. Examine the service file contents for suspicious commands or configurations -5. Review system logs around the time of modification for additional suspicious activity -6. If unauthorized, disable the service immediately and investigate the source of the modification -7. Check for other recently modified services or system files -8. Consider reimaging the system if compromise is confirmed -', '["https://attack.mitre.org/techniques/T1543/003/","https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/configuring_and_managing_security/auditing-the-system_configuring-and-managing-security"]', '(safe("log.process", "") == "systemctl" && safe("log.action", "") in ["enable", "disable", "mask", "unmask"]) || (safe("log.file_path", "").contains("/etc/systemd/system/") && safe("log.action", "") in ["create", "modify", "write"]) || (safe("log.file_path", "").contains("/usr/lib/systemd/system/") && safe("log.action", "") in ["create", "modify", "write"]) || (safe("log.command", "").contains("systemctl daemon-reload")) || (safe("log.event_type", "") == "service_modification" && safe("log.service_name", "") != "")', '2025-09-03 16:36:05.953642', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (932, 'Subscription Manager Security Issue', 2, 3, 2, 'Defense Evasion, Persistence, Privilege Escalation, Initial Access', 'T1078 - Valid Accounts', 'Detects suspicious subscription manager activities that could indicate unauthorized system registration, subscription tampering, or attempts to bypass licensing controls. This includes unauthorized unregistration attempts, forced registrations, configuration changes to RHSM servers, subscription overrides, and direct file modifications to RHSM configuration directories. - -Next Steps: -1. Verify the legitimacy of the subscription manager activity by checking with system administrators -2. Review the user account performing the action and validate their authorization level -3. Examine system logs for additional suspicious activities around the same timeframe -4. Check RHSM configuration files for unauthorized modifications -5. Validate current subscription status and certificate integrity -6. If unauthorized, isolate the affected system and investigate for potential privilege escalation -7. Review access controls and procedures for subscription management -', '["https://attack.mitre.org/techniques/T1078/","https://access.redhat.com/documentation/en-us/red_hat_subscription_management/"]', '( safe("log.comm", "") in ["subscription-manager", "rhsmcertd", "rhsm-service"] && ( (safe("log.args", "").contains("unregister") && safe("log.uid", "") != "0") || (safe("log.args", "").contains("register") && (safe("log.args", "").contains("--force") || safe("log.args", "").contains("--activationkey")) || (safe("log.args", "").contains("config") && (safe("log.args", "").contains("--server.hostname") || safe("log.args", "").contains("--server.proxy_hostname") || safe("log.args", "").contains("--rhsm.baseurl")) || safe("log.args", "").contains("override") || safe("log.args", "").contains("clean") ) ) || ( safe("log.path", "").contains("/etc/rhsm/") && safe("log.syscall", "") in ["2", "257", "82", "83"] && safe("log.comm", "") not in ["subscription-manager", "rhsmcertd", "python", "python3"] ) || ( safe("log.message", "").contains("subscription") && ( safe("log.message", "").contains("expired") || safe("log.message", "").contains("invalid") || safe("log.message", "").contains("failed") || safe("log.message", "").contains("certificate") || safe("log.message", "").contains("unauthorized") ) ) || ( safe("log.eventType", "") == "SUBSCRIPTION_EVENT" && ( safe("log.status", "") == "failed" || safe("log.action", "") in ["unregister", "override", "force_register"] ) )', '2025-09-03 16:36:26.843898', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1405, 'Windows: PowerShell Kerberos Ticket Request', 3, 2, 1, 'Credential Access', '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/"]', 'safe("log.message", "").matches("(KerberosRequestorSecurityToken)")', '2025-09-04 20:52:52.456117', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (936, 'YUM/DNF Repository Manipulation Attack', 3, 3, 2, 'Initial Access', 'T1195.001 - Supply Chain Compromise: Compromise Software Dependencies and Development Tools', 'Detects potential attacks on YUM/DNF repositories including unauthorized repository additions, GPG key modifications, or suspicious package installations that could indicate supply chain compromise. This rule identifies attempts to manipulate package management systems to install malicious software or bypass security controls. - -**Next Steps:** -1. **Immediate Response**: Isolate the affected system to prevent further compromise -2. **Investigate Repository Changes**: Review all recent repository configuration changes and verify legitimacy -3. **Package Verification**: Check all recently installed packages for authenticity and integrity -4. **GPG Key Analysis**: Verify all GPG keys and remove any unauthorized or suspicious keys -5. **System Integrity**: Perform full system integrity checks using tools like AIDE or Tripwire -6. **Network Analysis**: Monitor network traffic for suspicious download sources -7. **User Activity**: Review user activities and access logs for the timeframe of the incident -8. **Rollback Planning**: Prepare to rollback unauthorized changes and remove malicious packages -9. **Policy Review**: Strengthen package management policies and access controls -10. **Documentation**: Document all findings and remediation steps for incident response records -', '["https://attack.mitre.org/techniques/T1195/001/","https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html-single/managing_software_with_the_dnf_tool/index"]', '( safe("log.comm", "") in ["yum", "dnf", "rpm"] && safe("log.exe", "") in ["/usr/bin/yum", "/usr/bin/dnf", "/usr/bin/rpm"] && ( (safe("log.path", "").contains(".repo") && safe("log.syscall", "") in ["2", "257"]) || (safe("log.path", "").contains("gpgkey") && safe("log.operation", "") == "write") || (safe("log.args", "").contains("install") && safe("log.args", "").contains("--nogpgcheck") || (safe("log.message", "").contains("Repository") && safe("log.message", "").contains("added") || (safe("log.message", "").contains("GPG key") && safe("log.message", "").contains("imported") ) ) || ( safe("log.eventType", "") == "PACKAGE_INSTALLED" && ( safe("log.package_source", "") == "unknown" || safe("log.gpg_verification", "") == "failed" || safe("log.repository", "") not in ["rhel-8-baseos-rpms", "rhel-8-appstream-rpms", "rhel-9-baseos-rpms", "rhel-9-appstream-rpms"] ) )', '2025-09-03 16:36:28.723589', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (964, 'Apple Script Abuse Detection', 3, 3, 2, 'Execution', 'T1059.002 - Command and Scripting Interpreter: AppleScript', 'Detects suspicious AppleScript execution that could indicate malicious activity, including osascript command-line usage, automation attempts, and script-based attacks on macOS systems. AppleScript can be abused by attackers to execute commands, interact with applications, and perform system manipulation. Next Steps: @@ -10641,7 +10290,6 @@ INSERT INTO public.utm_correlation_rules VALUES (1283, 'Windows: Execution of Pe INSERT INTO public.utm_correlation_rules VALUES (1284, 'Windows: Potential Privileged Escalation via SamAccountName Spoofing', 2, 3, 1, 'Privilege Escalation', '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/"]', 'safe("action", "") == "renamed-user-account" && safe("target.user", "").startsWith("$") && safe("log.winlogEventDataNewTargetUserName", "").startsWith("$")', '2025-09-04 19:58:23.896064', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1285, 'Windows: Possible ransomware attack detected. Unusual File Extensions.', 3, 3, 3, 'Ransomware', 'Impact: 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/"]', 'safe("log.eventCode", 0.0) == double(4663) && safe("log.eventDataFileName", "").matches("\\.(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)$")', '2025-09-04 20:01:49.333289', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1286, 'Windows: Remote File Download via Desktopimgdownldr Utility', 2, 3, 1, 'Command and Control', '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/"]', 'safe("log.winlogEventDataProcessName", "").contains("desktopimgdownldr.exe")', '2025-09-04 20:01:50.038727', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1503, 'Windows: Possible ransomware attack detected. Unexpected PowerShell Activity.', 2, 3, 2, 'Ransomware', 'Execution: Scripting', '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. An unexpected PowerShell execution has been detected, potentially indicating ransomware or other malicious activity.', '["https://attack.mitre.org/techniques/T1059/"]', 'safe("log.eventDataObjectName", "").contains(".ps1") && !safe("log.winlogEventDataSubjectUserName", "") in [''SYSTEM'', ''NETWORK SERVICE'', ''LOCAL SERVICE''] && safe("log.winlogEventDataProcessName", "").matches("(cmd.exe|powershell.exe|mmc.exe|regedit.exe)") && (safe("log.eventCode", 0.0) == double(4688) || safe("log.eventCode", 0.0) == double(592)', '2025-09-04 21:17:12.963146', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (968, 'Apple Script Abuse Detection', 3, 3, 2, 'Execution', 'T1059.002 - Command and Scripting Interpreter: AppleScript', 'Detects suspicious AppleScript execution that could indicate malicious activity, including osascript command-line usage, automation attempts, and script-based attacks on macOS systems. AppleScript can be abused by attackers to execute commands, interact with applications, and perform system manipulation. Next Steps: @@ -10773,7 +10421,6 @@ INSERT INTO public.utm_correlation_rules VALUES (975, 'FileVault Tampering Attem 5. Verify FileVault status and configuration integrity 6. Consider temporarily disabling the affected user account if unauthorized activity is suspected ', '["https://support.apple.com/guide/mac-help/protect-data-on-your-mac-with-filevault-mh11785/mac","https://attack.mitre.org/techniques/T1562/001/"]', '(safe("log.subsystem", "") == "com.apple.security.filevault" && (safe("log.message", "").contains("disable") || safe("log.message", "").contains("bypass") || safe("log.message", "").contains("unlock") || safe("log.message", "").contains("error") || safe("log.message", "").contains("failed")) || (safe("log.process", "") == "fdesetup" && safe("log.messageType", "") == "error") || (safe("log.message", "").contains("FileVault") && (safe("log.message", "").contains("tampering") || safe("log.message", "").contains("modification") || safe("log.message", "").contains("unauthorized"))', '2025-09-04 15:11:23.422289', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1410, 'Windows: User logged using Remote Desktop Connection from loopback address, possible exploit over reverse tunneling using stolen credentials', 3, 2, 1, 'Potentially Compromised System', '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/"]', 'safe("log.eventData.logonType", "") == "10" && safe("log.origin.ips", "") in [''::1'',''127.0.0.1''] && safe("log.eventCode", 0.0) in [528, 540, 673, 4624, 4769]', '2025-09-04 20:52:54.519646', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (977, 'Gatekeeper Bypass Attempt Detected', 2, 3, 2, 'Defense Evasion', 'T1553.001 - Subvert Trust Controls: Gatekeeper Bypass', 'Detects attempts to bypass Gatekeeper security checks through database manipulation, quarantine attribute removal, or exploitation of assessment vulnerabilities. Gatekeeper is a macOS security feature that verifies downloaded applications before they run, and bypassing it can allow malicious software to execute without proper verification. Next Steps: @@ -11033,7 +10680,6 @@ Next Steps: 6. Review and strengthen application security controls based on the attack method identified 7. Monitor for lateral movement or privilege escalation attempts from the same source ', '["https://attack.mitre.org/techniques/T1190/","https://www.cisco.com/c/en/us/td/docs/security/asa/special/netflow/asa_netflow.html"]', '( (safe("target.port", 0.0) == double(80) || safe("target.port", 0.0) == double(443) || safe("target.port", 0.0) == double(8080) || safe("target.port", 0.0) == double(8443)) && ( safe("log.packets", 0.0) > double(10000) && safe("log.bytes", 0.0) < double(1048576) || safe("log.tcp_flags", "") == "SYN" && safe("log.flow_count", 0.0) > double(1000) || safe("log.duration", 0.0) > double(300) && safe("log.bytes", 0.0) < double(10240) ) ) || ( safe("target.port", 0.0) == double(21) && safe("log.commands", 0.0) > double(100) ) || ( safe("target.port", 0.0) == double(22) && safe("log.failed_attempts", 0.0) > double(10) ) || ( (safe("target.port", 0.0) == double(3306) || safe("target.port", 0.0) == double(5432) || safe("target.port", 0.0) == double(1433) || safe("target.port", 0.0) == double(1521)) && safe("log.bytes", 0.0) > double(104857600) ) || ( safe("log.http_requests_per_second", 0.0) > double(1000) || safe("log.connection_rate", 0.0) > double(500) )', '2025-09-04 15:17:46.782191', true, false, 'origin', '["adversary.ip","target.port"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1391, 'Windows: Suspicious Execution from mounted device', 1, 2, 3, 'Defense Evasion', '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/"]', 'safe("log.eventType", "") == ''start'' && safe("log.process.executable", "").matches("(^C:\\\\)") safe("log.processWorkingDirectory", "").matches("((^\\w:\\\\)") && !safe("log.processWorkingDirectory", "").matches("(^C:\\\\)") && safe("log.processParentName", "").contains("explorer.exe") && safe("log.processName", "") in [''rundll32.exe'', ''mshta.exe'', ''powershell.exe'', ''pwsh.exe'', ''cmd.exe'', ''regsvr32.exe'', ''cscript.exe'', ''wscript.exe'']', '2025-09-04 20:45:22.331322', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (998, 'Asset Enumeration Attempts via NetFlow', 3, 1, 2, 'Discovery', 'Network Service Discovery', 'Detects potential asset enumeration attempts by identifying a single source IP scanning multiple destination IPs on the same port within a short time window, indicating network reconnaissance activity. This pattern suggests an attacker is systematically probing the network to discover active services and assets. Next Steps: @@ -11702,9 +11348,8 @@ Next Steps: INSERT INTO public.utm_correlation_rules VALUES (1296, 'Windows: Multiple Vault Web Credentials Read', 2, 3, 2, 'Credential Access', '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/"]', 'safe("log.eventCode", 0.0) == double(5382) && safe("log.winlogEventDataSubjectLogonId", "") != "0x3e7" && safe("log.winlogEventDataSchemaFriendlyName", "").contains("Windows Web Password Credential") || safe("log.winlogEventDataResource", "").contains("http")', '2025-09-04 20:05:57.119601', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.winlogEventDataProcessPid.keyword","operator":"filter_term","value":"{{.log.winlogEventDataProcessPid}}"},{"field":"log.eventCode","operator":"filter_term","value":"5382"},{"field":"log.winlogEventDataSubjectLogonId.keyword","operator":"filter_not_match","value":"0x3e7"}],"or":[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.winlogEventDataSchemaFriendlyName.keyword","operator":"filter_term","value":"Windows Web Password Credential"},{"field":"log.winlogEventDataResource.keyword","operator":"filter_term","value":"http"}],"or":null,"within":"now-60s","count":1}],"within":"now-60s","count":1}]'); INSERT INTO public.utm_correlation_rules VALUES (1297, 'Windows: A scheduled task was created', 2, 3, 2, 'Persistence', 'Scheduled Task', 'Indicates the creation of a scheduled task using Windows event logs. Adversaries can use these to establish persistence, move laterally, and/or escalate privileges.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1053/005/"]', 'safe("action", "").contains("scheduled-task-created") && safe("log.winlogEventDataTaskName", "").matches("(\\\\OneDrive Standalone Update Task-S-1-5-21|\\\\Hewlett-Packard\\\\HP Web Products Detection|\\\\Hewlett-Packard\\\\HPDeviceCheck)") && !(safe("target.user", "").matches("\\$$"))', '2025-09-04 20:05:57.699998', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1298, 'Windows: Outbound Scheduled Task Activity via PowerShell', 2, 3, 2, 'Execution', '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/"]', 'safe("log.winlogEventDataProcessName", "") in [''powershell.exe'', ''pwsh.exe'', ''powershell_ise.exe''] && safe("log.message", "").matches("(powershell.exe|pwsh.exe|powershell_ise.exe)")', '2025-09-04 20:06:22.42722', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1299, 'Windows: Remote Scheduled Task Creation', 2, 2, 2, 'Lateral Movement', '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/"]', 'safe("log.winlogProcessName", "").contains("svchost.exe") && safe("log.winlogEventDataSourceNetworkAddress", "") in ["incoming", "ingress"] && safe("origin.port", 0.0) >= double(49152) && safe("target.port", 0.0) >= double(49152) && !safe("origin.ip", "") in ["127.0.0.1", ""::1"]', '2025-09-04 20:06:22.941022', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.winlogEventDataHostId.keyword","operator":"filter_term","value":"{{.log.winlogEventDataHostId}}"},{"field":"log.winlogProcessEntityId.keyword","operator":"filter_term","value":"{{.log.winlogEventDataProcessId}}"},{"field":"log.winlogEventDataRegistryPath.keyword","operator":"filter_match","value":"(HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Schedule\\\\TaskCache\\\\Tasks\\\\(.+)\\\\Actions)"}],"or":null,"within":"now-60s","count":1}]'); -INSERT INTO public.utm_correlation_rules VALUES (1300, 'Windows: A scheduled task was updated', 1, 3, 2, 'Persistence', 'Scheduled Task', 'Indicates the update of a scheduled task using Windows event logs. Adversaries can use these to establish persistence, by changing the configuration of a legit scheduled task. Some changes such as disabling or enabling a scheduled task are common and may may generate noise.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1053/005/"]', 'safe("action", "").contains("scheduled-task-updated") && !(safe("target.user", "").matches("(\\$$)")) && safe("log.winlogEventDataTaskName", ""). ).matches("(\\\\User_Feed_Synchronization-|\\\\OneDrive Reporting Task-S-1-5-21|\\\\Hewlett-Packard\\\\HP Web Products Detection|\\\\Hewlett-Packard\\\\HPDeviceCheck)")', '2025-09-04 20:06:23.490062', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1388, 'Windows: Potential Credential Access via Renamed COM+ Services DLL', 3, 3, 2, 'Credential Access', '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/"]', 'safe("log.winlogEventCategory", "").contains("process") && safe("log.winlogEventProcessName", "").contains("rundll32.exe") && safe("log.winlogEventDataset", "").matches("(windows.sysmon_operational)") && safe("log.winlogEventId", "") == ''7'' && safe("log.winlogFileName", "") != ''COMSVCS.DLL'' && (safe("log.winlogFilePeOriginalFileName", "").matches("(COMSVCS.DLL)") || safe("log.winlogFilePeImphash", "").matches("(EADBCCBB324829ACB5F2BBE87E5549A8)"))', '2025-09-04 20:45:20.647582', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.winlogProcessEntityId.keyword","operator":"filter_term","value":"{{.log.winlogProcessEntityId}}"},{"field":"log.winlogEventCategory.keyword","operator":"filter_term","value":"process"}],"or":null,"within":"now-5m","count":3}]'); +INSERT INTO public.utm_correlation_rules VALUES (1300, 'Windows: A scheduled task was updated', 1, 3, 2, 'Persistence', 'Scheduled Task', 'Indicates the update of a scheduled task using Windows event logs. Adversaries can use these to establish persistence, by changing the configuration of a legit scheduled task. Some changes such as disabling or enabling a scheduled task are common and may may generate noise.', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1053/005/"]', 'safe("action", "").contains("scheduled-task-updated") && !safe("target.user", "").matches("(\\$$)") && safe("log.winlogEventDataTaskName", "").matches("(\\\\User_Feed_Synchronization-|\\\\OneDrive Reporting Task-S-1-5-21|\\\\Hewlett-Packard\\\\HP Web Products Detection|\\\\Hewlett-Packard\\\\HPDeviceCheck)")', '2025-09-05 22:55:01.756321', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1051, 'MikroTik Critical Hardware Monitoring Alert', 1, 2, 3, 'Impact', 'T1496 - Resource Hijacking', 'Detects critical hardware monitoring alerts on MikroTik devices including high temperature, voltage issues, fan failures, or other hardware problems that could lead to system instability or failure. **Next Steps:** @@ -11887,7 +11532,6 @@ Next Steps: 9. Monitor for additional attempts from the same source or related networks 10. Consider implementing 802.1Q-in-Q (QinQ) protection if not already in place ', '["https://help.mikrotik.com/docs/spaces/ROS/pages/88014957/VLAN","https://attack.mitre.org/techniques/T1599/001/"]', 'safe("log.chain", "") != "" && (safe("log.chain", "").contains("vlan") || safe("log.chain", "").contains("invalid") || safe("log.len", 0) > 1522 || safe("log.topics", "").contains("vlan"))', '2025-09-04 15:29:46.482281', true, false, 'origin', '["origin.ip","origin.mac"]', '[{"indexPattern":"v11-log-mikrotik-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"}],"or":null,"within":"now-10m","count":5}]'); -INSERT INTO public.utm_correlation_rules VALUES (1304, 'Windows: A user account was added to administration groups', 1, 2, 3, 'Privilege Escalation', 'Domain or Tenant Policy Modification', 'This alert is generated every time a user account is added to administrations groups. This event generates on domain controllers, member servers, and workstations', '["https://attack.mitre.org/tactics/TA0004","https://attack.mitre.org/techniques/T1484/"]', 'safe("log.eventCode", 0.0) in [4732, 4728] && (safe("log.winlogEventDataTargetUserSid","").endsWith(''-544'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-512'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-518'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-519'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-520'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-500'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-548'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-549''))', '2025-09-04 20:22:05.995627', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1066, 'MikroTik Web Interface Brute Force Attack', 2, 2, 3, 'Credential Access', 'T1110.001 - Brute Force: Password Guessing', 'Multiple failed web interface login attempts detected on MikroTik device, indicating potential brute force attack against WebFig or management interface. This activity suggests an attacker is attempting to gain unauthorized access to the device''s management interface. Next Steps: @@ -12239,9 +11883,7 @@ Next Steps: 6. Check system logs for any data corruption or service interruptions 7. Ensure backup systems are functioning properly during power events ', '["https://blog.victormendonca.com/2020/10/28/how-to-setup-ups-on-pfsense/","https://attack.mitre.org/techniques/T1499/"]', 'safe("log.eventType", "") == "apcupsd" && (safe("raw", "").contains("power failure") || safe("raw", "").contains("running on UPS batteries") || safe("raw", "").contains("battery power") || safe("raw", "").contains("low battery") || safe("raw", "").contains("power restored") || safe("raw", "").contains("mains returned") || safe("raw", "").contains("shutdown") || safe("raw", "").contains("battery exhausted"))', '2025-09-04 15:35:23.410193', true, false, 'origin', '["log.syslogHost"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1306, 'Windows: A user account was removed from administration groups', 1, 2, 3, 'Impact', 'Account Access Removal', 'When an alert is generated, it is essential to immediately investigate the removal of the user from the administrators group. Verify if the action was authorized and performed by legitimate administrators. If the deletion was unauthorized, immediately restore the user to the administrators group and assess any potential impact caused by this unwanted action. Also, review and enforce the security of the environment to prevent unauthorized changes to the membership of the administrators group.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1531/"]', 'safe("log.eventCode", 0.0) in [4733, 4729] && (safe("log.winlogEventDataTargetUserSid","").endsWith(''-544'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-512'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-518'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-519'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-520'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-500'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-548'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-549''))', '2025-09-04 20:22:06.811417', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1307, 'Windows: Administrators group changed', 1, 2, 3, 'Defense Evasion, Privilege Escalation', 'Domain or Tenant Policy Modification', 'Adversaries may modify the configuration settings of a domain to evade defenses and/or escalate privileges in domain environments. Domains provide a centralized means of managing how computer resources (ex: computers, user accounts) can act, and interact with each other, on a network. The policy of the domain also includes configuration settings that may apply between domains in a multi-domain/forest environment. Modifications to domain settings may include altering domain Group Policy Objects (GPOs) or changing trust settings for domains, including federation trusts.', '["https://attack.mitre.org/techniques/T1484"]', 'safe("log.eventCode", 0.0) == double(4799) && safe("log.winlogEventDataTargetUserSid","").contains(''S-1-5-32-544'')', '2025-09-04 20:22:07.211714', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1308, 'Windows: An administrative group was removed', 1, 2, 3, 'Impact', 'Account Access Removal', 'When an alert is generated, it is essential to immediately investigate the removal of the administrator group. Verify if the action was authorized and performed by legitimate administrators. If the unauthorized deletion is confirmed, you should immediately restore the administrators group and assess any potential damage caused by this unwanted action. Additionally, review and enforce security of the environment to prevent unauthorized changes to administrator group membership and limit access to administrator accounts to authorized users only.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1531/"]', 'safe("log.eventCode", 0.0) in [4734, 4730] && (safe("log.winlogEventDataTargetUserSid","").endsWith(''-544'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-512'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-518'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-519'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-520'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-500'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-548'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-549''))', '2025-09-04 20:22:07.454041', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1309, 'Windows: An attempt was made to set the Directory Services Restore Mode', 1, 2, 3, 'Collection', '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"]', 'safe("log.eventCode", 0.0) == double(4794)', '2025-09-04 20:22:07.891473', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1096, 'SonicWall Application Control Policy Violation', 2, 2, 1, 'Command and Control', 'T1071 - Application Layer Protocol', 'Detects when users attempt to access blocked applications or violate application control policies on SonicWall firewalls. This may indicate attempts to bypass security controls or use unauthorized software for data exfiltration, command and control communications, or policy circumvention. @@ -12272,6 +11914,7 @@ Next Steps: 5. Consider implementing additional bandwidth controls if abuse is confirmed ', '["https://www.sonicwall.com/techdocs/pdf/sonicos-6-5-4-log-events-reference-guide.pdf"]', '(safe("log.category", "") == "Bandwidth" || safe("log.msg", "").contains("bandwidth") || safe("log.msg", "").contains("threshold exceeded")) && (safe("origin.bytesReceived", 0.0) > 1000000000.0 || safe("origin.bytesSend", 0.0) > 1000000000.0)', '2025-09-04 15:38:13.732782', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1414, 'Windows: Persistence via BITS Job Notify Cmdline', 2, 3, 2, 'Persistence', '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/"]', 'safe("log.message", "").contains("BITS") && safe("log.eventDataParentProcessName", "").contains("svchost.exe") && !(safe("log.winlogEventDataProcessName", "").matches("(:\\Windows\\System32\\WerFaultSecure.exe|:\\Windows\\System32\\WerFault.exe|:\\Windows\\System32\\wermgr.exe|:\\WINDOWS\\system32\\directxdatabaseupdater.exe)"))', '2025-09-04 20:57:06.61835', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1308, 'Windows: An administrative group was removed', 1, 2, 3, 'Impact', 'Account Access Removal', 'When an alert is generated, it is essential to immediately investigate the removal of the administrator group. Verify if the action was authorized and performed by legitimate administrators. If the unauthorized deletion is confirmed, you should immediately restore the administrators group and assess any potential damage caused by this unwanted action. Additionally, review and enforce security of the environment to prevent unauthorized changes to administrator group membership and limit access to administrator accounts to authorized users only.', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1531/"]', 'safe("log.eventCode", 0.0) in [double(4734), double(4730)] && (safe("log.winlogEventDataTargetUserSid","").endsWith(''-544'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-512'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-518'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-519'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-520'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-500'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-548'') || safe("log.winlogEventDataTargetUserSid","").endsWith(''-549''))', '2025-09-05 22:05:48.080815', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1098, 'SonicWall Botnet Activity Detected', 3, 3, 2, 'Command and Control', 'T1071 - Application Layer Protocol', 'Detects potential botnet command and control (C2) communication or infected host behavior identified by SonicWall''s botnet filter. This includes suspicious outbound connections, HTML infection chains, or known botnet signatures. Next Steps: @@ -12750,7 +12393,6 @@ Next Steps: safe("log.extensions", "").contains("=\\u") ) ', '2025-09-04 16:00:45.982896', true, false, 'origin', '["origin.ip","log.deviceProduct"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1311, 'Windows Defender: Antimalware engine found malware or other potentially unwanted software', 1, 2, 3, 'Execution', '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/"]', 'safe("log.eventCode", 0.0) in [1006, 1015, 1116] && safe("log.winlogProviderName", "") == ''SecurityCenter''', '2025-09-04 20:22:40.982418', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1131, 'CEF Header Malformation Detection', 2, 3, 2, 'Impact', 'Protocol Anomaly', 'Detects malformed CEF (Common Event Format) headers that may indicate attacks attempting to bypass security monitoring, cause parsing failures, or hide malicious activity. This rule identifies various forms of CEF header malformation including incorrect format structure, invalid severity values, missing required fields, excessive field lengths, and malformed version indicators. Next Steps: @@ -12928,7 +12570,6 @@ Next Steps: 6. Review device documentation for proper CEF field requirements 7. Consider implementing additional validation rules for critical log sources ', '["https://docs.nxlog.co/integrate/cef-logging.html","https://attack.mitre.org/techniques/T1001/"]', '(safe("log.cef_device_vendor", "") == "" && safe("log.cef_device_product", "") == "" && safe("log.message", "").contains("CEF:")) || (safe("log.cef_severity", -1) < 0 || safe("log.cef_severity", -1) > 10) || (safe("log.cef_device_event_class_id", "") == "" && safe("log.message", "").contains("|")) || (safe("log.field_mapping_error", false) == true || "field_mapping_error" in safe("log.tags", []))', '2025-09-04 16:03:11.231014', true, false, 'origin', '["log.source","log.cef_device_vendor"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1312, 'Windows Defender: Antimalware platform failed performing an action to protect you from potentially unwanted software', 1, 2, 3, 'Defense Evasion', 'Impair Defenses', 'This rule is triggered when the system detects that the antimalware platform has experienced a failure or error while trying to perform a protection action against potentially unwanted software. This alert is essential to identify situations where the system could not be adequately protected against unwanted software, which can increase the risk of infections and compromise system security.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1562/"]', 'safe("log.eventCode", 0.0) in [1118, 1119] && safe("log.winlogProviderName", "") == "SecurityCenter"', '2025-09-04 20:22:41.358564', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1138, 'CEF Message Truncation Detection', 2, 3, 2, 'Defense Evasion', 'T1070.001 - Indicator Removal: Clear Windows Event Logs', 'Detects truncated CEF messages that may indicate log tampering, transmission errors, or attempts to hide malicious activity by truncating log entries. Monitors for incomplete CEF headers or extensions. Next Steps: @@ -13028,7 +12669,6 @@ Next Steps: safe("log.message", "").matches(".*rt=[0-9]{10}[0-9]{4,}.*")) ) ', '2025-09-04 16:03:13.930613', true, false, 'origin', '["origin.ip","log.deviceProduct"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1313, 'Windows Defender: Protection Disabled', 1, 2, 3, 'Defense Evasion', '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/"]', 'safe("log.eventCode", 0.0) in [5001, 5012]', '2025-09-04 20:22:42.271564', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1314, 'Windows audit log was cleared', 1, 2, 3, 'Potentially Malicious Activity', 'Account Manipulation: SSH Authorized Keys', 'Adversaries may modify the SSH authorized_keys file to maintain persistence on a victim host.', '["https://attack.mitre.org/techniques/T1098/004/"]', 'safe("log.eventCode", 0.0) == double(1102)', '2025-09-04 20:22:42.691175', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1142, 'CEF User Agent Anomaly Detection', 2, 2, 1, 'Command and Control', 'T1071.001 - Application Layer Protocol: Web Protocols', 'Detects anomalous user agent strings in CEF logs that may indicate malicious scripts, bots, vulnerability scanners, or attempts to bypass security controls through user agent spoofing. This rule identifies known attack tools, template injection attempts, and unusually short or long user agent strings. @@ -13943,7 +13583,6 @@ safe("log.tls_cert_cn", "") != "" && safe("log.tls_sni", "") != safe("log.tls_cert_cn", "") && !safe("log.tls_cert_san", "").contains(safe("log.tls_sni", "")) ', '2025-09-04 16:07:48.712945', true, false, 'origin', '["origin.ip","log.tls_sni"]', '[{"indexPattern":"v11-log-syslog-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"},{"field":"log.tls_sni.keyword","operator":"must_not_term","value":"{{.log.tls_sni}}"}],"or":null,"within":"now-5m","count":3}]'); -INSERT INTO public.utm_correlation_rules VALUES (1318, 'Clearing Windows Event Logs with wevtutil', 1, 2, 3, 'Defense Evasion', '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/"]', 'safe("log.message", ""). ).matches(''(/e:false|cl|clear-log|Clear-EventLog)'') && safe("log.winlogEventDataLogonProcessName", "").matches(''wevtutil.exe'')'')', '2025-09-04 20:22:44.951457', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1477, 'Windows: File system full', 1, 2, 3, 'Potentially Malicious Activity', 'File and Directory Permissions', 'Detects events indicating that the file system is full, which may lead to system instability or data loss.', '["https://learn.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/troubleshoot"]', 'safe("log.winlogEventDataLogName", "") == "File Replication Service" && safe("log.eventCode", 0.0) == double(13570)', '2025-09-04 21:12:16.274385', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1192, 'TLS Handshake Failure Detection', 3, 3, 2, 'Initial Access', 'Protocol Impersonation', 'Detects TLS handshake failures in RFC-5425 syslog transport which may indicate certificate issues, protocol mismatches, or potential man-in-the-middle attacks. @@ -14018,7 +13657,6 @@ safe("log.payload", "").matches("\\x90{50,}|\\x00{100,}") || (safe("log.delimiter_missing", false) == true && safe("log.payload_size", 0) > 4096) ', '2025-09-04 16:08:17.36197', true, false, 'origin', '["adversary.ip","target.ip"]', '[{"indexPattern":"v11-log-syslog-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"},{"field":"target.ip.keyword","operator":"filter_term","value":"{{.target.ip}}"}],"or":null,"within":"now-5m","count":10}]'); -INSERT INTO public.utm_correlation_rules VALUES (1319, 'Windows: NTDS or SAM Database File Copied', 3, 3, 3, 'Credential Access', '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/"]', 'safe("log.winlogEventDataLogonProcessName", "").matches(''(Cmd.Exe|PowerShell.EXE|XCOPY.EXE|esentutl.exe|cmd.exe|powershell.exe)'') && safe("log.message", "").matches(''(copy|xcopy|Copy-Item|move|cp|mv|/y|/vss|/d)'') && message.matches(''(\\ntds.dit|\\config\\SAM|\\(.+)\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy(.+)\\|/system32/config/SAM)'')', '2025-09-04 20:22:45.312052', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1196, 'TCP Congestion Control Abuse', 1, 2, 3, 'Impact', 'T1499 - Endpoint Denial of Service', 'Detects TCP congestion control abuse where attackers exploit congestion control mechanisms to generate non-responsive traffic, cause denial of service, or suppress legitimate traffic. Monitors for ECN violations, optimistic ACKing, and malicious burst patterns. Next Steps: @@ -14148,7 +13786,6 @@ Next Steps: ', '["https://access.redhat.com/security/vulnerabilities/tcpsack","https://attack.mitre.org/techniques/T1499/"]', 'safe("log.tcp_mss", 0) > 0 && safe("log.tcp_mss", 0) < 48 && (safe("log.tcp_flags", "").contains("SYN") || safe("log.tcp_options", "").contains("mss")) ', '2025-09-04 16:08:21.873965', true, false, 'origin', '["adversary.ip"]', '[{"indexPattern":"v11-log-syslog-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"}],"or":null,"within":"now-5m","count":10}]'); -INSERT INTO public.utm_correlation_rules VALUES (1400, 'Windows: Suspicious MS Office Child Process', 1, 2, 3, 'Initial Access', '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/"]', 'safe("log.winlogEventDataProcessName", "") in ["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"] && safe("log.winlogeventDataParentProcessName", "") in ["eqnedt32.exe", "excel.exe", "fltldr.exe", "msaccess.exe", "mspub.exe", "powerpnt.exe", "winword.exe", "outlook.exe"]', '2025-09-04 20:46:22.725946', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1401, 'Windows: Suspicious MS Outlook Child Process', 1, 3, 2, 'Initial Access', '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/"]', 'safe("log.winlogEventDataProcessName", "") in ["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"] && safe("log.winlogEventDataParentProcessName", "").contains("outlook.exe")', '2025-09-04 20:46:23.196923', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1204, 'RFC-6587 Non-Transparent Framing Issues', 1, 3, 3, 'Impact', 'Data Manipulation', 'Detects issues with non-transparent framing method including improper line feed termination, mixed framing methods, or missing delimiters that could lead to message boundary confusion. These issues can result in log parsing failures, message corruption, or potential denial of service conditions. @@ -14169,9 +13806,7 @@ safe("log.framing", "") == "non-transparent" && (safe("log.message", "") != "" && !safe("log.message", "").endsWith("\n")) ) ', '2025-09-04 16:08:50.471467', true, false, 'origin', '["adversary.ip","log.facility"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1406, 'Windows: PowerShell Suspicious Discovery Related Windows API Functions', 2, 1, 3, 'Discovery', '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/"]', 'safe("log.message", ""). in [''NetShareEnum'', ''NetWkstaUserEnum'', ''NetSessionEnum'', ''NetLocalGroupEnum'', ''NetLocalGroupGetMembers'', ''DsGetSiteName'', ''DsEnumerateDomainTrusts'', ''WTSEnumerateSessionsEx'', ''WTSQuerySessionInformation'', ''LsaGetLogonSessionData'', ''QueryServiceObjectSecurity'']', '2025-09-04 20:52:52.67712', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1407, '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/"]', 'safe("log.message", "") in [''Invoke-TokenManipulation'', ''ImpersonateNamedPipeClient'', ''NtImpersonateThread''] && safe("log.message", "").matches("(UpdateProcThreadAttribute(.+)STARTUPINFOEX|STARTUPINFOEX(.+)UpdateProcThreadAttribute)") && safe("log.message", "").matches("(AdjustTokenPrivileges(.+)SeDebugPrivilege|SeDebugPrivilege(.+)AdjustTokenPrivileges)") && safe("log.message", "").matches("((SetThreadToken|ImpersonateLoggedOnUser|CreateProcessWithTokenW|CreatePRocessAsUserW|CreateProcessAsUserA)(.+)(DuplicateToken|DuplicateTokenEx)|(DuplicateToken|DuplicateTokenEx)(.+)(SetThreadToken|ImpersonateLoggedOnUser|CreateProcessWithTokenW|CreatePRocessAsUserW|CreateProcessAsUserA))")', '2025-09-04 20:52:53.255979', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1408, 'Windows: Possible addition of new item to Windows startup registry', 2, 3, 1, 'Potentially Compromised System', '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/"]', 'safe("log.message", "").matches("(SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run)")', '2025-09-04 20:52:53.556785', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1205, 'RFC-6587 Octet Counting Violations', 1, 3, 2, 'Command and Control', 'T1095 - Non-Application Layer Protocol', 'Detects violations of RFC-6587 octet counting format where the declared message length does not match the actual message size, indicating potential message injection, corruption attempts, or protocol manipulation attacks. Next Steps: @@ -14189,6 +13824,8 @@ safe("log.declared_length", 0) > 0 && safe("log.actual_length", 0) > 0 && safe("log.declared_length", 0) != safe("log.actual_length", 0) ', '2025-09-04 16:08:51.209947', true, false, 'origin', '["adversary.ip","log.facility"]', '[{"indexPattern":"v11-log-syslog-*","with":[{"field":"origin.ip.keyword","operator":"filter_term","value":"{{.origin.ip}}"}],"or":null,"within":"now-15m","count":10}]'); +INSERT INTO public.utm_correlation_rules VALUES (1408, 'Windows: Possible addition of new item to Windows startup registry', 2, 3, 1, 'Potentially Compromised System', '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/"]', 'safe("log.message", "").matches("SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run")', '2025-09-06 00:12:08.981364', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1406, 'Windows: PowerShell Suspicious Discovery Related Windows API Functions', 2, 1, 3, 'Discovery', '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/"]', 'safe("log.message", "") in [''NetShareEnum'', ''NetWkstaUserEnum'', ''NetSessionEnum'', ''NetLocalGroupEnum'', ''NetLocalGroupGetMembers'', ''DsGetSiteName'', ''DsEnumerateDomainTrusts'', ''WTSEnumerateSessionsEx'', ''WTSQuerySessionInformation'', ''LsaGetLogonSessionData'', ''QueryServiceObjectSecurity'']', '2025-09-06 00:08:03.019653', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1207, 'TCP Resource Exhaustion Attack Detection', 1, 2, 3, 'Impact', 'T1499 - Endpoint Denial of Service: Resource Exhaustion', 'Detects patterns indicative of resource exhaustion attacks against TCP syslog collectors including connection flooding, slow loris attacks, and memory exhaustion through large message queues. Next Steps: @@ -14545,8 +14182,6 @@ Next Steps: safe("log.message", "").contains("VMware Tools heartbeat failure")) && safe("origin.hostname", "") != "" ', '2025-09-04 18:59:26.403133', true, false, 'origin', '["origin.hostname"]', '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1487, 'Windows: Potential Process Herpaderping Attempt', 2, 2, 3, 'Defense Evasion', 'Masquerading', 'Identifies process execution followed by a file overwrite of an executable by the same parent process. This may indicate an evasion attempt to execute malicious code in a stealthy way.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1036/"]', '!(safe("log.eventDataParentProcessName", "").matches("(:\\Windows\\SoftwareDistribution\\(.+).exe|:\\Program Files\\Elastic\\Agent\\data\\(.+).exe|:\\Program Files (x86)\\Trend Micro\\(.+).exe)")) -', '2025-09-04 21:15:05.164865', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"action.keyword","operator":"filter_term","value":"overwrite"},{"field":"log.eventDataHostName.keyword","operator":"filter_term","value":"{{.log.eventDataHostName}}"},{"field":"log.eventDataProcessName.keyword","operator":"filter_term","value":"{{.log.eventDataProcessName}}"},{"field":"log.eventDataProcessId.keyword","operator":"filter_term","value":"{{.log.eventDataProcessId}}"}],"or":null,"within":"now-60s","count":1}]'); INSERT INTO public.utm_correlation_rules VALUES (115, 'AWS CloudTrail Log Updated', 3, 2, 2, 'Data Manipulation', 'Impact', 'Identifies an update to an AWS log trail setting that specifies the delivery of log files', '["https://attack.mitre.org/tactics/TA0040/","https://attack.mitre.org/techniques/T1565/","https://attack.mitre.org/tactics/TA0009/","https://attack.mitre.org/techniques/T1530/","https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_UpdateTrail.html","https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudtrail/update-trail.html"]', 'safe("log.eventSource", "") == "cloudtrail.amazonaws.com" && safe("log.eventName", "") == "UpdateTrail"', '2025-09-03 12:27:17.892384', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1, 'Antivirus Service Stopped or Disabled', 2, 3, 3, 'Defense Evasion', 'T1562.001 - Impair Defenses: Disable or Modify Tools', 'Detects when the Bitdefender antivirus service or critical security modules are stopped, disabled, or experiencing failures. This is a critical security event that could indicate malicious tampering or system issues. @@ -14646,29 +14281,19 @@ INSERT INTO public.utm_correlation_rules VALUES (1325, 'Windows: PowerShell Keyl INSERT INTO public.utm_correlation_rules VALUES (1326, 'Windows: Kerberos Pre-authentication Disabled for User', 2, 2, 3, 'Credential Access', '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/"]', 'safe("log.message", "").matches("(('')?[Dd]on('')?t [Rr]equire [Pp]reauth('')?(\\s)?(-)?(\\s)?[Ee]nabled)") && safe("log.eventCode", 0.0) == double(4738)', '2025-09-04 20:23:16.951249', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1327, 'Windows: Disable Windows Firewall Rules via Netsh', 2, 2, 3, 'Defense Evasion', 'Disable or Modify System Firewall', '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/TA0005/","https://attack.mitre.org/techniques/T1562/004/"]', 'safe("log.message", "").matches(''(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)'') && safe("log.winlogEventDataProcessName", "").matches(''netsh.exe'')', '2025-09-04 20:23:17.453122', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1328, 'Windows: Disabling Windows Defender Security Settings via PowerShell', 3, 3, 3, 'Defense Evasion', '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/"]', 'safe("log.message", "").matches("(Set-MpPreference)") && safe("log.message", "").matches("(-Disable|Disabled|NeverSend|-Exclusion)") && safe("log.winlogEventDataProcessName", "").matches("(powershell.exe|pwsh.dll|powershell_ise.exe)")', '2025-09-04 20:23:17.812321', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1416, 'Windows: Persistence via Update Orchestrator Service Hijack', 2, 3, 2, 'Persistence', '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/"]', 'safe("log.message", "").contains("UsoSvc") && safe("log.eventDataParentProcessName", "").matches("(C:\\Windows\\System32\\svchost\\.exe)") && !(safe("log.winlogEventDataProcessName", "").matches("(MoUsoCoreWorker.exe|OfficeC2RClient.exe)")) && !(safe("log.winlogEventDataProcessName", "").matches("(:\\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)"))"', '2025-09-04 20:57:07.636394', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1417, 'Windows: Persistence via WMI Event Subscription', 2, 3, 2, 'Persistence', 'Windows Management Instrumentation 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/"]', 'safe("log.message", "").contains("create") && safe("log.message", "").matches("(ActiveScriptEventConsumer|CommandLineEventConsumer)") && safe("log.winlogEventDataProcessName", "").contains("wmic.exe")', '2025-09-04 20:57:08.25663', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1429, 'Windows: System Shells via Services', 1, 3, 2, 'Persistence', '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/"]', 'safe("log.winlogEventDataProcessName", "").matches("(cmd.exe|powershell.exe|pwsh.exe|powershell_ise.exe)") && - safe("log.winlogEventDataParentProcessName", "").contains("services.exe") && - !(safe("log.message", "").matches("(NVDisplay.ContainerLocalSystem)"))) -', '2025-09-04 21:00:49.49788', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1430, 'Windows: The system stopped responding, crashed or lost power unexpectedly', 2, 2, 2, 'Potentially Malicious Activity', 'Service Stop', 'Adversaries may stop or disable services on a system to render those services unavailable to legitimate users. Stopping critical services or processes can inhibit or stop response to an incident or aid in the adversarys overall objectives to cause damage to the environment', '["https://attack.mitre.org/techniques/T1489/"]', 'safe("log.winlogEventDataLogName", "") == "System" && safe("log.winlogEventDataLevel", "") in ["Critical","critical"] &&', '2025-09-04 21:00:49.95941', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":"6008"},{"field":"log.winlogEventDataProviderName.keyword","operator":"filter_term","value":"Microsoft-Windows-Kernel-Power"}],"or":null,"within":"now-60s","count":1}]'); INSERT INTO public.utm_correlation_rules VALUES (1431, 'Windows: The value for the parameter to the browser service was illegal', 1, 3, 2, 'Potentially Malicious Activity', 'Exploitation for Client Execution', 'Adversaries may exploit software vulnerabilities in client applications to execute code. Vulnerabilities can exist in software due to unsecure coding practices that can lead to unanticipated behavior. Adversaries can take advantage of certain vulnerabilities through targeted exploitation for the purpose of arbitrary code execution. Oftentimes the most valuable exploits to an offensive toolkit are those that can be used to obtain code execution on a remote system because they can be used to gain access to that system. Users will expect to see files related to the applications they commonly used to do work, so they are a useful target for exploit research and development because of their high utility', '["https://attack.mitre.org/techniques/T1203/"]', 'safe("log.winlogEventDataProviderName", "") == "Browser" && safe("log.eventId", 0.0) == double(8023)', '2025-09-04 21:00:50.457146', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1465, 'Windows Defender: ERROR: BAD DB: CONTENT', 1, 2, 3, 'Potentially Compromised System', 'User Execution', 'Detects the ''BAD DB: CONTENT'' error event in Windows Defender, which may indicate a content-related issue.', '["https://learn.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/troubleshoot"]', 'safe("log.logName", "") == "Microsoft-Windows-Windows Defender/Operational" && safe("log.eventDataErrorCode", "") == "0x8050A004"', '2025-09-04 21:11:28.867477', true, false, 'target', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1432, 'Windows: SeDebugPrivilege Enabled by a Suspicious Process', 1, 3, 2, 'Privilege Escalation', '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/"]', 'safe("log.action", "").matches("(Token Right Adjusted Events)") && safe("log.eventProvider", "").matches("(Microsoft-Windows-Security-Auditing)") && safe("log.eventDataEnabledPrivilegeList", "").matches("(SeDebugPrivilege)") && !safe("log.eventDataSubjectUserSid", "") in [''S-1-5-18'', ''S-1-5-19'', ''S-1-5-20''] && !safe("log.eventDataProcessName", "").matches("(:\\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)")', '2025-09-04 21:00:50.995969', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1421, 'Windows: Suspicious PDF Reader Child Process', 1, 1, 2, 'Execution', '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/"]', 'safe("log.winlogEventDataParentProcessName", "") in ["AcroRd32.exe", "Acrobat.exe", "FoxitPhantomPDF.exe", "FoxitReader.exe"] && safe("log.winlogEventDataProcessName", "") in ["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"]', '2025-09-04 20:59:23.305021', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1422, 'Windows: Suspicious PowerShell Engine ImageLoad', 1, 3, 2, 'Execution', '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/"]', '!(safe("log.winlogEventDataProcessName", "") in [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]) && -!(safe("log.winlogEventDataProcessName", "").matches("(C:\\Windows\\System32\\RemoteFXvGPUDisablement.exe|C:\\Windows\\System32\\sdiagnhost.exe|C:\\Program Files( \\(x86\\))?\\(.+)\\.exe)")) && -safe("log.message", "") in ["System.Management.Automation.ni.dll", "System.Management.Automation.dll"] -', '2025-09-04 20:59:23.799602', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1429, 'Windows: System Shells via Services', 1, 3, 2, 'Persistence', '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/"]', 'safe("log.winlogEventDataProcessName", "").matches("(cmd.exe|powershell.exe|pwsh.exe|powershell_ise.exe)") && + safe("log.winlogEventDataParentProcessName", "").contains("services.exe") && + !(safe("log.message", "").matches("(NVDisplay.ContainerLocalSystem)")) +', '2025-09-06 00:41:49.738847', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1430, 'Windows: The system stopped responding, crashed or lost power unexpectedly', 2, 2, 2, 'Potentially Malicious Activity', 'Service Stop', 'Adversaries may stop or disable services on a system to render those services unavailable to legitimate users. Stopping critical services or processes can inhibit or stop response to an incident or aid in the adversarys overall objectives to cause damage to the environment', '["https://attack.mitre.org/techniques/T1489/"]', 'safe("log.winlogEventDataLogName", "") == "System" && safe("log.winlogEventDataLevel", "") in ["Critical","critical"] && safe("log.eventCode", 0.0) == double(6008)', '2025-09-06 00:46:37.003616', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.eventCode","operator":"filter_term","value":"6008"},{"field":"log.winlogEventDataProviderName.keyword","operator":"filter_term","value":"Microsoft-Windows-Kernel-Power"}],"or":[],"within":"now-60s","count":1}]'); INSERT INTO public.utm_correlation_rules VALUES (1423, 'Windows: Suspicious Process Access via Direct System Call', 1, 3, 2, 'Defense Evasion', '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/"]', 'safe("log.eventCode", 0.0) == double(10) && !(safe("log.winlogEventDataCallTrace", "").matches("(:\\WINDOWS\\SYSTEM32\\ntdll.dll|:\\WINDOWS\\SysWOW64\\ntdll.dll|:\\Windows\\System32\\wow64cpu.dll|:\\WINDOWS\\System32\\wow64win.dll|:\\Windows\\System32\\win32u.dll)")) && !(safe("log.winlogEventDataTargetImage","").matches("(:\\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)")) && !(safe("log.winlogEventDataProcessName","").matches("(:\\Program Files\\Adobe\\Acrobat DC\\Acrobat\\Acrobat.exe|:\\Program Files (x86)\\World of Warcraft\\_classic_\\WowClassic.exe)")) ', '2025-09-04 20:59:24.275438', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1425, 'Windows: Suspicious RDP ActiveX Client Loaded', 1, 3, 2, 'Lateral Movement', '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/"]', '!(safe("log.winlogEventDataProcessName", "").matches("(C:\\Windows\\System32\\mstsc.exe|C:\\Windows\\SysWOW64\\mstsc.exe)"))" && -safe("log.winlogEventDataProcessName", "").matches("(C:\\Windows\\|C:\\Users\\Public\\|C:\\Users\\Default\\|C:\\Intel\\|C:\\PerfLogs\\|C:\\ProgramData\\|\\Device\\Mup\\|\\\\)") && -safe("log.message", "").contains("mstscax.dll") -', '2025-09-04 20:59:25.271298', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1456, 'Windows: Volume Shadow Copy Service: COM+ database corrupted, writers will not receive events', 1, 2, 3, 'Persistence', 'Create or Modify System Process: Windows Service', 'Adversaries may create or modify Windows services to repeatedly execute malicious payloads as part of persistence. When Windows boots up, it starts programs or applications called services that perform background system functions. Windows service configuration information, including the file path to the service is executable or recovery programs/commands, is stored in the Windows Registry.', '["https://attack.mitre.org/techniques/T1543/003/"]', 'safe("log.eventDataProviderName", "") == ''VSS'' && safe("log.winlogEventId", "") in [''20'', ''21'']', '2025-09-04 21:02:17.710749', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1426, 'Windows: Suspicious Execution via Scheduled Task', 1, 3, 2, 'Persistence', '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/"]', 'safe("log.winlogEventDataEventType", "") == "start" && safe("log.winlogEventDataProcessParentName", "").contains("svchost.exe") && safe("log.winlogEventDataProcessParentArgs", "").contains("Schedule") && safe("log.winlogEventDataOriginalFileName", "").matches("(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)") && @@ -14679,6 +14304,9 @@ safe("log.winlogEventDataProcessArgs", "").matches("(C:\\\\Users\\\\|C:\\\\Progr ', '2025-09-04 20:59:25.801189', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1427, 'Windows: Zoom Child Process', 1, 3, 2, 'Defense Evasion', 'Masquerading', 'A suspicious Zoom child process was detected, which may indicate an attempt to run unnoticed. Verify process details such as command line, network connections, file writes and associated file signature details as well.', '["https://attack.mitre.org/tactics/TA0005/","https://attack.mitre.org/techniques/T1036/"]', 'safe("log.winlogEventDataParentProcessName", "").contains("zoom.exe") && safe("log.winlogEventDataProcessName", "").matches("(cmd.exe|powershell.exe|pwsh.exe|powershell_ise.exe)")', '2025-09-04 20:59:26.315144', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1433, 'Windows: UAC Bypass Attempt with IEditionUpgradeManager Elevated COM Interface', 1, 3, 2, 'Privilege Escalation', '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/"]', 'safe("log.eventDataProcessName", "").contains("Clipup.exe") && !safe("log.message", "").matches("(C:\\Windows\\System32\\ClipUp.exe)") && safe("log.eventDataParentProcessName", "").contains("dllhost.exe") && safe("log.message", "").matches("(/Processid:{BD54C901-076B-434E-B6C7-17C531F4AB41)")', '2025-09-04 21:00:51.500412', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1425, 'Windows: Suspicious RDP ActiveX Client Loaded', 1, 3, 2, 'Lateral Movement', '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/"]', '!(safe("log.winlogEventDataProcessName", "").matches("(C:\\\\Windows\\\\System32\\\\mstsc\\.exe|C:\\\\Windows\\\\SysWOW64\\\\mstsc\\.exe)")) && + safe("log.winlogEventDataProcessName", "").matches("(C:\\\\Windows\\\\|C:\\\\Users\\\\Public\\\\|C:\\\\Users\\\\Default\\\\|C:\\\\Intel\\\\|C:\\\\PerfLogs\\\\|C:\\\\ProgramData\\\\|\\\\Device\\\\Mup\\\\|\\\\\\\\)") && + safe("log.message", "").contains("mstscax.dll")', '2025-09-06 00:36:20.812022', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1434, 'Windows: UAC Bypass Attempt via Elevated COM Internet Explorer Add-On Installer', 1, 3, 2, 'Privilege Escalation', '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/"]', 'safe("log.message", "").matches("(C:\\(.+)\\AppData\\(.+)\\Temp\\IDC(.+).tmp\\(.+).exe)") && safe("log.processParentName", "").contains("ieinstall.exe") && safe("log.message", "").matches("(-Embedding)")', '2025-09-04 21:00:52.015755', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1435, 'Windows: UAC Bypass via ICMLuaUtil Elevated COM Interface', 1, 3, 2, 'Privilege Escalation', '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/"]', 'safe("log.eventDataProcessName", "").contains("dllhost.exe") && !safe("log.message", "").contains("WerFault.exe") && safe("log.message", "").matches("(/Processid:\\{3E5FC7F9-9A51-4367-9063-A120244FBEC7\\}|/Processid:\\{D2E7041B-2927-42FB-8E9F-7CE93B6DC937\\})")', '2025-09-04 21:00:52.54772', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1436, 'Windows: UAC Bypass Attempt via Privileged IFileOperation COM Interface', 1, 3, 2, 'Privilege Escalation', '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/technique s/T1548/002/"]', 'safe("log.eventDataFileName", "").matches("(wow64log.dll|comctl32.dll|DismCore.dll|OskSupport.dll|duser.dll|Accessibility.ni.dll)") && safe("log.eventDataProcessName", "").contains("dllhost.exe") && !safe("log.eventDataFileName", "").matches("(C:\\Windows\\SoftwareDistribution\\|C:\\Windows\\WinSxS\\)")', '2025-09-04 21:00:53.068129', true, false, 'origin', NULL, '[]'); @@ -14688,31 +14316,14 @@ INSERT INTO public.utm_correlation_rules VALUES (1439, 'Windows: Suspicious .NET INSERT INTO public.utm_correlation_rules VALUES (1440, 'Windows: PowerShell Suspicious Payload Encoded and Compressed', 2, 3, 1, 'Defense Evasion', '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/"]', 'safe("log.message", "").matches("(FromBase64String)") && safe("log.message", "").matches("(System.IO.Compression.DeflateStream|System.IO.Compression.GzipStream|IO.Compression.DeflateStream|IO.Compression.GzipStream)")', '2025-09-04 21:00:57.024952', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1479, 'Windows Service Installed via an Unusual Client', 3, 3, 2, 'Privilege Escalation', '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/"]', 'safe("action", "") == "service-installed" && safe("clientProcessId", "") == "0" && safe("parentProcessId", "") == "0"', '2025-09-04 21:12:37.273553', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1480, 'Windows: Wireless Credential Dumping using Netsh Command', 3, 3, 2, 'Credential Access', '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/"]', 'safe("log.message", "").contains("wlan") && safe("log.message", "").matches("(key(.+)clear)") && safe("log.winlogEventDataProcessName", "").contains("netsh.exe")', '2025-09-04 21:12:37.82278', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1481, 'Windows: Signed Proxy Execution via MS Work Folders', 1, 2, 3, 'Defense Evasion', '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/"]', 'safe("log.winlogventDataProcessName", "").contains("control.exe") && safe("log.winlogEventDataParentProcessName", "").contains("workfolders.exe") && !safe("log.winlogEventDataProcessName", "").matches("(:\\Windows\\(System32|SysWOW64)\\control.exe)")', '2025-09-04 21:12:38.50848', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1441, 'Windows: Bypass UAC via Sdclt', 3, 3, 2, 'Privilege Escalation', 'Abuse Elevation Control Mechanism: Bypass User Account Control', 'Identifies User Account Control (UAC) bypass via sdclt.exe. Attackers bypass UAC to stealthily execute code with elevated permissions.', '["https://attack.mitre.org/tactics/TA0004/","https://attack.mitre.org/techniques/T1548/002/"]', 'safe("log.winlogEventDataProcessName", "").contains("sdclt.exe") && safe("log.message", "").contains("/kickoffelev") safe("log.winlogEventDataParentProcessName", "").contains("sdclt.exe") && !safe("log.winlogEventDataProcessName", "").matches("(C:\\Windows\\System32\\sdclt.exe|C:\\Windows\\System32\\control.exe|C:\\Windows\\SysWOW64\\sdclt.exe|C:\\Windows\\SysWOW64\\control.exe)")', '2025-09-04 21:01:32.806194', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.winlogEventDataProcessID.keyword","operator":"filter_term","value":"{{.log.winlogEventDataProcessID}}"}],"or":null,"within":"now-5m","count":3}]'); -INSERT INTO public.utm_correlation_rules VALUES (1442, 'Windows: Unusual File Creation - Alternate Data Stream', 1, 3, 2, 'Defense Evasion', '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/"]', 'safe("log.eventDataFileName", "").matches("(^C:\\(.+):(.+))") && !safe("log.eventDataFileName", "").matches("(C:\\(.+):zone.identifier)") && safe("log.message", "").matches("(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)") && !safe("log.eventDataProcessName", "").matches("(:\\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)"', '2025-09-04 21:01:33.340425', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1504, 'Windows: Possible ransomware attack detected. Unusual DNS Requests.', 2, 2, 3, 'Ransomware', 'Impact: 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. Unusual DNS requests have been detected, potentially indicating ransomware activity.', '["https://attack.mitre.org/tactics/TA0040/"]', 'safe("log.eventDataQueryName", "").matches("(\\.(onion|top|xyz|cc)$)")', '2025-09-04 21:17:13.68444', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1445, 'Windows: Unusual Network Connection via DllHost or via RunDLL32', 2, 3, 2, 'Defense Evasion', '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/"]', 'safe("log.eventDataProcessName", "").matches("(dllhost.exe|rundll32.exe)") && safe("log.origin.ips", "").matches("((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)")', '2025-09-04 21:01:34.94437', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1446, 'Windows: Unusual Print Spooler Child Process', 1, 3, 2, 'Privilege Escalation', '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/"]', 'safe("log.eventDataParentProcessName", "").contains("spoolsv.exe") && !safe("log.eventDataProcessName", "").matches("(splwow64.exe|PDFCreator.exe|acrodist.exe|spoolsv.exe|msiexec.exe|route.exe|WerFault.exe|net.exe|cmd.exe|powershell.exe|netsh.exe|regsvr32.exe)") && !safe("log.message", "").matches("(\\WINDOWS\\system32\\spool\\DRIVERS|stop|start|.spl|\\program files(.+)route add|add portopening|rule name|PrintConfig.dll)") && safe("log.logName", "") == ''System''', '2025-09-04 21:01:35.493758', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1447, '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/"]', 'safe("log.eventDataProcessName", "").matches("(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)")', '2025-09-04 21:01:36.027342', true, false, 'origin', NULL, '[{"indexPattern":"v11-log-wineventlog-*","with":[{"field":"log.winlogEventDataProcessID.keyword","operator":"filter_term","value":"{{.log.winlogEventDataProcessID}}"}],"or":null,"within":"now-5m","count":3}]'); -INSERT INTO public.utm_correlation_rules VALUES (1483, 'Windows: Probable replay attack', 1, 3, 2, 'Impact', 'Replay attack', 'This event generates on domain controllers when KRB_AP_ERR_REPEAT Kerberos response was sent to the client. Domain controllers cache information from recently received tickets. If the server name, client name, time, and microsecond fields from the Authenticator match recently seen entries in the cache, it will return KRB_AP_ERR_REPEAT. You can read more about this in RFC-1510. One potential cause for this is a misconfigured network device between the client and server that could send the same packet(s) repeatedly.', '["https://attack.mitre.org/tactics/TA0040","https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4649"]', 'safe("log.eventCode", 0.0) in [4961, 4649]', '2025-09-04 21:15:02.484678', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (349, 'Firewall license expiration detected in Sophos Central', 2, 2, 2, 'Expiration', 'Obtain Capabilities', 'Your Central Firewall Reporting license has expired.', '["https://support.sophos.com/support/s/article/KB-000037006?language=en_US"]', 'safe("log.type", "") in [''Event::Firewall::ReportingGracePeriodStart'', ''Event::Firewall::ReportingXgsLicenseExpired'', ''Event::Firewall::ReportingGracePeriodExtension'']', '2025-09-03 15:00:17.82542', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1483, 'Windows: Probable replay attack', 1, 3, 2, 'Impact', 'Replay attack', 'This event generates on domain controllers when KRB_AP_ERR_REPEAT Kerberos response was sent to the client. Domain controllers cache information from recently received tickets. If the server name, client name, time, and microsecond fields from the Authenticator match recently seen entries in the cache, it will return KRB_AP_ERR_REPEAT. You can read more about this in RFC-1510. One potential cause for this is a misconfigured network device between the client and server that could send the same packet(s) repeatedly.', '["https://attack.mitre.org/tactics/TA0040","https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4649"]', 'safe("log.eventCode", 0.0) in [double(4961), double(4649)]', '2025-09-05 22:32:29.534424', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (253, 'Possible Consent Grant Attack via Azure-Registered Application', 1, 2, 3, 'Phishing', 'Credential Access', 'Detects when a user grants permissions to an Azure-registered application or when an administrator grants tenant-wide permissions to an application. An adversary may create an Azure-registered application that requests access to data such as contact information, email, or documents.', '["https://attack.mitre.org/techniques/T1566/"]', 'safe("log.operationName.localizedValue", "").contains("Consent to application") && safe("log.statusValue", "").contains("Succeeded") && (safe("log.resourceProviderNameValue", "").contains("azure.activitylogs") || safe("log.resourceProviderNameValue", "").contains("azure.auditlogs"))', '2025-09-05 14:16:11.467718', true, false, 'target', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (129, 'AWS RDS Security Group Creation', 3, 2, 2, 'Create Account', 'Persistence', 'Identifies the creation of an Amazon Relational Database Service (RDS) Security Group', '["https://attack.mitre.org/tactics/TA0003/","https://attack.mitre.org/techniques/T1136/","https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_CreateDBSecurityGroup.html"]', 'safe("log.eventSource", "") == "rds.amazonaws.com" && safe("log.eventName", "") == "CreateDBSecurityGroup"', '2025-09-03 12:39:13.575225', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1219, 'High level Suricata alert', 3, 3, 3, 'High level Suricata alert category', 'Network Intrusion Detection', 'Suricata has detected a high severity alert. This indicates potential malicious activity targeting the network infrastructure, such as exploitation attempts, malware communication, or suspicious network behavior.', '["https://suricata.readthedocs.io/en/latest/"]', 'safe("log.eventType", "") == "alert" && safe("severity", "") == "high"', '2025-09-04 19:09:15.535139', true, false, 'origin', NULL, '[]'); -INSERT INTO public.utm_correlation_rules VALUES (1495, 'Windows: Suspicious Print Spooler SPL File Created', 1, 3, 2, 'Privilege Escalation', '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/"]', '!safe("log.winlogEventDataProcessName", "").matches("(spoolsv.exe|printfilterpipelinesvc.exe|PrintIsolationHost.exe|splwow64.exe|msiexec.exe|poqexec.exe)") && safe("log.winlogEventDataProcessName", "").matches("(:\\Windows\\System32\\spool\\PRINTERS\\)") -', '2025-09-04 21:16:06.604788', true, false, 'origin', NULL, '[]'); +INSERT INTO public.utm_correlation_rules VALUES (1219, 'High level Suricata alert', 3, 3, 3, 'High level Suricata alert category', 'Network Intrusion Detection', 'Suricata has detected a high severity alert. This indicates potential malicious activity targeting the network infrastructure, such as exploitation attempts, malware communication, or suspicious network behavior.', '["https://suricata.readthedocs.io/en/latest/"]', 'safe("log.eventType", "") == "alert" && safe("severity", "") == "high"', '2025-09-05 23:20:01.223803', true, false, 'origin', NULL, '[]'); INSERT INTO public.utm_correlation_rules VALUES (1424, 'Windows: Suspicious Process Execution via Renamed PsExec Executable', 1, 3, 2, 'Execution', '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/"]', 'safe("log.winlogEventDataEventType", "") == "start" && safe("log.winlogEventDataProcessName", "").contains("PSEXESVC.exe") || safe("log.winlogEventDataOriginalFileName", "").contains("psexesvc.exe")', '2025-09-04 20:59:24.777522', true, false, 'target', NULL, '[]'); - --- --- Name: utm_correlation_rules_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres --- - -SELECT pg_catalog.setval('public.utm_correlation_rules_id_seq', 1504, true); - - --- --- PostgreSQL database dump complete --- - diff --git a/backend/src/main/resources/config/liquibase/data/utm_group_rules_data_type.sql b/backend/src/main/resources/config/liquibase/data/utm_group_rules_data_type.sql index a238c7b4b..52a1e36ae 100644 --- a/backend/src/main/resources/config/liquibase/data/utm_group_rules_data_type.sql +++ b/backend/src/main/resources/config/liquibase/data/utm_group_rules_data_type.sql @@ -1,3 +1,4 @@ + INSERT INTO public.utm_group_rules_data_type VALUES (1, 38, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (2, 38, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (3, 38, NULL); @@ -81,7 +82,6 @@ INSERT INTO public.utm_group_rules_data_type VALUES (80, 29, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (81, 29, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (82, 29, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (83, 29, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (84, 29, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (85, 29, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (86, 29, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (87, 29, NULL); @@ -250,8 +250,6 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1247, 11, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1248, 11, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1249, 11, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1250, 11, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (1219, 49, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (1220, 49, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1251, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1252, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1253, 1, NULL); @@ -290,7 +288,6 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1295, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1296, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1297, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1298, 1, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (1299, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1300, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1301, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1302, 1, NULL); @@ -320,7 +317,6 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1325, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1326, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1327, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1328, 1, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (1329, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1330, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1331, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1332, 1, NULL); @@ -329,7 +325,6 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1334, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1335, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1336, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1337, 1, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (1338, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1339, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1340, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1341, 1, NULL); @@ -386,7 +381,6 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1363, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1364, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1365, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1366, 1, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (1367, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1368, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1369, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1370, 1, NULL); @@ -417,7 +411,6 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1394, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1395, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1396, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1397, 1, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (1398, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1399, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1400, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1401, 1, NULL); @@ -460,7 +453,6 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1438, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1439, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1440, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1441, 1, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (1442, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1443, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1444, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1445, 1, NULL); @@ -505,7 +497,6 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1483, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1484, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1485, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1486, 1, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (1487, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1488, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1489, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1490, 1, NULL); @@ -513,7 +504,6 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1491, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1492, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1493, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1494, 1, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (1495, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1496, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1497, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1498, 1, NULL); @@ -523,6 +513,8 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1501, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1502, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1503, 1, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1504, 1, NULL); +INSERT INTO public.utm_group_rules_data_type VALUES (1220, 5004, NULL); +INSERT INTO public.utm_group_rules_data_type VALUES (1219, 5004, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (212, 13, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (213, 13, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (214, 13, NULL); @@ -1187,56 +1179,11 @@ INSERT INTO public.utm_group_rules_data_type VALUES (883, 42, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (884, 42, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (885, 42, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (886, 42, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (887, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (888, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (889, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (890, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (891, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (892, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (893, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (894, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (895, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (896, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (897, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (898, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (899, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (900, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (901, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (902, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (903, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (904, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (905, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (906, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (907, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (908, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (909, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (910, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (911, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (912, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (913, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (914, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (915, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (916, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (917, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (918, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (919, 26, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (920, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (921, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (922, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (923, 26, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (924, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (925, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (926, 26, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (927, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (928, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (929, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (930, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (931, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (932, 26, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (933, 26, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (934, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (935, 26, NULL); -INSERT INTO public.utm_group_rules_data_type VALUES (936, 26, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (937, 26, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (938, 26, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (939, 26, NULL); @@ -1479,7 +1426,3 @@ INSERT INTO public.utm_group_rules_data_type VALUES (1177, 43, NULL); INSERT INTO public.utm_group_rules_data_type VALUES (1178, 43, NULL); --- --- PostgreSQL database dump complete --- - diff --git a/backend/src/main/resources/config/liquibase/master.xml b/backend/src/main/resources/config/liquibase/master.xml index 43933f755..33736d9dc 100644 --- a/backend/src/main/resources/config/liquibase/master.xml +++ b/backend/src/main/resources/config/liquibase/master.xml @@ -249,7 +249,7 @@ - + From 7ed3bd3574baf921152be00778928767d54c92f8 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 8 Sep 2025 10:43:44 -0500 Subject: [PATCH 018/422] refactor(util): rename UtilResponse to ResponseUtil and update references --- backend/pom.xml | 7 +-- .../advice/GlobalExceptionHandler.java | 50 +++++++++++++++++++ .../aop/logging/ControllerTracingAspect.java | 30 +++++++++++ .../utmstack/loggin/LoggingContextFilter.java | 10 ++-- .../{UtilResponse.java => ResponseUtil.java} | 2 +- .../utmstack/web/rest/UserJWTController.java | 43 ++++++++-------- .../utmstack/web/rest/UtmAlertResource.java | 4 +- .../UtmConfigurationParameterResource.java | 17 +++---- .../rest/UtmConfigurationSectionResource.java | 4 +- .../utmstack/web/rest/UtmImagesResource.java | 9 ++-- .../utmstack/web/rest/UtmStackResource.java | 16 ++---- .../agent_manager/AgentManagerResource.java | 10 ++-- ...UtmAlertResponseRuleExecutionResource.java | 4 +- .../UtmAlertResponseRuleResource.java | 18 +++---- .../UtmModuleResource.java | 20 ++++---- .../UtmVisualizationResource.java | 7 ++- .../rest/collectors/UtmCollectorResource.java | 17 +++---- .../UtmComplianceReportScheduleResource.java | 15 +++--- .../UtmComplianceReportConfigResource.java | 7 ++- .../config/UtmDataTypesResource.java | 20 ++++---- .../config/UtmRegexPatternResource.java | 17 +++---- .../config/UtmTenantConfigResource.java | 17 +++---- .../rules/UtmCorrelationRulesResource.java | 26 +++++----- .../elasticsearch/ElasticsearchResource.java | 20 ++++---- .../rest/incident/UtmIncidentResource.java | 13 +++-- .../index_policy/IndexPolicyResource.java | 6 +-- .../UtmLogstashPipelineResource.java | 14 +++--- .../network_scan/UtmNetworkScanResource.java | 5 +- .../utmstack/web/rest/tfa/TfaController.java | 13 ++--- .../user_auditor/UtmAuditorUsersResource.java | 6 +-- .../web/rest/util/PdfGeneratorResource.java | 5 +- 31 files changed, 252 insertions(+), 200 deletions(-) create mode 100644 backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java create mode 100644 backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java rename backend/src/main/java/com/park/utmstack/util/{UtilResponse.java => ResponseUtil.java} (98%) diff --git a/backend/pom.xml b/backend/pom.xml index 87bf8a432..74fba88b4 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -14,7 +14,7 @@ 3.3.9 - 11 + 17 UTF-8 UTF-8 yyyyMMddHHmmss @@ -33,7 +33,7 @@ 7.3.1 - 2.5.5 + 3.1.5 5.4.32.Final @@ -376,9 +376,6 @@ 3.0.5 - - - diff --git a/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java b/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java new file mode 100644 index 000000000..364ab399c --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java @@ -0,0 +1,50 @@ +package com.park.utmstack.advice; + + +import com.park.utmstack.domain.application_events.enums.ApplicationEventType; +import com.park.utmstack.security.TooMuchLoginAttemptsException; +import com.park.utmstack.service.application_events.ApplicationEventService; +import com.park.utmstack.util.ResponseUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@Slf4j +@RestControllerAdvice +@RequiredArgsConstructor +public class GlobalExceptionHandler { + + private final ApplicationEventService applicationEventService; + + + @ExceptionHandler(BadCredentialsException.class) + public ResponseEntity handleForbidden(BadCredentialsException e) { + String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); + log.error(msg, e); + + return ResponseUtil.buildUnauthorizedResponse(msg); + } + + @ExceptionHandler(TooMuchLoginAttemptsException.class) + public ResponseEntity handleTo(BadCredentialsException e) { + String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); + log.error(msg, e); + + return ResponseUtil.buildUnauthorizedResponse(msg); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleGenericException(Exception e) { + String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); + log.error(msg, e); + + return ResponseUtil.buildInternalServerErrorResponse(msg); + } + + + +} diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java new file mode 100644 index 000000000..50fc21ba1 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java @@ -0,0 +1,30 @@ +package com.park.utmstack.aop.logging; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class ControllerTracingAspect { + + @Around("within(@org.springframework.web.bind.annotation.RestController *)") + public Object enrichMDC(ProceedingJoinPoint joinPoint) throws Throwable { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + String context = signature.getDeclaringType().getSimpleName() + "." + signature.getMethod().getName(); + MDC.put("context", context); + /* String username = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) + .map(Authentication::getName) + .orElse("anonymous"); + MDC.put("username", username);*/ + try { + return joinPoint.proceed(); + } finally { + MDC.clear(); + } + } +} + diff --git a/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java b/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java index 681b694bd..c2507c882 100644 --- a/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java +++ b/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java @@ -1,6 +1,8 @@ package com.park.utmstack.loggin; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import net.logstash.logback.argument.StructuredArguments; import org.slf4j.MDC; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -15,7 +17,8 @@ import java.util.Optional; import java.util.UUID; -@Component +/*@Component*/ +@Slf4j public class LoggingContextFilter extends OncePerRequestFilter { @Override @@ -23,6 +26,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + MDC.clear(); try { String username = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) @@ -36,12 +40,12 @@ protected void doFilterInternal(HttpServletRequest request, args.put("method", request.getMethod()); args.put("remoteAddr", request.getRemoteAddr()); - MDC.put("args", new ObjectMapper().writeValueAsString(args)); + log.debug("Request info: {}", StructuredArguments.keyValue("args", args)); filterChain.doFilter(request, response); } finally { MDC.clear(); } } -} +}/**/ diff --git a/backend/src/main/java/com/park/utmstack/util/UtilResponse.java b/backend/src/main/java/com/park/utmstack/util/ResponseUtil.java similarity index 98% rename from backend/src/main/java/com/park/utmstack/util/UtilResponse.java rename to backend/src/main/java/com/park/utmstack/util/ResponseUtil.java index d5d1862be..0da68983d 100644 --- a/backend/src/main/java/com/park/utmstack/util/UtilResponse.java +++ b/backend/src/main/java/com/park/utmstack/util/ResponseUtil.java @@ -4,7 +4,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -public class UtilResponse { +public class ResponseUtil { public static ResponseEntity buildErrorResponse(HttpStatus errorCode, String msg) { return ResponseEntity.status(errorCode) .headers(HeaderUtil.createFailureAlert("", "", diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java index 7be824025..7d23d5418 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java @@ -1,29 +1,25 @@ package com.park.utmstack.web.rest; -import com.fasterxml.jackson.annotation.JsonProperty; import com.park.utmstack.config.Constants; -import com.park.utmstack.domain.Authority; import com.park.utmstack.domain.User; import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.domain.federation_service.UtmFederationServiceClient; -import com.park.utmstack.domain.tfa.TfaMethod; import com.park.utmstack.repository.federation_service.UtmFederationServiceClientRepository; import com.park.utmstack.security.TooMuchLoginAttemptsException; import com.park.utmstack.security.jwt.JWTFilter; import com.park.utmstack.security.jwt.TokenProvider; -import com.park.utmstack.service.MailService; import com.park.utmstack.service.UserService; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.dto.jwt.JWTToken; import com.park.utmstack.service.dto.jwt.LoginResponseDTO; import com.park.utmstack.service.login_attempts.LoginAttemptService; -import com.park.utmstack.service.tfa.EmailTotpService; import com.park.utmstack.service.tfa.TfaService; import com.park.utmstack.util.CipherUtil; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.util.exceptions.InvalidConnectionKeyException; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.vm.LoginVM; +import net.logstash.logback.argument.StructuredArguments; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; @@ -33,16 +29,14 @@ import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.util.Base64Utils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; -import java.util.List; -import java.util.stream.Collectors; +import java.util.Map; /** * Controller to authenticate users. @@ -79,7 +73,7 @@ public UserJWTController(TokenProvider tokenProvider, } @PostMapping("/authenticate") - public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM) { + public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletRequest request) { final String ctx = CLASSNAME + ".authorize"; try { if (loginAttemptService.isBlocked()) @@ -107,21 +101,24 @@ public ResponseEntity authorize(@Valid @RequestBody LoginVM lo .success(true) .tfaRequired(isTfaEnabled) .build(), HttpStatus.OK); - } catch (BadCredentialsException e) { + } /*catch (BadCredentialsException e) { String msg = ctx + ": " + e.getMessage(); - log.error(msg); - applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildUnauthorizedResponse(msg); - } catch (TooMuchLoginAttemptsException e) { - String msg = ctx + ": " + e.getMessage(); - log.error(msg); + Map args = Map.of( + "method", request.getMethod(), + "path", request.getRequestURI(), + "remoteAddr", request.getRemoteAddr() + ); + + log.error("Authentication failure: {},", msg, StructuredArguments.keyValue("args", args)); + log.error("Authentication failure: {}, {}", msg, StructuredArguments.keyValue("args", args)); + applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildLockedResponse(msg); - } catch (Exception e) { + return ResponseUtil.buildUnauthorizedResponse(msg); + }*/ catch (TooMuchLoginAttemptsException e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildLockedResponse(msg); } } @@ -181,12 +178,12 @@ public ResponseEntity authorizeFederationServiceManager(@Valid @Reques String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildBadRequestResponse(msg); + return ResponseUtil.buildBadRequestResponse(msg); } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildInternalServerErrorResponse(msg); } } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java b/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java index 6c14e09d8..bc8bf6a0c 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java @@ -4,7 +4,7 @@ import com.park.utmstack.service.UtmAlertService; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.util.AlertUtil; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.util.HeaderUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -108,7 +108,7 @@ public ResponseEntity countOpenAlerts() { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UtmConfigurationParameterResource.java b/backend/src/main/java/com/park/utmstack/web/rest/UtmConfigurationParameterResource.java index 5f16bd876..5360dbc7d 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UtmConfigurationParameterResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UtmConfigurationParameterResource.java @@ -9,7 +9,7 @@ import com.park.utmstack.service.dto.UtmConfigurationParameterCriteria; import com.park.utmstack.service.mail_config.MailConfigService; import com.park.utmstack.service.validators.email.EmailValidatorService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.util.exceptions.UtmMailException; import com.park.utmstack.web.rest.util.PaginationUtil; import org.slf4j.Logger; @@ -25,7 +25,6 @@ import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import javax.mail.MessagingException; import javax.validation.Valid; @@ -85,7 +84,7 @@ public ResponseEntity updateConfigurationParameters(@Valid @RequestBody Li String msg = String.format("Validation failed for field %s.", parameter.getConfParamShort()); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildPreconditionFailedResponse(msg); + return ResponseUtil.buildPreconditionFailedResponse(msg); } } } @@ -95,17 +94,17 @@ public ResponseEntity updateConfigurationParameters(@Valid @RequestBody Li String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildPreconditionFailedResponse(msg); + return ResponseUtil.buildPreconditionFailedResponse(msg); } catch (IllegalArgumentException e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildBadRequestResponse(msg); + return ResponseUtil.buildBadRequestResponse(msg); } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildInternalServerErrorResponse(msg); } } @@ -135,7 +134,7 @@ public ResponseEntity> getAllUtmConfigurationPar public ResponseEntity getUtmConfigurationParameter(@PathVariable Long id) { log.debug("REST request to get UtmConfigurationParameter : {}", id); Optional utmConfigurationParameter = utmConfigurationParameterService.findOne(id); - return ResponseUtil.wrapOrNotFound(utmConfigurationParameter); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(utmConfigurationParameter); } @PostMapping ("/checkEmailConfiguration") @@ -148,12 +147,12 @@ public ResponseEntity checkEmailConfiguration(@Valid @RequestBody List> getConfigurationSections(@P final String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildInternalServerErrorResponse(msg); } } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UtmImagesResource.java b/backend/src/main/java/com/park/utmstack/web/rest/UtmImagesResource.java index d45720ba5..3deddc470 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UtmImagesResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UtmImagesResource.java @@ -6,14 +6,13 @@ import com.park.utmstack.repository.UtmImagesRepository; import com.park.utmstack.service.UtmImagesService; import com.park.utmstack.service.application_events.ApplicationEventService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.util.HeaderUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import javax.validation.Valid; import java.util.List; @@ -50,7 +49,7 @@ public ResponseEntity updateImage(@Valid @RequestBody UtmImages image Optional imageOpt = imagesRepository.findById(image.getShortName()); if (imageOpt.isEmpty()) - return UtilResponse.buildBadRequestResponse("Image short name not recognized: " + image.getShortName()); + return ResponseUtil.buildBadRequestResponse("Image short name not recognized: " + image.getShortName()); UtmImages img = imageOpt.get(); img.setUserImg(image.getUserImg()); @@ -59,7 +58,7 @@ public ResponseEntity updateImage(@Valid @RequestBody UtmImages image String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildInternalServerErrorResponse(msg); } } @@ -82,7 +81,7 @@ public ResponseEntity getImage(@PathVariable ImageShortName shortName final String ctx = CLASSNAME + ".getImage"; try { Optional utmImages = utmImagesService.findOne(shortName); - return ResponseUtil.wrapOrNotFound(utmImages); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(utmImages); } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UtmStackResource.java b/backend/src/main/java/com/park/utmstack/web/rest/UtmStackResource.java index 4c3f03825..c13feb28e 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UtmStackResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UtmStackResource.java @@ -2,15 +2,12 @@ import com.park.utmstack.config.Constants; -import com.park.utmstack.domain.UtmConfigurationParameter; import com.park.utmstack.domain.application_events.enums.ApplicationEventType; -import com.park.utmstack.domain.mail_sender.MailConfig; import com.park.utmstack.service.UtmConfigurationParameterService; import com.park.utmstack.service.UtmStackService; import com.park.utmstack.service.application_events.ApplicationEventService; -import com.park.utmstack.service.mail_config.MailConfigService; import com.park.utmstack.util.CipherUtil; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.actuate.info.InfoEndpoint; @@ -19,9 +16,6 @@ import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; -import javax.mail.MessagingException; -import javax.validation.Valid; -import java.util.List; import java.util.Map; /** @@ -64,7 +58,7 @@ public ResponseEntity> dateFormat() { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -78,7 +72,7 @@ public ResponseEntity healthCheck() { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -91,7 +85,7 @@ public ResponseEntity isInDevelop() { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -106,7 +100,7 @@ public ResponseEntity encrypt(@RequestBody String str) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/agent_manager/AgentManagerResource.java b/backend/src/main/java/com/park/utmstack/web/rest/agent_manager/AgentManagerResource.java index 42ef11832..c0c28f94d 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/agent_manager/AgentManagerResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/agent_manager/AgentManagerResource.java @@ -6,7 +6,7 @@ import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.dto.agent_manager.*; import com.park.utmstack.service.incident_response.UtmIncidentVariableService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.application_modules.UtmModuleResource; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.vm.AgentRequestVM; @@ -64,7 +64,7 @@ public ResponseEntity> listAgents( String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -88,7 +88,7 @@ public ResponseEntity> listAgentsWithCommands() { String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -105,7 +105,7 @@ public ResponseEntity getAgentByHostname( String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -140,7 +140,7 @@ public ResponseEntity> listAgentCommands( String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/alert_response_rule/UtmAlertResponseRuleExecutionResource.java b/backend/src/main/java/com/park/utmstack/web/rest/alert_response_rule/UtmAlertResponseRuleExecutionResource.java index 23b8207ec..ebf85b2f4 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/alert_response_rule/UtmAlertResponseRuleExecutionResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/alert_response_rule/UtmAlertResponseRuleExecutionResource.java @@ -5,7 +5,7 @@ import com.park.utmstack.service.alert_response_rule.UtmAlertResponseRuleExecutionQueryService; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.dto.UtmAlertResponseRuleExecutionCriteria; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.util.PaginationUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +46,7 @@ public ResponseEntity> getAllAlertResponseRu String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildInternalServerErrorResponse(msg); } } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/alert_response_rule/UtmAlertResponseRuleResource.java b/backend/src/main/java/com/park/utmstack/web/rest/alert_response_rule/UtmAlertResponseRuleResource.java index a2a69bb67..571d1635f 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/alert_response_rule/UtmAlertResponseRuleResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/alert_response_rule/UtmAlertResponseRuleResource.java @@ -11,7 +11,7 @@ import com.park.utmstack.service.dto.UtmAlertResponseActionTemplateDTO; import com.park.utmstack.service.dto.UtmAlertResponseRuleCriteria; import com.park.utmstack.service.dto.UtmAlertResponseRuleDTO; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.util.PaginationUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,14 +58,14 @@ public ResponseEntity createAlertResponseRule(@Valid @R String msg = ctx + ": A new rule cannot already have an ID"; log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } return ResponseEntity.ok(new UtmAlertResponseRuleDTO(alertResponseRuleService.save(new UtmAlertResponseRule(dto)))); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -77,14 +77,14 @@ public ResponseEntity updateAlertResponseRule(@Valid @R String msg = ctx + ": The rule you are trying to update does not have a valid ID"; log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } return ResponseEntity.ok(new UtmAlertResponseRuleDTO(alertResponseRuleService.save(new UtmAlertResponseRule(dto)))); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -100,7 +100,7 @@ public ResponseEntity> getAllAlertResponseRules(@P String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -117,7 +117,7 @@ public ResponseEntity> getAllAlertRespon String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -130,7 +130,7 @@ public ResponseEntity getAlertResponseRule(@PathVariabl String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -143,7 +143,7 @@ public ResponseEntity>> resolveFilterValues() { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/application_modules/UtmModuleResource.java b/backend/src/main/java/com/park/utmstack/web/rest/application_modules/UtmModuleResource.java index 868d18800..3042f1810 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/application_modules/UtmModuleResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/application_modules/UtmModuleResource.java @@ -14,7 +14,7 @@ import com.park.utmstack.event_processor.EventProcessorManagerService; import com.park.utmstack.service.dto.application_modules.ModuleDTO; import com.park.utmstack.service.dto.application_modules.UtmModuleCriteria; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.util.PaginationUtil; import lombok.RequiredArgsConstructor; import org.slf4j.Logger; @@ -59,7 +59,7 @@ public ResponseEntity activateDeactivate(@RequestParam Long serverId, String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -81,7 +81,7 @@ public ResponseEntity> getAllUtmModules(UtmModuleCriteria criter String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -94,7 +94,7 @@ public ResponseEntity getModuleById(@PathVariable Long id) { String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -108,7 +108,7 @@ public ResponseEntity getModuleDetails(@RequestParam Long serverId, String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -123,7 +123,7 @@ public ResponseEntity getModuleDetailsDecrypted(@RequestParam ModuleN String msg = ctx + ": You must provide the header used to communicate internally with this resource"; log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } return ResponseEntity.ok(module); @@ -131,7 +131,7 @@ public ResponseEntity getModuleDetailsDecrypted(@RequestParam ModuleN String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -158,7 +158,7 @@ public ResponseEntity checkRequirements(@RequestParam String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -171,7 +171,7 @@ public ResponseEntity> getModuleCategories(@RequestParam(required = String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -184,7 +184,7 @@ public ResponseEntity isActive(@RequestParam ModuleName moduleName) { String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/chart_builder/UtmVisualizationResource.java b/backend/src/main/java/com/park/utmstack/web/rest/chart_builder/UtmVisualizationResource.java index 100bbd705..d34e067ef 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/chart_builder/UtmVisualizationResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/chart_builder/UtmVisualizationResource.java @@ -10,7 +10,7 @@ import com.park.utmstack.service.chart_builder.UtmVisualizationService; import com.park.utmstack.service.dto.chart_builder.UtmVisualizationCriteria; import com.park.utmstack.service.elasticsearch.ElasticsearchService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.util.chart_builder.elasticsearch_dsl.requests.RequestDsl; import com.park.utmstack.util.chart_builder.elasticsearch_dsl.responses.ResponseParser; import com.park.utmstack.util.chart_builder.elasticsearch_dsl.responses.ResponseParserFactory; @@ -31,7 +31,6 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import javax.validation.Valid; import java.net.URI; @@ -238,7 +237,7 @@ public ResponseEntity getUtmVisualization(@PathVariable Long i final String ctx = CLASSNAME + ".getUtmVisualization"; try { Optional utmVisualization = visualizationService.findOne(id); - return ResponseUtil.wrapOrNotFound(utmVisualization); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(utmVisualization); } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); @@ -306,7 +305,7 @@ public ResponseEntity> run(@RequestBody UtmVisualization visualization, String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/collectors/UtmCollectorResource.java b/backend/src/main/java/com/park/utmstack/web/rest/collectors/UtmCollectorResource.java index 84b2f3d0c..84e438923 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/collectors/UtmCollectorResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/collectors/UtmCollectorResource.java @@ -19,7 +19,7 @@ import com.park.utmstack.service.dto.collectors.dto.ErrorResponse; import com.park.utmstack.service.dto.collectors.dto.ListCollectorsResponseDTO; import com.park.utmstack.service.dto.network_scan.AssetGroupDTO; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.errors.BadRequestAlertException; import com.park.utmstack.web.rest.errors.InternalServerErrorException; import com.park.utmstack.web.rest.network_scan.UtmNetworkScanResource; @@ -35,7 +35,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; @@ -143,12 +142,12 @@ public ResponseEntity listCollectorsByModule(@Request String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (CollectorServiceGrpcException e) { String msg = ctx + ": UtmCollector manager is not available or was an error getting the collector list. " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_GATEWAY, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_GATEWAY, msg); } } @@ -180,12 +179,12 @@ public ResponseEntity listCollectorHostNames(@RequestParam(r String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (CollectorServiceGrpcException e) { String msg = ctx + ": UtmCollector manager is not available or the parameters are wrong, please check." + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_GATEWAY, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_GATEWAY, msg); } } @@ -208,12 +207,12 @@ public ResponseEntity listCollectorByHostNameAndModul String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (CollectorServiceGrpcException e) { String msg = ctx + ": UtmCollector manager is not available or was an error getting configuration. " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_GATEWAY, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_GATEWAY, msg); } } @@ -364,7 +363,7 @@ private ErrorResponse getError(Exception e, CollectorConfig cacheConfig) { private ResponseEntity logAndResponse(ErrorResponse error) { log.error(error.getMessage()); applicationEventService.createEvent(error.getMessage(), ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(error.getStatus(), error.getMessage()); + return ResponseUtil.buildErrorResponse(error.getStatus(), error.getMessage()); } private void upsert(CollectorConfigKeysDTO collectorConfig) throws Exception { diff --git a/backend/src/main/java/com/park/utmstack/web/rest/compliance/UtmComplianceReportScheduleResource.java b/backend/src/main/java/com/park/utmstack/web/rest/compliance/UtmComplianceReportScheduleResource.java index 8f2eeee7d..c67e66573 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/compliance/UtmComplianceReportScheduleResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/compliance/UtmComplianceReportScheduleResource.java @@ -6,7 +6,7 @@ import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.compliance.UtmComplianceReportScheduleService; import com.park.utmstack.service.dto.compliance.UtmComplianceReportScheduleCriteria; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.errors.BadRequestAlertException; import java.net.URISyntaxException; @@ -23,7 +23,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import javax.validation.Valid; @@ -82,7 +81,7 @@ public ResponseEntity createUtmComplianceReportSche String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -119,7 +118,7 @@ public ResponseEntity updateUtmComplianceReportSche String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -140,7 +139,7 @@ public ResponseEntity> getAllUtmComplianceRepo String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -155,12 +154,12 @@ public ResponseEntity getUtmComplianceReportSchedul final String ctx = CLASSNAME + ".getUtmComplianceReportScheduleById"; try { log.debug("REST request to get a UtmComplianceReportSchedule by id"); - return ResponseUtil.wrapOrNotFound(utmComplianceReportScheduleService.findOne(id)); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(utmComplianceReportScheduleService.findOne(id)); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -181,7 +180,7 @@ public ResponseEntity deleteUtmComplianceReportSchedule(@PathVariable Long String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/compliance/config/UtmComplianceReportConfigResource.java b/backend/src/main/java/com/park/utmstack/web/rest/compliance/config/UtmComplianceReportConfigResource.java index 6c0de0b6a..acf3b2e63 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/compliance/config/UtmComplianceReportConfigResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/compliance/config/UtmComplianceReportConfigResource.java @@ -10,7 +10,7 @@ import com.park.utmstack.service.compliance.config.UtmComplianceStandardSectionService; import com.park.utmstack.service.compliance.config.UtmComplianceStandardService; import com.park.utmstack.service.dto.compliance.UtmComplianceReportConfigCriteria; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.util.PaginationUtil; import org.slf4j.Logger; @@ -21,7 +21,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import javax.validation.Valid; import javax.validation.constraints.NotNull; @@ -143,7 +142,7 @@ public ResponseEntity getComplianceReportConfig(@Path dashboardVisualizationService.findAllByIdDashboard(report.getDashboardId()).ifPresent(report::setDashboard); return ResponseEntity.ok(report); } - return ResponseUtil.wrapOrNotFound(standard); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(standard); } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); @@ -201,7 +200,7 @@ public ResponseEntity importReports(@Valid @RequestBody ImportReportsBody String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmDataTypesResource.java b/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmDataTypesResource.java index 578fcf5cb..02ad3c5f8 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmDataTypesResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmDataTypesResource.java @@ -4,8 +4,7 @@ import com.park.utmstack.domain.correlation.config.UtmDataTypes; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.correlation.config.UtmDataTypesService; -import com.park.utmstack.util.UtilResponse; -import com.park.utmstack.web.rest.errors.BadRequestAlertException; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.util.PaginationUtil; import io.undertow.util.BadRequestException; @@ -17,7 +16,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import javax.validation.Valid; import java.util.List; @@ -55,12 +53,12 @@ public ResponseEntity addDataType(@Valid @RequestBody UtmDataTypes dataTyp String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -80,12 +78,12 @@ public ResponseEntity updateDataTypes(@Valid @RequestBody UtmDataTypes dat String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -102,7 +100,7 @@ public ResponseEntity updateDataTypesList(@Valid @RequestBody List removeDataTypes(@PathVariable Long id) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -163,6 +161,6 @@ public ResponseEntity> getAllDataTypes(@RequestParam(required public ResponseEntity getDataType(@PathVariable Long id) { log.debug("REST request to get UtmDataTypes : {}", id); Optional datatype = dataTypesService.findOne(id); - return ResponseUtil.wrapOrNotFound(datatype); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(datatype); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmRegexPatternResource.java b/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmRegexPatternResource.java index 38cbf1bdb..5528f8699 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmRegexPatternResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmRegexPatternResource.java @@ -4,7 +4,7 @@ import com.park.utmstack.domain.correlation.config.UtmRegexPattern; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.correlation.config.UtmRegexPatternService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.util.PaginationUtil; import io.undertow.util.BadRequestException; @@ -16,7 +16,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import javax.validation.Valid; import java.util.List; @@ -54,12 +53,12 @@ public ResponseEntity addRegexPattern(@Valid @RequestBody UtmRegexPattern String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -79,12 +78,12 @@ public ResponseEntity updateRegexPattern(@Valid @RequestBody UtmRegexPatte String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -104,12 +103,12 @@ public ResponseEntity removeRegexPattern(@PathVariable Long id) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -145,6 +144,6 @@ public ResponseEntity> getAllRegexPatterns(@RequestParam(r public ResponseEntity getRegexPattern(@PathVariable Long id) { log.debug("REST request to get UtmRegexPattern : {}", id); Optional pattern = regexPatternService.findOne(id); - return ResponseUtil.wrapOrNotFound(pattern); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(pattern); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmTenantConfigResource.java b/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmTenantConfigResource.java index a59cee9bc..4ea70a96c 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmTenantConfigResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/correlation/config/UtmTenantConfigResource.java @@ -4,7 +4,7 @@ import com.park.utmstack.domain.correlation.config.UtmTenantConfig; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.correlation.config.UtmTenantConfigService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.util.PaginationUtil; import io.undertow.util.BadRequestException; @@ -16,7 +16,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import javax.validation.Valid; import java.util.List; @@ -54,12 +53,12 @@ public ResponseEntity addTenantConfig(@Valid @RequestBody UtmTenantConfig String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -79,12 +78,12 @@ public ResponseEntity updateTenantConfig(@Valid @RequestBody UtmTenantConf String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -104,12 +103,12 @@ public ResponseEntity removeTenantConfig(@PathVariable Long id) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -145,6 +144,6 @@ public ResponseEntity> getAllTenantConfig(@RequestParam(re public ResponseEntity getTenantConfig(@PathVariable Long id) { log.debug("REST request to get UtmTenantConfig : {}", id); Optional tenantConfig = tenantConfigService.findOne(id); - return ResponseUtil.wrapOrNotFound(tenantConfig); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(tenantConfig); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/correlation/rules/UtmCorrelationRulesResource.java b/backend/src/main/java/com/park/utmstack/web/rest/correlation/rules/UtmCorrelationRulesResource.java index 19a73ed7b..63e313c67 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/correlation/rules/UtmCorrelationRulesResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/correlation/rules/UtmCorrelationRulesResource.java @@ -10,7 +10,7 @@ import com.park.utmstack.service.dto.correlation.UtmCorrelationRulesDTO; import com.park.utmstack.service.dto.correlation.UtmCorrelationRulesMapper; import com.park.utmstack.service.dto.correlation.validators.CorrelationRuleValidator; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.errors.BadRequestAlertException; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.util.PaginationUtil; @@ -24,9 +24,7 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.WebDataBinder; import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import javax.persistence.EntityNotFoundException; import javax.validation.Valid; @@ -93,12 +91,12 @@ public ResponseEntity addCorrelationRule(@Valid @RequestBody UtmCorrelatio String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -119,12 +117,12 @@ public ResponseEntity activateOrDeactivateCorrelationRule(@RequestParam Lo String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -147,17 +145,17 @@ public ResponseEntity updateCorrelationRule(@Valid @RequestBody UtmCorrela String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (EntityNotFoundException e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.NOT_FOUND, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.NOT_FOUND, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -207,9 +205,9 @@ public ResponseEntity getRule(@PathVariable Long id) { Optional utmCorrelationRule = rulesService.findOne(id); if (utmCorrelationRule.isPresent()) { UtmCorrelationRulesDTO dto = utmCorrelationRulesMapper.toDto(utmCorrelationRule.get()); - return ResponseUtil.wrapOrNotFound(Optional.of(dto)); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(Optional.of(dto)); } else { - return ResponseUtil.wrapOrNotFound(Optional.empty()); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(Optional.empty()); } } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); @@ -236,12 +234,12 @@ public ResponseEntity removeCorrelationRule(@PathVariable Long id) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/elasticsearch/ElasticsearchResource.java b/backend/src/main/java/com/park/utmstack/web/rest/elasticsearch/ElasticsearchResource.java index 39edd2f57..923b01efb 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/elasticsearch/ElasticsearchResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/elasticsearch/ElasticsearchResource.java @@ -9,7 +9,7 @@ import com.park.utmstack.service.elasticsearch.processor.SearchResultProcessor; import com.park.utmstack.util.UtilCsv; import com.park.utmstack.util.UtilPagination; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.util.chart_builder.IndexPropertyType; import com.park.utmstack.util.chart_builder.IndexType; import com.park.utmstack.util.exceptions.OpenSearchIndexNotFoundException; @@ -64,7 +64,7 @@ public ResponseEntity> getFieldValues(@RequestParam String keyword, String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -78,7 +78,7 @@ public ResponseEntity> getFieldValuesWithCount(@Valid @Request String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -97,12 +97,12 @@ public ResponseEntity> getIndexProperties(@RequestParam String msg = ctx + ": " + e.getMessage(); log.info(msg); applicationEventService.createEvent(msg, ApplicationEventType.INFO); - return UtilResponse.buildNotFoundResponse(msg); + return ResponseUtil.buildNotFoundResponse(msg); } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildInternalServerErrorResponse(msg); } } @@ -118,7 +118,7 @@ public ResponseEntity> getAllIndexes(@RequestParam(defaultValue String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -173,7 +173,7 @@ public ResponseEntity>> search(@RequestBody(required = String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -195,7 +195,7 @@ public ResponseEntity searchToCsv(@RequestBody @Valid CsvExportingParams p String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -220,7 +220,7 @@ public ResponseEntity> genericSearch(@Valid @RequestBody GenericSearch String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -234,7 +234,7 @@ public ResponseEntity getClusterStatus() { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/incident/UtmIncidentResource.java b/backend/src/main/java/com/park/utmstack/web/rest/incident/UtmIncidentResource.java index 470e48fed..bb3126878 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/incident/UtmIncidentResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/incident/UtmIncidentResource.java @@ -7,7 +7,7 @@ import com.park.utmstack.service.incident.UtmIncidentAlertService; import com.park.utmstack.service.incident.UtmIncidentQueryService; import com.park.utmstack.service.incident.UtmIncidentService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.errors.BadRequestAlertException; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.util.PaginationUtil; @@ -20,7 +20,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import javax.validation.Valid; import java.net.URI; @@ -82,7 +81,7 @@ public ResponseEntity createUtmIncident(@Valid @RequestBody NewInci String msg = ctx + ": A new incident has to have at least one alert related"; log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.BAD_REQUEST, msg); } List alertIds = newIncidentDTO.getAlertList().stream() @@ -96,7 +95,7 @@ public ResponseEntity createUtmIncident(@Valid @RequestBody NewInci String msg = "Some alerts are already linked to another incident. Alert IDs: " + alertIdsList + ". Check the related incidents for more details."; log.error(msg); applicationEventService.createEvent(ctx + ": " + msg , ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.CONFLICT, utmIncidentAlertService.formatAlertMessage(alertsFound)); + return ResponseUtil.buildErrorResponse(HttpStatus.CONFLICT, utmIncidentAlertService.formatAlertMessage(alertsFound)); } @@ -105,7 +104,7 @@ public ResponseEntity createUtmIncident(@Valid @RequestBody NewInci String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -142,7 +141,7 @@ public ResponseEntity addAlertsToUtmIncident(@Valid @RequestBody Ad String msg = "Some alerts are already linked to another incident. Alert IDs: " + alertIdsList + ". Check the related incidents for more details."; log.error(msg); applicationEventService.createEvent(ctx + ": " + msg , ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.CONFLICT, utmIncidentAlertService.formatAlertMessage(alertsFound)); + return ResponseUtil.buildErrorResponse(HttpStatus.CONFLICT, utmIncidentAlertService.formatAlertMessage(alertsFound)); } UtmIncident result = utmIncidentService.addAlertsIncident(addToIncidentDTO); return ResponseEntity.created(new URI("/api/utm-incidents/add-alerts" + result.getId())) @@ -238,7 +237,7 @@ public ResponseEntity getUtmIncident(@PathVariable Long id) { try { log.debug("REST request to get UtmIncident : {}", id); Optional utmIncident = utmIncidentService.findOne(id); - return ResponseUtil.wrapOrNotFound(utmIncident); + return tech.jhipster.web.util.ResponseUtil.wrapOrNotFound(utmIncident); } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); diff --git a/backend/src/main/java/com/park/utmstack/web/rest/index_policy/IndexPolicyResource.java b/backend/src/main/java/com/park/utmstack/web/rest/index_policy/IndexPolicyResource.java index 73cba8d3f..06adc0cbf 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/index_policy/IndexPolicyResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/index_policy/IndexPolicyResource.java @@ -5,7 +5,7 @@ import com.park.utmstack.domain.index_policy.*; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.index_policy.IndexPolicyService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -54,7 +54,7 @@ public ResponseEntity getPolicy() { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -69,7 +69,7 @@ public ResponseEntity updateIndexPolicy(@Valid String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/logstash_pipeline/UtmLogstashPipelineResource.java b/backend/src/main/java/com/park/utmstack/web/rest/logstash_pipeline/UtmLogstashPipelineResource.java index 2aef240ee..85d172b86 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/logstash_pipeline/UtmLogstashPipelineResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/logstash_pipeline/UtmLogstashPipelineResource.java @@ -8,7 +8,7 @@ import com.park.utmstack.service.dto.logstash_pipeline.UtmLogstashPipelineDTO; import com.park.utmstack.service.logstash_pipeline.UtmLogstashPipelineService; import com.park.utmstack.service.logstash_pipeline.response.ApiStatsResponse; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.errors.BadRequestAlertException; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.vm.UtmLogstashPipelineVM; @@ -76,7 +76,7 @@ public ResponseEntity> getAllActivePipelines( String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -96,12 +96,12 @@ public ResponseEntity getLogstashStats() { String msg = ctx + ": Logstash server can't be reached, may be it's down, check the message -> " + ex.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } catch (Exception e) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -130,7 +130,7 @@ public ResponseEntity getUtmLogstashPipeline(@PathVariabl String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -155,7 +155,7 @@ public ResponseEntity validatePipelines(@RequestBody UtmLogstash String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -182,7 +182,7 @@ public ResponseEntity deleteUtmLogstashPipeline(@PathVariable Long id) { String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/network_scan/UtmNetworkScanResource.java b/backend/src/main/java/com/park/utmstack/web/rest/network_scan/UtmNetworkScanResource.java index b4437bd3a..ee8294059 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/network_scan/UtmNetworkScanResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/network_scan/UtmNetworkScanResource.java @@ -4,13 +4,12 @@ import com.park.utmstack.domain.network_scan.NetworkScanFilter; import com.park.utmstack.domain.network_scan.Property; import com.park.utmstack.domain.network_scan.UtmNetworkScan; -import com.park.utmstack.domain.network_scan.enums.PropertyFilter; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.dto.network_scan.NetworkScanDTO; import com.park.utmstack.service.dto.network_scan.UtmNetworkScanCriteria; import com.park.utmstack.service.network_scan.UtmNetworkScanQueryService; import com.park.utmstack.service.network_scan.UtmNetworkScanService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.errors.BadRequestAlertException; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.util.PaginationUtil; @@ -284,7 +283,7 @@ public ResponseEntity> getAgentsOsPlatform() { String msg = ctx + ": " + e.getMessage(); log.error(msg); eventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java b/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java index 148ab1b7b..e85ccf49a 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java @@ -1,9 +1,7 @@ package com.park.utmstack.web.rest.tfa; -import com.park.utmstack.config.Constants; import com.park.utmstack.domain.Authority; import com.park.utmstack.domain.User; -import com.park.utmstack.domain.UtmConfigurationParameter; import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.domain.tfa.TfaMethod; import com.park.utmstack.security.jwt.JWTFilter; @@ -18,7 +16,7 @@ import com.park.utmstack.service.dto.tfa.verify.TfaVerifyRequest; import com.park.utmstack.service.dto.tfa.verify.TfaVerifyResponse; import com.park.utmstack.service.tfa.TfaService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.util.exceptions.UtmMailException; import com.park.utmstack.web.rest.util.HeaderUtil; import lombok.RequiredArgsConstructor; @@ -27,7 +25,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.web.bind.annotation.*; @@ -35,8 +32,6 @@ import java.util.List; import java.util.stream.Collectors; -import static com.park.utmstack.config.Constants.PROP_TFA_METHOD; - @RestController @RequiredArgsConstructor @RequestMapping("api/tfa") @@ -110,17 +105,17 @@ public ResponseEntity completeTfa(@RequestBody TfaSaveRequest request) { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildPreconditionFailedResponse(msg); + return ResponseUtil.buildPreconditionFailedResponse(msg); } catch (IllegalArgumentException e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildBadRequestResponse(msg); + return ResponseUtil.buildBadRequestResponse(msg); } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildInternalServerErrorResponse(msg); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/user_auditor/UtmAuditorUsersResource.java b/backend/src/main/java/com/park/utmstack/web/rest/user_auditor/UtmAuditorUsersResource.java index 08ab745fa..858457d88 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/user_auditor/UtmAuditorUsersResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/user_auditor/UtmAuditorUsersResource.java @@ -2,7 +2,7 @@ import com.park.utmstack.config.Constants; import com.park.utmstack.service.dto.user_auditor.UtmAuditorUsersDTO; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.user_auditor.dto.MicroserviceRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,7 +89,7 @@ public ResponseEntity> getUtmAuditorUsers(@RequestParam String sid, } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } @@ -135,7 +135,7 @@ public ResponseEntity> getAllUtmAuditorUsers(Pageable p } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/util/PdfGeneratorResource.java b/backend/src/main/java/com/park/utmstack/web/rest/util/PdfGeneratorResource.java index 676db5cd4..85ebab44a 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/util/PdfGeneratorResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/util/PdfGeneratorResource.java @@ -1,11 +1,10 @@ package com.park.utmstack.web.rest.util; -import com.park.utmstack.config.Constants; import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.security.jwt.JWTFilter; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.service.util.PdfService; -import com.park.utmstack.util.UtilResponse; +import com.park.utmstack.util.ResponseUtil; import com.park.utmstack.web.rest.errors.BadRequestAlertException; import javassist.NotFoundException; import org.slf4j.Logger; @@ -76,7 +75,7 @@ public ResponseEntity getPdfReportInBytes(@RequestParam String url, String msg = ctx + ": " + e.getLocalizedMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); + return ResponseUtil.buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, msg); } } } From cecfad949180b0cfd49694a273a40594fc5a4820 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 8 Sep 2025 10:43:55 -0500 Subject: [PATCH 019/422] feat(logging): update JSON console appender to use LoggingEventCompositeJsonEncoder for improved log formatting --- backend/src/main/resources/logback-spring.xml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml index bcf89e606..656817603 100644 --- a/backend/src/main/resources/logback-spring.xml +++ b/backend/src/main/resources/logback-spring.xml @@ -3,11 +3,26 @@ - + + + + + timestamp + + + severity + + + msg + + + + From e12b323499e9b87129a807d3718192e2e5dcce5b Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 8 Sep 2025 13:40:01 -0500 Subject: [PATCH 020/422] feat(logging): implement MDC cleanup filter and enhance exception handling for improved logging context management --- .../advice/GlobalExceptionHandler.java | 22 ++++++- .../aop/logging/ControllerTracingAspect.java | 7 +- .../config/SecurityConfiguration.java | 11 ++++ .../loggin/filter/MdcCleanupFilter.java | 27 ++++++++ .../park/utmstack/service/UserService.java | 11 +--- .../utmstack/service/tfa/EmailTfaService.java | 2 +- .../park/utmstack/service/tfa/TfaService.java | 2 +- .../utmstack/service/tfa/TotpTfaService.java | 2 +- .../utmstack/web/rest/UserJWTController.java | 65 +++++++------------ 9 files changed, 88 insertions(+), 61 deletions(-) create mode 100644 backend/src/main/java/com/park/utmstack/loggin/filter/MdcCleanupFilter.java diff --git a/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java b/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java index 364ab399c..dfa9465db 100644 --- a/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java +++ b/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java @@ -13,6 +13,8 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import java.util.NoSuchElementException; + @Slf4j @RestControllerAdvice @RequiredArgsConstructor @@ -20,21 +22,35 @@ public class GlobalExceptionHandler { private final ApplicationEventService applicationEventService; - @ExceptionHandler(BadCredentialsException.class) public ResponseEntity handleForbidden(BadCredentialsException e) { String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); + Map args = Map.of( + "method", request.getMethod(), + "path", request.getRequestURI(), + "remoteAddr", request.getRemoteAddr() + ); + + log.error("Authentication failure: {},", msg, StructuredArguments.keyValue("args", args)); log.error(msg, e); return ResponseUtil.buildUnauthorizedResponse(msg); } @ExceptionHandler(TooMuchLoginAttemptsException.class) - public ResponseEntity handleTo(BadCredentialsException e) { + public ResponseEntity handleTooManyLoginAttempts(TooMuchLoginAttemptsException e) { String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); log.error(msg, e); - return ResponseUtil.buildUnauthorizedResponse(msg); + return ResponseUtil.buildLockedResponse(msg); + } + + @ExceptionHandler(NoSuchElementException.class) + public ResponseEntity handleNotFound(NoSuchElementException e) { + String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); + log.error(msg, e); + + return ResponseUtil.buildNotFoundResponse(msg); } @ExceptionHandler(Exception.class) diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java index 50fc21ba1..61001792b 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java @@ -20,11 +20,8 @@ public Object enrichMDC(ProceedingJoinPoint joinPoint) throws Throwable { .map(Authentication::getName) .orElse("anonymous"); MDC.put("username", username);*/ - try { - return joinPoint.proceed(); - } finally { - MDC.clear(); - } + + return joinPoint.proceed(); } } 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 0413a950e..ef197e6e5 100644 --- a/backend/src/main/java/com/park/utmstack/config/SecurityConfiguration.java +++ b/backend/src/main/java/com/park/utmstack/config/SecurityConfiguration.java @@ -1,11 +1,13 @@ package com.park.utmstack.config; +import com.park.utmstack.loggin.filter.MdcCleanupFilter; import com.park.utmstack.security.AuthoritiesConstants; import com.park.utmstack.security.internalApiKey.InternalApiKeyConfigurer; import com.park.utmstack.security.internalApiKey.InternalApiKeyProvider; import com.park.utmstack.security.jwt.JWTConfigurer; import com.park.utmstack.security.jwt.TokenProvider; import org.springframework.beans.factory.BeanInitializationException; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -73,6 +75,15 @@ public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } + @Bean + public FilterRegistrationBean mdcCleanupFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new MdcCleanupFilter()); + registrationBean.setOrder(Integer.MAX_VALUE); + registrationBean.addUrlPatterns("/*"); + return registrationBean; + } + @Bean public WebSecurityCustomizer webSecurityCustomizer() { return web -> web.ignoring() diff --git a/backend/src/main/java/com/park/utmstack/loggin/filter/MdcCleanupFilter.java b/backend/src/main/java/com/park/utmstack/loggin/filter/MdcCleanupFilter.java new file mode 100644 index 000000000..1e3f25735 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/loggin/filter/MdcCleanupFilter.java @@ -0,0 +1,27 @@ +package com.park.utmstack.loggin.filter; + +import org.jetbrains.annotations.NotNull; +import org.slf4j.MDC; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class MdcCleanupFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(@NotNull HttpServletRequest request, + @NotNull HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { + try { + filterChain.doFilter(request, response); + } finally { + MDC.clear(); + } + } +} + diff --git a/backend/src/main/java/com/park/utmstack/service/UserService.java b/backend/src/main/java/com/park/utmstack/service/UserService.java index 5a40c4286..578548dd7 100644 --- a/backend/src/main/java/com/park/utmstack/service/UserService.java +++ b/backend/src/main/java/com/park/utmstack/service/UserService.java @@ -213,19 +213,14 @@ public Optional updateUser(UserDTO userDTO) { }).map(UserDTO::new); } - public void updateUserTfaSecret(String userLogin, String tfaSecret, String tfaMethod) throws Exception { - final String ctx = CLASS_NAME + ".updateUserTfaSecret"; - try { + public void updateUserTfaSecret(String userLogin, String tfaSecret, String tfaMethod) { User user = userRepository.findOneByLogin(userLogin) - .orElseThrow(() -> new Exception(String.format("User %1$s not found", userLogin))); + .orElseThrow(() -> new NoSuchElementException(String.format("User %1$s not found", userLogin))); user.setTfaMethod(tfaMethod); user.setTfaSecret(tfaSecret); - } catch (Exception e) { - throw new Exception(ctx + ": " + e.getMessage()); - } } - public void deleteUser(String login) throws Exception { + public void deleteUser(String login) { String ctx = CLASS_NAME + ".deleteUser"; User user = userRepository.findOneByLogin(login) .orElseThrow(() -> new NoSuchElementException(String.format("User %1$s not found", login))); diff --git a/backend/src/main/java/com/park/utmstack/service/tfa/EmailTfaService.java b/backend/src/main/java/com/park/utmstack/service/tfa/EmailTfaService.java index 585ef6952..33f2d5e5e 100644 --- a/backend/src/main/java/com/park/utmstack/service/tfa/EmailTfaService.java +++ b/backend/src/main/java/com/park/utmstack/service/tfa/EmailTfaService.java @@ -73,7 +73,7 @@ public TfaVerifyResponse verifyCode(User user, String code) { } @Override - public void persistConfiguration(User user) throws Exception { + public void persistConfiguration(User user) { String secret = cache.getState(user.getLogin(), TfaMethod.EMAIL) .orElseThrow(() -> new IllegalStateException("No TFA setup found for user: " + user.getLogin())) .getSecret(); diff --git a/backend/src/main/java/com/park/utmstack/service/tfa/TfaService.java b/backend/src/main/java/com/park/utmstack/service/tfa/TfaService.java index 5a82975b1..c2ed5bed4 100644 --- a/backend/src/main/java/com/park/utmstack/service/tfa/TfaService.java +++ b/backend/src/main/java/com/park/utmstack/service/tfa/TfaService.java @@ -42,7 +42,7 @@ public void persistConfiguration(TfaMethod method) throws Exception { selected.persistConfiguration(user); } - public void generateChallenge(User user) throws Exception { + public void generateChallenge(User user) { TfaMethod method = TfaMethod.valueOf(user.getTfaMethod()); diff --git a/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java b/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java index 8d6ef094e..b78ab8277 100644 --- a/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java +++ b/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java @@ -75,7 +75,7 @@ public TfaVerifyResponse verifyCode(User user, String code) { @Override - public void persistConfiguration(User user) throws Exception { + public void persistConfiguration(User user) { String secret = cache.getState(user.getLogin(), TfaMethod.TOTP) .orElseThrow(() -> new IllegalStateException("No TFA setup found for user: " + user.getLogin())) .getSecret(); diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java index 7d23d5418..9dd1e1b0f 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java @@ -75,51 +75,32 @@ public UserJWTController(TokenProvider tokenProvider, @PostMapping("/authenticate") public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletRequest request) { final String ctx = CLASSNAME + ".authorize"; - try { - if (loginAttemptService.isBlocked()) - throw new TooMuchLoginAttemptsException(String.format("Client IP %1$s blocked due to too many failed login attempts", loginAttemptService.getClientIP())); - - boolean isTfaEnabled = Boolean.parseBoolean(Constants.CFG.get(Constants.PROP_TFA_ENABLE)); - - UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword()); - Authentication authentication = this.authenticationManager.authenticate(authenticationToken); - SecurityContextHolder.getContext().setAuthentication(authentication); - String tempToken = tokenProvider.createToken(authentication, false, false); + if (loginAttemptService.isBlocked()) + throw new TooMuchLoginAttemptsException(String.format("Client IP %1$s blocked due to too many failed login attempts", loginAttemptService.getClientIP())); - User user = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername()) - .orElseThrow(() -> new BadCredentialsException("User " + loginVM.getUsername() + " not found")); + boolean isTfaEnabled = Boolean.parseBoolean(Constants.CFG.get(Constants.PROP_TFA_ENABLE)); - if (isTfaEnabled && (user.getTfaMethod() != null && !user.getTfaMethod().isEmpty())) { - tfaService.generateChallenge(user); - } + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword()); - return new ResponseEntity<>( LoginResponseDTO.builder() - .token(tempToken) - .method(user.getTfaMethod()) - .success(true) - .tfaRequired(isTfaEnabled) - .build(), HttpStatus.OK); - } /*catch (BadCredentialsException e) { - String msg = ctx + ": " + e.getMessage(); - Map args = Map.of( - "method", request.getMethod(), - "path", request.getRequestURI(), - "remoteAddr", request.getRemoteAddr() - ); + Authentication authentication = this.authenticationManager.authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + String tempToken = tokenProvider.createToken(authentication, false, false); - log.error("Authentication failure: {},", msg, StructuredArguments.keyValue("args", args)); - log.error("Authentication failure: {}, {}", msg, StructuredArguments.keyValue("args", args)); + User user = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername()) + .orElseThrow(() -> new BadCredentialsException("User " + loginVM.getUsername() + " not found")); - applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return ResponseUtil.buildUnauthorizedResponse(msg); - }*/ catch (TooMuchLoginAttemptsException e) { - String msg = ctx + ": " + e.getMessage(); - log.error(msg); - applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return ResponseUtil.buildLockedResponse(msg); + if (isTfaEnabled && (user.getTfaMethod() != null && !user.getTfaMethod().isEmpty())) { + tfaService.generateChallenge(user); } + + return new ResponseEntity<>(LoginResponseDTO.builder() + .token(tempToken) + .method(user.getTfaMethod()) + .success(true) + .tfaRequired(isTfaEnabled) + .build(), HttpStatus.OK); } @GetMapping("/check-credentials") @@ -128,7 +109,7 @@ public ResponseEntity checkPassword(@Valid @RequestParam String password try { User user = userService.getCurrentUserLogin(); UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(user.getLogin(), password); + new UsernamePasswordAuthenticationToken(user.getLogin(), password); Authentication authentication = this.authenticationManager.authenticate(authenticationToken); if (authentication.isAuthenticated()) { return new ResponseEntity<>(checkUUID, HttpStatus.OK); @@ -140,7 +121,7 @@ public ResponseEntity checkPassword(@Valid @RequestParam String password log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).headers( - HeaderUtil.createFailureAlert("", "", msg)).body(null); + HeaderUtil.createFailureAlert("", "", msg)).body(null); } } @@ -152,7 +133,7 @@ public ResponseEntity authorizeFederationServiceManager(@Valid @Reques throw new InvalidConnectionKeyException("It's needed to provide a connection key"); UtmFederationServiceClient fsToken = fsClientRepository.findByFsClientToken(token) - .orElseThrow(() -> new InvalidConnectionKeyException("Unrecognized connection key")); + .orElseThrow(() -> new InvalidConnectionKeyException("Unrecognized connection key")); String[] tokenInfo = new String(Base64Utils.decodeFromUrlSafeString(fsToken.getFsClientToken())).split("\\|"); @@ -163,7 +144,7 @@ public ResponseEntity authorizeFederationServiceManager(@Valid @Reques throw new InvalidConnectionKeyException("Connection key is corrupt, unrecognized instance");*/ UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(Constants.FS_USER, CipherUtil.decrypt(tokenInfo[1], System.getenv(Constants.ENV_ENCRYPTION_KEY))); + new UsernamePasswordAuthenticationToken(Constants.FS_USER, CipherUtil.decrypt(tokenInfo[1], System.getenv(Constants.ENV_ENCRYPTION_KEY))); Authentication authentication = this.authenticationManager.authenticate(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication); From e486e9377edf105759f3ef2ca74391a032efadb4 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Mon, 8 Sep 2025 17:30:33 -0500 Subject: [PATCH 021/422] feat(logging): add ExceptionLogger for centralized exception handling and enhanced logging context management --- .../park/utmstack/advice/ExceptionLogger.java | 32 +++++++++++ .../advice/GlobalExceptionHandler.java | 42 ++++---------- .../utmstack/loggin/LogContextBuilder.java | 57 +++++++++---------- .../utmstack/loggin/LoggingContextFilter.java | 51 ----------------- .../utmstack/web/rest/UserJWTController.java | 1 - backend/src/main/resources/logback-spring.xml | 3 + 6 files changed, 72 insertions(+), 114 deletions(-) create mode 100644 backend/src/main/java/com/park/utmstack/advice/ExceptionLogger.java delete mode 100644 backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java diff --git a/backend/src/main/java/com/park/utmstack/advice/ExceptionLogger.java b/backend/src/main/java/com/park/utmstack/advice/ExceptionLogger.java new file mode 100644 index 000000000..eadfa42c6 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/advice/ExceptionLogger.java @@ -0,0 +1,32 @@ +package com.park.utmstack.advice; + +import com.park.utmstack.loggin.LogContextBuilder; +import com.park.utmstack.util.ResponseUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.logstash.logback.argument.StructuredArguments; +import org.slf4j.MDC; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; + +@Slf4j +@Component +@RequiredArgsConstructor +public class ExceptionLogger { + + private final LogContextBuilder logContextBuilder; + + public ResponseEntity buildResponse(Exception e, HttpServletRequest request, HttpStatus status) { + log(e, request); + return ResponseUtil.buildErrorResponse(status, e.getMessage()); + } + + private void log(Exception e, HttpServletRequest request) { + String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); + log.error(msg, e, StructuredArguments.keyValue("args",logContextBuilder.buildArgs(e, request))); + } +} + diff --git a/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java b/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java index dfa9465db..9f7b6342b 100644 --- a/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java +++ b/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java @@ -1,18 +1,19 @@ package com.park.utmstack.advice; -import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.security.TooMuchLoginAttemptsException; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.util.ResponseUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import javax.servlet.http.HttpServletRequest; import java.util.NoSuchElementException; @Slf4j @@ -21,46 +22,25 @@ public class GlobalExceptionHandler { private final ApplicationEventService applicationEventService; + private final ExceptionLogger exceptionLogger; @ExceptionHandler(BadCredentialsException.class) - public ResponseEntity handleForbidden(BadCredentialsException e) { - String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); - Map args = Map.of( - "method", request.getMethod(), - "path", request.getRequestURI(), - "remoteAddr", request.getRemoteAddr() - ); - - log.error("Authentication failure: {},", msg, StructuredArguments.keyValue("args", args)); - log.error(msg, e); - - return ResponseUtil.buildUnauthorizedResponse(msg); + public ResponseEntity handleForbidden(BadCredentialsException e, HttpServletRequest request) { + return exceptionLogger.buildResponse(e, request, HttpStatus.UNAUTHORIZED); } @ExceptionHandler(TooMuchLoginAttemptsException.class) - public ResponseEntity handleTooManyLoginAttempts(TooMuchLoginAttemptsException e) { - String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); - log.error(msg, e); - - return ResponseUtil.buildLockedResponse(msg); + public ResponseEntity handleTooManyLoginAttempts(TooMuchLoginAttemptsException e, HttpServletRequest request) { + return exceptionLogger.buildResponse(e, request, HttpStatus.LOCKED); } @ExceptionHandler(NoSuchElementException.class) - public ResponseEntity handleNotFound(NoSuchElementException e) { - String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); - log.error(msg, e); - - return ResponseUtil.buildNotFoundResponse(msg); + public ResponseEntity handleNotFound(NoSuchElementException e, HttpServletRequest request) { + return exceptionLogger.buildResponse(e, request, HttpStatus.NOT_FOUND); } @ExceptionHandler(Exception.class) - public ResponseEntity handleGenericException(Exception e) { - String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); - log.error(msg, e); - - return ResponseUtil.buildInternalServerErrorResponse(msg); + public ResponseEntity handleGenericException(Exception e, HttpServletRequest request) { + return exceptionLogger.buildResponse(e, request, HttpStatus.INTERNAL_SERVER_ERROR); } - - - } diff --git a/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java b/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java index b40932cca..69eaea7f3 100644 --- a/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java +++ b/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java @@ -1,45 +1,40 @@ package com.park.utmstack.loggin; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.park.utmstack.domain.User; +import com.park.utmstack.security.SecurityUtils; +import com.park.utmstack.service.UserService; +import lombok.RequiredArgsConstructor; +import net.logstash.logback.argument.StructuredArgument; import org.slf4j.MDC; +import net.logstash.logback.argument.StructuredArguments; +import org.springframework.stereotype.Component; -import java.util.*; -import java.util.stream.Collectors; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +@Component +@RequiredArgsConstructor public class LogContextBuilder { - private static final ObjectMapper mapper = new ObjectMapper(); + private final UserService userService; - public static void init(String traceCode, Map args, Throwable exception) { - MDC.put("code", traceCode != null ? traceCode : UUID.randomUUID().toString().replace("-", "")); - MDC.put("args", serializeArgs(args)); - MDC.put("trace", serializeStackTrace(exception)); - } + public Map buildArgs(Exception e, HttpServletRequest request) { + Map args = new HashMap<>(); - public static void clear() { - MDC.remove("code"); - MDC.remove("args"); - MDC.remove("trace"); - } + String userName = SecurityUtils.getCurrentUserLogin().orElse("anonymous"); - private static String serializeArgs(Map args) { - try { - return args != null ? mapper.writeValueAsString(args) : "{}"; - } catch (Exception e) { - return "{\"error\":\"Failed to serialize args\"}"; + if (Objects.nonNull(e.getCause())) { + args.put("cause", e.getCause().toString()); } - } - private static String serializeStackTrace(Throwable ex) { - if (ex == null) return "[]"; - List traceList = Arrays.stream(ex.getStackTrace()) - .map(ste -> ste.getClassName() + "." + ste.getMethodName() + " " + ste.getLineNumber()) - .collect(Collectors.toList()); - try { - return mapper.writeValueAsString(traceList); - } catch (Exception e) { - return "[\"Failed to serialize stack trace\"]"; - } + args.put("username", userName); + args.put("method", request.getMethod()); + args.put("path", request.getRequestURI()); + args.put("remoteAddr", request.getRemoteAddr()); + args.put("context", MDC.get("context")); + + return args; } } - diff --git a/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java b/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java deleted file mode 100644 index c2507c882..000000000 --- a/backend/src/main/java/com/park/utmstack/loggin/LoggingContextFilter.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.park.utmstack.loggin; - -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.extern.slf4j.Slf4j; -import net.logstash.logback.argument.StructuredArguments; -import org.slf4j.MDC; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -/*@Component*/ -@Slf4j -public class LoggingContextFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain) - throws ServletException, IOException { - MDC.clear(); - - try { - String username = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) - .map(Authentication::getName) - .orElse("anonymous"); - - Map args = new HashMap<>(); - args.put("username", username); - args.put("requestId", UUID.randomUUID().toString()); - args.put("path", request.getRequestURI()); - args.put("method", request.getMethod()); - args.put("remoteAddr", request.getRemoteAddr()); - - log.debug("Request info: {}", StructuredArguments.keyValue("args", args)); - - filterChain.doFilter(request, response); - } finally { - MDC.clear(); - } - } -}/**/ - diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java index 9dd1e1b0f..9685a1858 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java @@ -74,7 +74,6 @@ public UserJWTController(TokenProvider tokenProvider, @PostMapping("/authenticate") public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletRequest request) { - final String ctx = CLASSNAME + ".authorize"; if (loginAttemptService.isBlocked()) throw new TooMuchLoginAttemptsException(String.format("Client IP %1$s blocked due to too many failed login attempts", loginAttemptService.getClientIP())); diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml index 656817603..053c3fe5d 100644 --- a/backend/src/main/resources/logback-spring.xml +++ b/backend/src/main/resources/logback-spring.xml @@ -20,6 +20,9 @@ msg + From 81162b626deb3494eed46c879bf1106578107c40 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 10 Sep 2025 09:23:37 -0500 Subject: [PATCH 022/422] feat(logging): remove commented-out username logging from ControllerTracingAspect for cleaner code --- .../park/utmstack/aop/logging/ControllerTracingAspect.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java index 61001792b..96cc74775 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java @@ -16,10 +16,6 @@ public Object enrichMDC(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String context = signature.getDeclaringType().getSimpleName() + "." + signature.getMethod().getName(); MDC.put("context", context); - /* String username = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()) - .map(Authentication::getName) - .orElse("anonymous"); - MDC.put("username", username);*/ return joinPoint.proceed(); } From 73035d05952a118c8362b081d4be9b621b9c5d12 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 16 Sep 2025 11:40:08 -0500 Subject: [PATCH 023/422] feat(UtmGroupRulesDataType): refactor to use embedded ID for rule and data type relationships --- .../rules/UtmGroupRulesDataType.java | 57 ++++--------------- .../rules/UtmGroupRulesDataTypeKey.java | 25 ++++++++ 2 files changed, 37 insertions(+), 45 deletions(-) create mode 100644 backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataTypeKey.java diff --git a/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataType.java b/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataType.java index 828595cd7..cc2766e66 100644 --- a/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataType.java +++ b/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataType.java @@ -1,5 +1,7 @@ package com.park.utmstack.domain.correlation.rules; +import com.park.utmstack.domain.correlation.config.UtmDataTypes; + import javax.persistence.*; import java.io.Serializable; import java.time.Clock; @@ -11,54 +13,19 @@ @Entity @Table(name = "utm_group_rules_data_type") public class UtmGroupRulesDataType implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id - @Column(name = "rule_id", nullable = false) - private Long ruleId; - - @Id - @Column(name = "data_type_id", nullable = false) - private Long dataTypeId; + @EmbeddedId + private UtmGroupRulesDataTypeKey id; @Column(name = "last_update", nullable = false) private Instant lastUpdate; - public UtmGroupRulesDataType(Long id, Long ruleId, Long dataTypeId, Instant lastUpdate) { - this.ruleId = ruleId; - this.dataTypeId = dataTypeId; - this.lastUpdate = lastUpdate; - } - - public UtmGroupRulesDataType() { - } - - public Long getRuleId() { - return ruleId; - } - - public void setRuleId(Long ruleId) { - this.ruleId = ruleId; - } - - public Long getDataTypeId() { - return dataTypeId; - } - - public void setDataTypeId(Long dataTypeId) { - this.dataTypeId = dataTypeId; - } - - public Instant getLastUpdate() { - return lastUpdate; - } - - public void setLastUpdate() { - this.lastUpdate = Instant.now(Clock.systemUTC()); - } + @ManyToOne(fetch = FetchType.LAZY) + @MapsId("ruleId") + @JoinColumn(name = "rule_id", insertable = false, updatable = false) + private UtmCorrelationRules rule; - public void setLastUpdate(Instant lastUpdate) { - this.lastUpdate = lastUpdate; - } + @ManyToOne(fetch = FetchType.LAZY) + @MapsId("dataTypeId") + @JoinColumn(name = "data_type_id", insertable = false, updatable = false) + private UtmDataTypes dataType; } diff --git a/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataTypeKey.java b/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataTypeKey.java new file mode 100644 index 000000000..aa3035c8a --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataTypeKey.java @@ -0,0 +1,25 @@ +package com.park.utmstack.domain.correlation.rules; + +import javax.persistence.Embeddable; +import java.io.Serializable; +import java.util.Objects; + +@Embeddable +public class UtmGroupRulesDataTypeKey implements Serializable { + private Long ruleId; + private Long dataTypeId; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UtmGroupRulesDataTypeKey that)) return false; + return Objects.equals(ruleId, that.ruleId) && + Objects.equals(dataTypeId, that.dataTypeId); + } + + @Override + public int hashCode() { + return Objects.hash(ruleId, dataTypeId); + } +} + From f56c9ac7be335b71f494a0bef1e86c76b3ee6999 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 16 Sep 2025 11:40:21 -0500 Subject: [PATCH 024/422] feat(logging): update logback configuration to improve logging clarity and structure --- backend/src/main/resources/logback-spring.xml | 26 ++----------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml index 053c3fe5d..eea104579 100644 --- a/backend/src/main/resources/logback-spring.xml +++ b/backend/src/main/resources/logback-spring.xml @@ -33,30 +33,7 @@ - - - - + @@ -86,6 +63,7 @@ + From aa6a3c7d0eaad113e989ad631090f66a90440ed0 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 16 Sep 2025 11:40:28 -0500 Subject: [PATCH 025/422] feat(tfa): refactor error response handling to use ResponseUtil for consistency --- .../com/park/utmstack/web/rest/tfa/TfaController.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java b/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java index 5c3785e24..0d50a741f 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java @@ -1,7 +1,9 @@ package com.park.utmstack.web.rest.tfa; +import com.park.utmstack.config.Constants; import com.park.utmstack.domain.Authority; import com.park.utmstack.domain.User; +import com.park.utmstack.domain.UtmConfigurationParameter; import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.domain.tfa.TfaMethod; import com.park.utmstack.security.jwt.JWTFilter; @@ -28,11 +30,12 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.web.bind.annotation.*; -import tech.jhipster.web.util.ResponseUtil; import java.util.List; import java.util.stream.Collectors; +import static com.park.utmstack.config.Constants.PROP_TFA_METHOD; + @RestController @RequiredArgsConstructor @RequestMapping("api/tfa") @@ -58,7 +61,7 @@ public ResponseEntity initTfa(@RequestBody TfaInitRequest reque String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildInternalServerErrorResponse(msg); } } @@ -73,7 +76,7 @@ public ResponseEntity verifyTfa(@RequestBody TfaVerifyRequest } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildInternalServerErrorResponse(msg); } } @@ -88,7 +91,7 @@ public ResponseEntity generateChallenge() { String msg = ctx + ": " + e.getMessage(); log.error(msg); applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return UtilResponse.buildInternalServerErrorResponse(msg); + return ResponseUtil.buildInternalServerErrorResponse(msg); } } From 820bd1499c3567ceda5f7834a82025bbe8272364 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Tue, 16 Sep 2025 15:29:34 -0500 Subject: [PATCH 026/422] feat(audit): implement audit logging with context extraction and event creation --- .../park/utmstack/advice/ExceptionLogger.java | 32 ------- .../advice/GlobalExceptionHandler.java | 11 +-- .../park/utmstack/aop/logging/AuditEvent.java | 16 ++++ .../aop/logging/ControllerTracingAspect.java | 23 ----- .../utmstack/aop/logging/NoLogException.java | 11 +++ .../{ => impl}/AlertLoggingAspect.java | 2 +- .../aop/logging/impl/AuditEventAspect.java | 38 ++++++++ .../logging/impl/ControllerTracingAspect.java | 40 +++++++++ .../aop/logging/{ => impl}/LoggingAspect.java | 2 +- .../aop/utils/AuditContextExtractor.java | 10 +++ .../impl/JwtLoginAuditContextExtractor.java | 27 ++++++ .../config/LoggingAspectConfiguration.java | 2 +- .../enums/ApplicationEventType.java | 9 ++ .../types/ApplicationEvent.java | 89 +++---------------- .../rules/UtmGroupRulesDataTypeKey.java | 4 + .../utmstack/loggin/LogContextBuilder.java | 51 +++++++---- .../ApplicationEventService.java | 12 ++- .../utmstack/util/RequestContextUtils.java | 14 +++ .../utmstack/web/rest/UserJWTController.java | 60 ++++++++----- .../utmstack/web/rest/util/HeaderUtil.java | 2 +- backend/src/main/resources/logback-spring.xml | 4 +- 21 files changed, 270 insertions(+), 189 deletions(-) delete mode 100644 backend/src/main/java/com/park/utmstack/advice/ExceptionLogger.java create mode 100644 backend/src/main/java/com/park/utmstack/aop/logging/AuditEvent.java delete mode 100644 backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java create mode 100644 backend/src/main/java/com/park/utmstack/aop/logging/NoLogException.java rename backend/src/main/java/com/park/utmstack/aop/logging/{ => impl}/AlertLoggingAspect.java (99%) create mode 100644 backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java create mode 100644 backend/src/main/java/com/park/utmstack/aop/logging/impl/ControllerTracingAspect.java rename backend/src/main/java/com/park/utmstack/aop/logging/{ => impl}/LoggingAspect.java (98%) create mode 100644 backend/src/main/java/com/park/utmstack/aop/utils/AuditContextExtractor.java create mode 100644 backend/src/main/java/com/park/utmstack/aop/utils/impl/JwtLoginAuditContextExtractor.java create mode 100644 backend/src/main/java/com/park/utmstack/util/RequestContextUtils.java diff --git a/backend/src/main/java/com/park/utmstack/advice/ExceptionLogger.java b/backend/src/main/java/com/park/utmstack/advice/ExceptionLogger.java deleted file mode 100644 index eadfa42c6..000000000 --- a/backend/src/main/java/com/park/utmstack/advice/ExceptionLogger.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.park.utmstack.advice; - -import com.park.utmstack.loggin.LogContextBuilder; -import com.park.utmstack.util.ResponseUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import net.logstash.logback.argument.StructuredArguments; -import org.slf4j.MDC; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; - -@Slf4j -@Component -@RequiredArgsConstructor -public class ExceptionLogger { - - private final LogContextBuilder logContextBuilder; - - public ResponseEntity buildResponse(Exception e, HttpServletRequest request, HttpStatus status) { - log(e, request); - return ResponseUtil.buildErrorResponse(status, e.getMessage()); - } - - private void log(Exception e, HttpServletRequest request) { - String msg = String.format("%s: %s", MDC.get("context"), e.getMessage()); - log.error(msg, e, StructuredArguments.keyValue("args",logContextBuilder.buildArgs(e, request))); - } -} - diff --git a/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java b/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java index 9f7b6342b..de9f258d0 100644 --- a/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java +++ b/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java @@ -6,8 +6,6 @@ import com.park.utmstack.util.ResponseUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.slf4j.MDC; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -22,25 +20,24 @@ public class GlobalExceptionHandler { private final ApplicationEventService applicationEventService; - private final ExceptionLogger exceptionLogger; @ExceptionHandler(BadCredentialsException.class) public ResponseEntity handleForbidden(BadCredentialsException e, HttpServletRequest request) { - return exceptionLogger.buildResponse(e, request, HttpStatus.UNAUTHORIZED); + return ResponseUtil.buildUnauthorizedResponse(e.getMessage()); } @ExceptionHandler(TooMuchLoginAttemptsException.class) public ResponseEntity handleTooManyLoginAttempts(TooMuchLoginAttemptsException e, HttpServletRequest request) { - return exceptionLogger.buildResponse(e, request, HttpStatus.LOCKED); + return ResponseUtil.buildLockedResponse(e.getMessage()); } @ExceptionHandler(NoSuchElementException.class) public ResponseEntity handleNotFound(NoSuchElementException e, HttpServletRequest request) { - return exceptionLogger.buildResponse(e, request, HttpStatus.NOT_FOUND); + return ResponseUtil.buildNotFoundResponse(e.getMessage()); } @ExceptionHandler(Exception.class) public ResponseEntity handleGenericException(Exception e, HttpServletRequest request) { - return exceptionLogger.buildResponse(e, request, HttpStatus.INTERNAL_SERVER_ERROR); + return ResponseUtil.buildInternalServerErrorResponse(e.getMessage()); } } diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/AuditEvent.java b/backend/src/main/java/com/park/utmstack/aop/logging/AuditEvent.java new file mode 100644 index 000000000..a5743260d --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/aop/logging/AuditEvent.java @@ -0,0 +1,16 @@ +package com.park.utmstack.aop.logging; + +import com.park.utmstack.domain.application_events.enums.ApplicationEventType; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuditEvent { + ApplicationEventType value(); + String message() default ""; +} + diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java deleted file mode 100644 index 96cc74775..000000000 --- a/backend/src/main/java/com/park/utmstack/aop/logging/ControllerTracingAspect.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.park.utmstack.aop.logging; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.reflect.MethodSignature; -import org.slf4j.MDC; -import org.springframework.stereotype.Component; - -@Aspect -@Component -public class ControllerTracingAspect { - - @Around("within(@org.springframework.web.bind.annotation.RestController *)") - public Object enrichMDC(ProceedingJoinPoint joinPoint) throws Throwable { - MethodSignature signature = (MethodSignature) joinPoint.getSignature(); - String context = signature.getDeclaringType().getSimpleName() + "." + signature.getMethod().getName(); - MDC.put("context", context); - - return joinPoint.proceed(); - } -} - diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/NoLogException.java b/backend/src/main/java/com/park/utmstack/aop/logging/NoLogException.java new file mode 100644 index 000000000..4325d84e0 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/aop/logging/NoLogException.java @@ -0,0 +1,11 @@ +package com.park.utmstack.aop.logging; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface NoLogException {} + diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/AlertLoggingAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/AlertLoggingAspect.java similarity index 99% rename from backend/src/main/java/com/park/utmstack/aop/logging/AlertLoggingAspect.java rename to backend/src/main/java/com/park/utmstack/aop/logging/impl/AlertLoggingAspect.java index 86bf16d7a..2c2103686 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/AlertLoggingAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/AlertLoggingAspect.java @@ -1,4 +1,4 @@ -package com.park.utmstack.aop.logging; +package com.park.utmstack.aop.logging.impl; import com.park.utmstack.config.Constants; import com.park.utmstack.domain.UtmAlertLog; diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java new file mode 100644 index 000000000..ac317b8fb --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java @@ -0,0 +1,38 @@ +package com.park.utmstack.aop.logging.impl; + +import com.park.utmstack.aop.logging.AuditEvent; +import com.park.utmstack.aop.utils.AuditContextExtractor; +import com.park.utmstack.loggin.LogContextBuilder; +import com.park.utmstack.service.application_events.ApplicationEventService; +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +@Aspect +@Component +@RequiredArgsConstructor +public class AuditEventAspect { + + private final ApplicationEventService applicationEventService; + private final LogContextBuilder logContextBuilder; + private final List extractors; + + @Around("@annotation(auditEvent)") + public Object logAuditEvent(ProceedingJoinPoint joinPoint, AuditEvent auditEvent) throws Throwable { + Object result = joinPoint.proceed(); + + Map args = logContextBuilder.buildArgs(); + for (AuditContextExtractor extractor : extractors) { + args.putAll(extractor.extract(joinPoint)); + } + applicationEventService.createEvent(auditEvent.message(), auditEvent.value(), args); + + return result; + } +} + diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/impl/ControllerTracingAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/ControllerTracingAspect.java new file mode 100644 index 000000000..281bf238f --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/ControllerTracingAspect.java @@ -0,0 +1,40 @@ +package com.park.utmstack.aop.logging.impl; + +import com.park.utmstack.aop.logging.NoLogException; +import com.park.utmstack.loggin.LogContextBuilder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.logstash.logback.argument.StructuredArguments; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; + +@Aspect +@Slf4j +@Component +@RequiredArgsConstructor +public class ControllerTracingAspect { + + private final LogContextBuilder logContextBuilder; + + @Around("within(@org.springframework.web.bind.annotation.RestController *)") + public Object enrichMDC(ProceedingJoinPoint joinPoint) throws Throwable { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + String context = signature.getDeclaringType().getSimpleName() + "." + signature.getMethod().getName(); + MDC.put("context", context); + + try { + return joinPoint.proceed(); + } catch (Exception e) { + if (!e.getClass().isAnnotationPresent(NoLogException.class)) { + String msg = String.format("%s: %s", context, e.getMessage()); + log.error(msg, e, StructuredArguments.keyValue("args",logContextBuilder.buildArgs(e))); + } + throw e; + } + } +} + diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/LoggingAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingAspect.java similarity index 98% rename from backend/src/main/java/com/park/utmstack/aop/logging/LoggingAspect.java rename to backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingAspect.java index dbeabd956..c76960d64 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/LoggingAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingAspect.java @@ -1,4 +1,4 @@ -package com.park.utmstack.aop.logging; +package com.park.utmstack.aop.logging.impl; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; diff --git a/backend/src/main/java/com/park/utmstack/aop/utils/AuditContextExtractor.java b/backend/src/main/java/com/park/utmstack/aop/utils/AuditContextExtractor.java new file mode 100644 index 000000000..24a052eef --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/aop/utils/AuditContextExtractor.java @@ -0,0 +1,10 @@ +package com.park.utmstack.aop.utils; + +import org.aspectj.lang.ProceedingJoinPoint; + +import java.util.Map; + +public interface AuditContextExtractor { + Map extract(ProceedingJoinPoint joinPoint); +} + diff --git a/backend/src/main/java/com/park/utmstack/aop/utils/impl/JwtLoginAuditContextExtractor.java b/backend/src/main/java/com/park/utmstack/aop/utils/impl/JwtLoginAuditContextExtractor.java new file mode 100644 index 000000000..5de6d4d08 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/aop/utils/impl/JwtLoginAuditContextExtractor.java @@ -0,0 +1,27 @@ +package com.park.utmstack.aop.utils.impl; + +import com.park.utmstack.aop.utils.AuditContextExtractor; +import com.park.utmstack.web.rest.vm.LoginVM; +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class JwtLoginAuditContextExtractor implements AuditContextExtractor { + + @Override + public Map extract(ProceedingJoinPoint joinPoint) { + Map context = new HashMap<>(); + + for (Object arg : joinPoint.getArgs()) { + if (arg instanceof LoginVM loginVM) { + context.put("loginAttempt", loginVM.getUsername()); + } + } + + return context; + } +} + diff --git a/backend/src/main/java/com/park/utmstack/config/LoggingAspectConfiguration.java b/backend/src/main/java/com/park/utmstack/config/LoggingAspectConfiguration.java index 2e438501e..49e666ead 100644 --- a/backend/src/main/java/com/park/utmstack/config/LoggingAspectConfiguration.java +++ b/backend/src/main/java/com/park/utmstack/config/LoggingAspectConfiguration.java @@ -1,6 +1,6 @@ package com.park.utmstack.config; -import com.park.utmstack.aop.logging.LoggingAspect; +import com.park.utmstack.aop.logging.impl.LoggingAspect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; diff --git a/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java b/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java index 1a901c7ac..778f01c57 100644 --- a/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java +++ b/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java @@ -1,6 +1,15 @@ package com.park.utmstack.domain.application_events.enums; public enum ApplicationEventType { + AUTH_ATTEMPT, + AUTH_SUCCESS, + AUTH_FAILURE, + TFA_CODE_SENT, + TFA_VERIFIED, + AUTH_LOGOUT, + CONFIG_CHANGED, + USER_MANAGEMENT, + ACCESS_DENIED, ERROR, WARNING, INFO diff --git a/backend/src/main/java/com/park/utmstack/domain/application_events/types/ApplicationEvent.java b/backend/src/main/java/com/park/utmstack/domain/application_events/types/ApplicationEvent.java index ef0195d5b..1ceadc3e4 100644 --- a/backend/src/main/java/com/park/utmstack/domain/application_events/types/ApplicationEvent.java +++ b/backend/src/main/java/com/park/utmstack/domain/application_events/types/ApplicationEvent.java @@ -1,88 +1,21 @@ package com.park.utmstack.domain.application_events.types; import com.fasterxml.jackson.annotation.JsonProperty; - +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor public class ApplicationEvent { + @JsonProperty("@timestamp") private String timestamp; + private String source; private String message; private String type; - - public ApplicationEvent() { - } - - public ApplicationEvent(String timestamp, String source, String message, String type) { - this.timestamp = timestamp; - this.source = source; - this.message = message; - this.type = type; - } - - public String getTimestamp() { - return timestamp; - } - - public void setTimestamp(String timestamp) { - this.timestamp = timestamp; - } - - public String getSource() { - return source; - } - - public void setSource(String source) { - this.source = source; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private String timestamp; - private String source; - private String message; - private String type; - - public ApplicationEvent build() { - return new ApplicationEvent(timestamp, source, message, type); - } - - public Builder timestamp(String timestamp) { - this.timestamp = timestamp; - return this; - } - - public Builder source(String source) { - this.source = source; - return this; - } - - public Builder message(String message) { - this.message = message; - return this; - } - - public Builder type(String type) { - this.type = type; - return this; - } - } } diff --git a/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataTypeKey.java b/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataTypeKey.java index aa3035c8a..9336bb8ca 100644 --- a/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataTypeKey.java +++ b/backend/src/main/java/com/park/utmstack/domain/correlation/rules/UtmGroupRulesDataTypeKey.java @@ -1,12 +1,16 @@ package com.park.utmstack.domain.correlation.rules; +import javax.persistence.Column; import javax.persistence.Embeddable; import java.io.Serializable; import java.util.Objects; @Embeddable public class UtmGroupRulesDataTypeKey implements Serializable { + @Column(name = "rule_id") private Long ruleId; + + @Column(name = "data_type_id") private Long dataTypeId; @Override diff --git a/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java b/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java index 69eaea7f3..de7ac4a8c 100644 --- a/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java +++ b/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java @@ -1,12 +1,8 @@ package com.park.utmstack.loggin; -import com.park.utmstack.domain.User; import com.park.utmstack.security.SecurityUtils; -import com.park.utmstack.service.UserService; -import lombok.RequiredArgsConstructor; -import net.logstash.logback.argument.StructuredArgument; +import com.park.utmstack.util.RequestContextUtils; import org.slf4j.MDC; -import net.logstash.logback.argument.StructuredArguments; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; @@ -15,26 +11,47 @@ import java.util.Objects; @Component -@RequiredArgsConstructor public class LogContextBuilder { - private final UserService userService; - - public Map buildArgs(Exception e, HttpServletRequest request) { - Map args = new HashMap<>(); - - String userName = SecurityUtils.getCurrentUserLogin().orElse("anonymous"); + public Map buildArgs(Exception e) { + return RequestContextUtils.getCurrentRequest() + .map(request -> buildArgs(e, request)) + .orElse(buildFallbackArgs(e)); + } - if (Objects.nonNull(e.getCause())) { - args.put("cause", e.getCause().toString()); - } + public Map buildArgs() { + return RequestContextUtils.getCurrentRequest() + .map(this::buildArgs) + .orElse(buildFallbackArgs(null)); + } - args.put("username", userName); + public Map buildArgs(HttpServletRequest request) { + Map args = new HashMap<>(); + args.put("username", SecurityUtils.getCurrentUserLogin().orElse("anonymous")); args.put("method", request.getMethod()); args.put("path", request.getRequestURI()); args.put("remoteAddr", request.getRemoteAddr()); args.put("context", MDC.get("context")); + args.put("traceId", MDC.get("traceId")); + return args; + } - return args; + public Map buildArgs(Exception e, HttpServletRequest request) { + Map args = buildArgs(request); + if (e != null && e.getCause() != null) { + args.put("cause", e.getCause().toString()); + } + return args; + } + + private Map buildFallbackArgs(Exception e) { + Map args = new HashMap<>(); + args.put("username", SecurityUtils.getCurrentUserLogin().orElse("anonymous")); + args.put("context", MDC.get("context")); + args.put("traceId", MDC.get("traceId")); + if (e != null && e.getCause() != null) { + args.put("cause", e.getCause().toString()); + } + return args; } } diff --git a/backend/src/main/java/com/park/utmstack/service/application_events/ApplicationEventService.java b/backend/src/main/java/com/park/utmstack/service/application_events/ApplicationEventService.java index 83bb23176..b8d262849 100644 --- a/backend/src/main/java/com/park/utmstack/service/application_events/ApplicationEventService.java +++ b/backend/src/main/java/com/park/utmstack/service/application_events/ApplicationEventService.java @@ -4,12 +4,15 @@ import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.domain.application_events.types.ApplicationEvent; import com.park.utmstack.service.elasticsearch.OpensearchClientBuilder; +import net.logstash.logback.argument.StructuredArguments; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.MDC; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.time.Instant; +import java.util.Map; @Service public class ApplicationEventService { @@ -36,9 +39,14 @@ public void createEvent(String message, ApplicationEventType type) { .message(message).timestamp(Instant.now().toString()) .source(ApplicationEventSource.PANEL.name()).type(type.name()) .build(); - client.getClient().index(".utmstack-logs", applicationEvent); + /*client.getClient().index(".utmstack-logs", applicationEvent);*/ } catch (Exception e) { - log.error(ctx + ": " + e.getMessage()); + log.error(ctx + ": {}", e.getMessage()); } } + + public void createEvent(String message, ApplicationEventType type, Map details) { + String msg = String.format("%s: %s", MDC.get("context"), message); + log.info(msg, StructuredArguments.keyValue("args", details)); + } } diff --git a/backend/src/main/java/com/park/utmstack/util/RequestContextUtils.java b/backend/src/main/java/com/park/utmstack/util/RequestContextUtils.java new file mode 100644 index 000000000..5e11eddaa --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/util/RequestContextUtils.java @@ -0,0 +1,14 @@ +package com.park.utmstack.util; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +public class RequestContextUtils { + public static Optional getCurrentRequest() { + ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + return Optional.ofNullable(attrs).map(ServletRequestAttributes::getRequest); + } +} diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java index 9685a1858..71524e9b6 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java @@ -1,9 +1,11 @@ package com.park.utmstack.web.rest; +import com.park.utmstack.aop.logging.AuditEvent; import com.park.utmstack.config.Constants; import com.park.utmstack.domain.User; import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.domain.federation_service.UtmFederationServiceClient; +import com.park.utmstack.loggin.LogContextBuilder; import com.park.utmstack.repository.federation_service.UtmFederationServiceClientRepository; import com.park.utmstack.security.TooMuchLoginAttemptsException; import com.park.utmstack.security.jwt.JWTFilter; @@ -19,6 +21,8 @@ import com.park.utmstack.util.exceptions.InvalidConnectionKeyException; import com.park.utmstack.web.rest.util.HeaderUtil; import com.park.utmstack.web.rest.vm.LoginVM; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import net.logstash.logback.argument.StructuredArguments; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,12 +46,12 @@ * Controller to authenticate users. */ @RestController +@RequiredArgsConstructor +@Slf4j @RequestMapping("/api") public class UserJWTController { private static final String CLASSNAME = "UserJWTController"; - private static final String TFA_METHOD = "Tfa-Method"; - private final Logger log = LoggerFactory.getLogger(UserJWTController.class); private final TokenProvider tokenProvider; private final AuthenticationManager authenticationManager; @@ -56,52 +60,60 @@ public class UserJWTController { private final LoginAttemptService loginAttemptService; private final UtmFederationServiceClientRepository fsClientRepository; private final TfaService tfaService; + private final LogContextBuilder logContextBuilder; + - public UserJWTController(TokenProvider tokenProvider, - AuthenticationManager authenticationManager, - ApplicationEventService applicationEventService, - UserService userService, - LoginAttemptService loginAttemptService, - UtmFederationServiceClientRepository fsClientRepository, TfaService tfaService) { - this.tokenProvider = tokenProvider; - this.authenticationManager = authenticationManager; - this.applicationEventService = applicationEventService; - this.userService = userService; - this.loginAttemptService = loginAttemptService; - this.fsClientRepository = fsClientRepository; - this.tfaService = tfaService; - } + @AuditEvent( + value = ApplicationEventType.AUTH_SUCCESS, + message = "Authentication successful: access token issued" + ) @PostMapping("/authenticate") public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletRequest request) { - if (loginAttemptService.isBlocked()) - throw new TooMuchLoginAttemptsException(String.format("Client IP %1$s blocked due to too many failed login attempts", loginAttemptService.getClientIP())); + if (loginAttemptService.isBlocked()) { + String ip = loginAttemptService.getClientIP(); + throw new TooMuchLoginAttemptsException(String.format("Authentication blocked: IP %s exceeded login attempt threshold", ip)); + } boolean isTfaEnabled = Boolean.parseBoolean(Constants.CFG.get(Constants.PROP_TFA_ENABLE)); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword()); - Authentication authentication = this.authenticationManager.authenticate(authenticationToken); + Authentication authentication = authenticationManager.authenticate(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication); + String tempToken = tokenProvider.createToken(authentication, false, false); User user = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername()) - .orElseThrow(() -> new BadCredentialsException("User " + loginVM.getUsername() + " not found")); + .orElseThrow(() -> new BadCredentialsException("Authentication failed: user '" + loginVM.getUsername() + "' not found")); - if (isTfaEnabled && (user.getTfaMethod() != null && !user.getTfaMethod().isEmpty())) { + boolean tfaRequired = isTfaEnabled && user.getTfaMethod() != null && !user.getTfaMethod().isEmpty(); + + if (tfaRequired) { tfaService.generateChallenge(user); + + Map args = logContextBuilder.buildArgs(request); + args.put("tfaMethod", user.getTfaMethod()); + applicationEventService.createEvent( + "TFA challenge issued for user '" + user.getLogin() + "' via method '" + user.getTfaMethod() + "'", + ApplicationEventType.TFA_CODE_SENT, + args + ); } - return new ResponseEntity<>(LoginResponseDTO.builder() + LoginResponseDTO response = LoginResponseDTO.builder() .token(tempToken) .method(user.getTfaMethod()) .success(true) - .tfaRequired(isTfaEnabled) - .build(), HttpStatus.OK); + .tfaRequired(tfaRequired) + .build(); + + return ResponseEntity.ok(response); } + @GetMapping("/check-credentials") public ResponseEntity checkPassword(@Valid @RequestParam String password, @RequestParam String checkUUID) { final String ctx = CLASSNAME + ".checkPassword"; diff --git a/backend/src/main/java/com/park/utmstack/web/rest/util/HeaderUtil.java b/backend/src/main/java/com/park/utmstack/web/rest/util/HeaderUtil.java index 72f13e786..861165a16 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/util/HeaderUtil.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/util/HeaderUtil.java @@ -36,7 +36,7 @@ public static HttpHeaders createEntityDeletionAlert(String entityName, String pa } public static HttpHeaders createFailureAlert(String entityName, String errorKey, String defaultMessage) { - log.error("Entity processing failed, {}", defaultMessage); + /* log.error("Entity processing failed, {}", defaultMessage);*/ HttpHeaders headers = new HttpHeaders(); headers.add("X-" + APPLICATION_NAME + "-error", defaultMessage); headers.add("X-" + APPLICATION_NAME + "-params", entityName); diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml index eea104579..c065038e3 100644 --- a/backend/src/main/resources/logback-spring.xml +++ b/backend/src/main/resources/logback-spring.xml @@ -49,8 +49,8 @@ - - + + From 02205306414bb32174815ed3aa2e276b27438dd4 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 17 Sep 2025 08:50:41 -0500 Subject: [PATCH 027/422] feat(tfa): add TFA verification exception handling and logging enhancements --- .../advice/GlobalExceptionHandler.java | 7 +++ .../exceptions/TfaVerificationException.java | 8 +++ .../utmstack/web/rest/tfa/TfaController.java | 58 ++++++++++--------- backend/src/main/resources/logback-spring.xml | 6 ++ 4 files changed, 52 insertions(+), 27 deletions(-) create mode 100644 backend/src/main/java/com/park/utmstack/util/exceptions/TfaVerificationException.java diff --git a/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java b/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java index de9f258d0..88c1e0003 100644 --- a/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java +++ b/backend/src/main/java/com/park/utmstack/advice/GlobalExceptionHandler.java @@ -4,8 +4,10 @@ import com.park.utmstack.security.TooMuchLoginAttemptsException; import com.park.utmstack.service.application_events.ApplicationEventService; import com.park.utmstack.util.ResponseUtil; +import com.park.utmstack.util.exceptions.TfaVerificationException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -21,6 +23,11 @@ public class GlobalExceptionHandler { private final ApplicationEventService applicationEventService; + @ExceptionHandler(TfaVerificationException.class) + public ResponseEntity TfaVerificationException(TfaVerificationException e, HttpServletRequest request) { + return ResponseUtil.buildErrorResponse(HttpStatus.PRECONDITION_FAILED, e.getMessage()); + } + @ExceptionHandler(BadCredentialsException.class) public ResponseEntity handleForbidden(BadCredentialsException e, HttpServletRequest request) { return ResponseUtil.buildUnauthorizedResponse(e.getMessage()); diff --git a/backend/src/main/java/com/park/utmstack/util/exceptions/TfaVerificationException.java b/backend/src/main/java/com/park/utmstack/util/exceptions/TfaVerificationException.java new file mode 100644 index 000000000..68e73cc58 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/util/exceptions/TfaVerificationException.java @@ -0,0 +1,8 @@ +package com.park.utmstack.util.exceptions; + +public class TfaVerificationException extends RuntimeException { + public TfaVerificationException(String message) { + super(message); + } +} + diff --git a/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java b/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java index 0d50a741f..3fe9c94e6 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java @@ -6,6 +6,7 @@ import com.park.utmstack.domain.UtmConfigurationParameter; import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.domain.tfa.TfaMethod; +import com.park.utmstack.loggin.LogContextBuilder; import com.park.utmstack.security.jwt.JWTFilter; import com.park.utmstack.security.jwt.TokenProvider; import com.park.utmstack.service.UserService; @@ -19,9 +20,11 @@ import com.park.utmstack.service.dto.tfa.verify.TfaVerifyResponse; import com.park.utmstack.service.tfa.TfaService; import com.park.utmstack.util.ResponseUtil; +import com.park.utmstack.util.exceptions.TfaVerificationException; import com.park.utmstack.util.exceptions.UtmMailException; import com.park.utmstack.web.rest.util.HeaderUtil; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; @@ -31,17 +34,19 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static com.park.utmstack.config.Constants.PROP_TFA_METHOD; @RestController @RequiredArgsConstructor +@Slf4j @RequestMapping("api/tfa") public class TfaController { - private final Logger log = LoggerFactory.getLogger(TfaController.class); private static final String CLASSNAME = "TfaController"; private final TfaService tfaService; @@ -49,6 +54,7 @@ public class TfaController { private final ApplicationEventService applicationEventService; private final UtmConfigurationParameterService utmConfigurationParameterService; private final TokenProvider tokenProvider; + private final LogContextBuilder logContextBuilder; @PostMapping("/init") public ResponseEntity initTfa(@RequestBody TfaInitRequest request) { @@ -137,39 +143,37 @@ public ResponseEntity completeTfa(@RequestBody TfaSaveRequest request) { } @PostMapping("/verifyCode") - public ResponseEntity verifyCode(@RequestBody String code) { + public ResponseEntity verifyCode(@RequestBody String code, HttpServletRequest request) { final String ctx = CLASSNAME + ".verifyCode"; - try { - User user = userService.getCurrentUserLogin(); - TfaMethod method = TfaMethod.valueOf(user.getTfaMethod()); - TfaVerifyRequest request = new TfaVerifyRequest(method, code); - TfaVerifyResponse response = tfaService.verifyCode(user, request); + User user = userService.getCurrentUserLogin(); + TfaMethod method = TfaMethod.valueOf(user.getTfaMethod()); + TfaVerifyRequest tfaVerifyRequest = new TfaVerifyRequest(method, code); + TfaVerifyResponse response = tfaService.verifyCode(user, tfaVerifyRequest); - if (!response.isValid()){ - return ResponseEntity.status(HttpStatus.PRECONDITION_FAILED) - .headers(HeaderUtil.createFailureAlert("", "", response.getMessage())) - .body(null); - } + if (!response.isValid()) { + throw new TfaVerificationException("TFA invalid for user '" + user.getLogin() + "': " + response.getMessage()); + } - List authorities = user.getAuthorities().stream().map(Authority::getName) - .map(SimpleGrantedAuthority::new).collect(Collectors.toList()); + List authorities = user.getAuthorities().stream().map(Authority::getName) + .map(SimpleGrantedAuthority::new).collect(Collectors.toList()); - org.springframework.security.core.userdetails.User principal = new org.springframework.security.core.userdetails.User(user.getLogin(), "", authorities); + org.springframework.security.core.userdetails.User principal = new org.springframework.security.core.userdetails.User(user.getLogin(), "", authorities); - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(principal, "", authorities); + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(principal, "", authorities); - String jwt = tokenProvider.createToken(authentication, true, true); + String jwt = tokenProvider.createToken(authentication, true, true); + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); + Map args = logContextBuilder.buildArgs(request); + args.put("tfaMethod", user.getTfaMethod()); + applicationEventService.createEvent( + "Login successfully completed for user '" + user.getLogin() + "' via TFA method '" + user.getTfaMethod() + "'", + ApplicationEventType.AUTH_SUCCESS, + args + ); + return new ResponseEntity<>(new JWTToken(jwt, true), httpHeaders, HttpStatus.OK); - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); - return new ResponseEntity<>(new JWTToken(jwt, true), httpHeaders, HttpStatus.OK); - } catch (Exception e) { - String msg = ctx + ": " + e.getMessage(); - log.error(msg); - applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).headers( - HeaderUtil.createFailureAlert("", "", msg)).body(null); - } } } diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml index c065038e3..39ff5894d 100644 --- a/backend/src/main/resources/logback-spring.xml +++ b/backend/src/main/resources/logback-spring.xml @@ -64,6 +64,12 @@ + + + + + + From fcc51a663cb76f59743c2d6b8d923d9aade0c614 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 17 Sep 2025 10:07:44 -0500 Subject: [PATCH 028/422] feat(alert): refactor UtmAlertResponseActionTemplate and UtmAlertResponseRule for improved relationship management and add merge functionality --- .../UtmAlertResponseActionTemplate.java | 11 ++------ .../UtmAlertResponseRule.java | 26 +++++++++++++++++++ .../UtmAlertResponseRuleService.java | 7 +++-- .../service/dto/UtmAlertResponseRuleDTO.java | 3 +++ 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseActionTemplate.java b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseActionTemplate.java index 58cc4799b..35d4ef2e5 100644 --- a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseActionTemplate.java +++ b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseActionTemplate.java @@ -41,14 +41,7 @@ public class UtmAlertResponseActionTemplate implements Serializable { @Column(name = "system_owner", nullable = false) private Boolean systemOwner; - @ManyToMany - @JoinTable( - name = "utm_alert_response_rule_template", - joinColumns = @JoinColumn(name = "rule_id"), - inverseJoinColumns = @JoinColumn(name = "template_id") - ) - private List utmAlertResponseActionTemplates = new ArrayList<>(); - - + @ManyToMany(mappedBy = "utmAlertResponseActionTemplates") + private List utmAlertResponseRules = new ArrayList<>(); } diff --git a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java index 7b0a675d7..7170e88b3 100644 --- a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java +++ b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java @@ -109,4 +109,30 @@ public UtmAlertResponseRule(UtmAlertResponseRuleDTO dto) { } } + public void mergeInto(UtmAlertResponseRule target) { + target.setRuleName(this.getRuleName()); + target.setRuleDescription(this.getRuleDescription()); + target.setRuleCmd(this.getRuleCmd()); + target.setRuleActive(this.getRuleActive()); + target.setAgentPlatform(this.getAgentPlatform()); + target.setDefaultAgent(this.getDefaultAgent()); + target.setExcludedAgents(this.getExcludedAgents()); + target.setRuleConditions(this.getRuleConditions()); + + if (!this.getUtmAlertResponseActionTemplates().isEmpty()) { + List targetActions = target.getUtmAlertResponseActionTemplates(); + targetActions.clear(); + targetActions.addAll(this.getUtmAlertResponseActionTemplates().stream().map(templateDto -> { + UtmAlertResponseActionTemplate template = new UtmAlertResponseActionTemplate(); + template.setId(templateDto.getId()); + template.setTitle(templateDto.getTitle()); + template.setDescription(templateDto.getDescription()); + template.setCommand(templateDto.getCommand()); + return template; + }).collect(Collectors.toList())); + } + + } + + } diff --git a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java index 8d5a6b1ce..26403de62 100644 --- a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java +++ b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java @@ -93,9 +93,12 @@ public UtmAlertResponseRule save(UtmAlertResponseRule alertResponseRule) { final String ctx = CLASSNAME + ".save"; try { if (alertResponseRule.getId() != null) { + String alertRuleId = String.valueOf(alertResponseRule.getId()); UtmAlertResponseRule current = alertResponseRuleRepository.findById(alertResponseRule.getId()) - .orElseThrow(() -> new RuntimeException(String.format("Incident response rule with ID: %1$s not found", alertResponseRule.getId()))); - alertResponseRuleHistoryRepository.save(new UtmAlertResponseRuleHistory(new UtmAlertResponseRuleDTO(current))); + .orElseThrow(() -> new RuntimeException(String.format("Incident response rule with ID: %1$s not found", alertRuleId))); + alertResponseRule.mergeInto(current); + alertResponseRuleHistoryRepository.save(new UtmAlertResponseRuleHistory(new UtmAlertResponseRuleDTO(alertResponseRule))); + alertResponseRule = current; } if (alertResponseRule.getUtmAlertResponseActionTemplates() != null) { diff --git a/backend/src/main/java/com/park/utmstack/service/dto/UtmAlertResponseRuleDTO.java b/backend/src/main/java/com/park/utmstack/service/dto/UtmAlertResponseRuleDTO.java index f036c2666..30d0cae83 100644 --- a/backend/src/main/java/com/park/utmstack/service/dto/UtmAlertResponseRuleDTO.java +++ b/backend/src/main/java/com/park/utmstack/service/dto/UtmAlertResponseRuleDTO.java @@ -3,10 +3,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; +import com.park.utmstack.domain.alert_response_rule.UtmAlertResponseActionTemplate; import com.park.utmstack.domain.alert_response_rule.UtmAlertResponseRule; import com.park.utmstack.domain.chart_builder.types.query.FilterType; import lombok.Data; import lombok.NoArgsConstructor; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.validation.constraints.*; @@ -95,4 +97,5 @@ public UtmAlertResponseRuleDTO(UtmAlertResponseRule rule) { } } + } From 6ec20691bcdb5aff1e5631bcc4d0901723580595 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Wed, 17 Sep 2025 12:36:10 -0500 Subject: [PATCH 029/422] feat(alert): enhance UtmAlertResponseRule merging logic and update relationship management --- .../UtmAlertResponseRule.java | 29 +------- .../UtmAlertResponseRuleService.java | 72 +++++++++++-------- 2 files changed, 42 insertions(+), 59 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java index 7170e88b3..2be00c2d5 100644 --- a/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java +++ b/backend/src/main/java/com/park/utmstack/domain/alert_response_rule/UtmAlertResponseRule.java @@ -65,8 +65,7 @@ public class UtmAlertResponseRule implements Serializable { @OneToMany(mappedBy = "rule", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) List utmAlertResponseRuleExecutions; - - @ManyToMany + @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinTable( name = "utm_alert_response_rule_template", joinColumns = @JoinColumn(name = "rule_id"), @@ -109,30 +108,4 @@ public UtmAlertResponseRule(UtmAlertResponseRuleDTO dto) { } } - public void mergeInto(UtmAlertResponseRule target) { - target.setRuleName(this.getRuleName()); - target.setRuleDescription(this.getRuleDescription()); - target.setRuleCmd(this.getRuleCmd()); - target.setRuleActive(this.getRuleActive()); - target.setAgentPlatform(this.getAgentPlatform()); - target.setDefaultAgent(this.getDefaultAgent()); - target.setExcludedAgents(this.getExcludedAgents()); - target.setRuleConditions(this.getRuleConditions()); - - if (!this.getUtmAlertResponseActionTemplates().isEmpty()) { - List targetActions = target.getUtmAlertResponseActionTemplates(); - targetActions.clear(); - targetActions.addAll(this.getUtmAlertResponseActionTemplates().stream().map(templateDto -> { - UtmAlertResponseActionTemplate template = new UtmAlertResponseActionTemplate(); - template.setId(templateDto.getId()); - template.setTitle(templateDto.getTitle()); - template.setDescription(templateDto.getDescription()); - template.setCommand(templateDto.getCommand()); - return template; - }).collect(Collectors.toList())); - } - - } - - } diff --git a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java index 26403de62..1f078b628 100644 --- a/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java +++ b/backend/src/main/java/com/park/utmstack/service/alert_response_rule/UtmAlertResponseRuleService.java @@ -32,6 +32,7 @@ import com.park.utmstack.util.UtilJson; import com.park.utmstack.util.exceptions.UtmNotImplementedException; import io.grpc.stub.StreamObserver; +import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; @@ -49,6 +50,7 @@ import java.util.stream.Collectors; @Service +@RequiredArgsConstructor @Transactional public class UtmAlertResponseRuleService { @@ -64,30 +66,9 @@ public class UtmAlertResponseRuleService { private final UtmAlertResponseRuleExecutionRepository alertResponseRuleExecutionRepository; private final UtmIncidentVariableService utmIncidentVariableService; private final UtmAlertResponseActionTemplateRepository utmAlertResponseActionTemplateRepository; - private final UtmAlertResponseActionTemplateService utmAlertResponseActionTemplateService; - public UtmAlertResponseRuleService(UtmAlertResponseRuleRepository alertResponseRuleRepository, - UtmAlertResponseRuleHistoryRepository alertResponseRuleHistoryRepository, - UtmNetworkScanRepository networkScanRepository, - ApplicationEventService eventService, - AgentService agentService, - IncidentResponseCommandService incidentResponseCommandService, - UtmAlertResponseRuleExecutionRepository alertResponseRuleExecutionRepository, - UtmIncidentVariableService utmIncidentVariableService, - UtmAlertResponseActionTemplateRepository utmAlertResponseActionTemplateRepository, - UtmAlertResponseActionTemplateService utmAlertResponseActionTemplateService) { - this.alertResponseRuleRepository = alertResponseRuleRepository; - this.alertResponseRuleHistoryRepository = alertResponseRuleHistoryRepository; - this.networkScanRepository = networkScanRepository; - this.eventService = eventService; - this.agentService = agentService; - this.incidentResponseCommandService = incidentResponseCommandService; - this.alertResponseRuleExecutionRepository = alertResponseRuleExecutionRepository; - this.utmIncidentVariableService = utmIncidentVariableService; - this.utmAlertResponseActionTemplateRepository = utmAlertResponseActionTemplateRepository; - this.utmAlertResponseActionTemplateService = utmAlertResponseActionTemplateService; - } + public UtmAlertResponseRule save(UtmAlertResponseRule alertResponseRule) { final String ctx = CLASSNAME + ".save"; @@ -96,19 +77,11 @@ public UtmAlertResponseRule save(UtmAlertResponseRule alertResponseRule) { String alertRuleId = String.valueOf(alertResponseRule.getId()); UtmAlertResponseRule current = alertResponseRuleRepository.findById(alertResponseRule.getId()) .orElseThrow(() -> new RuntimeException(String.format("Incident response rule with ID: %1$s not found", alertRuleId))); - alertResponseRule.mergeInto(current); + this.mergeInto(current, alertResponseRule); alertResponseRuleHistoryRepository.save(new UtmAlertResponseRuleHistory(new UtmAlertResponseRuleDTO(alertResponseRule))); alertResponseRule = current; } - if (alertResponseRule.getUtmAlertResponseActionTemplates() != null) { - for (UtmAlertResponseActionTemplate action : alertResponseRule.getUtmAlertResponseActionTemplates()) { - if (action.getId() == null || !utmAlertResponseActionTemplateRepository.existsById(action.getId())) { - utmAlertResponseActionTemplateService.save(action); - } - } - } - return alertResponseRuleRepository.save(alertResponseRule); } catch (Exception e) { throw new RuntimeException(ctx + ": " + e.getLocalizedMessage()); @@ -347,4 +320,41 @@ public void onCompleted() { log.error(msg); } } + + public void mergeInto(UtmAlertResponseRule target, UtmAlertResponseRule source) { + target.setRuleName(source.getRuleName()); + target.setRuleDescription(source.getRuleDescription()); + target.setRuleCmd(source.getRuleCmd()); + target.setRuleActive(source.getRuleActive()); + target.setAgentPlatform(source.getAgentPlatform()); + target.setDefaultAgent(source.getDefaultAgent()); + target.setExcludedAgents(source.getExcludedAgents()); + target.setRuleConditions(source.getRuleConditions()); + + target.getUtmAlertResponseActionTemplates().clear(); + + if (!source.getUtmAlertResponseActionTemplates().isEmpty()) { + List managedTemplates = source.getUtmAlertResponseActionTemplates() + .stream() + .map(t -> { + if (t.getId() != null) { + UtmAlertResponseActionTemplate existing = utmAlertResponseActionTemplateRepository.findById(t.getId()) + .orElseThrow(() -> new RuntimeException("Template not found: " + t.getId())); + existing.setTitle(t.getTitle()); + existing.setDescription(t.getDescription()); + existing.setCommand(t.getCommand()); + return existing; + } else { + UtmAlertResponseActionTemplate newT = new UtmAlertResponseActionTemplate(); + newT.setTitle(t.getTitle()); + newT.setDescription(t.getDescription()); + newT.setCommand(t.getCommand()); + return utmAlertResponseActionTemplateService.save(newT); + } + }) + .collect(Collectors.toList()); + + target.getUtmAlertResponseActionTemplates().addAll(managedTemplates); + } + } } From aa03fb11124b2e3f2e74c9b8f423ef266fa76cc3 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 18 Sep 2025 09:32:51 -0500 Subject: [PATCH 030/422] feat(logging): add Loggable annotation and LoggingMethodAspect for method execution logging --- .../park/utmstack/aop/logging/Loggable.java | 13 ++++++++ .../aop/logging/impl/LoggingMethodAspect.java | 30 +++++++++++++++++++ .../utmstack/service/tfa/TotpTfaService.java | 2 ++ 3 files changed, 45 insertions(+) create mode 100644 backend/src/main/java/com/park/utmstack/aop/logging/Loggable.java create mode 100644 backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/Loggable.java b/backend/src/main/java/com/park/utmstack/aop/logging/Loggable.java new file mode 100644 index 000000000..01bfe5054 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/aop/logging/Loggable.java @@ -0,0 +1,13 @@ +package com.park.utmstack.aop.logging; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Loggable { +} + + diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java new file mode 100644 index 000000000..a06e212cc --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java @@ -0,0 +1,30 @@ +package com.park.utmstack.aop.logging.impl; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; + +@Aspect +@Component +@Slf4j +public class LoggingMethodAspect { + @Around("@annotation(com.park.utmstack.aop.logging.Loggable)") + public Object logExecution(ProceedingJoinPoint joinPoint) throws Throwable { + String traceId = MDC.get("traceId"); + String methodName = joinPoint.getSignature().toShortString(); + long start = System.currentTimeMillis(); + + try { + Object result = joinPoint.proceed(); + long duration = System.currentTimeMillis() - start; + log.debug("[{}] Method {} executed successfully in {}ms", traceId, methodName, duration); + return result; + } catch (Exception ex) { + log.error("[{}] Method {} failed: {}", traceId, methodName, ex.getMessage(), ex); + throw ex; + } + } +} diff --git a/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java b/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java index f88a624f0..8d940e854 100644 --- a/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java +++ b/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java @@ -4,6 +4,7 @@ import com.google.zxing.MultiFormatWriter; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; +import com.park.utmstack.aop.logging.Loggable; import com.park.utmstack.config.Constants; import com.park.utmstack.domain.User; import com.park.utmstack.domain.tfa.TfaMethod; @@ -58,6 +59,7 @@ public TfaInitResponse initiateSetup(User user) { } @Override + @Loggable public TfaVerifyResponse verifyCode(User user, String code) { TfaSetupState tfaSetupState = cache.getState(user.getLogin(), TfaMethod.TOTP) .orElseThrow(() -> new IllegalStateException("No TFA setup found for user: " + user.getLogin())); From cda52b2770748424687109d3885e19ed1b5006b2 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 18 Sep 2025 09:49:59 -0500 Subject: [PATCH 031/422] feat(docker): update Docker configuration to use Java 17 --- ...-used-docker-java-11.yml => v11-used-docker-java-17.yml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{v11-used-docker-java-11.yml => v11-used-docker-java-17.yml} (93%) diff --git a/.github/workflows/v11-used-docker-java-11.yml b/.github/workflows/v11-used-docker-java-17.yml similarity index 93% rename from .github/workflows/v11-used-docker-java-11.yml rename to .github/workflows/v11-used-docker-java-17.yml index d1b002747..6c247d1be 100644 --- a/.github/workflows/v11-used-docker-java-11.yml +++ b/.github/workflows/v11-used-docker-java-17.yml @@ -1,4 +1,4 @@ -name: Docker Image Java 11 +name: Docker Image Java 17 on: workflow_call: @@ -17,10 +17,10 @@ jobs: - name: Check out code into the right branch uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: "11" + java-version: "17" distribution: "temurin" - name: Build with Maven From 738e2fffcaf2182f00292f5f116fd8612822104e Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 18 Sep 2025 09:53:43 -0500 Subject: [PATCH 032/422] feat(docker): update Docker configuration to use Java 17 --- .github/workflows/v11-used-images-without-dependencies.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/v11-used-images-without-dependencies.yml b/.github/workflows/v11-used-images-without-dependencies.yml index 18a301567..d88d182ba 100644 --- a/.github/workflows/v11-used-images-without-dependencies.yml +++ b/.github/workflows/v11-used-images-without-dependencies.yml @@ -60,8 +60,8 @@ jobs: java_11_deployment: name: Java 11 deployment needs: prepare_deployment - if: ${{ needs.prepare_deployment.outputs.tech == 'java-11' }} - uses: ./.github/workflows/v11-used-docker-java-11.yml + if: ${{ needs.prepare_deployment.outputs.tech == 'java-17' }} + uses: ./.github/workflows/v11-used-docker-java-17.yml with: image_name: ${{ inputs.microservice }} tag: ${{inputs.tag}}-community From bb68fdc430b6baf5dbd877ba8229ef7183fe36b4 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 18 Sep 2025 11:49:20 -0500 Subject: [PATCH 033/422] feat(tracing): implement trace ID and context logging for request handling --- .../logging/impl/ControllerTracingAspect.java | 3 +- .../aop/logging/impl/LoggingMethodAspect.java | 16 ++++++-- .../com/park/utmstack/config/Constants.java | 10 +++++ .../utmstack/loggin/LogContextBuilder.java | 32 ++++++++++------ .../utmstack/loggin/filter/TraceIdFilter.java | 37 +++++++++++++++++++ 5 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 backend/src/main/java/com/park/utmstack/loggin/filter/TraceIdFilter.java diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/impl/ControllerTracingAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/ControllerTracingAspect.java index 281bf238f..71c18e452 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/impl/ControllerTracingAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/ControllerTracingAspect.java @@ -25,13 +25,12 @@ public Object enrichMDC(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String context = signature.getDeclaringType().getSimpleName() + "." + signature.getMethod().getName(); MDC.put("context", context); - try { return joinPoint.proceed(); } catch (Exception e) { if (!e.getClass().isAnnotationPresent(NoLogException.class)) { String msg = String.format("%s: %s", context, e.getMessage()); - log.error(msg, e, StructuredArguments.keyValue("args",logContextBuilder.buildArgs(e))); + log.error(msg, e, StructuredArguments.keyValue("args", logContextBuilder.buildArgs(e))); } throw e; } diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java index a06e212cc..4707c128e 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java @@ -1,6 +1,10 @@ package com.park.utmstack.aop.logging.impl; +import com.park.utmstack.config.Constants; +import com.park.utmstack.loggin.LogContextBuilder; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import net.logstash.logback.argument.StructuredArguments; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -9,21 +13,27 @@ @Aspect @Component +@RequiredArgsConstructor @Slf4j public class LoggingMethodAspect { + private final LogContextBuilder logContextBuilder; + + @Around("@annotation(com.park.utmstack.aop.logging.Loggable)") public Object logExecution(ProceedingJoinPoint joinPoint) throws Throwable { - String traceId = MDC.get("traceId"); + String traceId = MDC.get(Constants.TRACE_ID_KEY); String methodName = joinPoint.getSignature().toShortString(); long start = System.currentTimeMillis(); try { Object result = joinPoint.proceed(); long duration = System.currentTimeMillis() - start; - log.debug("[{}] Method {} executed successfully in {}ms", traceId, methodName, duration); + String msg = String.format("Method %s executed successfully in %sms", methodName, duration); + log.info( msg, StructuredArguments.keyValue("args", logContextBuilder.buildArgs(methodName, String.valueOf(duration)))); return result; } catch (Exception ex) { - log.error("[{}] Method {} failed: {}", traceId, methodName, ex.getMessage(), ex); + String msg = String.format("%s Method %s failed: 5s", traceId, methodName); + log.error(msg, ex, StructuredArguments.keyValue("args", logContextBuilder.buildArgs(ex))); throw ex; } } diff --git a/backend/src/main/java/com/park/utmstack/config/Constants.java b/backend/src/main/java/com/park/utmstack/config/Constants.java index 888b86e45..e2b7547c4 100644 --- a/backend/src/main/java/com/park/utmstack/config/Constants.java +++ b/backend/src/main/java/com/park/utmstack/config/Constants.java @@ -137,6 +137,16 @@ public final class Constants { // ---------------------------------------------------------------------------------- public static final String STATISTICS_INDEX_PATTERN = "v11-statistics-*"; + // Logging + public static final String TRACE_ID_KEY = "traceId"; + public static final String CONTEXT_KEY = "context"; + public static final String USERNAME_KEY = "username"; + public static final String METHOD_KEY = "method"; + public static final String PATH_KEY = "path"; + public static final String REMOTE_ADDR_KEY = "remoteAddr"; + public static final String DURATION_KEY = "duration"; + public static final String CAUSE_KEY = "cause"; + private Constants() { } } diff --git a/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java b/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java index de7ac4a8c..c37e8f725 100644 --- a/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java +++ b/backend/src/main/java/com/park/utmstack/loggin/LogContextBuilder.java @@ -1,5 +1,6 @@ package com.park.utmstack.loggin; +import com.park.utmstack.config.Constants; import com.park.utmstack.security.SecurityUtils; import com.park.utmstack.util.RequestContextUtils; import org.slf4j.MDC; @@ -25,32 +26,41 @@ public Map buildArgs() { .orElse(buildFallbackArgs(null)); } + public Map buildArgs(String methodName, String duration) { + Map args = new HashMap<>(); + args.put(Constants.USERNAME_KEY, SecurityUtils.getCurrentUserLogin().orElse("anonymous")); + args.put(Constants.CONTEXT_KEY, methodName); + args.put(Constants.DURATION_KEY, duration); + args.put(Constants.TRACE_ID_KEY, MDC.get(Constants.TRACE_ID_KEY)); + return args; + } + public Map buildArgs(HttpServletRequest request) { Map args = new HashMap<>(); - args.put("username", SecurityUtils.getCurrentUserLogin().orElse("anonymous")); - args.put("method", request.getMethod()); - args.put("path", request.getRequestURI()); - args.put("remoteAddr", request.getRemoteAddr()); - args.put("context", MDC.get("context")); - args.put("traceId", MDC.get("traceId")); + args.put(Constants.USERNAME_KEY, SecurityUtils.getCurrentUserLogin().orElse("anonymous")); + args.put(Constants.METHOD_KEY, request.getMethod()); + args.put(Constants.PATH_KEY, request.getRequestURI()); + args.put(Constants.REMOTE_ADDR_KEY, request.getRemoteAddr()); + args.put(Constants.CONTEXT_KEY, MDC.get(Constants.CONTEXT_KEY)); + args.put(Constants.TRACE_ID_KEY, MDC.get(Constants.TRACE_ID_KEY)); return args; } public Map buildArgs(Exception e, HttpServletRequest request) { Map args = buildArgs(request); if (e != null && e.getCause() != null) { - args.put("cause", e.getCause().toString()); + args.put(Constants.CAUSE_KEY, e.getCause().toString()); } return args; } private Map buildFallbackArgs(Exception e) { Map args = new HashMap<>(); - args.put("username", SecurityUtils.getCurrentUserLogin().orElse("anonymous")); - args.put("context", MDC.get("context")); - args.put("traceId", MDC.get("traceId")); + args.put(Constants.USERNAME_KEY, SecurityUtils.getCurrentUserLogin().orElse("anonymous")); + args.put(Constants.CONTEXT_KEY, MDC.get(Constants.CONTEXT_KEY)); + args.put(Constants.TRACE_ID_KEY, MDC.get(Constants.TRACE_ID_KEY)); if (e != null && e.getCause() != null) { - args.put("cause", e.getCause().toString()); + args.put(Constants.CAUSE_KEY, e.getCause().toString()); } return args; } diff --git a/backend/src/main/java/com/park/utmstack/loggin/filter/TraceIdFilter.java b/backend/src/main/java/com/park/utmstack/loggin/filter/TraceIdFilter.java new file mode 100644 index 000000000..566adbfd3 --- /dev/null +++ b/backend/src/main/java/com/park/utmstack/loggin/filter/TraceIdFilter.java @@ -0,0 +1,37 @@ +package com.park.utmstack.loggin.filter; + +import org.slf4j.MDC; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.UUID; + +import static com.park.utmstack.config.Constants.CONTEXT_KEY; +import static com.park.utmstack.config.Constants.TRACE_ID_KEY; + +@Component +public class TraceIdFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + String traceId = UUID.randomUUID().toString(); + MDC.put(TRACE_ID_KEY, traceId); + + String context = request.getMethod() + " " + request.getRequestURI(); + MDC.put(CONTEXT_KEY, context); + try { + filterChain.doFilter(request, response); + } finally { + MDC.remove(TRACE_ID_KEY); + MDC.remove(CONTEXT_KEY); + } + } +} From 9535f0513f0285bc6ed9786b9945cbbdbd35e2f0 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 18 Sep 2025 11:49:49 -0500 Subject: [PATCH 034/422] refactor(logging): remove unnecessary blank lines in LoggingMethodAspect --- .../com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java index 4707c128e..96c7dba37 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/LoggingMethodAspect.java @@ -18,7 +18,6 @@ public class LoggingMethodAspect { private final LogContextBuilder logContextBuilder; - @Around("@annotation(com.park.utmstack.aop.logging.Loggable)") public Object logExecution(ProceedingJoinPoint joinPoint) throws Throwable { String traceId = MDC.get(Constants.TRACE_ID_KEY); From f2f1656f38a1bb8bd6e201d5b901b00556d48b4a Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 18 Sep 2025 12:16:41 -0500 Subject: [PATCH 035/422] refactor(auth): rename tfaRequired to tfaConfigured for clarity in LoginResponseDTO and UserJWTController --- .../utmstack/service/dto/jwt/LoginResponseDTO.java | 2 +- .../com/park/utmstack/web/rest/UserJWTController.java | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/dto/jwt/LoginResponseDTO.java b/backend/src/main/java/com/park/utmstack/service/dto/jwt/LoginResponseDTO.java index d81e732e5..f14ff5103 100644 --- a/backend/src/main/java/com/park/utmstack/service/dto/jwt/LoginResponseDTO.java +++ b/backend/src/main/java/com/park/utmstack/service/dto/jwt/LoginResponseDTO.java @@ -9,7 +9,7 @@ @Builder public class LoginResponseDTO { private boolean success; - private boolean tfaRequired; + private boolean tfaConfigured; private String method; private String token; } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java index 71524e9b6..7b0159a58 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java @@ -23,9 +23,6 @@ import com.park.utmstack.web.rest.vm.LoginVM; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.logstash.logback.argument.StructuredArguments; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -62,8 +59,6 @@ public class UserJWTController { private final TfaService tfaService; private final LogContextBuilder logContextBuilder; - - @AuditEvent( value = ApplicationEventType.AUTH_SUCCESS, message = "Authentication successful: access token issued" @@ -89,9 +84,9 @@ public ResponseEntity authorize(@Valid @RequestBody LoginVM lo User user = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername()) .orElseThrow(() -> new BadCredentialsException("Authentication failed: user '" + loginVM.getUsername() + "' not found")); - boolean tfaRequired = isTfaEnabled && user.getTfaMethod() != null && !user.getTfaMethod().isEmpty(); + boolean isTfaSetup = isTfaEnabled && user.getTfaMethod() != null && !user.getTfaMethod().isEmpty(); - if (tfaRequired) { + if (isTfaSetup) { tfaService.generateChallenge(user); Map args = logContextBuilder.buildArgs(request); @@ -107,7 +102,7 @@ public ResponseEntity authorize(@Valid @RequestBody LoginVM lo .token(tempToken) .method(user.getTfaMethod()) .success(true) - .tfaRequired(tfaRequired) + .tfaConfigured(isTfaSetup) .build(); return ResponseEntity.ok(response); From d6ad7c4f4dc361839f3f1f781290017a52de5ddc Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 11 Sep 2025 15:19:40 -0500 Subject: [PATCH 036/422] feat: enhance agent management with new views and improved filtering --- frontend/src/environments/environment.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index d1fff92cc..a9c376b4f 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -4,8 +4,8 @@ export const environment = { production: false, - SERVER_API_URL: 'https://192.168.1.18/', - // SERVER_API_URL: 'http://localhost:8080/', + //SERVER_API_URL: 'https://192.168.1.18/', + SERVER_API_URL: 'http://localhost:8080/', SERVER_API_CONTEXT: '', SESSION_AUTH_TOKEN: window.location.host.split(':')[0].toLocaleUpperCase(), WEBSOCKET_URL: '//localhost:8080', From 3718946537b4eb9dee1f13cfe943d444cf1b9740 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 18 Sep 2025 12:17:09 -0500 Subject: [PATCH 037/422] feat(login): update TFA condition check for improved login flow --- .../src/app/shared/components/auth/login/login.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/shared/components/auth/login/login.component.ts b/frontend/src/app/shared/components/auth/login/login.component.ts index 05409a56e..0176a26f8 100644 --- a/frontend/src/app/shared/components/auth/login/login.component.ts +++ b/frontend/src/app/shared/components/auth/login/login.component.ts @@ -135,7 +135,7 @@ export class LoginComponent implements OnInit { this.logged = true; this.startLogin = false; this.spinner.show(); - } else if (data.tfaRequired && !!data.method ) { + } else if (data.tfaConfigured && !!data.method ) { this.spinner.show(); this.router.navigate(['/totp']) .then(() => this.spinner.hide()); From 4e3f7a5aabb5d4bb3d893ca1e975a5ee87ee47a7 Mon Sep 17 00:00:00 2001 From: Yadian Llada Lopez Date: Fri, 19 Sep 2025 09:27:46 -0400 Subject: [PATCH 038/422] Add UTMSTACK collector module to Collector model and proto definition - Updated the CollectorModule enum in collector.proto to include UTMSTACK. - Added UTMSTACK as a new CollectorModule in the collector.go model. --- agent-manager/agent/collector.pb.go | 540 +++++++++------------------ agent-manager/models/collector.go | 3 +- agent-manager/protos/collector.proto | 1 + 3 files changed, 176 insertions(+), 368 deletions(-) diff --git a/agent-manager/agent/collector.pb.go b/agent-manager/agent/collector.pb.go index 02c598a6f..bcf8d63b7 100644 --- a/agent-manager/agent/collector.pb.go +++ b/agent-manager/agent/collector.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 +// protoc-gen-go v1.36.9 // protoc v3.21.12 // source: collector.proto @@ -11,6 +11,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -23,16 +24,19 @@ const ( type CollectorModule int32 const ( - CollectorModule_AS_400 CollectorModule = 0 + CollectorModule_AS_400 CollectorModule = 0 + CollectorModule_UTMSTACK CollectorModule = 1 ) // Enum value maps for CollectorModule. var ( CollectorModule_name = map[int32]string{ 0: "AS_400", + 1: "UTMSTACK", } CollectorModule_value = map[string]int32{ - "AS_400": 0, + "AS_400": 0, + "UTMSTACK": 1, } ) @@ -64,23 +68,20 @@ func (CollectorModule) EnumDescriptor() ([]byte, []int) { } type RegisterRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + 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"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + Collector CollectorModule `protobuf:"varint,4,opt,name=collector,proto3,enum=agent.CollectorModule" json:"collector,omitempty"` 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"` - Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` - Collector CollectorModule `protobuf:"varint,4,opt,name=collector,proto3,enum=agent.CollectorModule" json:"collector,omitempty"` + sizeCache protoimpl.SizeCache } func (x *RegisterRequest) Reset() { *x = RegisterRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_collector_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_collector_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RegisterRequest) String() string { @@ -91,7 +92,7 @@ func (*RegisterRequest) ProtoMessage() {} func (x *RegisterRequest) ProtoReflect() protoreflect.Message { mi := &file_collector_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) @@ -135,21 +136,18 @@ func (x *RegisterRequest) GetCollector() CollectorModule { } type ListCollectorResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Rows []*Collector `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 []*Collector `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 *ListCollectorResponse) Reset() { *x = ListCollectorResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_collector_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_collector_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ListCollectorResponse) String() string { @@ -160,7 +158,7 @@ func (*ListCollectorResponse) ProtoMessage() {} func (x *ListCollectorResponse) ProtoReflect() protoreflect.Message { mi := &file_collector_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) @@ -190,27 +188,24 @@ func (x *ListCollectorResponse) GetTotal() int32 { } type Collector struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Status Status `protobuf:"varint,2,opt,name=status,proto3,enum=agent.Status" json:"status,omitempty"` + CollectorKey string `protobuf:"bytes,3,opt,name=collector_key,json=collectorKey,proto3" json:"collector_key,omitempty"` + Ip string `protobuf:"bytes,4,opt,name=ip,proto3" json:"ip,omitempty"` + Hostname string `protobuf:"bytes,5,opt,name=hostname,proto3" json:"hostname,omitempty"` + Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` + Module CollectorModule `protobuf:"varint,7,opt,name=module,proto3,enum=agent.CollectorModule" json:"module,omitempty"` + LastSeen string `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` unknownFields protoimpl.UnknownFields - - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Status Status `protobuf:"varint,2,opt,name=status,proto3,enum=agent.Status" json:"status,omitempty"` - CollectorKey string `protobuf:"bytes,3,opt,name=collector_key,json=collectorKey,proto3" json:"collector_key,omitempty"` - Ip string `protobuf:"bytes,4,opt,name=ip,proto3" json:"ip,omitempty"` - Hostname string `protobuf:"bytes,5,opt,name=hostname,proto3" json:"hostname,omitempty"` - Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` - Module CollectorModule `protobuf:"varint,7,opt,name=module,proto3,enum=agent.CollectorModule" json:"module,omitempty"` - LastSeen string `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + sizeCache protoimpl.SizeCache } func (x *Collector) Reset() { *x = Collector{} - if protoimpl.UnsafeEnabled { - mi := &file_collector_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_collector_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Collector) String() string { @@ -221,7 +216,7 @@ func (*Collector) ProtoMessage() {} func (x *Collector) ProtoReflect() protoreflect.Message { mi := &file_collector_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) @@ -293,24 +288,21 @@ func (x *Collector) GetLastSeen() string { } type CollectorMessages 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: // // *CollectorMessages_Config // *CollectorMessages_Result StreamMessage isCollectorMessages_StreamMessage `protobuf_oneof:"stream_message"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *CollectorMessages) Reset() { *x = CollectorMessages{} - if protoimpl.UnsafeEnabled { - mi := &file_collector_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_collector_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CollectorMessages) String() string { @@ -321,7 +313,7 @@ func (*CollectorMessages) ProtoMessage() {} func (x *CollectorMessages) ProtoReflect() protoreflect.Message { mi := &file_collector_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) @@ -336,23 +328,27 @@ func (*CollectorMessages) Descriptor() ([]byte, []int) { return file_collector_proto_rawDescGZIP(), []int{3} } -func (m *CollectorMessages) GetStreamMessage() isCollectorMessages_StreamMessage { - if m != nil { - return m.StreamMessage +func (x *CollectorMessages) GetStreamMessage() isCollectorMessages_StreamMessage { + if x != nil { + return x.StreamMessage } return nil } func (x *CollectorMessages) GetConfig() *CollectorConfig { - if x, ok := x.GetStreamMessage().(*CollectorMessages_Config); ok { - return x.Config + if x != nil { + if x, ok := x.StreamMessage.(*CollectorMessages_Config); ok { + return x.Config + } } return nil } func (x *CollectorMessages) GetResult() *ConfigKnowledge { - if x, ok := x.GetStreamMessage().(*CollectorMessages_Result); ok { - return x.Result + if x != nil { + if x, ok := x.StreamMessage.(*CollectorMessages_Result); ok { + return x.Result + } } return nil } @@ -374,22 +370,19 @@ func (*CollectorMessages_Config) isCollectorMessages_StreamMessage() {} func (*CollectorMessages_Result) isCollectorMessages_StreamMessage() {} type CollectorConfig struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + CollectorId string `protobuf:"bytes,1,opt,name=collector_id,json=collectorId,proto3" json:"collector_id,omitempty"` + Groups []*CollectorConfigGroup `protobuf:"bytes,2,rep,name=groups,proto3" json:"groups,omitempty"` + RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` unknownFields protoimpl.UnknownFields - - CollectorId string `protobuf:"bytes,1,opt,name=collector_id,json=collectorId,proto3" json:"collector_id,omitempty"` - Groups []*CollectorConfigGroup `protobuf:"bytes,2,rep,name=groups,proto3" json:"groups,omitempty"` - RequestId string `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + sizeCache protoimpl.SizeCache } func (x *CollectorConfig) Reset() { *x = CollectorConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_collector_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_collector_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CollectorConfig) String() string { @@ -400,7 +393,7 @@ func (*CollectorConfig) ProtoMessage() {} func (x *CollectorConfig) ProtoReflect() protoreflect.Message { mi := &file_collector_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) @@ -437,24 +430,21 @@ func (x *CollectorConfig) GetRequestId() string { } type CollectorConfigGroup struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` GroupName string `protobuf:"bytes,2,opt,name=group_name,json=groupName,proto3" json:"group_name,omitempty"` GroupDescription string `protobuf:"bytes,3,opt,name=group_description,json=groupDescription,proto3" json:"group_description,omitempty"` Configurations []*CollectorGroupConfigurations `protobuf:"bytes,4,rep,name=configurations,proto3" json:"configurations,omitempty"` CollectorId int32 `protobuf:"varint,5,opt,name=collector_id,json=collectorId,proto3" json:"collector_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *CollectorConfigGroup) Reset() { *x = CollectorConfigGroup{} - if protoimpl.UnsafeEnabled { - mi := &file_collector_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_collector_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CollectorConfigGroup) String() string { @@ -465,7 +455,7 @@ func (*CollectorConfigGroup) ProtoMessage() {} func (x *CollectorConfigGroup) ProtoReflect() protoreflect.Message { mi := &file_collector_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) @@ -516,27 +506,24 @@ func (x *CollectorConfigGroup) GetCollectorId() int32 { } type CollectorGroupConfigurations struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - GroupId int32 `protobuf:"varint,2,opt,name=group_id,json=groupId,proto3" json:"group_id,omitempty"` - ConfKey string `protobuf:"bytes,3,opt,name=conf_key,json=confKey,proto3" json:"conf_key,omitempty"` - ConfValue string `protobuf:"bytes,4,opt,name=conf_value,json=confValue,proto3" json:"conf_value,omitempty"` - ConfName string `protobuf:"bytes,5,opt,name=conf_name,json=confName,proto3" json:"conf_name,omitempty"` - ConfDescription string `protobuf:"bytes,6,opt,name=conf_description,json=confDescription,proto3" json:"conf_description,omitempty"` - ConfDataType string `protobuf:"bytes,7,opt,name=conf_data_type,json=confDataType,proto3" json:"conf_data_type,omitempty"` - ConfRequired bool `protobuf:"varint,8,opt,name=conf_required,json=confRequired,proto3" json:"conf_required,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + GroupId int32 `protobuf:"varint,2,opt,name=group_id,json=groupId,proto3" json:"group_id,omitempty"` + ConfKey string `protobuf:"bytes,3,opt,name=conf_key,json=confKey,proto3" json:"conf_key,omitempty"` + ConfValue string `protobuf:"bytes,4,opt,name=conf_value,json=confValue,proto3" json:"conf_value,omitempty"` + ConfName string `protobuf:"bytes,5,opt,name=conf_name,json=confName,proto3" json:"conf_name,omitempty"` + ConfDescription string `protobuf:"bytes,6,opt,name=conf_description,json=confDescription,proto3" json:"conf_description,omitempty"` + ConfDataType string `protobuf:"bytes,7,opt,name=conf_data_type,json=confDataType,proto3" json:"conf_data_type,omitempty"` + ConfRequired bool `protobuf:"varint,8,opt,name=conf_required,json=confRequired,proto3" json:"conf_required,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *CollectorGroupConfigurations) Reset() { *x = CollectorGroupConfigurations{} - if protoimpl.UnsafeEnabled { - mi := &file_collector_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_collector_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CollectorGroupConfigurations) String() string { @@ -547,7 +534,7 @@ func (*CollectorGroupConfigurations) ProtoMessage() {} func (x *CollectorGroupConfigurations) ProtoReflect() protoreflect.Message { mi := &file_collector_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) @@ -619,21 +606,18 @@ func (x *CollectorGroupConfigurations) GetConfRequired() bool { } type ConfigKnowledge struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Accepted string `protobuf:"bytes,1,opt,name=accepted,proto3" json:"accepted,omitempty"` + RequestId string `protobuf:"bytes,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` unknownFields protoimpl.UnknownFields - - Accepted string `protobuf:"bytes,1,opt,name=accepted,proto3" json:"accepted,omitempty"` - RequestId string `protobuf:"bytes,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ConfigKnowledge) Reset() { *x = ConfigKnowledge{} - if protoimpl.UnsafeEnabled { - mi := &file_collector_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_collector_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ConfigKnowledge) String() string { @@ -644,7 +628,7 @@ func (*ConfigKnowledge) ProtoMessage() {} func (x *ConfigKnowledge) ProtoReflect() protoreflect.Message { mi := &file_collector_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) @@ -674,20 +658,17 @@ func (x *ConfigKnowledge) GetRequestId() string { } type ConfigRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Module CollectorModule `protobuf:"varint,1,opt,name=module,proto3,enum=agent.CollectorModule" json:"module,omitempty"` unknownFields protoimpl.UnknownFields - - Module CollectorModule `protobuf:"varint,1,opt,name=module,proto3,enum=agent.CollectorModule" json:"module,omitempty"` + sizeCache protoimpl.SizeCache } func (x *ConfigRequest) Reset() { *x = ConfigRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_collector_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_collector_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ConfigRequest) String() string { @@ -698,7 +679,7 @@ func (*ConfigRequest) ProtoMessage() {} func (x *ConfigRequest) ProtoReflect() protoreflect.Message { mi := &file_collector_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -722,150 +703,86 @@ func (x *ConfigRequest) GetModule() CollectorModule { var File_collector_proto protoreflect.FileDescriptor -var file_collector_proto_rawDesc = []byte{ - 0x0a, 0x0f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 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, 0x8d, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 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, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x34, 0x0a, 0x09, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x09, 0x63, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x53, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x24, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 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, 0xfa, 0x01, 0x0a, 0x09, - 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x02, 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, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, - 0x65, 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, 0x2e, 0x0a, 0x06, 0x6d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6c, - 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x22, 0x89, 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x30, - 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x30, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4b, - 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 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, 0x88, 0x01, 0x0a, 0x0f, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x06, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, - 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, - 0xe2, 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x10, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x49, 0x64, 0x22, 0x96, 0x02, 0x0a, 0x1c, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, - 0x12, 0x19, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x63, - 0x6f, 0x6e, 0x66, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, - 0x6e, 0x66, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, - 0x6f, 0x6e, 0x66, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x5f, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x66, - 0x44, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x22, 0x4c, 0x0a, - 0x0f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x3f, 0x0a, 0x0d, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x06, - 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2a, 0x1d, 0x0a, 0x0f, - 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, - 0x0a, 0x0a, 0x06, 0x41, 0x53, 0x5f, 0x34, 0x30, 0x30, 0x10, 0x00, 0x32, 0xee, 0x02, 0x0a, 0x10, - 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x42, 0x0a, 0x11, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 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, 0x3e, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 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, 0x43, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0f, 0x43, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, 0x2e, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x18, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, - 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x2e, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x00, 0x32, 0x64, 0x0a, 0x15, - 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4b, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x16, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x16, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, - 0x22, 0x00, 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_collector_proto_rawDesc = "" + + "\n" + + "\x0fcollector.proto\x12\x05agent\x1a\fcommon.proto\"\x8d\x01\n" + + "\x0fRegisterRequest\x12\x0e\n" + + "\x02ip\x18\x01 \x01(\tR\x02ip\x12\x1a\n" + + "\bhostname\x18\x02 \x01(\tR\bhostname\x12\x18\n" + + "\aversion\x18\x03 \x01(\tR\aversion\x124\n" + + "\tcollector\x18\x04 \x01(\x0e2\x16.agent.CollectorModuleR\tcollector\"S\n" + + "\x15ListCollectorResponse\x12$\n" + + "\x04rows\x18\x01 \x03(\v2\x10.agent.CollectorR\x04rows\x12\x14\n" + + "\x05total\x18\x02 \x01(\x05R\x05total\"\xfa\x01\n" + + "\tCollector\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x05R\x02id\x12%\n" + + "\x06status\x18\x02 \x01(\x0e2\r.agent.StatusR\x06status\x12#\n" + + "\rcollector_key\x18\x03 \x01(\tR\fcollectorKey\x12\x0e\n" + + "\x02ip\x18\x04 \x01(\tR\x02ip\x12\x1a\n" + + "\bhostname\x18\x05 \x01(\tR\bhostname\x12\x18\n" + + "\aversion\x18\x06 \x01(\tR\aversion\x12.\n" + + "\x06module\x18\a \x01(\x0e2\x16.agent.CollectorModuleR\x06module\x12\x1b\n" + + "\tlast_seen\x18\b \x01(\tR\blastSeen\"\x89\x01\n" + + "\x11CollectorMessages\x120\n" + + "\x06config\x18\x01 \x01(\v2\x16.agent.CollectorConfigH\x00R\x06config\x120\n" + + "\x06result\x18\x02 \x01(\v2\x16.agent.ConfigKnowledgeH\x00R\x06resultB\x10\n" + + "\x0estream_message\"\x88\x01\n" + + "\x0fCollectorConfig\x12!\n" + + "\fcollector_id\x18\x01 \x01(\tR\vcollectorId\x123\n" + + "\x06groups\x18\x02 \x03(\v2\x1b.agent.CollectorConfigGroupR\x06groups\x12\x1d\n" + + "\n" + + "request_id\x18\x03 \x01(\tR\trequestId\"\xe2\x01\n" + + "\x14CollectorConfigGroup\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x05R\x02id\x12\x1d\n" + + "\n" + + "group_name\x18\x02 \x01(\tR\tgroupName\x12+\n" + + "\x11group_description\x18\x03 \x01(\tR\x10groupDescription\x12K\n" + + "\x0econfigurations\x18\x04 \x03(\v2#.agent.CollectorGroupConfigurationsR\x0econfigurations\x12!\n" + + "\fcollector_id\x18\x05 \x01(\x05R\vcollectorId\"\x96\x02\n" + + "\x1cCollectorGroupConfigurations\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x05R\x02id\x12\x19\n" + + "\bgroup_id\x18\x02 \x01(\x05R\agroupId\x12\x19\n" + + "\bconf_key\x18\x03 \x01(\tR\aconfKey\x12\x1d\n" + + "\n" + + "conf_value\x18\x04 \x01(\tR\tconfValue\x12\x1b\n" + + "\tconf_name\x18\x05 \x01(\tR\bconfName\x12)\n" + + "\x10conf_description\x18\x06 \x01(\tR\x0fconfDescription\x12$\n" + + "\x0econf_data_type\x18\a \x01(\tR\fconfDataType\x12#\n" + + "\rconf_required\x18\b \x01(\bR\fconfRequired\"L\n" + + "\x0fConfigKnowledge\x12\x1a\n" + + "\baccepted\x18\x01 \x01(\tR\baccepted\x12\x1d\n" + + "\n" + + "request_id\x18\x02 \x01(\tR\trequestId\"?\n" + + "\rConfigRequest\x12.\n" + + "\x06module\x18\x01 \x01(\x0e2\x16.agent.CollectorModuleR\x06module*+\n" + + "\x0fCollectorModule\x12\n" + + "\n" + + "\x06AS_400\x10\x00\x12\f\n" + + "\bUTMSTACK\x10\x012\xee\x02\n" + + "\x10CollectorService\x12B\n" + + "\x11RegisterCollector\x12\x16.agent.RegisterRequest\x1a\x13.agent.AuthResponse\"\x00\x12>\n" + + "\x0fDeleteCollector\x12\x14.agent.DeleteRequest\x1a\x13.agent.AuthResponse\"\x00\x12C\n" + + "\rListCollector\x12\x12.agent.ListRequest\x1a\x1c.agent.ListCollectorResponse\"\x00\x12K\n" + + "\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" var ( file_collector_proto_rawDescOnce sync.Once - file_collector_proto_rawDescData = file_collector_proto_rawDesc + file_collector_proto_rawDescData []byte ) func file_collector_proto_rawDescGZIP() []byte { file_collector_proto_rawDescOnce.Do(func() { - file_collector_proto_rawDescData = protoimpl.X.CompressGZIP(file_collector_proto_rawDescData) + file_collector_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_collector_proto_rawDesc), len(file_collector_proto_rawDesc))) }) return file_collector_proto_rawDescData } var file_collector_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_collector_proto_msgTypes = make([]protoimpl.MessageInfo, 9) -var file_collector_proto_goTypes = []interface{}{ +var file_collector_proto_goTypes = []any{ (CollectorModule)(0), // 0: agent.CollectorModule (*RegisterRequest)(nil), // 1: agent.RegisterRequest (*ListCollectorResponse)(nil), // 2: agent.ListCollectorResponse @@ -916,117 +833,7 @@ func file_collector_proto_init() { return } file_common_proto_init() - if !protoimpl.UnsafeEnabled { - file_collector_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_collector_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListCollectorResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_collector_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Collector); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_collector_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CollectorMessages); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_collector_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CollectorConfig); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_collector_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CollectorConfigGroup); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_collector_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CollectorGroupConfigurations); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_collector_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConfigKnowledge); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_collector_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConfigRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_collector_proto_msgTypes[3].OneofWrappers = []interface{}{ + file_collector_proto_msgTypes[3].OneofWrappers = []any{ (*CollectorMessages_Config)(nil), (*CollectorMessages_Result)(nil), } @@ -1034,7 +841,7 @@ func file_collector_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_collector_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_collector_proto_rawDesc), len(file_collector_proto_rawDesc)), NumEnums: 1, NumMessages: 9, NumExtensions: 0, @@ -1046,7 +853,6 @@ func file_collector_proto_init() { MessageInfos: file_collector_proto_msgTypes, }.Build() File_collector_proto = out.File - file_collector_proto_rawDesc = nil file_collector_proto_goTypes = nil file_collector_proto_depIdxs = nil } diff --git a/agent-manager/models/collector.go b/agent-manager/models/collector.go index 577ac86ba..ff4b084c7 100644 --- a/agent-manager/models/collector.go +++ b/agent-manager/models/collector.go @@ -5,7 +5,8 @@ import "gorm.io/gorm" type CollectorModule string const ( - AS_400 CollectorModule = "AS_400" + AS_400 CollectorModule = "AS_400" + UTMSTACK CollectorModule = "UTMSTACK" ) type Collector struct { diff --git a/agent-manager/protos/collector.proto b/agent-manager/protos/collector.proto index a4d3275ab..99fd4d551 100644 --- a/agent-manager/protos/collector.proto +++ b/agent-manager/protos/collector.proto @@ -19,6 +19,7 @@ service PanelCollectorService { enum CollectorModule{ AS_400 = 0; + UTMSTACK = 1; } message RegisterRequest { From 09c07a0866b4270dfb4956d3a58d405c64e12c5e Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 18 Sep 2025 13:31:19 -0500 Subject: [PATCH 039/422] Revert "feat(docker): update Docker configuration to use Java 17" This reverts commit cda52b2770748424687109d3885e19ed1b5006b2. --- ...-used-docker-java-17.yml => v11-used-docker-java-11.yml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{v11-used-docker-java-17.yml => v11-used-docker-java-11.yml} (93%) diff --git a/.github/workflows/v11-used-docker-java-17.yml b/.github/workflows/v11-used-docker-java-11.yml similarity index 93% rename from .github/workflows/v11-used-docker-java-17.yml rename to .github/workflows/v11-used-docker-java-11.yml index 6c247d1be..d1b002747 100644 --- a/.github/workflows/v11-used-docker-java-17.yml +++ b/.github/workflows/v11-used-docker-java-11.yml @@ -1,4 +1,4 @@ -name: Docker Image Java 17 +name: Docker Image Java 11 on: workflow_call: @@ -17,10 +17,10 @@ jobs: - name: Check out code into the right branch uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 11 uses: actions/setup-java@v4 with: - java-version: "17" + java-version: "11" distribution: "temurin" - name: Build with Maven From a603c52ef74ebe67d293196a891e532e3d9ca7bc Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 18 Sep 2025 13:31:28 -0500 Subject: [PATCH 040/422] Revert "feat(docker): update Docker configuration to use Java 17" This reverts commit 738e2fffcaf2182f00292f5f116fd8612822104e. --- .github/workflows/v11-used-images-without-dependencies.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/v11-used-images-without-dependencies.yml b/.github/workflows/v11-used-images-without-dependencies.yml index d88d182ba..18a301567 100644 --- a/.github/workflows/v11-used-images-without-dependencies.yml +++ b/.github/workflows/v11-used-images-without-dependencies.yml @@ -60,8 +60,8 @@ jobs: java_11_deployment: name: Java 11 deployment needs: prepare_deployment - if: ${{ needs.prepare_deployment.outputs.tech == 'java-17' }} - uses: ./.github/workflows/v11-used-docker-java-17.yml + if: ${{ needs.prepare_deployment.outputs.tech == 'java-11' }} + uses: ./.github/workflows/v11-used-docker-java-11.yml with: image_name: ${{ inputs.microservice }} tag: ${{inputs.tag}}-community From bf38609b689e6cfd2c630713fb7b28fa852571e5 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 09:34:26 -0500 Subject: [PATCH 041/422] feat(auth): add forceTfa flag to LoginResponseDTO and update authentication logic --- .../main/java/com/park/utmstack/config/Constants.java | 2 ++ .../utmstack/service/dto/jwt/LoginResponseDTO.java | 1 + .../com/park/utmstack/web/rest/UserJWTController.java | 10 +++++++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/config/Constants.java b/backend/src/main/java/com/park/utmstack/config/Constants.java index e2b7547c4..209442b62 100644 --- a/backend/src/main/java/com/park/utmstack/config/Constants.java +++ b/backend/src/main/java/com/park/utmstack/config/Constants.java @@ -147,6 +147,8 @@ public final class Constants { public static final String DURATION_KEY = "duration"; public static final String CAUSE_KEY = "cause"; + public static final String ENV_TFA_ENABLE = "APP_TFA_ENABLED"; + private Constants() { } } diff --git a/backend/src/main/java/com/park/utmstack/service/dto/jwt/LoginResponseDTO.java b/backend/src/main/java/com/park/utmstack/service/dto/jwt/LoginResponseDTO.java index f14ff5103..69572ee49 100644 --- a/backend/src/main/java/com/park/utmstack/service/dto/jwt/LoginResponseDTO.java +++ b/backend/src/main/java/com/park/utmstack/service/dto/jwt/LoginResponseDTO.java @@ -10,6 +10,7 @@ public class LoginResponseDTO { private boolean success; private boolean tfaConfigured; + private boolean forceTfa; private String method; private String token; } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java index 7b0159a58..db9ae643e 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java @@ -38,6 +38,7 @@ import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.util.Map; +import java.util.Optional; /** * Controller to authenticate users. @@ -73,18 +74,20 @@ public ResponseEntity authorize(@Valid @RequestBody LoginVM lo boolean isTfaEnabled = Boolean.parseBoolean(Constants.CFG.get(Constants.PROP_TFA_ENABLE)); + boolean forceTfaAuth = Boolean.parseBoolean(Optional.ofNullable(System.getenv(Constants.ENV_TFA_ENABLE)).orElse("true")); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword()); Authentication authentication = authenticationManager.authenticate(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication); - String tempToken = tokenProvider.createToken(authentication, false, false); + String token = tokenProvider.createToken(authentication, false, !forceTfaAuth); User user = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername()) .orElseThrow(() -> new BadCredentialsException("Authentication failed: user '" + loginVM.getUsername() + "' not found")); - boolean isTfaSetup = isTfaEnabled && user.getTfaMethod() != null && !user.getTfaMethod().isEmpty(); + boolean isTfaSetup = isTfaEnabled && user.getTfaMethod() != null && !user.getTfaMethod().isEmpty() && forceTfaAuth; if (isTfaSetup) { tfaService.generateChallenge(user); @@ -99,10 +102,11 @@ public ResponseEntity authorize(@Valid @RequestBody LoginVM lo } LoginResponseDTO response = LoginResponseDTO.builder() - .token(tempToken) + .token(token) .method(user.getTfaMethod()) .success(true) .tfaConfigured(isTfaSetup) + .forceTfa(forceTfaAuth) .build(); return ResponseEntity.ok(response); From 823edd68249f42d2e35248099b49470805b46eab Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 09:35:23 -0500 Subject: [PATCH 042/422] feat(docker): update workflow to use Java 17 instead of Java 11 --- ...-used-docker-java-11.yml => v11-used-docker-java-17.yml} | 6 +++--- .github/workflows/v11-used-images-without-dependencies.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename .github/workflows/{v11-used-docker-java-11.yml => v11-used-docker-java-17.yml} (93%) diff --git a/.github/workflows/v11-used-docker-java-11.yml b/.github/workflows/v11-used-docker-java-17.yml similarity index 93% rename from .github/workflows/v11-used-docker-java-11.yml rename to .github/workflows/v11-used-docker-java-17.yml index d1b002747..6c247d1be 100644 --- a/.github/workflows/v11-used-docker-java-11.yml +++ b/.github/workflows/v11-used-docker-java-17.yml @@ -1,4 +1,4 @@ -name: Docker Image Java 11 +name: Docker Image Java 17 on: workflow_call: @@ -17,10 +17,10 @@ jobs: - name: Check out code into the right branch uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: "11" + java-version: "17" distribution: "temurin" - name: Build with Maven diff --git a/.github/workflows/v11-used-images-without-dependencies.yml b/.github/workflows/v11-used-images-without-dependencies.yml index 18a301567..d88d182ba 100644 --- a/.github/workflows/v11-used-images-without-dependencies.yml +++ b/.github/workflows/v11-used-images-without-dependencies.yml @@ -60,8 +60,8 @@ jobs: java_11_deployment: name: Java 11 deployment needs: prepare_deployment - if: ${{ needs.prepare_deployment.outputs.tech == 'java-11' }} - uses: ./.github/workflows/v11-used-docker-java-11.yml + if: ${{ needs.prepare_deployment.outputs.tech == 'java-17' }} + uses: ./.github/workflows/v11-used-docker-java-17.yml with: image_name: ${{ inputs.microservice }} tag: ${{inputs.tag}}-community From 2a95460a8dc5ed463e03c5f484a2d90ada860bf8 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 09:50:46 -0500 Subject: [PATCH 043/422] feat(auth): enhance TFA event logging and update authentication success message --- .../workflows/v11-used-images-without-dependencies.yml | 2 +- .../com/park/utmstack/web/rest/UserJWTController.java | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/v11-used-images-without-dependencies.yml b/.github/workflows/v11-used-images-without-dependencies.yml index d88d182ba..228f7d813 100644 --- a/.github/workflows/v11-used-images-without-dependencies.yml +++ b/.github/workflows/v11-used-images-without-dependencies.yml @@ -26,7 +26,7 @@ jobs: run: | service="${{inputs.microservice}}" if [[ "$service" == "backend" ]]; then - tech="java-11" + tech="java-17" elif [[ "$service" == "frontend" ]]; then tech="frontend" elif [[ "$service" == "user-auditor" || "web-pdf" ]]; then diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java index db9ae643e..98897c524 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java @@ -88,17 +88,23 @@ public ResponseEntity authorize(@Valid @RequestBody LoginVM lo .orElseThrow(() -> new BadCredentialsException("Authentication failed: user '" + loginVM.getUsername() + "' not found")); boolean isTfaSetup = isTfaEnabled && user.getTfaMethod() != null && !user.getTfaMethod().isEmpty() && forceTfaAuth; + Map args = logContextBuilder.buildArgs(request); if (isTfaSetup) { tfaService.generateChallenge(user); - Map args = logContextBuilder.buildArgs(request); args.put("tfaMethod", user.getTfaMethod()); applicationEventService.createEvent( "TFA challenge issued for user '" + user.getLogin() + "' via method '" + user.getTfaMethod() + "'", ApplicationEventType.TFA_CODE_SENT, args ); + } else { + applicationEventService.createEvent( + "Login successfully completed for user '" + user.getLogin() + "'", + ApplicationEventType.AUTH_SUCCESS, + args + ); } LoginResponseDTO response = LoginResponseDTO.builder() From 10ce742410583a45cf2117a78385cc3077d5f594 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 09:54:09 -0500 Subject: [PATCH 044/422] feat(docker): update Docker configuration to use Java 17 --- .github/workflows/v11-used-images-without-dependencies.yml | 4 ++-- backend/Dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/v11-used-images-without-dependencies.yml b/.github/workflows/v11-used-images-without-dependencies.yml index 228f7d813..3a66ee65b 100644 --- a/.github/workflows/v11-used-images-without-dependencies.yml +++ b/.github/workflows/v11-used-images-without-dependencies.yml @@ -57,8 +57,8 @@ jobs: tag: ${{inputs.tag}}-community flags: ${{inputs.flags}} - java_11_deployment: - name: Java 11 deployment + java_17_deployment: + name: Java 17 deployment needs: prepare_deployment if: ${{ needs.prepare_deployment.outputs.tech == 'java-17' }} uses: ./.github/workflows/v11-used-docker-java-17.yml diff --git a/backend/Dockerfile b/backend/Dockerfile index 3947d9d0b..b1e8eb732 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:11 +FROM eclipse-temurin:17 ADD target/utmstack.war ./ From f6c23d11b7b26b936a8ad73a74d1cb809053408a Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Thu, 18 Sep 2025 17:58:20 -0500 Subject: [PATCH 045/422] feat(login): enhance login flow with force TFA handling and navigation improvements --- .../src/app/core/auth/auth-jwt.service.ts | 2 +- .../components/auth/login/login.component.ts | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/core/auth/auth-jwt.service.ts b/frontend/src/app/core/auth/auth-jwt.service.ts index 3f2737a4c..45ac385c7 100644 --- a/frontend/src/app/core/auth/auth-jwt.service.ts +++ b/frontend/src/app/core/auth/auth-jwt.service.ts @@ -35,7 +35,7 @@ export class AuthServerProvider { }; const authenticateSuccess = (resp: HttpResponse) => { this.storeAuthenticationToken(resp.body.token); - if (resp.body.method) { + if (resp.body.method && resp.body.forceTfa) { this.tfaMethod = resp.body.method as TfaMethod; } return resp.body; diff --git a/frontend/src/app/shared/components/auth/login/login.component.ts b/frontend/src/app/shared/components/auth/login/login.component.ts index 0176a26f8..8297563aa 100644 --- a/frontend/src/app/shared/components/auth/login/login.component.ts +++ b/frontend/src/app/shared/components/auth/login/login.component.ts @@ -50,6 +50,7 @@ export class LoginComponent implements OnInit { private modalService: NgbModal, private themeChangeBehavior: ThemeChangeBehavior, private spinner: NgxSpinnerService, + private stateStorageService: StateStorageService, private apiServiceCheckerService: ApiServiceCheckerService ) { this.credentials = {}; @@ -130,11 +131,12 @@ export class LoginComponent implements OnInit { this.loginService .login(this.formLogin.value) .then((data) => { - if (data.auth) { + if (!data.forceTfa) { this.authenticationError = false; this.logged = true; this.startLogin = false; this.spinner.show(); + this.startNavigation(); } else if (data.tfaConfigured && !!data.method ) { this.spinner.show(); this.router.navigate(['/totp']) @@ -168,6 +170,25 @@ export class LoginComponent implements OnInit { } } + startNavigation() { + this.accountService.identity(true).then(account => { + if (account) { + const { path, queryParams } = + extractQueryParamsForNavigation(this.stateStorageService.getUrl() ? this.stateStorageService.getUrl() : '' ); + if (path) { + this.stateStorageService.resetPreviousUrl(); + } + const redirectTo = (account.authorities.includes(ADMIN_ROLE) && account.email === ADMIN_DEFAULT_EMAIL) + ? '/getting-started' : !!path ? path : '/dashboard/overview'; + this.router.navigate([redirectTo], {queryParams}) + .then(() => this.spinner.hide()); + } else { + this.logged = false; + this.utmToast.showError('Login error', 'User without privileges.'); + } + }); + } + startInternalNavigation(params) { if (params.url) { this.checkLogin(params.url); From 94a678df17ec973a6fb582fdf7eb434b33c58320 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 11:02:41 -0500 Subject: [PATCH 046/422] feat(tfa): add TFA code verification attempt event and enhance logging --- .../park/utmstack/aop/logging/impl/AuditEventAspect.java | 3 +-- .../application_events/enums/ApplicationEventType.java | 1 + .../java/com/park/utmstack/service/tfa/EmailTfaService.java | 5 +++++ .../java/com/park/utmstack/service/tfa/TotpTfaService.java | 3 +++ .../java/com/park/utmstack/web/rest/UserJWTController.java | 4 ++-- .../java/com/park/utmstack/web/rest/tfa/TfaController.java | 6 ++++++ 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java index ac317b8fb..0fc8f4ee4 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java @@ -24,7 +24,6 @@ public class AuditEventAspect { @Around("@annotation(auditEvent)") public Object logAuditEvent(ProceedingJoinPoint joinPoint, AuditEvent auditEvent) throws Throwable { - Object result = joinPoint.proceed(); Map args = logContextBuilder.buildArgs(); for (AuditContextExtractor extractor : extractors) { @@ -32,7 +31,7 @@ public Object logAuditEvent(ProceedingJoinPoint joinPoint, AuditEvent auditEvent } applicationEventService.createEvent(auditEvent.message(), auditEvent.value(), args); - return result; + return joinPoint.proceed(); } } diff --git a/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java b/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java index 778f01c57..a98d112c4 100644 --- a/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java +++ b/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java @@ -5,6 +5,7 @@ public enum ApplicationEventType { AUTH_SUCCESS, AUTH_FAILURE, TFA_CODE_SENT, + TFA_CODE_VERIFY_ATTEMPT, TFA_VERIFIED, AUTH_LOGOUT, CONFIG_CHANGED, diff --git a/backend/src/main/java/com/park/utmstack/service/tfa/EmailTfaService.java b/backend/src/main/java/com/park/utmstack/service/tfa/EmailTfaService.java index 41f854351..fcee54e75 100644 --- a/backend/src/main/java/com/park/utmstack/service/tfa/EmailTfaService.java +++ b/backend/src/main/java/com/park/utmstack/service/tfa/EmailTfaService.java @@ -1,5 +1,6 @@ package com.park.utmstack.service.tfa; +import com.park.utmstack.aop.logging.Loggable; import com.park.utmstack.config.Constants; import com.park.utmstack.domain.User; import com.park.utmstack.domain.tfa.TfaMethod; @@ -32,6 +33,7 @@ public TfaMethod getMethod() { } @Override + @Loggable public TfaInitResponse initiateSetup(User user) { final String ctx = CLASSNAME + ".initiateSetup"; try { @@ -56,6 +58,7 @@ public TfaInitResponse initiateSetup(User user) { @Override + @Loggable public TfaVerifyResponse verifyCode(User user, String code) { TfaSetupState tfaSetupState = cache.getState(user.getLogin(), TfaMethod.EMAIL) @@ -73,6 +76,7 @@ public TfaVerifyResponse verifyCode(User user, String code) { } @Override + @Loggable public void persistConfiguration(User user) { String secret = cache.getState(user.getLogin(), TfaMethod.EMAIL) .orElseThrow(() -> new IllegalStateException("No TFA setup found for user: " + user.getLogin())) @@ -82,6 +86,7 @@ public void persistConfiguration(User user) { } @Override + @Loggable public void generateChallenge(User user) { String secret = user.getTfaSecret(); String code = tfaService.generateCode(secret); diff --git a/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java b/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java index 8d940e854..fa15c68ec 100644 --- a/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java +++ b/backend/src/main/java/com/park/utmstack/service/tfa/TotpTfaService.java @@ -43,6 +43,7 @@ public TfaMethod getMethod() { } @Override + @Loggable public TfaInitResponse initiateSetup(User user) { String secret = authenticator.createCredentials().getKey(); long expiresAt = System.currentTimeMillis() + Constants.EXPIRES_IN_SECONDS * 10 * 1000; @@ -76,6 +77,7 @@ public TfaVerifyResponse verifyCode(User user, String code) { @Override + @Loggable public void persistConfiguration(User user) { String secret = cache.getState(user.getLogin(), TfaMethod.TOTP) .orElseThrow(() -> new IllegalStateException("No TFA setup found for user: " + user.getLogin())) @@ -85,6 +87,7 @@ public void persistConfiguration(User user) { } @Override + @Loggable public void generateChallenge(User user) { cache.clear(user.getLogin(), TfaMethod.TOTP); String secret = user.getTfaSecret(); diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java index 98897c524..0a0216e80 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java @@ -61,8 +61,8 @@ public class UserJWTController { private final LogContextBuilder logContextBuilder; @AuditEvent( - value = ApplicationEventType.AUTH_SUCCESS, - message = "Authentication successful: access token issued" + value = ApplicationEventType.AUTH_ATTEMPT, + message = "Authentication attempt registered" ) @PostMapping("/authenticate") public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletRequest request) { diff --git a/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java b/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java index 3fe9c94e6..26653aa50 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java @@ -1,5 +1,6 @@ package com.park.utmstack.web.rest.tfa; +import com.park.utmstack.aop.logging.AuditEvent; import com.park.utmstack.config.Constants; import com.park.utmstack.domain.Authority; import com.park.utmstack.domain.User; @@ -79,6 +80,7 @@ public ResponseEntity verifyTfa(@RequestBody TfaVerifyRequest User user = userService.getCurrentUserLogin(); TfaVerifyResponse response = tfaService.verifyCode(user, request); return ResponseEntity.ok(response); + } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); @@ -142,6 +144,10 @@ public ResponseEntity completeTfa(@RequestBody TfaSaveRequest request) { } } + @AuditEvent( + value = ApplicationEventType.TFA_CODE_VERIFY_ATTEMPT, + message = "Verification attempt for second-factor authentication" + ) @PostMapping("/verifyCode") public ResponseEntity verifyCode(@RequestBody String code, HttpServletRequest request) { final String ctx = CLASSNAME + ".verifyCode"; From 999b7dfc277d24c6787c971d4ccacbec31aa66e4 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 12:40:33 -0500 Subject: [PATCH 047/422] feat(logstash): clean up HTML templates and improve filter handling logic --- .../logstash-filters.component.html | 10 +++++----- .../logstash-filters/logstash-filters.component.ts | 13 ++++++++----- .../logstash-pipelines.component.html | 10 +++++----- .../elastic-metric-health.component.html | 2 +- 4 files changed, 19 insertions(+), 16 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 32c592c7d..6f83fb987 100644 --- a/frontend/src/app/logstash/logstash-filters/logstash-filters.component.html +++ b/frontend/src/app/logstash/logstash-filters/logstash-filters.component.html @@ -1,9 +1,9 @@
Pipeline filters
- +
+
-
+ -->
-
+
diff --git a/frontend/src/app/shared/components/utm/elastic/elastic-metric-health/elastic-metric-health.component.html b/frontend/src/app/shared/components/utm/elastic/elastic-metric-health/elastic-metric-health.component.html index 81d6a4fe3..a2de9e211 100644 --- a/frontend/src/app/shared/components/utm/elastic/elastic-metric-health/elastic-metric-health.component.html +++ b/frontend/src/app/shared/components/utm/elastic/elastic-metric-health/elastic-metric-health.component.html @@ -41,7 +41,7 @@
+ ' used of ' + clusterHealth.heapMax + 'GB'}})
-
--> +
Date: Fri, 19 Sep 2025 11:40:35 -0500 Subject: [PATCH 048/422] feat(audit): enhance audit event logging with attempt and success message handling --- .../com/park/utmstack/aop/logging/AuditEvent.java | 7 +++++-- .../aop/logging/impl/AuditEventAspect.java | 15 ++++++++++++--- .../enums/ApplicationEventType.java | 7 ++++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/AuditEvent.java b/backend/src/main/java/com/park/utmstack/aop/logging/AuditEvent.java index a5743260d..133077af4 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/AuditEvent.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/AuditEvent.java @@ -10,7 +10,10 @@ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AuditEvent { - ApplicationEventType value(); - String message() default ""; + ApplicationEventType attemptType(); + String attemptMessage(); + + ApplicationEventType successType(); + String successMessage(); } diff --git a/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java b/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java index 0fc8f4ee4..8d822b98d 100644 --- a/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java +++ b/backend/src/main/java/com/park/utmstack/aop/logging/impl/AuditEventAspect.java @@ -2,6 +2,7 @@ import com.park.utmstack.aop.logging.AuditEvent; import com.park.utmstack.aop.utils.AuditContextExtractor; +import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.loggin.LogContextBuilder; import com.park.utmstack.service.application_events.ApplicationEventService; import lombok.RequiredArgsConstructor; @@ -12,6 +13,7 @@ import java.util.List; import java.util.Map; +import java.util.Objects; @Aspect @Component @@ -24,14 +26,21 @@ public class AuditEventAspect { @Around("@annotation(auditEvent)") public Object logAuditEvent(ProceedingJoinPoint joinPoint, AuditEvent auditEvent) throws Throwable { - Map args = logContextBuilder.buildArgs(); for (AuditContextExtractor extractor : extractors) { args.putAll(extractor.extract(joinPoint)); } - applicationEventService.createEvent(auditEvent.message(), auditEvent.value(), args); - return joinPoint.proceed(); + applicationEventService.createEvent(auditEvent.attemptMessage(), auditEvent.attemptType(), args); + + Object result = joinPoint.proceed(); + + if (!auditEvent.successType().equals(ApplicationEventType.UNDEFINED)) { + applicationEventService.createEvent(auditEvent.successMessage(), auditEvent.successType(), args); + } + + return result; } + } diff --git a/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java b/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java index a98d112c4..ca59c2775 100644 --- a/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java +++ b/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java @@ -11,7 +11,12 @@ public enum ApplicationEventType { CONFIG_CHANGED, USER_MANAGEMENT, ACCESS_DENIED, + ALERT_UPDATE_ATTEMPT, + ALERT_UPDATE_SUCCESS, + ALERT_NOTE_UPDATE_ATTEMPT, + ALERT_NOTE_UPDATE_SUCCESS, ERROR, WARNING, - INFO + INFO, + UNDEFINED } From 3528b0475cef6eee0628f02170f082013f7d5131 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 11:40:43 -0500 Subject: [PATCH 049/422] feat(tfa): enhance TFA code verification and authentication event logging --- .../park/utmstack/web/rest/UserJWTController.java | 6 ++++-- .../park/utmstack/web/rest/tfa/TfaController.java | 14 +++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java index 0a0216e80..538fdd162 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UserJWTController.java @@ -61,8 +61,10 @@ public class UserJWTController { private final LogContextBuilder logContextBuilder; @AuditEvent( - value = ApplicationEventType.AUTH_ATTEMPT, - message = "Authentication attempt registered" + attemptType = ApplicationEventType.AUTH_ATTEMPT, + attemptMessage = "Authentication attempt registered", + successType = ApplicationEventType.UNDEFINED, + successMessage = "" ) @PostMapping("/authenticate") public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM, HttpServletRequest request) { diff --git a/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java b/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java index 26653aa50..2860fc037 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/tfa/TfaController.java @@ -145,8 +145,10 @@ public ResponseEntity completeTfa(@RequestBody TfaSaveRequest request) { } @AuditEvent( - value = ApplicationEventType.TFA_CODE_VERIFY_ATTEMPT, - message = "Verification attempt for second-factor authentication" + attemptType = ApplicationEventType.TFA_CODE_VERIFY_ATTEMPT, + attemptMessage = "Verification attempt for second-factor authentication", + successType = ApplicationEventType.AUTH_SUCCESS, + successMessage = "Login successfully completed" ) @PostMapping("/verifyCode") public ResponseEntity verifyCode(@RequestBody String code, HttpServletRequest request) { @@ -171,13 +173,7 @@ public ResponseEntity verifyCode(@RequestBody String code, HttpServlet HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); - Map args = logContextBuilder.buildArgs(request); - args.put("tfaMethod", user.getTfaMethod()); - applicationEventService.createEvent( - "Login successfully completed for user '" + user.getLogin() + "' via TFA method '" + user.getTfaMethod() + "'", - ApplicationEventType.AUTH_SUCCESS, - args - ); + return new ResponseEntity<>(new JWTToken(jwt, true), httpHeaders, HttpStatus.OK); } From c7a61e89e512be7e382fba5a75201a744871455b Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 11:40:49 -0500 Subject: [PATCH 050/422] feat(exception): change ElasticsearchIndexDocumentUpdateException to extend RuntimeException --- .../exceptions/ElasticsearchIndexDocumentUpdateException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/park/utmstack/util/exceptions/ElasticsearchIndexDocumentUpdateException.java b/backend/src/main/java/com/park/utmstack/util/exceptions/ElasticsearchIndexDocumentUpdateException.java index 195ef2af7..3b0e7bc78 100644 --- a/backend/src/main/java/com/park/utmstack/util/exceptions/ElasticsearchIndexDocumentUpdateException.java +++ b/backend/src/main/java/com/park/utmstack/util/exceptions/ElasticsearchIndexDocumentUpdateException.java @@ -1,6 +1,6 @@ package com.park.utmstack.util.exceptions; -public class ElasticsearchIndexDocumentUpdateException extends Exception { +public class ElasticsearchIndexDocumentUpdateException extends RuntimeException { public ElasticsearchIndexDocumentUpdateException(String message) { super(message); } From 44ab865b2d7d4ea6463361ba51e94333d80de116 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 11:40:58 -0500 Subject: [PATCH 051/422] feat(audit): add audit logging for alert status and notes updates --- .../exceptions/UtmElasticsearchException.java | 2 +- .../utmstack/web/rest/UtmAlertResource.java | 45 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/util/exceptions/UtmElasticsearchException.java b/backend/src/main/java/com/park/utmstack/util/exceptions/UtmElasticsearchException.java index 9f9488ee0..693014d73 100644 --- a/backend/src/main/java/com/park/utmstack/util/exceptions/UtmElasticsearchException.java +++ b/backend/src/main/java/com/park/utmstack/util/exceptions/UtmElasticsearchException.java @@ -1,6 +1,6 @@ package com.park.utmstack.util.exceptions; -public class UtmElasticsearchException extends Exception { +public class UtmElasticsearchException extends RuntimeException { public UtmElasticsearchException(String message) { super(message); } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java b/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java index bc8bf6a0c..c09ccdb8f 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java @@ -1,5 +1,6 @@ package com.park.utmstack.web.rest; +import com.park.utmstack.aop.logging.AuditEvent; import com.park.utmstack.domain.application_events.enums.ApplicationEventType; import com.park.utmstack.service.UtmAlertService; import com.park.utmstack.service.application_events.ApplicationEventService; @@ -15,6 +16,7 @@ import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; +import java.io.IOException; import java.util.List; /** @@ -40,33 +42,34 @@ public UtmAlertResource(UtmAlertService utmAlertService, } @PostMapping("/utm-alerts/status") - public ResponseEntity updateAlertStatus(@RequestBody UpdateAlertStatusRequestBody rq) { + @AuditEvent( + attemptType = ApplicationEventType.ALERT_UPDATE_ATTEMPT, + attemptMessage = "Attempt to update alert status initiated", + successType = ApplicationEventType.ALERT_UPDATE_SUCCESS, + successMessage = "Alert status updated successfully" + ) + public ResponseEntity updateAlertStatus(@RequestBody UpdateAlertStatusRequestBody rq) throws IOException { final String ctx = CLASSNAME + ".updateAlertStatus"; - try { - utmAlertService.updateStatus(rq.getAlertIds(), rq.getStatus(), rq.getStatusObservation()); - return ResponseEntity.ok().build(); - } catch (Exception e) { - String msg = ctx + ": " + e.getMessage(); - log.error(msg); - applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).headers( - HeaderUtil.createFailureAlert("", "", msg)).body(null); - } + utmAlertService.updateStatus(rq.getAlertIds(), rq.getStatus(), rq.getStatusObservation()); + + return ResponseEntity.ok().build(); } @PostMapping("/utm-alerts/notes") + @AuditEvent( + attemptType = ApplicationEventType.ALERT_NOTE_UPDATE_ATTEMPT, + attemptMessage = "Attempt to update alert notes initiated", + successType = ApplicationEventType.ALERT_NOTE_UPDATE_SUCCESS, + successMessage = "Alert notes updated successfully" + ) public ResponseEntity updateAlertNotes(@RequestBody(required = false) String notes, @RequestParam String alertId) { final String ctx = CLASSNAME + ".updateAlertNotes"; - try { - utmAlertService.updateNotes(alertId, notes); - return ResponseEntity.ok().build(); - } catch (Exception e) { - String msg = ctx + ": " + e.getMessage(); - log.error(msg); - applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).headers( - HeaderUtil.createFailureAlert("", "", msg)).body(null); - } + utmAlertService.updateNotes(alertId, notes); + applicationEventService.createEvent( + "Alert notes updated successfully", + ApplicationEventType.ALERT_NOTE_UPDATE_SUCCESS + ); + return ResponseEntity.ok().build(); } @PostMapping("/utm-alerts/tags") From ad0983829ff2328583b72f923d0dc93f41e7ba34 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 11:47:53 -0500 Subject: [PATCH 052/422] feat(audit): add audit events for alert tag updates and incident conversions --- .../enums/ApplicationEventType.java | 4 ++ .../utmstack/web/rest/UtmAlertResource.java | 38 +++++++++---------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java b/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java index ca59c2775..fe7df12d1 100644 --- a/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java +++ b/backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java @@ -15,6 +15,10 @@ public enum ApplicationEventType { ALERT_UPDATE_SUCCESS, ALERT_NOTE_UPDATE_ATTEMPT, ALERT_NOTE_UPDATE_SUCCESS, + ALERT_TAG_UPDATE_ATTEMPT, + ALERT_CONVERT_TO_INCIDENT_ATTEMPT, + ALERT_CONVERT_TO_INCIDENT_SUCCESS, + ALERT_TAG_UPDATE_SUCCESS, ERROR, WARNING, INFO, diff --git a/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java b/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java index c09ccdb8f..903fab37c 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/UtmAlertResource.java @@ -73,33 +73,31 @@ public ResponseEntity updateAlertNotes(@RequestBody(required = false) Stri } @PostMapping("/utm-alerts/tags") + @AuditEvent( + attemptType = ApplicationEventType.ALERT_TAG_UPDATE_ATTEMPT, + attemptMessage = "Attempt to update alert tags initiated", + successType = ApplicationEventType.ALERT_TAG_UPDATE_SUCCESS, + successMessage = "Alert tags updated successfully" + ) public ResponseEntity updateAlertTags(@RequestBody @Valid UpdateAlertTagsRequestBody body) { final String ctx = CLASSNAME + ".updateAlertTags"; - try { - utmAlertService.updateTags(body.getAlertIds(), body.getTags(), body.isCreateRule()); - return ResponseEntity.ok().build(); - } catch (Exception ex) { - String msg = ctx + ": " + ex.getMessage(); - log.error(msg); - applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).headers( - HeaderUtil.createFailureAlert("", "", msg)).body(null); - } + utmAlertService.updateTags(body.getAlertIds(), body.getTags(), body.isCreateRule()); + return ResponseEntity.ok().build(); } @PostMapping("/utm-alerts/convert-to-incident") + @AuditEvent( + attemptType = ApplicationEventType.ALERT_CONVERT_TO_INCIDENT_ATTEMPT, + attemptMessage = "Attempt to convert alerts to incident initiated", + successType = ApplicationEventType.ALERT_CONVERT_TO_INCIDENT_SUCCESS, + successMessage = "Alerts converted to incident successfully" + ) public ResponseEntity convertToIncident(@RequestBody @Valid ConvertToIncidentRequestBody body) { final String ctx = CLASSNAME + ".convertToIncident"; - try { - utmAlertService.convertToIncident(body.getEventIds(), body.getIncidentName(),body.getIncidentId(), body.getIncidentSource()); - return ResponseEntity.ok().build(); - } catch (Exception ex) { - String msg = ctx + ": " + ex.getMessage(); - log.error(msg); - applicationEventService.createEvent(msg, ApplicationEventType.ERROR); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).headers( - HeaderUtil.createFailureAlert("", "", msg)).body(null); - } + + utmAlertService.convertToIncident(body.getEventIds(), body.getIncidentName(),body.getIncidentId(), body.getIncidentSource()); + return ResponseEntity.ok().build(); + } @GetMapping("/utm-alerts/count-open-alerts") From 043bbfc0937bc00ddcece9edac34bbfe926dafdf Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 11:48:48 -0500 Subject: [PATCH 053/422] feat(logging): change log level for com.park.utmstack from error to info --- backend/src/main/resources/config/application-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/resources/config/application-prod.yml b/backend/src/main/resources/config/application-prod.yml index 20d1ca27b..90c98cbba 100644 --- a/backend/src/main/resources/config/application-prod.yml +++ b/backend/src/main/resources/config/application-prod.yml @@ -2,7 +2,7 @@ logging: level: ROOT: error tech.jhipster: error - com.park.utmstack: error + com.park.utmstack: info spring: devtools: From 5c0d967e7409b5c34775a045f8cbd83a322b0894 Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 14:39:09 -0500 Subject: [PATCH 054/422] feat(elasticsearch): simplify search response processing and remove unused groupByField parameter --- .../processor/GroupByFieldProcessor.java | 5 ++--- .../rest/elasticsearch/ElasticsearchResource.java | 14 +++----------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/backend/src/main/java/com/park/utmstack/service/elasticsearch/processor/GroupByFieldProcessor.java b/backend/src/main/java/com/park/utmstack/service/elasticsearch/processor/GroupByFieldProcessor.java index 8282d63dd..aa2d5b615 100644 --- a/backend/src/main/java/com/park/utmstack/service/elasticsearch/processor/GroupByFieldProcessor.java +++ b/backend/src/main/java/com/park/utmstack/service/elasticsearch/processor/GroupByFieldProcessor.java @@ -12,7 +12,6 @@ public class GroupByFieldProcessor implements SearchResultProcessor { @Override public List> process(List> rawResults) { - // Fase 1: Indexación por ID Map> byId = new LinkedHashMap<>(); for (Map item : rawResults) { Object id = item.get("id"); @@ -22,13 +21,13 @@ public List> process(List> rawResults) { } Map>> childrenByParent = new LinkedHashMap<>(); - Set childIds = new HashSet<>(); // ✅ Rastrear IDs de hijos + Set childIds = new HashSet<>(); for (Map item : rawResults) { Object parentId = item.get("parentId"); if (parentId != null && byId.containsKey(parentId)) { childrenByParent.computeIfAbsent(parentId, k -> new ArrayList<>()).add(item); - childIds.add(item.get("id")); // ✅ Marcar como hijo + childIds.add(item.get("id")); } } diff --git a/backend/src/main/java/com/park/utmstack/web/rest/elasticsearch/ElasticsearchResource.java b/backend/src/main/java/com/park/utmstack/web/rest/elasticsearch/ElasticsearchResource.java index 923b01efb..c92a4467a 100644 --- a/backend/src/main/java/com/park/utmstack/web/rest/elasticsearch/ElasticsearchResource.java +++ b/backend/src/main/java/com/park/utmstack/web/rest/elasticsearch/ElasticsearchResource.java @@ -144,9 +144,8 @@ public ResponseEntity deleteIndex(@RequestBody List indexes) { } @PostMapping("/search") - public ResponseEntity>> search(@RequestBody(required = false) List filters, + public ResponseEntity> search(@RequestBody(required = false) List filters, @RequestParam Integer top, @RequestParam String indexPattern, - @RequestParam(required = false) String groupByField, Pageable pageable) { final String ctx = CLASSNAME + ".search"; try { @@ -160,15 +159,8 @@ public ResponseEntity>> search(@RequestBody(required = HttpHeaders headers = UtilPagination.generatePaginationHttpHeaders(Math.min(hits.total().value(), top), pageable.getPageNumber(), pageable.getPageSize(), "/api/elasticsearch/search"); - - List> flatResults = hits.hits().stream() - .map(hit -> (Map)hit.source()) - .collect(Collectors.toList()); - - SearchResultProcessor processor = searchProcessorRegistry.resolve(groupByField); - List> processed = processor.process(flatResults); - - return ResponseEntity.ok().headers(headers).body(processed); + return ResponseEntity.ok().headers(headers).body(hits.hits().stream() + .map(Hit::source).collect(Collectors.toList())); } catch (Exception e) { String msg = ctx + ": " + e.getMessage(); log.error(msg); From ea9477c4c940725e2f1d75e052a097c5327c9a0c Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 14:39:44 -0500 Subject: [PATCH 055/422] feat(alerts): update alert view template and add parent ID filter to Elastic filters --- .../alert-view/alert-view.component.html | 10 +++++----- .../alert-view/alert-view.component.ts | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html index e0c6fb69f..b119772e8 100644 --- a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html +++ b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html @@ -118,7 +118,7 @@
- + - + + <!– Fila principal (sin parentId) –>
@@ -247,7 +247,7 @@
- + --> diff --git a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts index 116ffe7c5..a4a0bc6ae 100644 --- a/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts +++ b/frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts @@ -87,6 +87,7 @@ export class AlertViewComponent implements OnInit, OnDestroy { filters: ElasticFilterType[] = [ {field: ALERT_STATUS_FIELD_AUTO, operator: ElasticOperatorsEnum.IS_NOT, value: AUTOMATIC_REVIEW}, {field: ALERT_TAGS_FIELD, operator: ElasticOperatorsEnum.IS_NOT, value: FALSE_POSITIVE_OBJECT.tagName}, + {field: ALERT_PARENT_ID, operator: ElasticOperatorsEnum.DOES_NOT_EXIST}, {field: ALERT_TIMESTAMP_FIELD, operator: ElasticOperatorsEnum.IS_BETWEEN, value: ['now-7d', 'now']} ]; defaultStatus: number; From bd6a99eef330fee2b3cb4d906f959fc2b9ee5c0b Mon Sep 17 00:00:00 2001 From: Manuel Abascal Date: Fri, 19 Sep 2025 16:07:00 -0500 Subject: [PATCH 056/422] feat(alerts): add button to view related logs and integrate with Elastic search --- .../alert-management.module.ts | 1 - .../alert-logs-related-button.component.css | 0 .../alert-logs-related-button.component.html | 3 + .../alert-logs-related-button.component.ts | 59 +++++++++++++++++++ .../shared/alert-management-shared.module.ts | 36 +++++------ .../alert-view-detail.component.html | 9 +-- .../alert-view-detail.component.ts | 20 ------- .../elasticsearch/elastic-data.service.ts | 7 +++ 8 files changed, 93 insertions(+), 42 deletions(-) create mode 100644 frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.css create mode 100644 frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.html create mode 100644 frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.ts diff --git a/frontend/src/app/data-management/alert-management/alert-management.module.ts b/frontend/src/app/data-management/alert-management/alert-management.module.ts index db2d1faaf..ec847c84f 100644 --- a/frontend/src/app/data-management/alert-management/alert-management.module.ts +++ b/frontend/src/app/data-management/alert-management/alert-management.module.ts @@ -47,7 +47,6 @@ import {AlertManagementSharedModule} from './shared/alert-management-shared.modu SaveAlertReportComponent, AlertReportFilterComponent], providers: [NewAlertBehavior, AlertIncidentStatusChangeBehavior], - exports: [], schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA] }) export class AlertManagementModule { diff --git a/frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.css b/frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.css new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.html b/frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.html new file mode 100644 index 000000000..4a1f4ed8e --- /dev/null +++ b/frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.html @@ -0,0 +1,3 @@ + diff --git a/frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.ts b/frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.ts new file mode 100644 index 000000000..074efac0c --- /dev/null +++ b/frontend/src/app/data-management/alert-management/shared/alert-logs-related-button/alert-logs-related-button.component.ts @@ -0,0 +1,59 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {Router} from '@angular/router'; +import {NgxSpinnerService} from 'ngx-spinner'; +import {of} from "rxjs"; +import {catchError, tap} from 'rxjs/operators'; +import {ALERT_TIMESTAMP_FIELD} from '../../../../shared/constants/alert/alert-field.constant'; +import {LOG_ROUTE} from '../../../../shared/constants/app-routes.constant'; +import {LOG_INDEX_PATTERN, LOG_INDEX_PATTERN_ID} from '../../../../shared/constants/main-index-pattern.constant'; +import {ElasticOperatorsEnum} from '../../../../shared/enums/elastic-operators.enum'; +import {ElasticDataService} from '../../../../shared/services/elasticsearch/elastic-data.service'; + +const LOG_ID_FIELD = 'id'; + +@Component({ + selector: 'app-alert-logs-related-button', + templateUrl: './alert-logs-related-button.component.html', + styleUrls: ['./alert-logs-related-button.component.css'] +}) +export class AlertLogsRelatedButtonComponent implements OnInit { + + @Input() logs: any[] = []; + showButton = false; + + constructor(private router: Router, + private spinner: NgxSpinnerService, + private elasticDataService: ElasticDataService) { } + + ngOnInit() { + const ids = this.logs.map(log => log.id); + const filters = [ + {field: LOG_ID_FIELD, operator: ElasticOperatorsEnum.CONTAIN_ONE_OF, value: ids}, + {field: ALERT_TIMESTAMP_FIELD, operator: ElasticOperatorsEnum.IS_BETWEEN, value: ['now-1y', 'now']} + ]; + + + this.elasticDataService.exists(LOG_INDEX_PATTERN, filters).pipe( + tap((exists: boolean) => this.showButton = exists), + catchError(err => { + console.error('Error checking related logs:', err); + this.showButton = false; + return of(false); + }) + ).subscribe(); + + } + + navigateToEvents() { + const queryParams = {patternId: LOG_INDEX_PATTERN_ID, indexPattern: LOG_INDEX_PATTERN}; + queryParams[LOG_ID_FIELD] = ElasticOperatorsEnum.IS_ONE_OF + '->' + this.logs.map(log => log.id).slice(0, 100); + queryParams[ALERT_TIMESTAMP_FIELD] = ElasticOperatorsEnum.IS_BETWEEN + '->' + 'now-1y' + ',' + 'now'; + this.spinner.show('loadingSpinner'); + this.router.navigate([LOG_ROUTE], { + queryParams + }).then(() => { + this.spinner.hide('loadingSpinner'); + }); + } + +} diff --git a/frontend/src/app/data-management/alert-management/shared/alert-management-shared.module.ts b/frontend/src/app/data-management/alert-management/shared/alert-management-shared.module.ts index d3b6904de..3a48e06c1 100644 --- a/frontend/src/app/data-management/alert-management/shared/alert-management-shared.module.ts +++ b/frontend/src/app/data-management/alert-management/shared/alert-management-shared.module.ts @@ -33,6 +33,7 @@ import { AlertEntityDisplayComponent } from './components/alert-entity-display/a import {AlertFullLogComponent} from './components/alert-full-log/alert-full-log.component'; import {AlertHistoryComponent} from './components/alert-history/alert-history.component'; import {AlertHostDetailComponent} from './components/alert-host-detail/alert-host-detail.component'; +import {AlertImpactComponent} from "./components/alert-impact/alert-impact.component"; import {AlertIncidentDetailComponent} from './components/alert-incident-detail/alert-incident-detail.component'; import {AlertIpComponent} from './components/alert-ip/alert-ip.component'; import {AlertMapLocationComponent} from './components/alert-map-location/alert-map-location.component'; @@ -54,7 +55,7 @@ import {AlertGenericFilterComponent} from './components/filters/alert-generic-fi import {FilterAppliedComponent} from './components/filters/filter-applied/filter-applied.component'; import {RowToFiltersComponent} from './components/filters/row-to-filter/row-to-filters.component'; import {StatusFilterComponent} from './components/filters/status-filter/status-filter.component'; -import {AlertImpactComponent} from "./components/alert-impact/alert-impact.component"; +import {AlertLogsRelatedButtonComponent} from "./alert-logs-related-button/alert-logs-related-button.component"; @NgModule({ declarations: [ @@ -99,7 +100,8 @@ import {AlertImpactComponent} from "./components/alert-impact/alert-impact.compo AlertIncidentDetailComponent, AlertSocAiComponent, AlertEntityDisplayComponent, - AlertBadgeFieldComponent + AlertBadgeFieldComponent, + AlertLogsRelatedButtonComponent ], entryComponents: [ AlertStatusComponent, @@ -154,21 +156,21 @@ import {AlertImpactComponent} from "./components/alert-impact/alert-impact.compo AlertEntityDisplayComponent, AlertBadgeFieldComponent ], - imports: [ - CommonModule, - TranslateModule, - UtmSharedModule, - FormsModule, - NgbModule, - ReactiveFormsModule, - NgSelectModule, - InfiniteScrollModule, - NgxJsonViewerModule, - IncidentResponseSharedModule, - DataMgmtSharedModule, - RouterModule, - InlineSVGModule, - ] + imports: [ + CommonModule, + TranslateModule, + UtmSharedModule, + FormsModule, + NgbModule, + ReactiveFormsModule, + NgSelectModule, + InfiniteScrollModule, + NgxJsonViewerModule, + IncidentResponseSharedModule, + DataMgmtSharedModule, + RouterModule, + InlineSVGModule, + ] }) export class AlertManagementSharedModule { } diff --git a/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.html b/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.html index fbad3b4a1..5cfcac2ec 100644 --- a/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.html +++ b/frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.html @@ -34,11 +34,12 @@ Rules applied -