File: host_alert_store.lua

package info (click to toggle)
ntopng 5.2.1%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 121,832 kB
  • sloc: javascript: 143,431; cpp: 71,175; ansic: 11,108; sh: 4,687; makefile: 911; python: 587; sql: 512; pascal: 234; perl: 118; ruby: 52; exp: 4
file content (323 lines) | stat: -rw-r--r-- 10,646 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
--
-- (C) 2021-22 - ntop.org
--

local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
package.path = dirs.installdir .. "/scripts/lua/modules/alert_store/?.lua;" .. package.path

-- Import the classes library.
local classes = require "classes"

require "lua_utils"
local alert_store = require "alert_store"
local format_utils = require "format_utils"
local alert_consts = require "alert_consts"
local alert_utils = require "alert_utils"
local alert_entities = require "alert_entities"
local alert_roles = require "alert_roles"
local json = require "dkjson"
local tag_utils = require "tag_utils"

-- ##############################################

local host_alert_store = classes.class(alert_store)

-- ##############################################

function host_alert_store:init(args)
   self.super:init()

   self._table_name = "host_alerts"
   self._alert_entity = alert_entities.host
end

-- ##############################################

function host_alert_store:insert(alert)
   local is_attacker = ternary(alert.is_attacker, 1, 0)
   local is_victim = ternary(alert.is_victim, 1, 0)
   local is_client = ternary(alert.is_client, 1, 0)
   local is_server = ternary(alert.is_server, 1, 0)
   local ip_version = alert.ip_version
   local ip = alert.ip
   local vlan_id = alert.vlan_id

   if not ip then -- Compatibility with Lua alerts
      local host_info = hostkey2hostinfo(alert.entity_val)
      ip = host_info.host
      vlan_id = host_info.vlan
   end

   if not ip_version then
      if isIPv4(ip) then
         ip_version = 4
      else
         ip_version = 6
      end
   end

   local insert_stmt = string.format("INSERT INTO %s "..
      "(alert_id, interface_id, ip_version, ip, vlan_id, name, is_attacker, is_victim, is_client, is_server, tstamp, tstamp_end, severity, score, granularity, json) "..
      "VALUES (%u, %d, %u, '%s', %u, '%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, '%s'); ",
      self._table_name, 
      alert.alert_id,
      self:_convert_ifid(interface.getId()),
      ip_version,
      ip,
      vlan_id or 0,
      self:_escape(alert.name),
      is_attacker,
      is_victim,
      is_client,
      is_server,
      alert.tstamp,
      alert.tstamp_end,
      ntop.mapScoreToSeverity(alert.score),
      alert.score,
      alert.granularity,
      self:_escape(alert.json))

   -- traceError(TRACE_NORMAL, TRACE_CONSOLE, insert_stmt)

   return interface.alert_store_query(insert_stmt)
end

-- ##############################################

--@brief Performs a query for the top hosts by alert count
function host_alert_store:top_ip_historical()
   -- Preserve all the filters currently set
   local where_clause = self:build_where_clause()

   local q
   if ntop.isClickHouseEnabled() then
      q = string.format("SELECT ip, name, vlan_id, count(*) count FROM %s WHERE %s GROUP BY ip, vlan_id, name ORDER BY count DESC LIMIT %u",
         self._table_name, where_clause, self._top_limit)
   else
      q = string.format("SELECT ip, name, vlan_id, count(*) count FROM %s WHERE %s GROUP BY ip ORDER BY count DESC LIMIT %u",
         self._table_name, where_clause, self._top_limit)
   end

   local q_res = interface.alert_store_query(q) or {}

   return q_res
end

-- ##############################################

--@brief Stats used by the dashboard
function host_alert_store:_get_additional_stats()
   local stats = {}
   stats.top = {}
   stats.top.ip = self:top_ip_historical()
   return stats
end

-- ##############################################

--@brief Add ip filter
function host_alert_store:add_ip_filter(ip)
   self:add_filter_condition('ip', 'eq', ip);
end

-- ##############################################

--@brief Add filters according to what is specified inside the REST API
function host_alert_store:_add_additional_request_filters()
   local vlan_id = _GET["vlan_id"]
   local ip_version = _GET["ip_version"]
   local ip = _GET["ip"]
   local name = _GET["name"]
   local role = _GET["role"]
   local role_cli_srv = _GET["role_cli_srv"]

   self:add_filter_condition_list('vlan_id', vlan_id, 'number')
   self:add_filter_condition_list('ip_version', ip_version)
   self:add_filter_condition_list('ip', ip)
   self:add_filter_condition_list('name', name)
   self:add_filter_condition_list('host_role', role)
   self:add_filter_condition_list('role_cli_srv', role_cli_srv)
end

-- ##############################################

--@brief Get info about additional available filters
function host_alert_store:_get_additional_available_filters()
   local filters = {
      ip_version = {
         value_type = 'ip_version',
	 i18n_label = i18n('db_search.tags.ip_version'),
      },
      ip = {
         value_type = 'ip',
	 i18n_label = i18n('db_search.tags.ip'),
      },
      name = {
         value_type = 'hostname',
	 i18n_label = i18n('db_search.tags.name'),
      },
      role = {
	 value_type = 'role',
	 i18n_label = i18n('db_search.tags.role'),
      },
      role_cli_srv = {
	 value_type = 'role_cli_srv',
	 i18n_label = i18n('db_search.tags.role_cli_srv'),
      },
   }

   return filters
end 

-- ##############################################

