diff --git a/scripts/locales/en.lua b/scripts/locales/en.lua
index 628cface81..f59cba1035 100644
--- a/scripts/locales/en.lua
+++ b/scripts/locales/en.lua
@@ -1244,6 +1244,7 @@ local lang = {
["alert_ids_ips_jail_add"] = "Host %{host} added to the jailed hosts pool on %{when}",
["alert_ids_ips_jail_remove"] = "Host %{host} removed from the jailed hosts pool on %{when}",
["alert_port_too_many_macs"] = "Too many MACs on non-trunk %{ip} Interface Id %{port} [%{value} %{op} %{threshold} MACs]",
+ ["alert_as_ranking_changed"] = "[ASN %{as}] The current ranking has changed from %{current_ranking} to %{previous_ranking}",
["anomalous_tcp_flags"] = "%{entity} has %{sent_or_rcvd} too many TCP RST flags vs SYN [Ratio: %{ratio}%%]",
["attack_mitigation_via_snmp_failure"] = "Failure to set interface %{port} admin status on SNMP device %{device} to %{admin_down}: %{granularity} %{metric} crossed by %{entity} [%{value} %{op} %{threshold}]. Make sure the SNMP device has a (valid) write community configured.",
["attack_mitigation_via_snmp_success"] = "Interface %{port} admin status on SNMP device %{device} set to %{admin_down}: %{granularity} %{metric} crossed by %{entity} [%{value} %{op} %{threshold}]",
@@ -1445,6 +1446,8 @@ local lang = {
["alerts_ts_description"] = "Generate process alerts timeseries",
["all_hosts"] = "All Hosts",
["anomalous_tcp_flags"] = "Anomalous TCP Flags",
+ ["as_ranking_changed"] = "AS Ranking Changed",
+ ["as_ranking_changed_description"] = "Trigger an alert when an AS Ranking has changed",
["attack_mitigation_snmp_description"] = "Set host SNMP access port admin status to down when the client score exceeds the specified threshold",
["attack_mitigation_snmp_title"] = "Attack Mitigation via SNMP",
["binary_application_transfer"] = "Binary App Transfer",
diff --git a/scripts/lua/modules/alert_consts.lua b/scripts/lua/modules/alert_consts.lua
index d6d14e3593..2717ec189f 100644
--- a/scripts/lua/modules/alert_consts.lua
+++ b/scripts/lua/modules/alert_consts.lua
@@ -235,6 +235,18 @@ end
-- ##############################################
+function alert_consts.formatRanking(ranking)
+ local result = ""
+ for _, table in pairs(ranking) do
+ local ex = getProbeName(table.exporter)
+ if result == "" then result = ex
+ else result = result..",".. ex end
+ end
+ return result
+end
+
+-- ##############################################
+
function getMacUrl(mac)
if not mac then
return ""
diff --git a/scripts/lua/modules/alert_definitions/other/alert_as_ranking_changed.lua b/scripts/lua/modules/alert_definitions/other/alert_as_ranking_changed.lua
new file mode 100644
index 0000000000..e2facff4e2
--- /dev/null
+++ b/scripts/lua/modules/alert_definitions/other/alert_as_ranking_changed.lua
@@ -0,0 +1,72 @@
+--
+-- (C) 2019-25 - ntop.org
+--
+
+-- ##############################################
+
+local other_alert_keys = require "other_alert_keys"
+
+local json = require("dkjson")
+local alert_creators = require "alert_creators"
+local classes = require "classes"
+local alert = require "alert"
+local mitre = require "mitre_utils"
+local alert_entities = require "alert_entities"
+
+
+-- ##############################################
+
+local alert_as_ranking_changed = classes.class(alert)
+
+-- ##############################################
+
+alert_as_ranking_changed.meta = {
+ alert_key = other_alert_keys.alert_as_ranking_changed,
+ i18n_title = "alerts_dashboard.as_ranking_changed",
+ icon = "fas fa-exclamation-triangle",
+ entities = {
+ alert_entities.as,
+ }
+
+ -- Mitre Att&ck Matrix values
+ -- mitre_values = {
+ -- mitre_tactic =
+ -- mitre_technique =
+ -- mitre_id =
+ -- }
+}
+
+-- ##############################################
+
+-- @brief Prepare an alert table used to generate the alert
+-- @return A table with the alert built
+function alert_as_ranking_changed:init(as, current_ranking, previous_ranking)
+ self.super:init()
+ self.alert_type_params = {
+ as = as,
+ current_ranking = current_ranking,
+ previous_ranking = previous_ranking
+ }
+end
+
+-- #######################################################
+
+-- @brief Format an alert into a human-readable string
+-- @param ifid The integer interface id of the generated alert
+-- @param alert The alert description table, including alert data such as the generating entity, timestamp, granularity, type
+-- @param alert_type_params Table `alert_type_params` as built in the `:init` method
+-- @return A human-readable string
+function alert_as_ranking_changed.format(ifid, alert, alert_type_params)
+ local alert_consts = require("alert_consts")
+ current = alert_consts.formatRanking(alert_type_params.current_ranking)
+ prev = alert_consts.formatRanking(alert_type_params.previous_ranking)
+ return i18n("alert_messages.alert_as_ranking_changed", {
+ as = alert_type_params.as,
+ current_ranking = current,
+ previous_ranking = prev
+ })
+end
+
+-- #######################################################
+
+return alert_as_ranking_changed
diff --git a/scripts/lua/modules/alert_keys/other_alert_keys.lua b/scripts/lua/modules/alert_keys/other_alert_keys.lua
index 584fa8bfd7..30eb0e185d 100644
--- a/scripts/lua/modules/alert_keys/other_alert_keys.lua
+++ b/scripts/lua/modules/alert_keys/other_alert_keys.lua
@@ -110,6 +110,7 @@ local other_alert_keys = {
alert_acl_violation_arp = OTHER_BASE_KEY + 97,
alert_redis_reads_writes_exceeded = OTHER_BASE_KEY + 98,
alert_asn_rule_threshold_crossed = OTHER_BASE_KEY + 99,
+ alert_as_ranking_changed = OTHER_BASE_KEY + 100,
MAX_OTHER_ALERT_TYPE = OTHER_BASE_KEY + 127 -- see ntop_typedefs.h
}