local RNAME = {
   IP = { name = "ip", export = true},
   IS_VICTIM = { name = "is_victim", export = true},
   IS_ATTACKER = { name = "is_attacker", export = true},
   IS_CLIENT = { name = "is_client", export = true},
   IS_SERVER = { name = "is_server", export = true},
   VLAN_ID = { name = "vlan_id", export = true},
   ALERT_NAME = { name = "alert_name", export = true},
   DESCRIPTION = { name = "description", export = true},
   MSG = { name = "msg", export = true, elements = {"name", "value", "description"}},
   LINK_TO_PAST_FLOWS = { name = "link_to_past_flows", export = false},
}

function host_alert_store:get_rnames()
   return RNAME
end

--@brief Convert an alert coming from the DB (value) to an host_info table
function host_alert_store:_alert2hostinfo(value)
   return {ip = value["ip"], vlan = value["vlan_id"], name = value["name"]}
end

--@brief Convert an alert coming from the DB (value) to a record returned by the REST API
function host_alert_store:format_record(value, no_html)
   local href_icon = "<i class='fas fa-laptop'></i>"
   local record = self:format_json_record_common(value, alert_entities.host.entity_id)

   local alert_info = alert_utils.getAlertInfo(value)
   local alert_name = alert_consts.alertTypeLabel(tonumber(value["alert_id"]), no_html, alert_entities.host.entity_id)
   local alert_fullname = alert_consts.alertTypeLabel(tonumber(value["alert_id"]), true, alert_entities.host.entity_id)
   local msg = alert_utils.formatAlertMessage(ifid, value, alert_info)
   local host = hostinfo2hostkey(value)
   local reference_html = nil

   reference_html = hostinfo2detailshref({ip = value["ip"], vlan = value["vlan_id"]}, nil, href_icon, "", true)
   if reference_html == href_icon then
      reference_html = nil
   end

   record[RNAME.IP.name] = {
      value = host,
      label = host,
      shown_label = host,
      reference = reference_html,
      country = interface.getHostCountry(host)
   }

   -- Long, unshortened label
   local host_label_long = hostinfo2label(self:_alert2hostinfo(value), true --[[ Show VLAN --]], false)

   if no_html then
      record[RNAME.IP.name]["label"] = host_label_long
   else
      local host_label_short = shortenString(host_label_long)
      record[RNAME.IP.name]["label"] = host_label_short
      record[RNAME.IP.name]["label_long"] = host_label_long
   end

   record[RNAME.IS_VICTIM.name] = ""
   record[RNAME.IS_ATTACKER.name] = ""
   record[RNAME.IS_CLIENT.name] = ""
   record[RNAME.IS_SERVER.name] = ""

   if value["is_victim"] == true or value["is_victim"] == "1" then
      if no_html then
         record[RNAME.IS_VICTIM.name] = tostring(true) -- when no_html is enabled a default value must be present
      else
         record[RNAME.IS_VICTIM.name] = '<i class="fas fa-sad-tear"></i>'
         record["role"] = {
            label = i18n("victim"),
            value = "victim",
          }
      end
   elseif no_html then
      record[RNAME.IS_VICTIM.name] = tostring(false) -- when no_html is enabled a default value must be present
   end

   if value["is_attacker"] == true or value["is_attacker"] == "1" then
      if no_html then
         record[RNAME.IS_ATTACKER.name] = tostring(true) -- when no_html is enabled a default value must be present
      else
         record[RNAME.IS_ATTACKER.name] = '<i class="fas fa-skull"></i>'
         record["role"] = {
           label = i18n("attacker"),
           value = "attacker",
         }
      end
   elseif no_html then
      record[RNAME.IS_ATTACKER.name] = tostring(false)  -- when no_html is enabled a default value must be present
   end

   if value["is_client"] == true or value["is_client"] == "1" then
      if no_html then
         record[RNAME.IS_CLIENT.name] = tostring(true) -- when no_html is enabled a default value must be present
      else
         record[RNAME.IS_CLIENT.name] = '<i class="fas fa-long-arrow-alt-right"></i>'
         record["role_cli_srv"] = {
           label = i18n("client"),
           value = "client",
         }
      end
   elseif no_html then
      record[RNAME.IS_CLIENT.name] = tostring(false)  -- when no_html is enabled a default value must be present
   end

   if value["is_server"] == true or value["is_server"] == "1" then
      if no_html then
         record[RNAME.IS_SERVER.name] = tostring(true) -- when no_html is enabled a default value must be present
      else
         record[RNAME.IS_SERVER.name] = '<i class="fas fa-long-arrow-alt-left"></i>'
         record["role_cli_srv"] = {
           label = i18n("server"),
           value = "server",
         }
      end
   elseif no_html then
      record[RNAME.IS_SERVER.name] = tostring(false)  -- when no_html is enabled a default value must be present
   end

   record[RNAME.VLAN_ID.name] = value["vlan_id"] or 0

   record[RNAME.ALERT_NAME.name] = alert_name

   record[RNAME.DESCRIPTION.name] = msg

   if string.lower(noHtml(msg)) == string.lower(noHtml(alert_name)) then
      msg = ""
   end

   if no_html then
      msg = noHtml(msg)
   end

   record[RNAME.MSG.name] = {
     name = noHtml(alert_name),
     fullname = alert_fullname,
     value = tonumber(value["alert_id"]),
     description = msg,
     configset_ref = alert_utils.getConfigsetAlertLink(alert_info, value)
   }

   record[RNAME.LINK_TO_PAST_FLOWS.name] = alert_utils.getLinkToPastFlows(ifid, value, alert_info)

   return record
end

-- ##############################################

return host_alert_store